This commit is contained in:
2026-03-03 15:23:00 +00:00
parent 5e3726de39
commit 8e223bfbec
3689 changed files with 955330 additions and 1011 deletions

11
node_modules/@hapi/statehood/LICENSE.md generated vendored Executable file
View File

@@ -0,0 +1,11 @@
Copyright (c) 2014-2022, Project contributors
Copyright (c) 2014-2020, Sideway Inc
Copyright (c) 2014, Walmart.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* The names of any contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS OFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

5
node_modules/@hapi/statehood/README.md generated vendored Executable file
View File

@@ -0,0 +1,5 @@
<a href="http://hapijs.com"><img src="https://raw.githubusercontent.com/hapijs/assets/master/images/family.png" width="180px" align="right" /></a>
# @hapi/statehood
#### HTTP State Management Utilities.

186
node_modules/@hapi/statehood/lib/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,186 @@
import type { Boom } from '@hapi/boom';
import { SealOptions, SealOptionsSub } from '@hapi/iron';
export { SealOptions, SealOptionsSub };
export type SameSitePolicy = false | 'None' | 'Lax' | 'Strict';
export interface StateOptions<HapiRequest> {
/**
* If `false`, allows any cookie value including values in violation of [RFC 6265](https://tools.ietf.org/html/rfc6265).
*
* @default true
*/
strictHeader?: boolean | undefined;
/**
* If `true`, errors are ignored and treated as missing cookies.
*/
ignoreErrors?: boolean | undefined;
/**
* Sets the `Secure` flag.
*
* @default true
*/
isSecure?: boolean | undefined;
/**
* Sets the `HttpOnly` flag.
*
* @default true
*/
isHttpOnly?: boolean | undefined;
/**
* Sets the `SameSite` flag. The value must be one of:
*
* - `false` - no flag.
* - `Strict` - sets the value to `Strict`.
* - `Lax` - sets the value to `Lax`.
* - `None` - sets the value to `None`.
*
* @default 'Strict'
*/
isSameSite?: SameSitePolicy | undefined;
/**
* Sets the `Partitioned` flag. For more information, please access https://developers.google.com/privacy-sandbox/3pcd/chips
*/
isPartitioned?: boolean | undefined;
/**
* The path scope.
*
* @default null (no path)
*/
path?: string | null | undefined;
/**
* The domain scope.
*
* @default null (no domain)
*/
domain?: string | null | undefined;
/**
* Time-to-live in milliseconds.
*
* @default null (session time-life - cookies are deleted when the browser is closed)
*/
ttl?: number | null | undefined;
/**
* Encoding performs on the provided value before serialization. Options are:
*
* - `none` - no encoding. When used, the cookie value must be a string.
* - `base64` - string value is encoded using Base64.
* - `base64json` - object value is JSON-stringified then encoded using Base64.
* - `form` - object value is encoded using the x-www-form-urlencoded method.
* - `iron` - Encrypts and sign the value using iron.
*
* @default 'none'
*/
encoding?: 'none' | 'base64' | 'base64json' | 'form' | 'iron' | undefined;
/**
* An object used to calculate an HMAC for cookie integrity validation. This does not provide privacy, only a mean
* to verify that the cookie value was generated by the server. Redundant when 'iron' encoding is used. Options are:
* - integrity -
* - password -
*/
sign?:
| {
/**
* Algorithm options.
*/
integrity?: SealOptionsSub | undefined;
/**
* Password used for HMAC key generation (must be at least 32 characters long).
*/
password: string;
}
| undefined;
/**
* Options for 'iron' encoding.
*/
iron?: SealOptions | undefined;
/**
* Password used for 'iron' encoding (must be at least 32 characters long).
*/
password?: string | undefined;
/**
* A function using the signature `async function(definition, request)` used to override a request-specific cookie settings.
*/
contextualize?(
/**
* A copy of the options to be used for formatting the cookie that can be manipulated by the function to customize
* the request cookie header. Note that changing the `definition.contextualize` property will be ignored.
*/
definition: this,
/**
* The request object.
*/
request: HapiRequest
): void | Promise<void>;
/**
* If true, automatically instruct the client to remove invalid cookies.
*
* @default false
*/
clearInvalid?: boolean | undefined;
/**
* if present and the cookie was not received from the client or explicitly set by the route handler, the
* cookie is automatically added to the response with the provided value. The value can be
* a function with signature async function(request) where:
*/
autoValue?(request: HapiRequest): any;
/**
* Used by proxy plugins (e.g. h2o2).
*/
passThrough?: any | undefined;
}
export interface FormatCookie<HapiRequest> {
name: string;
value: any;
options: StateOptions<HapiRequest>;
}
export class Definitions<HapiRequest> {
constructor(options: StateOptions<HapiRequest>);
add(name: string, options: StateOptions<HapiRequest>): void;
parse(cookies: string): Promise<{
states: Record<string, string>;
failed: {
name?: string;
value?: string;
settings: StateOptions<HapiRequest>;
reason: string;
}[];
}>;
format(
cookies: FormatCookie<HapiRequest> | FormatCookie<HapiRequest>[],
context: HapiRequest
): Promise<string[]>;
passThrough(header: string, fallback: boolean): string | Boom;
}
export function prepareValue(
name: string,
value: any,
options: StateOptions<any>
): Promise<string>;
export function exclude(cookies: string, excludes: string[]): string | Boom;

555
node_modules/@hapi/statehood/lib/index.js generated vendored Executable file
View File

@@ -0,0 +1,555 @@
'use strict';
const Querystring = require('querystring');
const Boom = require('@hapi/boom');
const Bounce = require('@hapi/bounce');
const Bourne = require('@hapi/bourne');
const Cryptiles = require('@hapi/cryptiles');
const Hoek = require('@hapi/hoek');
const Iron = require('@hapi/iron');
const Validate = require('@hapi/validate');
const internals = {
macPrefix: 'hapi.signed.cookie.1'
};
internals.schema = Validate.object({
strictHeader: Validate.boolean(),
ignoreErrors: Validate.boolean(),
isSecure: Validate.boolean(),
isHttpOnly: Validate.boolean(),
isPartitioned: Validate.boolean(),
isSameSite: Validate.valid('Strict', 'Lax', 'None', false),
path: Validate.string().allow(null),
domain: Validate.string().allow(null),
ttl: Validate.number().allow(null),
encoding: Validate.string().valid('base64json', 'base64', 'form', 'iron', 'none'),
sign: Validate.object({
password: [Validate.string(), Validate.binary(), Validate.object()],
integrity: Validate.object()
}),
iron: Validate.object(),
password: [Validate.string(), Validate.binary(), Validate.object()],
contextualize: Validate.function(),
// Used by hapi
clearInvalid: Validate.boolean(),
autoValue: Validate.any(),
passThrough: Validate.boolean()
});
internals.defaults = {
strictHeader: true, // Require an RFC 6265 compliant header format
ignoreErrors: false,
isSecure: true,
isHttpOnly: true,
isPartitioned: false,
isSameSite: 'Strict',
path: null,
domain: null,
ttl: null, // MSecs, 0 means remove
encoding: 'none' // options: 'base64json', 'base64', 'form', 'iron', 'none'
};
// Header format
internals.validateRx = {
nameRx: {
strict: /^[^\x00-\x20()<>@,;:\\"\/\[\]?={}\x7F]+$/,
loose: /^[^=\s]*$/
},
valueRx: {
strict: /^[^\x00-\x20",;\\\x7F]*$/,
loose: /^("[^"]*"|[^;]*)$/
},
domainRx: /^\.?[a-z\d]+(?:-[a-z\d]+)*(?:\.[a-z\d]+(?:-[a-z\d]+)*)*$/,
domainLabelLenRx: /^\.?[a-z\d\-]{1,63}(?:\.[a-z\d\-]{1,63})*$/,
pathRx: /^\/[^\x00-\x1F;]*$/
};
// 1: name 2: value
internals.pairRx = /^([^=\s]*)\s*=\s*(.*)$/;
exports.Definitions = class {
constructor(options) {
this.settings = Hoek.applyToDefaults(internals.defaults, options ?? {});
Validate.assert(this.settings, internals.schema, 'Invalid state definition defaults');
this.cookies = {};
this.names = [];
}
add(name, options) {
Hoek.assert(name && typeof name === 'string', 'Invalid name');
Hoek.assert(!this.cookies[name], 'State already defined:', name);
const settings = Hoek.applyToDefaults(this.settings, options ?? {}, { nullOverride: true });
Validate.assert(settings, internals.schema, 'Invalid state definition: ' + name);
this.cookies[name] = settings;
this.names.push(name);
}
async parse(cookies) {
const state = {};
const names = [];
const verify = internals.parsePairs(cookies, (name, value) => {
if (name === '__proto__') {
throw Boom.badRequest('Invalid cookie header');
}
if (state[name]) {
if (!Array.isArray(state[name])) {
state[name] = [state[name]];
}
state[name].push(value);
}
else {
state[name] = value;
names.push(name);
}
});
// Validate cookie header syntax
const failed = []; // All errors
if (verify !== null) {
if (!this.settings.ignoreErrors) {
throw Boom.badRequest('Invalid cookie header');
}
failed.push({ settings: this.settings, reason: `Header contains unexpected syntax: ${verify}` });
}
// Collect errors
const errored = []; // Unignored errors
const record = (reason, name, value, definition) => {
const details = {
name,
value,
settings: definition,
reason: typeof reason === 'string' ? reason : reason.message
};
failed.push(details);
if (!definition.ignoreErrors) {
errored.push(details);
}
};
// Parse cookies
const parsed = {};
for (const name of names) {
const value = state[name];
const definition = this.cookies[name] ?? this.settings;
// Validate cookie
if (definition.strictHeader) {
const reason = internals.validate(name, state);
if (reason) {
record(reason, name, value, definition);
continue;
}
}
// Check cookie format
if (definition.encoding === 'none') {
parsed[name] = value;
continue;
}
// Single value
if (!Array.isArray(value)) {
try {
const unsigned = await internals.unsign(name, value, definition);
const result = await internals.decode(unsigned, definition);
parsed[name] = result;
}
catch (err) {
Bounce.rethrow(err, 'system');
record(err, name, value, definition);
}
continue;
}
// Array
const arrayResult = [];
for (const arrayValue of value) {
try {
const unsigned = await internals.unsign(name, arrayValue, definition);
const result = await internals.decode(unsigned, definition);
arrayResult.push(result);
}
catch (err) {
Bounce.rethrow(err, 'system');
record(err, name, value, definition);
}
}
parsed[name] = arrayResult;
}
if (errored.length) {
const error = Boom.badRequest('Invalid cookie value', errored);
error.states = parsed;
error.failed = failed;
throw error;
}
return { states: parsed, failed };
}
async format(cookies, context) {
if (!cookies ||
Array.isArray(cookies) && !cookies.length) {
return [];
}
if (!Array.isArray(cookies)) {
cookies = [cookies];
}
const header = [];
for (let i = 0; i < cookies.length; ++i) {
const cookie = cookies[i];
// Apply definition to local configuration
const base = this.cookies[cookie.name] ?? this.settings;
let definition = cookie.options ? Hoek.applyToDefaults(base, cookie.options, { nullOverride: true }) : base;
// Contextualize definition
if (definition.contextualize) {
if (definition === base) {
definition = Hoek.clone(definition);
}
await definition.contextualize(definition, context);
}
// Validate name
const nameRx = definition.strictHeader ? internals.validateRx.nameRx.strict : internals.validateRx.nameRx.loose;
if (!nameRx.test(cookie.name)) {
throw Boom.badImplementation('Invalid cookie name: ' + cookie.name);
}
// Prepare value (encode, sign)
const value = await exports.prepareValue(cookie.name, cookie.value, definition);
// Validate prepared value
const valueRx = definition.strictHeader ? internals.validateRx.valueRx.strict : internals.validateRx.valueRx.loose;
if (value &&
(typeof value !== 'string' || !value.match(valueRx))) {
throw Boom.badImplementation('Invalid cookie value: ' + cookie.value);
}
// Construct cookie
let segment = cookie.name + '=' + (value || '');
if (definition.ttl !== null &&
definition.ttl !== undefined) { // Can be zero
const expires = new Date(definition.ttl ? Date.now() + definition.ttl : 0);
segment = segment + '; Max-Age=' + Math.floor(definition.ttl / 1000) + '; Expires=' + expires.toUTCString();
}
if (definition.isSecure) {
segment = segment + '; Secure';
}
if (definition.isHttpOnly) {
segment = segment + '; HttpOnly';
}
if (definition.isSameSite) {
segment = `${segment}; SameSite=${definition.isSameSite}`;
}
if (definition.isPartitioned) {
if (!definition.isSecure) {
throw Boom.badImplementation('Partitioned cookies must be secure');
}
if (definition.isSameSite !== 'None') {
throw Boom.badImplementation('Partitioned cookies must have SameSite=None');
}
segment = `${segment}; Partitioned`;
}
if (definition.domain) {
const domain = definition.domain.toLowerCase();
if (!domain.match(internals.validateRx.domainLabelLenRx)) {
throw Boom.badImplementation('Cookie domain too long: ' + definition.domain);
}
if (!domain.match(internals.validateRx.domainRx)) {
throw Boom.badImplementation('Invalid cookie domain: ' + definition.domain);
}
segment = segment + '; Domain=' + domain;
}
if (definition.path) {
if (!definition.path.match(internals.validateRx.pathRx)) {
throw Boom.badImplementation('Invalid cookie path: ' + definition.path);
}
segment = segment + '; Path=' + definition.path;
}
header.push(segment);
}
return header;
}
passThrough(header, fallback) {
if (!this.names.length) {
return header;
}
const exclude = [];
for (let i = 0; i < this.names.length; ++i) {
const name = this.names[i];
const definition = this.cookies[name];
const passCookie = definition.passThrough !== undefined ? definition.passThrough : fallback;
if (!passCookie) {
exclude.push(name);
}
}
return exports.exclude(header, exclude);
}
};
internals.parsePairs = function (cookies, eachPairFn) {
let index = 0;
while (index < cookies.length) {
const eqIndex = cookies.indexOf('=', index);
if (eqIndex === -1) {
return cookies.slice(index); // E.g. 'a=1;xyz' -> 'xyz'
}
const semiIndex = cookies.indexOf(';', eqIndex);
const endOfValueIndex = semiIndex !== -1 ? semiIndex : cookies.length;
const name = cookies.slice(index, eqIndex).trim();
const value = cookies.slice(eqIndex + 1, endOfValueIndex).trim();
const unquotedValue = (value.startsWith('"') && value.endsWith('"') && value !== '"') ?
value.slice(1, -1) : // E.g. '"abc"' -> 'abc'
value;
eachPairFn(name, unquotedValue);
index = endOfValueIndex + 1;
}
return null;
};
internals.validate = function (name, state) {
if (!name.match(internals.validateRx.nameRx.strict)) {
return 'Invalid cookie name';
}
const values = [].concat(state[name]);
for (let i = 0; i < values.length; ++i) {
if (!values[i].match(internals.validateRx.valueRx.strict)) {
return 'Invalid cookie value';
}
}
return null;
};
internals.unsign = async function (name, value, definition) {
if (!definition.sign) {
return value;
}
const pos = value.lastIndexOf('.');
if (pos === -1) {
throw Boom.badRequest('Missing signature separator');
}
const unsigned = value.slice(0, pos);
const sig = value.slice(pos + 1);
if (!sig) {
throw Boom.badRequest('Missing signature');
}
const sigParts = sig.split('*');
if (sigParts.length !== 2) {
throw Boom.badRequest('Invalid signature format');
}
const hmacSalt = sigParts[0];
const hmac = sigParts[1];
const macOptions = Hoek.clone(definition.sign.integrity ?? Iron.defaults.integrity);
macOptions.salt = hmacSalt;
const mac = await Iron.hmacWithPassword(definition.sign.password, macOptions, [internals.macPrefix, name, unsigned].join('\n'));
if (!Cryptiles.fixedTimeComparison(mac.digest, hmac)) {
throw Boom.badRequest('Invalid hmac value');
}
return unsigned;
};
internals.decode = async function (value, definition) {
if (!value &&
definition.encoding === 'form') {
return {};
}
Hoek.assert(typeof value === 'string', 'Invalid string');
// Encodings: 'base64json', 'base64', 'form', 'iron', 'none'
if (definition.encoding === 'iron') {
return await Iron.unseal(value, definition.password, definition.iron ?? Iron.defaults);
}
if (definition.encoding === 'base64json') {
const decoded = Buffer.from(value, 'base64').toString('binary');
try {
return Bourne.parse(decoded);
}
catch {
throw Boom.badRequest('Invalid JSON payload');
}
}
if (definition.encoding === 'base64') {
return Buffer.from(value, 'base64').toString('binary');
}
// encoding: 'form'
return Querystring.parse(value);
};
exports.prepareValue = async function (name, value, options) {
Hoek.assert(options && typeof options === 'object', 'Missing or invalid options');
try {
const encoded = await internals.encode(value, options);
const signed = await internals.sign(name, encoded, options.sign);
return signed;
}
catch (err) {
throw Boom.badImplementation('Failed to encode cookie (' + name + ') value: ' + err.message);
}
};
internals.encode = function (value, options) {
// Encodings: 'base64json', 'base64', 'form', 'iron', 'none'
if (value === undefined ||
options.encoding === 'none') {
return value;
}
if (options.encoding === 'iron') {
return Iron.seal(value, options.password, options.iron ?? Iron.defaults);
}
if (options.encoding === 'base64') {
return Buffer.from(value, 'binary').toString('base64');
}
if (options.encoding === 'base64json') {
const stringified = JSON.stringify(value);
return Buffer.from(stringified, 'binary').toString('base64');
}
// encoding: 'form'
return Querystring.stringify(value);
};
internals.sign = async function (name, value, options) {
if (value === undefined ||
!options) {
return value;
}
const mac = await Iron.hmacWithPassword(options.password, options.integrity ?? Iron.defaults.integrity, [internals.macPrefix, name, value].join('\n'));
const signed = value + '.' + mac.salt + '*' + mac.digest;
return signed;
};
exports.exclude = function (cookies, excludes) {
const result = [];
const chunks = cookies.split(';');
for (const chunk of chunks) {
const match = internals.pairRx.exec(chunk.trim());
if (!match) {
return Boom.badRequest('Invalid cookie header');
}
const [, name, value] = match;
if (!excludes.includes(name)) {
result.push(`${name}=${value}`);
}
}
return result.join(';');
};

41
node_modules/@hapi/statehood/package.json generated vendored Executable file
View File

@@ -0,0 +1,41 @@
{
"name": "@hapi/statehood",
"description": "HTTP State Management Utilities",
"version": "8.2.1",
"repository": "git://github.com/hapijs/statehood",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"files": [
"lib"
],
"keywords": [
"HTTP",
"state",
"cookies",
"secure"
],
"eslintConfig": {
"extends": [
"plugin:@hapi/module"
]
},
"dependencies": {
"@hapi/boom": "^10.0.1",
"@hapi/bounce": "^3.0.1",
"@hapi/bourne": "^3.0.0",
"@hapi/cryptiles": "^6.0.1",
"@hapi/hoek": "^11.0.2",
"@hapi/iron": "^7.0.1",
"@hapi/validate": "^2.0.1"
},
"devDependencies": {
"@hapi/code": "^9.0.3",
"@hapi/eslint-plugin": "^6.0.0",
"@hapi/lab": "^25.1.2"
},
"scripts": {
"test": "lab -a @hapi/code -t 100 -L",
"test-cov-html": "lab -a @hapi/code -r html -o coverage.html"
},
"license": "BSD-3-Clause"
}