update
This commit is contained in:
@@ -0,0 +1,272 @@
|
||||
/* eslint-env browser */
|
||||
|
||||
/**
|
||||
* This is the web browser implementation of `debug()`.
|
||||
*/
|
||||
|
||||
exports.formatArgs = formatArgs;
|
||||
exports.save = save;
|
||||
exports.load = load;
|
||||
exports.useColors = useColors;
|
||||
exports.storage = localstorage();
|
||||
exports.destroy = (() => {
|
||||
let warned = false;
|
||||
|
||||
return () => {
|
||||
if (!warned) {
|
||||
warned = true;
|
||||
console.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.');
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
/**
|
||||
* Colors.
|
||||
*/
|
||||
|
||||
exports.colors = [
|
||||
'#0000CC',
|
||||
'#0000FF',
|
||||
'#0033CC',
|
||||
'#0033FF',
|
||||
'#0066CC',
|
||||
'#0066FF',
|
||||
'#0099CC',
|
||||
'#0099FF',
|
||||
'#00CC00',
|
||||
'#00CC33',
|
||||
'#00CC66',
|
||||
'#00CC99',
|
||||
'#00CCCC',
|
||||
'#00CCFF',
|
||||
'#3300CC',
|
||||
'#3300FF',
|
||||
'#3333CC',
|
||||
'#3333FF',
|
||||
'#3366CC',
|
||||
'#3366FF',
|
||||
'#3399CC',
|
||||
'#3399FF',
|
||||
'#33CC00',
|
||||
'#33CC33',
|
||||
'#33CC66',
|
||||
'#33CC99',
|
||||
'#33CCCC',
|
||||
'#33CCFF',
|
||||
'#6600CC',
|
||||
'#6600FF',
|
||||
'#6633CC',
|
||||
'#6633FF',
|
||||
'#66CC00',
|
||||
'#66CC33',
|
||||
'#9900CC',
|
||||
'#9900FF',
|
||||
'#9933CC',
|
||||
'#9933FF',
|
||||
'#99CC00',
|
||||
'#99CC33',
|
||||
'#CC0000',
|
||||
'#CC0033',
|
||||
'#CC0066',
|
||||
'#CC0099',
|
||||
'#CC00CC',
|
||||
'#CC00FF',
|
||||
'#CC3300',
|
||||
'#CC3333',
|
||||
'#CC3366',
|
||||
'#CC3399',
|
||||
'#CC33CC',
|
||||
'#CC33FF',
|
||||
'#CC6600',
|
||||
'#CC6633',
|
||||
'#CC9900',
|
||||
'#CC9933',
|
||||
'#CCCC00',
|
||||
'#CCCC33',
|
||||
'#FF0000',
|
||||
'#FF0033',
|
||||
'#FF0066',
|
||||
'#FF0099',
|
||||
'#FF00CC',
|
||||
'#FF00FF',
|
||||
'#FF3300',
|
||||
'#FF3333',
|
||||
'#FF3366',
|
||||
'#FF3399',
|
||||
'#FF33CC',
|
||||
'#FF33FF',
|
||||
'#FF6600',
|
||||
'#FF6633',
|
||||
'#FF9900',
|
||||
'#FF9933',
|
||||
'#FFCC00',
|
||||
'#FFCC33'
|
||||
];
|
||||
|
||||
/**
|
||||
* Currently only WebKit-based Web Inspectors, Firefox >= v31,
|
||||
* and the Firebug extension (any Firefox version) are known
|
||||
* to support "%c" CSS customizations.
|
||||
*
|
||||
* TODO: add a `localStorage` variable to explicitly enable/disable colors
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line complexity
|
||||
function useColors() {
|
||||
// NB: In an Electron preload script, document will be defined but not fully
|
||||
// initialized. Since we know we're in Chrome, we'll just detect this case
|
||||
// explicitly
|
||||
if (typeof window !== 'undefined' && window.process && (window.process.type === 'renderer' || window.process.__nwjs)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Internet Explorer and Edge do not support colors.
|
||||
if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let m;
|
||||
|
||||
// Is webkit? http://stackoverflow.com/a/16459606/376773
|
||||
// document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
|
||||
// eslint-disable-next-line no-return-assign
|
||||
return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||
|
||||
// Is firebug? http://stackoverflow.com/a/398120/376773
|
||||
(typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||
|
||||
// Is firefox >= v31?
|
||||
// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
|
||||
(typeof navigator !== 'undefined' && navigator.userAgent && (m = navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)) && parseInt(m[1], 10) >= 31) ||
|
||||
// Double check webkit in userAgent just in case we are in a worker
|
||||
(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
|
||||
}
|
||||
|
||||
/**
|
||||
* Colorize log arguments if enabled.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function formatArgs(args) {
|
||||
args[0] = (this.useColors ? '%c' : '') +
|
||||
this.namespace +
|
||||
(this.useColors ? ' %c' : ' ') +
|
||||
args[0] +
|
||||
(this.useColors ? '%c ' : ' ') +
|
||||
'+' + module.exports.humanize(this.diff);
|
||||
|
||||
if (!this.useColors) {
|
||||
return;
|
||||
}
|
||||
|
||||
const c = 'color: ' + this.color;
|
||||
args.splice(1, 0, c, 'color: inherit');
|
||||
|
||||
// The final "%c" is somewhat tricky, because there could be other
|
||||
// arguments passed either before or after the %c, so we need to
|
||||
// figure out the correct index to insert the CSS into
|
||||
let index = 0;
|
||||
let lastC = 0;
|
||||
args[0].replace(/%[a-zA-Z%]/g, match => {
|
||||
if (match === '%%') {
|
||||
return;
|
||||
}
|
||||
index++;
|
||||
if (match === '%c') {
|
||||
// We only are interested in the *last* %c
|
||||
// (the user may have provided their own)
|
||||
lastC = index;
|
||||
}
|
||||
});
|
||||
|
||||
args.splice(lastC, 0, c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes `console.debug()` when available.
|
||||
* No-op when `console.debug` is not a "function".
|
||||
* If `console.debug` is not available, falls back
|
||||
* to `console.log`.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
exports.log = console.debug || console.log || (() => {});
|
||||
|
||||
/**
|
||||
* Save `namespaces`.
|
||||
*
|
||||
* @param {String} namespaces
|
||||
* @api private
|
||||
*/
|
||||
function save(namespaces) {
|
||||
try {
|
||||
if (namespaces) {
|
||||
exports.storage.setItem('debug', namespaces);
|
||||
} else {
|
||||
exports.storage.removeItem('debug');
|
||||
}
|
||||
} catch (error) {
|
||||
// Swallow
|
||||
// XXX (@Qix-) should we be logging these?
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load `namespaces`.
|
||||
*
|
||||
* @return {String} returns the previously persisted debug modes
|
||||
* @api private
|
||||
*/
|
||||
function load() {
|
||||
let r;
|
||||
try {
|
||||
r = exports.storage.getItem('debug') || exports.storage.getItem('DEBUG') ;
|
||||
} catch (error) {
|
||||
// Swallow
|
||||
// XXX (@Qix-) should we be logging these?
|
||||
}
|
||||
|
||||
// If debug isn't set in LS, and we're in Electron, try to load $DEBUG
|
||||
if (!r && typeof process !== 'undefined' && 'env' in process) {
|
||||
r = process.env.DEBUG;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Localstorage attempts to return the localstorage.
|
||||
*
|
||||
* This is necessary because safari throws
|
||||
* when a user disables cookies/localstorage
|
||||
* and you attempt to access it.
|
||||
*
|
||||
* @return {LocalStorage}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function localstorage() {
|
||||
try {
|
||||
// TVMLKit (Apple TV JS Runtime) does not have a window object, just localStorage in the global context
|
||||
// The Browser also has localStorage in the global context.
|
||||
return localStorage;
|
||||
} catch (error) {
|
||||
// Swallow
|
||||
// XXX (@Qix-) should we be logging these?
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = require('./common')(exports);
|
||||
|
||||
const {formatters} = module.exports;
|
||||
|
||||
/**
|
||||
* Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
|
||||
*/
|
||||
|
||||
formatters.j = function (v) {
|
||||
try {
|
||||
return JSON.stringify(v);
|
||||
} catch (error) {
|
||||
return '[UnexpectedJSONParseError]: ' + error.message;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* Encode a string into another string.
|
||||
*/
|
||||
export type Encode = (value: string) => string;
|
||||
/**
|
||||
* Decode a string into another string.
|
||||
*/
|
||||
export type Decode = (value: string) => string;
|
||||
export interface ParseOptions {
|
||||
/**
|
||||
* A function for encoding input strings.
|
||||
*/
|
||||
encodePath?: Encode;
|
||||
}
|
||||
export interface PathToRegexpOptions {
|
||||
/**
|
||||
* Matches the path completely without trailing characters. (default: `true`)
|
||||
*/
|
||||
end?: boolean;
|
||||
/**
|
||||
* Allows optional trailing delimiter to match. (default: `true`)
|
||||
*/
|
||||
trailing?: boolean;
|
||||
/**
|
||||
* Match will be case sensitive. (default: `false`)
|
||||
*/
|
||||
sensitive?: boolean;
|
||||
/**
|
||||
* The default delimiter for segments. (default: `'/'`)
|
||||
*/
|
||||
delimiter?: string;
|
||||
}
|
||||
export interface MatchOptions extends PathToRegexpOptions {
|
||||
/**
|
||||
* Function for decoding strings for params, or `false` to disable entirely. (default: `decodeURIComponent`)
|
||||
*/
|
||||
decode?: Decode | false;
|
||||
}
|
||||
export interface CompileOptions {
|
||||
/**
|
||||
* Function for encoding input strings for output into the path, or `false` to disable entirely. (default: `encodeURIComponent`)
|
||||
*/
|
||||
encode?: Encode | false;
|
||||
/**
|
||||
* The default delimiter for segments. (default: `'/'`)
|
||||
*/
|
||||
delimiter?: string;
|
||||
}
|
||||
/**
|
||||
* Plain text.
|
||||
*/
|
||||
export interface Text {
|
||||
type: "text";
|
||||
value: string;
|
||||
}
|
||||
/**
|
||||
* A parameter designed to match arbitrary text within a segment.
|
||||
*/
|
||||
export interface Parameter {
|
||||
type: "param";
|
||||
name: string;
|
||||
}
|
||||
/**
|
||||
* A wildcard parameter designed to match multiple segments.
|
||||
*/
|
||||
export interface Wildcard {
|
||||
type: "wildcard";
|
||||
name: string;
|
||||
}
|
||||
/**
|
||||
* A set of possible tokens to expand when matching.
|
||||
*/
|
||||
export interface Group {
|
||||
type: "group";
|
||||
tokens: Token[];
|
||||
}
|
||||
/**
|
||||
* A token that corresponds with a regexp capture.
|
||||
*/
|
||||
export type Key = Parameter | Wildcard;
|
||||
/**
|
||||
* A sequence of `path-to-regexp` keys that match capturing groups.
|
||||
*/
|
||||
export type Keys = Array<Key>;
|
||||
/**
|
||||
* A sequence of path match characters.
|
||||
*/
|
||||
export type Token = Text | Parameter | Wildcard | Group;
|
||||
/**
|
||||
* Tokenized path instance.
|
||||
*/
|
||||
export declare class TokenData {
|
||||
readonly tokens: Token[];
|
||||
constructor(tokens: Token[]);
|
||||
}
|
||||
/**
|
||||
* Parse a string for the raw tokens.
|
||||
*/
|
||||
export declare function parse(str: string, options?: ParseOptions): TokenData;
|
||||
/**
|
||||
* Compile a string to a template function for the path.
|
||||
*/
|
||||
export declare function compile<P extends ParamData = ParamData>(path: Path, options?: CompileOptions & ParseOptions): (data?: P) => string;
|
||||
export type ParamData = Partial<Record<string, string | string[]>>;
|
||||
export type PathFunction<P extends ParamData> = (data?: P) => string;
|
||||
/**
|
||||
* A match result contains data about the path match.
|
||||
*/
|
||||
export interface MatchResult<P extends ParamData> {
|
||||
path: string;
|
||||
params: P;
|
||||
}
|
||||
/**
|
||||
* A match is either `false` (no match) or a match result.
|
||||
*/
|
||||
export type Match<P extends ParamData> = false | MatchResult<P>;
|
||||
/**
|
||||
* The match function takes a string and returns whether it matched the path.
|
||||
*/
|
||||
export type MatchFunction<P extends ParamData> = (path: string) => Match<P>;
|
||||
/**
|
||||
* Supported path types.
|
||||
*/
|
||||
export type Path = string | TokenData;
|
||||
/**
|
||||
* Transform a path into a match function.
|
||||
*/
|
||||
export declare function match<P extends ParamData>(path: Path | Path[], options?: MatchOptions & ParseOptions): MatchFunction<P>;
|
||||
export declare function pathToRegexp(path: Path | Path[], options?: PathToRegexpOptions & ParseOptions): {
|
||||
regexp: RegExp;
|
||||
keys: Keys;
|
||||
};
|
||||
/**
|
||||
* Stringify token data into a path string.
|
||||
*/
|
||||
export declare function stringify(data: TokenData): string;
|
||||
@@ -0,0 +1,82 @@
|
||||
'use strict'
|
||||
|
||||
const SemVer = require('../classes/semver')
|
||||
const Comparator = require('../classes/comparator')
|
||||
const { ANY } = Comparator
|
||||
const Range = require('../classes/range')
|
||||
const satisfies = require('../functions/satisfies')
|
||||
const gt = require('../functions/gt')
|
||||
const lt = require('../functions/lt')
|
||||
const lte = require('../functions/lte')
|
||||
const gte = require('../functions/gte')
|
||||
|
||||
const outside = (version, range, hilo, options) => {
|
||||
version = new SemVer(version, options)
|
||||
range = new Range(range, options)
|
||||
|
||||
let gtfn, ltefn, ltfn, comp, ecomp
|
||||
switch (hilo) {
|
||||
case '>':
|
||||
gtfn = gt
|
||||
ltefn = lte
|
||||
ltfn = lt
|
||||
comp = '>'
|
||||
ecomp = '>='
|
||||
break
|
||||
case '<':
|
||||
gtfn = lt
|
||||
ltefn = gte
|
||||
ltfn = gt
|
||||
comp = '<'
|
||||
ecomp = '<='
|
||||
break
|
||||
default:
|
||||
throw new TypeError('Must provide a hilo val of "<" or ">"')
|
||||
}
|
||||
|
||||
// If it satisfies the range it is not outside
|
||||
if (satisfies(version, range, options)) {
|
||||
return false
|
||||
}
|
||||
|
||||
// From now on, variable terms are as if we're in "gtr" mode.
|
||||
// but note that everything is flipped for the "ltr" function.
|
||||
|
||||
for (let i = 0; i < range.set.length; ++i) {
|
||||
const comparators = range.set[i]
|
||||
|
||||
let high = null
|
||||
let low = null
|
||||
|
||||
comparators.forEach((comparator) => {
|
||||
if (comparator.semver === ANY) {
|
||||
comparator = new Comparator('>=0.0.0')
|
||||
}
|
||||
high = high || comparator
|
||||
low = low || comparator
|
||||
if (gtfn(comparator.semver, high.semver, options)) {
|
||||
high = comparator
|
||||
} else if (ltfn(comparator.semver, low.semver, options)) {
|
||||
low = comparator
|
||||
}
|
||||
})
|
||||
|
||||
// If the edge version comparator has a operator then our version
|
||||
// isn't outside it
|
||||
if (high.operator === comp || high.operator === ecomp) {
|
||||
return false
|
||||
}
|
||||
|
||||
// If the lowest version comparator has an operator and our version
|
||||
// is less than it then it isn't higher than the range
|
||||
if ((!low.operator || low.operator === comp) &&
|
||||
ltefn(version, low.semver)) {
|
||||
return false
|
||||
} else if (low.operator === ecomp && ltfn(version, low.semver)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
module.exports = outside
|
||||
@@ -0,0 +1,4 @@
|
||||
'use strict';
|
||||
|
||||
/** @type {import('./eval')} */
|
||||
module.exports = EvalError;
|
||||
@@ -0,0 +1,588 @@
|
||||
"use strict"
|
||||
|
||||
Object.defineProperties(exports, {__esModule: {value: true}, [Symbol.toStringTag]: {value: "Module"}})
|
||||
|
||||
const dangerouslyDisableDefaultSrc = Symbol("dangerouslyDisableDefaultSrc")
|
||||
const SHOULD_BE_QUOTED = new Set(["none", "self", "strict-dynamic", "report-sample", "inline-speculation-rules", "unsafe-inline", "unsafe-eval", "unsafe-hashes", "wasm-unsafe-eval"])
|
||||
const getDefaultDirectives = () => ({
|
||||
"default-src": ["'self'"],
|
||||
"base-uri": ["'self'"],
|
||||
"font-src": ["'self'", "https:", "data:"],
|
||||
"form-action": ["'self'"],
|
||||
"frame-ancestors": ["'self'"],
|
||||
"img-src": ["'self'", "data:"],
|
||||
"object-src": ["'none'"],
|
||||
"script-src": ["'self'"],
|
||||
"script-src-attr": ["'none'"],
|
||||
"style-src": ["'self'", "https:", "'unsafe-inline'"],
|
||||
"upgrade-insecure-requests": []
|
||||
})
|
||||
const dashify = str => str.replace(/[A-Z]/g, capitalLetter => "-" + capitalLetter.toLowerCase())
|
||||
const assertDirectiveValueIsValid = (directiveName, directiveValue) => {
|
||||
if (/;|,/.test(directiveValue)) {
|
||||
throw new Error(`Content-Security-Policy received an invalid directive value for ${JSON.stringify(directiveName)}`)
|
||||
}
|
||||
}
|
||||
const assertDirectiveValueEntryIsValid = (directiveName, directiveValueEntry) => {
|
||||
if (SHOULD_BE_QUOTED.has(directiveValueEntry) || directiveValueEntry.startsWith("nonce-") || directiveValueEntry.startsWith("sha256-") || directiveValueEntry.startsWith("sha384-") || directiveValueEntry.startsWith("sha512-")) {
|
||||
throw new Error(`Content-Security-Policy received an invalid directive value for ${JSON.stringify(directiveName)}. ${JSON.stringify(directiveValueEntry)} should be quoted`)
|
||||
}
|
||||
}
|
||||
function normalizeDirectives(options) {
|
||||
const defaultDirectives = getDefaultDirectives()
|
||||
const {useDefaults = true, directives: rawDirectives = defaultDirectives} = options
|
||||
const result = new Map()
|
||||
const directiveNamesSeen = new Set()
|
||||
const directivesExplicitlyDisabled = new Set()
|
||||
for (const rawDirectiveName in rawDirectives) {
|
||||
if (!Object.hasOwn(rawDirectives, rawDirectiveName)) {
|
||||
continue
|
||||
}
|
||||
if (rawDirectiveName.length === 0 || /[^a-zA-Z0-9-]/.test(rawDirectiveName)) {
|
||||
throw new Error(`Content-Security-Policy received an invalid directive name ${JSON.stringify(rawDirectiveName)}`)
|
||||
}
|
||||
const directiveName = dashify(rawDirectiveName)
|
||||
if (directiveNamesSeen.has(directiveName)) {
|
||||
throw new Error(`Content-Security-Policy received a duplicate directive ${JSON.stringify(directiveName)}`)
|
||||
}
|
||||
directiveNamesSeen.add(directiveName)
|
||||
const rawDirectiveValue = rawDirectives[rawDirectiveName]
|
||||
let directiveValue
|
||||
if (rawDirectiveValue === null) {
|
||||
if (directiveName === "default-src") {
|
||||
throw new Error("Content-Security-Policy needs a default-src but it was set to `null`. If you really want to disable it, set it to `contentSecurityPolicy.dangerouslyDisableDefaultSrc`.")
|
||||
}
|
||||
directivesExplicitlyDisabled.add(directiveName)
|
||||
continue
|
||||
} else if (typeof rawDirectiveValue === "string") {
|
||||
directiveValue = [rawDirectiveValue]
|
||||
} else if (!rawDirectiveValue) {
|
||||
throw new Error(`Content-Security-Policy received an invalid directive value for ${JSON.stringify(directiveName)}`)
|
||||
} else if (rawDirectiveValue === dangerouslyDisableDefaultSrc) {
|
||||
if (directiveName === "default-src") {
|
||||
directivesExplicitlyDisabled.add("default-src")
|
||||
continue
|
||||
} else {
|
||||
throw new Error(`Content-Security-Policy: tried to disable ${JSON.stringify(directiveName)} as if it were default-src; simply omit the key`)
|
||||
}
|
||||
} else {
|
||||
directiveValue = rawDirectiveValue
|
||||
}
|
||||
for (const element of directiveValue) {
|
||||
if (typeof element !== "string") continue
|
||||
assertDirectiveValueIsValid(directiveName, element)
|
||||
assertDirectiveValueEntryIsValid(directiveName, element)
|
||||
}
|
||||
result.set(directiveName, directiveValue)
|
||||
}
|
||||
if (useDefaults) {
|
||||
Object.entries(defaultDirectives).forEach(([defaultDirectiveName, defaultDirectiveValue]) => {
|
||||
if (!result.has(defaultDirectiveName) && !directivesExplicitlyDisabled.has(defaultDirectiveName)) {
|
||||
result.set(defaultDirectiveName, defaultDirectiveValue)
|
||||
}
|
||||
})
|
||||
}
|
||||
if (!result.size) {
|
||||
throw new Error("Content-Security-Policy has no directives. Either set some or disable the header")
|
||||
}
|
||||
if (!result.has("default-src") && !directivesExplicitlyDisabled.has("default-src")) {
|
||||
throw new Error("Content-Security-Policy needs a default-src but none was provided. If you really want to disable it, set it to `contentSecurityPolicy.dangerouslyDisableDefaultSrc`.")
|
||||
}
|
||||
return result
|
||||
}
|
||||
function getHeaderValue(req, res, normalizedDirectives) {
|
||||
const result = []
|
||||
for (const [directiveName, rawDirectiveValue] of normalizedDirectives) {
|
||||
let directiveValue = ""
|
||||
for (const element of rawDirectiveValue) {
|
||||
if (typeof element === "function") {
|
||||
const newElement = element(req, res)
|
||||
assertDirectiveValueEntryIsValid(directiveName, newElement)
|
||||
directiveValue += " " + newElement
|
||||
} else {
|
||||
directiveValue += " " + element
|
||||
}
|
||||
}
|
||||
if (directiveValue) {
|
||||
assertDirectiveValueIsValid(directiveName, directiveValue)
|
||||
result.push(`${directiveName}${directiveValue}`)
|
||||
} else {
|
||||
result.push(directiveName)
|
||||
}
|
||||
}
|
||||
return result.join(";")
|
||||
}
|
||||
const contentSecurityPolicy = function contentSecurityPolicy(options = {}) {
|
||||
const headerName = options.reportOnly ? "Content-Security-Policy-Report-Only" : "Content-Security-Policy"
|
||||
const normalizedDirectives = normalizeDirectives(options)
|
||||
return function contentSecurityPolicyMiddleware(req, res, next) {
|
||||
const result = getHeaderValue(req, res, normalizedDirectives)
|
||||
if (result instanceof Error) {
|
||||
next(result)
|
||||
} else {
|
||||
res.setHeader(headerName, result)
|
||||
next()
|
||||
}
|
||||
}
|
||||
}
|
||||
contentSecurityPolicy.getDefaultDirectives = getDefaultDirectives
|
||||
contentSecurityPolicy.dangerouslyDisableDefaultSrc = dangerouslyDisableDefaultSrc
|
||||
|
||||
const ALLOWED_POLICIES$2 = new Set(["require-corp", "credentialless", "unsafe-none"])
|
||||
function getHeaderValueFromOptions$6({policy = "require-corp"}) {
|
||||
if (ALLOWED_POLICIES$2.has(policy)) {
|
||||
return policy
|
||||
} else {
|
||||
throw new Error(`Cross-Origin-Embedder-Policy does not support the ${JSON.stringify(policy)} policy`)
|
||||
}
|
||||
}
|
||||
function crossOriginEmbedderPolicy(options = {}) {
|
||||
const headerValue = getHeaderValueFromOptions$6(options)
|
||||
return function crossOriginEmbedderPolicyMiddleware(_req, res, next) {
|
||||
res.setHeader("Cross-Origin-Embedder-Policy", headerValue)
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
const ALLOWED_POLICIES$1 = new Set(["same-origin", "same-origin-allow-popups", "unsafe-none"])
|
||||
function getHeaderValueFromOptions$5({policy = "same-origin"}) {
|
||||
if (ALLOWED_POLICIES$1.has(policy)) {
|
||||
return policy
|
||||
} else {
|
||||
throw new Error(`Cross-Origin-Opener-Policy does not support the ${JSON.stringify(policy)} policy`)
|
||||
}
|
||||
}
|
||||
function crossOriginOpenerPolicy(options = {}) {
|
||||
const headerValue = getHeaderValueFromOptions$5(options)
|
||||
return function crossOriginOpenerPolicyMiddleware(_req, res, next) {
|
||||
res.setHeader("Cross-Origin-Opener-Policy", headerValue)
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
const ALLOWED_POLICIES = new Set(["same-origin", "same-site", "cross-origin"])
|
||||
function getHeaderValueFromOptions$4({policy = "same-origin"}) {
|
||||
if (ALLOWED_POLICIES.has(policy)) {
|
||||
return policy
|
||||
} else {
|
||||
throw new Error(`Cross-Origin-Resource-Policy does not support the ${JSON.stringify(policy)} policy`)
|
||||
}
|
||||
}
|
||||
function crossOriginResourcePolicy(options = {}) {
|
||||
const headerValue = getHeaderValueFromOptions$4(options)
|
||||
return function crossOriginResourcePolicyMiddleware(_req, res, next) {
|
||||
res.setHeader("Cross-Origin-Resource-Policy", headerValue)
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
function originAgentCluster() {
|
||||
return function originAgentClusterMiddleware(_req, res, next) {
|
||||
res.setHeader("Origin-Agent-Cluster", "?1")
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
const ALLOWED_TOKENS = new Set(["no-referrer", "no-referrer-when-downgrade", "same-origin", "origin", "strict-origin", "origin-when-cross-origin", "strict-origin-when-cross-origin", "unsafe-url", ""])
|
||||
function getHeaderValueFromOptions$3({policy = ["no-referrer"]}) {
|
||||
const tokens = typeof policy === "string" ? [policy] : policy
|
||||
if (tokens.length === 0) {
|
||||
throw new Error("Referrer-Policy received no policy tokens")
|
||||
}
|
||||
const tokensSeen = new Set()
|
||||
tokens.forEach(token => {
|
||||
if (!ALLOWED_TOKENS.has(token)) {
|
||||
throw new Error(`Referrer-Policy received an unexpected policy token ${JSON.stringify(token)}`)
|
||||
} else if (tokensSeen.has(token)) {
|
||||
throw new Error(`Referrer-Policy received a duplicate policy token ${JSON.stringify(token)}`)
|
||||
}
|
||||
tokensSeen.add(token)
|
||||
})
|
||||
return tokens.join(",")
|
||||
}
|
||||
function referrerPolicy(options = {}) {
|
||||
const headerValue = getHeaderValueFromOptions$3(options)
|
||||
return function referrerPolicyMiddleware(_req, res, next) {
|
||||
res.setHeader("Referrer-Policy", headerValue)
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_MAX_AGE = 365 * 24 * 60 * 60
|
||||
function parseMaxAge(value = DEFAULT_MAX_AGE) {
|
||||
if (value >= 0 && Number.isFinite(value)) {
|
||||
return Math.floor(value)
|
||||
} else {
|
||||
throw new Error(`Strict-Transport-Security: ${JSON.stringify(value)} is not a valid value for maxAge. Please choose a positive integer.`)
|
||||
}
|
||||
}
|
||||
function getHeaderValueFromOptions$2(options) {
|
||||
if ("maxage" in options) {
|
||||
throw new Error("Strict-Transport-Security received an unsupported property, `maxage`. Did you mean to pass `maxAge`?")
|
||||
}
|
||||
if ("includeSubdomains" in options) {
|
||||
throw new Error('Strict-Transport-Security middleware should use `includeSubDomains` instead of `includeSubdomains`. (The correct one has an uppercase "D".)')
|
||||
}
|
||||
const directives = [`max-age=${parseMaxAge(options.maxAge)}`]
|
||||
if (options.includeSubDomains === undefined || options.includeSubDomains) {
|
||||
directives.push("includeSubDomains")
|
||||
}
|
||||
if (options.preload) {
|
||||
directives.push("preload")
|
||||
}
|
||||
return directives.join("; ")
|
||||
}
|
||||
function strictTransportSecurity(options = {}) {
|
||||
const headerValue = getHeaderValueFromOptions$2(options)
|
||||
return function strictTransportSecurityMiddleware(_req, res, next) {
|
||||
res.setHeader("Strict-Transport-Security", headerValue)
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
function xContentTypeOptions() {
|
||||
return function xContentTypeOptionsMiddleware(_req, res, next) {
|
||||
res.setHeader("X-Content-Type-Options", "nosniff")
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
function xDnsPrefetchControl(options = {}) {
|
||||
const headerValue = options.allow ? "on" : "off"
|
||||
return function xDnsPrefetchControlMiddleware(_req, res, next) {
|
||||
res.setHeader("X-DNS-Prefetch-Control", headerValue)
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
function xDownloadOptions() {
|
||||
return function xDownloadOptionsMiddleware(_req, res, next) {
|
||||
res.setHeader("X-Download-Options", "noopen")
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
function getHeaderValueFromOptions$1({action = "sameorigin"}) {
|
||||
const normalizedAction = typeof action === "string" ? action.toUpperCase() : action
|
||||
switch (normalizedAction) {
|
||||
case "SAME-ORIGIN":
|
||||
return "SAMEORIGIN"
|
||||
case "DENY":
|
||||
case "SAMEORIGIN":
|
||||
return normalizedAction
|
||||
default:
|
||||
throw new Error(`X-Frame-Options received an invalid action ${JSON.stringify(action)}`)
|
||||
}
|
||||
}
|
||||
function xFrameOptions(options = {}) {
|
||||
const headerValue = getHeaderValueFromOptions$1(options)
|
||||
return function xFrameOptionsMiddleware(_req, res, next) {
|
||||
res.setHeader("X-Frame-Options", headerValue)
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
const ALLOWED_PERMITTED_POLICIES = new Set(["none", "master-only", "by-content-type", "all"])
|
||||
function getHeaderValueFromOptions({permittedPolicies = "none"}) {
|
||||
if (ALLOWED_PERMITTED_POLICIES.has(permittedPolicies)) {
|
||||
return permittedPolicies
|
||||
} else {
|
||||
throw new Error(`X-Permitted-Cross-Domain-Policies does not support ${JSON.stringify(permittedPolicies)}`)
|
||||
}
|
||||
}
|
||||
function xPermittedCrossDomainPolicies(options = {}) {
|
||||
const headerValue = getHeaderValueFromOptions(options)
|
||||
return function xPermittedCrossDomainPoliciesMiddleware(_req, res, next) {
|
||||
res.setHeader("X-Permitted-Cross-Domain-Policies", headerValue)
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
function xPoweredBy() {
|
||||
return function xPoweredByMiddleware(_req, res, next) {
|
||||
res.removeHeader("X-Powered-By")
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
function xXssProtection() {
|
||||
return function xXssProtectionMiddleware(_req, res, next) {
|
||||
res.setHeader("X-XSS-Protection", "0")
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
function getMiddlewareFunctionsFromOptions(options) {
|
||||
const result = []
|
||||
switch (options.contentSecurityPolicy) {
|
||||
case undefined:
|
||||
case true:
|
||||
result.push(contentSecurityPolicy())
|
||||
break
|
||||
case false:
|
||||
break
|
||||
default:
|
||||
result.push(contentSecurityPolicy(options.contentSecurityPolicy))
|
||||
break
|
||||
}
|
||||
switch (options.crossOriginEmbedderPolicy) {
|
||||
case undefined:
|
||||
case false:
|
||||
break
|
||||
case true:
|
||||
result.push(crossOriginEmbedderPolicy())
|
||||
break
|
||||
default:
|
||||
result.push(crossOriginEmbedderPolicy(options.crossOriginEmbedderPolicy))
|
||||
break
|
||||
}
|
||||
switch (options.crossOriginOpenerPolicy) {
|
||||
case undefined:
|
||||
case true:
|
||||
result.push(crossOriginOpenerPolicy())
|
||||
break
|
||||
case false:
|
||||
break
|
||||
default:
|
||||
result.push(crossOriginOpenerPolicy(options.crossOriginOpenerPolicy))
|
||||
break
|
||||
}
|
||||
switch (options.crossOriginResourcePolicy) {
|
||||
case undefined:
|
||||
case true:
|
||||
result.push(crossOriginResourcePolicy())
|
||||
break
|
||||
case false:
|
||||
break
|
||||
default:
|
||||
result.push(crossOriginResourcePolicy(options.crossOriginResourcePolicy))
|
||||
break
|
||||
}
|
||||
switch (options.originAgentCluster) {
|
||||
case undefined:
|
||||
case true:
|
||||
result.push(originAgentCluster())
|
||||
break
|
||||
case false:
|
||||
break
|
||||
default:
|
||||
console.warn("Origin-Agent-Cluster does not take options. Remove the property to silence this warning.")
|
||||
result.push(originAgentCluster())
|
||||
break
|
||||
}
|
||||
switch (options.referrerPolicy) {
|
||||
case undefined:
|
||||
case true:
|
||||
result.push(referrerPolicy())
|
||||
break
|
||||
case false:
|
||||
break
|
||||
default:
|
||||
result.push(referrerPolicy(options.referrerPolicy))
|
||||
break
|
||||
}
|
||||
if ("strictTransportSecurity" in options && "hsts" in options) {
|
||||
throw new Error("Strict-Transport-Security option was specified twice. Remove `hsts` to silence this warning.")
|
||||
}
|
||||
const strictTransportSecurityOption = options.strictTransportSecurity ?? options.hsts
|
||||
switch (strictTransportSecurityOption) {
|
||||
case undefined:
|
||||
case true:
|
||||
result.push(strictTransportSecurity())
|
||||
break
|
||||
case false:
|
||||
break
|
||||
default:
|
||||
result.push(strictTransportSecurity(strictTransportSecurityOption))
|
||||
break
|
||||
}
|
||||
if ("xContentTypeOptions" in options && "noSniff" in options) {
|
||||
throw new Error("X-Content-Type-Options option was specified twice. Remove `noSniff` to silence this warning.")
|
||||
}
|
||||
const xContentTypeOptionsOption = options.xContentTypeOptions ?? options.noSniff
|
||||
switch (xContentTypeOptionsOption) {
|
||||
case undefined:
|
||||
case true:
|
||||
result.push(xContentTypeOptions())
|
||||
break
|
||||
case false:
|
||||
break
|
||||
default:
|
||||
console.warn("X-Content-Type-Options does not take options. Remove the property to silence this warning.")
|
||||
result.push(xContentTypeOptions())
|
||||
break
|
||||
}
|
||||
if ("xDnsPrefetchControl" in options && "dnsPrefetchControl" in options) {
|
||||
throw new Error("X-DNS-Prefetch-Control option was specified twice. Remove `dnsPrefetchControl` to silence this warning.")
|
||||
}
|
||||
const xDnsPrefetchControlOption = options.xDnsPrefetchControl ?? options.dnsPrefetchControl
|
||||
switch (xDnsPrefetchControlOption) {
|
||||
case undefined:
|
||||
case true:
|
||||
result.push(xDnsPrefetchControl())
|
||||
break
|
||||
case false:
|
||||
break
|
||||
default:
|
||||
result.push(xDnsPrefetchControl(xDnsPrefetchControlOption))
|
||||
break
|
||||
}
|
||||
if ("xDownloadOptions" in options && "ieNoOpen" in options) {
|
||||
throw new Error("X-Download-Options option was specified twice. Remove `ieNoOpen` to silence this warning.")
|
||||
}
|
||||
const xDownloadOptionsOption = options.xDownloadOptions ?? options.ieNoOpen
|
||||
switch (xDownloadOptionsOption) {
|
||||
case undefined:
|
||||
case true:
|
||||
result.push(xDownloadOptions())
|
||||
break
|
||||
case false:
|
||||
break
|
||||
default:
|
||||
console.warn("X-Download-Options does not take options. Remove the property to silence this warning.")
|
||||
result.push(xDownloadOptions())
|
||||
break
|
||||
}
|
||||
if ("xFrameOptions" in options && "frameguard" in options) {
|
||||
throw new Error("X-Frame-Options option was specified twice. Remove `frameguard` to silence this warning.")
|
||||
}
|
||||
const xFrameOptionsOption = options.xFrameOptions ?? options.frameguard
|
||||
switch (xFrameOptionsOption) {
|
||||
case undefined:
|
||||
case true:
|
||||
result.push(xFrameOptions())
|
||||
break
|
||||
case false:
|
||||
break
|
||||
default:
|
||||
result.push(xFrameOptions(xFrameOptionsOption))
|
||||
break
|
||||
}
|
||||
if ("xPermittedCrossDomainPolicies" in options && "permittedCrossDomainPolicies" in options) {
|
||||
throw new Error("X-Permitted-Cross-Domain-Policies option was specified twice. Remove `permittedCrossDomainPolicies` to silence this warning.")
|
||||
}
|
||||
const xPermittedCrossDomainPoliciesOption = options.xPermittedCrossDomainPolicies ?? options.permittedCrossDomainPolicies
|
||||
switch (xPermittedCrossDomainPoliciesOption) {
|
||||
case undefined:
|
||||
case true:
|
||||
result.push(xPermittedCrossDomainPolicies())
|
||||
break
|
||||
case false:
|
||||
break
|
||||
default:
|
||||
result.push(xPermittedCrossDomainPolicies(xPermittedCrossDomainPoliciesOption))
|
||||
break
|
||||
}
|
||||
if ("xPoweredBy" in options && "hidePoweredBy" in options) {
|
||||
throw new Error("X-Powered-By option was specified twice. Remove `hidePoweredBy` to silence this warning.")
|
||||
}
|
||||
const xPoweredByOption = options.xPoweredBy ?? options.hidePoweredBy
|
||||
switch (xPoweredByOption) {
|
||||
case undefined:
|
||||
case true:
|
||||
result.push(xPoweredBy())
|
||||
break
|
||||
case false:
|
||||
break
|
||||
default:
|
||||
console.warn("X-Powered-By does not take options. Remove the property to silence this warning.")
|
||||
result.push(xPoweredBy())
|
||||
break
|
||||
}
|
||||
if ("xXssProtection" in options && "xssFilter" in options) {
|
||||
throw new Error("X-XSS-Protection option was specified twice. Remove `xssFilter` to silence this warning.")
|
||||
}
|
||||
const xXssProtectionOption = options.xXssProtection ?? options.xssFilter
|
||||
switch (xXssProtectionOption) {
|
||||
case undefined:
|
||||
case true:
|
||||
result.push(xXssProtection())
|
||||
break
|
||||
case false:
|
||||
break
|
||||
default:
|
||||
console.warn("X-XSS-Protection does not take options. Remove the property to silence this warning.")
|
||||
result.push(xXssProtection())
|
||||
break
|
||||
}
|
||||
return result
|
||||
}
|
||||
const helmet = Object.assign(
|
||||
function helmet(options = {}) {
|
||||
// People should be able to pass an options object with no prototype,
|
||||
// so we want this optional chaining.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (options.constructor?.name === "IncomingMessage") {
|
||||
throw new Error("It appears you have done something like `app.use(helmet)`, but it should be `app.use(helmet())`.")
|
||||
}
|
||||
const middlewareFunctions = getMiddlewareFunctionsFromOptions(options)
|
||||
return function helmetMiddleware(req, res, next) {
|
||||
let middlewareIndex = 0
|
||||
;(function internalNext(err) {
|
||||
if (err) {
|
||||
next(err)
|
||||
return
|
||||
}
|
||||
const middlewareFunction = middlewareFunctions[middlewareIndex]
|
||||
if (middlewareFunction) {
|
||||
middlewareIndex++
|
||||
middlewareFunction(req, res, internalNext)
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
})()
|
||||
}
|
||||
},
|
||||
{
|
||||
contentSecurityPolicy,
|
||||
crossOriginEmbedderPolicy,
|
||||
crossOriginOpenerPolicy,
|
||||
crossOriginResourcePolicy,
|
||||
originAgentCluster,
|
||||
referrerPolicy,
|
||||
strictTransportSecurity,
|
||||
xContentTypeOptions,
|
||||
xDnsPrefetchControl,
|
||||
xDownloadOptions,
|
||||
xFrameOptions,
|
||||
xPermittedCrossDomainPolicies,
|
||||
xPoweredBy,
|
||||
xXssProtection,
|
||||
// Legacy aliases
|
||||
dnsPrefetchControl: xDnsPrefetchControl,
|
||||
xssFilter: xXssProtection,
|
||||
permittedCrossDomainPolicies: xPermittedCrossDomainPolicies,
|
||||
ieNoOpen: xDownloadOptions,
|
||||
noSniff: xContentTypeOptions,
|
||||
frameguard: xFrameOptions,
|
||||
hidePoweredBy: xPoweredBy,
|
||||
hsts: strictTransportSecurity
|
||||
}
|
||||
)
|
||||
|
||||
exports.contentSecurityPolicy = contentSecurityPolicy
|
||||
exports.crossOriginEmbedderPolicy = crossOriginEmbedderPolicy
|
||||
exports.crossOriginOpenerPolicy = crossOriginOpenerPolicy
|
||||
exports.crossOriginResourcePolicy = crossOriginResourcePolicy
|
||||
exports.default = helmet
|
||||
exports.dnsPrefetchControl = xDnsPrefetchControl
|
||||
exports.frameguard = xFrameOptions
|
||||
exports.hidePoweredBy = xPoweredBy
|
||||
exports.hsts = strictTransportSecurity
|
||||
exports.ieNoOpen = xDownloadOptions
|
||||
exports.noSniff = xContentTypeOptions
|
||||
exports.originAgentCluster = originAgentCluster
|
||||
exports.permittedCrossDomainPolicies = xPermittedCrossDomainPolicies
|
||||
exports.referrerPolicy = referrerPolicy
|
||||
exports.strictTransportSecurity = strictTransportSecurity
|
||||
exports.xContentTypeOptions = xContentTypeOptions
|
||||
exports.xDnsPrefetchControl = xDnsPrefetchControl
|
||||
exports.xDownloadOptions = xDownloadOptions
|
||||
exports.xFrameOptions = xFrameOptions
|
||||
exports.xPermittedCrossDomainPolicies = xPermittedCrossDomainPolicies
|
||||
exports.xPoweredBy = xPoweredBy
|
||||
exports.xXssProtection = xXssProtection
|
||||
exports.xssFilter = xXssProtection
|
||||
|
||||
module.exports = exports.default
|
||||
module.exports.default = module.exports
|
||||
Reference in New Issue
Block a user