UNPKG

feature-policy

Version:

Middleware to set the Feature-Policy HTTP header

114 lines (113 loc) 4.95 kB
"use strict"; 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(); }; };