feature-policy
Version:
Middleware to set the Feature-Policy HTTP header
114 lines (113 loc) • 4.95 kB
JavaScript
;
var __spreadArrays = (this && this.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
};
function isPlainObject(value) {
return typeof value === "object" && !Array.isArray(value) && value !== null;
}
function getHeaderValueFromOptions(options) {
var FEATURES = {
accelerometer: "accelerometer",
ambientLightSensor: "ambient-light-sensor",
autoplay: "autoplay",
battery: "battery",
camera: "camera",
displayCapture: "display-capture",
documentDomain: "document-domain",
documentWrite: "document-write",
encryptedMedia: "encrypted-media",
executionWhileNotRendered: "execution-while-not-rendered",
executionWhileOutOfViewport: "execution-while-out-of-viewport",
fontDisplayLateSwap: "font-display-late-swap",
fullscreen: "fullscreen",
geolocation: "geolocation",
gyroscope: "gyroscope",
layoutAnimations: "layout-animations",
legacyImageFormats: "legacy-image-formats",
loadingFrameDefaultEager: "loading-frame-default-eager",
magnetometer: "magnetometer",
microphone: "microphone",
midi: "midi",
navigationOverride: "navigation-override",
notifications: "notifications",
oversizedImages: "oversized-images",
payment: "payment",
pictureInPicture: "picture-in-picture",
publickeyCredentials: "publickey-credentials",
push: "push",
serial: "serial",
speaker: "speaker",
syncScript: "sync-script",
syncXhr: "sync-xhr",
unoptimizedImages: "unoptimized-images",
unoptimizedLosslessImages: "unoptimized-lossless-images",
unoptimizedLossyImages: "unoptimized-lossy-images",
unsizedMedia: "unsized-media",
usb: "usb",
verticalScroll: "vertical-scroll",
vibrate: "vibrate",
vr: "vr",
wakeLock: "wake-lock",
xr: "xr",
xrSpatialTracking: "xr-spatial-tracking",
};
if (!isPlainObject(options)) {
throw new Error("featurePolicy must be called with an object argument. See the documentation.");
}
var features = options.features;
if (!isPlainObject(features)) {
throw new Error('featurePolicy must have a single key, "features", which is an object of features. See the documentation.');
}
var result = Object.entries(features)
.map(function (_a) {
var featureKeyCamelCase = _a[0], featureValue = _a[1];
if (!Object.prototype.hasOwnProperty.call(FEATURES, featureKeyCamelCase)) {
throw new Error("featurePolicy does not support the \"" + featureKeyCamelCase + "\" feature.");
}
if (!Array.isArray(featureValue) || featureValue.length === 0) {
throw new Error("The value of the \"" + featureKeyCamelCase + "\" feature must be a non-empty array of strings.");
}
var allowedValuesSeen = new Set();
featureValue.forEach(function (allowedValue) {
if (typeof allowedValue !== "string") {
throw new Error("The value of the \"" + featureKeyCamelCase + "\" feature contains a non-string, which is not supported.");
}
else if (allowedValuesSeen.has(allowedValue)) {
throw new Error("The value of the \"" + featureKeyCamelCase + "\" feature contains duplicates, which it shouldn't.");
}
else if (allowedValue === "self") {
throw new Error("'self' must be quoted.");
}
else if (allowedValue === "none") {
throw new Error("'none' must be quoted.");
}
allowedValuesSeen.add(allowedValue);
});
if (featureValue.length > 1) {
if (allowedValuesSeen.has("*")) {
throw new Error("The value of the \"" + featureKeyCamelCase + "\" feature cannot contain * and other values.");
}
else if (allowedValuesSeen.has("'none'")) {
throw new Error("The value of the \"" + featureKeyCamelCase + "\" feature cannot contain 'none' and other values.");
}
}
var featureKeyDashed = FEATURES[featureKeyCamelCase];
return __spreadArrays([featureKeyDashed], featureValue).join(" ");
})
.join(";");
if (result.length === 0) {
throw new Error("At least one feature is required.");
}
return result;
}
module.exports = function featurePolicy(options) {
var headerValue = getHeaderValueFromOptions(options);
return function featurePolicy(_req, res, next) {
res.setHeader("Feature-Policy", headerValue);
next();
};
};