From db1bc1d55ab1402dfcc9cdb8ff3b262b850e833b Mon Sep 17 00:00:00 2001 From: MaxNad Date: Fri, 5 Jan 2024 15:54:26 -0500 Subject: [PATCH 01/12] Remove the compilation artifacts from Git --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 405d6f3..1a65c52 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ dest/ -*.js \ No newline at end of file +*.js +dist +node_modules \ No newline at end of file From fc049b47590fd34fdec0ff19f0c91d68b335acb7 Mon Sep 17 00:00:00 2001 From: MaxNad Date: Fri, 5 Jan 2024 16:01:21 -0500 Subject: [PATCH 02/12] Parse CSP according to RFC --- README.md | 2 +- checks/checker.ts | 5 +- checks/parser_checks.ts | 141 ++++---- checks/parser_checks_test.ts | 2 +- checks/security_checks.ts | 587 ++++++++++++++++++-------------- checks/security_checks_test.ts | 69 +++- checks/strictcsp_checks.ts | 161 +++++---- checks/strictcsp_checks_test.ts | 2 +- csp_test.ts | 30 +- enforced_csps.ts | 210 ++++++++++++ enforced_csps_test.ts | 50 +++ evaluator.ts | 23 +- evaluator_test.ts | 16 +- finding.ts | 5 + package-lock.json | 572 ++++++++++++++++++++++++++----- package.json | 8 +- parser.ts | 71 +++- parser_test.ts | 220 +++++++++++- 18 files changed, 1618 insertions(+), 556 deletions(-) create mode 100644 enforced_csps.ts create mode 100644 enforced_csps_test.ts diff --git a/README.md b/README.md index ea0fd78..a0b1b95 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ npm install && npm test import {CspEvaluator} from "csp_evaluator/dist/evaluator.js"; import {CspParser} from "csp_evaluator/dist/parser.js"; -const parsed = new CspParser("script-src https://google.com").csp; +const parsed = new CspParser(["script-src https://google.com"]).csps; console.log(new CspEvaluator(parsed).evaluate()); ``` diff --git a/checks/checker.ts b/checks/checker.ts index 3cda548..55b017a 100644 --- a/checks/checker.ts +++ b/checks/checker.ts @@ -3,10 +3,11 @@ */ import {Csp} from '../csp'; +import { EnforcedCsps } from '../enforced_csps'; import {Finding} from '../finding'; /** - * A function that checks a given Csp for problems and returns an unordered + * A function that checks a list of Csps for problems and returns an unordered * list of Findings. */ -export type CheckerFunction = (csp: Csp) => Finding[]; +export type CheckerFunction = (csps: EnforcedCsps) => Finding[]; diff --git a/checks/parser_checks.ts b/checks/parser_checks.ts index 4d64e8b..816d746 100644 --- a/checks/parser_checks.ts +++ b/checks/parser_checks.ts @@ -21,6 +21,7 @@ import * as csp from '../csp'; import {Csp, Keyword} from '../csp'; +import { EnforcedCsps } from '../enforced_csps'; import {Finding, Severity, Type} from '../finding'; @@ -33,24 +34,26 @@ import {Finding, Severity, Type} from '../finding'; * * @param parsedCsp A parsed csp. */ -export function checkUnknownDirective(parsedCsp: Csp): Finding[] { +export function checkUnknownDirective(parsedCsps: EnforcedCsps): Finding[] { const findings: Finding[] = []; - for (const directive of Object.keys(parsedCsp.directives)) { - if (csp.isDirective(directive)) { - // Directive is known. - continue; - } - - if (directive.endsWith(':')) { - findings.push(new Finding( - Type.UNKNOWN_DIRECTIVE, 'CSP directives don\'t end with a colon.', - Severity.SYNTAX, directive)); - } else { - findings.push(new Finding( - Type.UNKNOWN_DIRECTIVE, - 'Directive "' + directive + '" is not a known CSP directive.', - Severity.SYNTAX, directive)); + for (const cspChecked of parsedCsps) { + for (const directive of Object.keys(cspChecked.directives)) { + if (csp.isDirective(directive)) { + // Directive is known. + continue; + } + + if (directive.endsWith(':')) { + findings.push(new Finding( + Type.UNKNOWN_DIRECTIVE, 'CSP directives don\'t end with a colon.', + Severity.SYNTAX, directive)); + } else { + findings.push(new Finding( + Type.UNKNOWN_DIRECTIVE, + 'Directive "' + directive + '" is not a known CSP directive.', + Severity.SYNTAX, directive)); + } } } @@ -67,23 +70,25 @@ export function checkUnknownDirective(parsedCsp: Csp): Finding[] { * * @param parsedCsp A parsed csp. */ -export function checkMissingSemicolon(parsedCsp: Csp): Finding[] { +export function checkMissingSemicolon(parsedCsps: EnforcedCsps): Finding[] { const findings: Finding[] = []; - for (const [directive, directiveValues] of Object.entries( - parsedCsp.directives)) { - if (directiveValues === undefined) { - continue; - } - for (const value of directiveValues) { - // If we find a known directive inside a directive value, it is very - // likely that a semicolon was forgoten. - if (csp.isDirective(value)) { - findings.push(new Finding( - Type.MISSING_SEMICOLON, - 'Did you forget the semicolon? ' + - '"' + value + '" seems to be a directive, not a value.', - Severity.SYNTAX, directive, value)); + for (const cspChecked of parsedCsps) { + for (const [directive, directiveValues] of Object.entries( + cspChecked.directives)) { + if (directiveValues === undefined) { + continue; + } + for (const value of directiveValues) { + // If we find a known directive inside a directive value, it is very + // likely that a semicolon was forgoten. + if (csp.isDirective(value)) { + findings.push(new Finding( + Type.MISSING_SEMICOLON, + 'Did you forget the semicolon? ' + + '"' + value + '" seems to be a directive, not a value.', + Severity.SYNTAX, directive, value)); + } } } } @@ -100,54 +105,56 @@ export function checkMissingSemicolon(parsedCsp: Csp): Finding[] { * * @param parsedCsp A parsed csp. */ -export function checkInvalidKeyword(parsedCsp: Csp): Finding[] { +export function checkInvalidKeyword(parsedCsps: EnforcedCsps): Finding[] { const findings: Finding[] = []; const keywordsNoTicks = Object.values(Keyword).map((k) => k.replace(/'/g, '')); - for (const [directive, directiveValues] of Object.entries( - parsedCsp.directives)) { - if (directiveValues === undefined) { - continue; - } - for (const value of directiveValues) { - // Check if single ticks have been forgotten. - if (keywordsNoTicks.some((k) => k === value) || - value.startsWith('nonce-') || - value.match(/^(sha256|sha384|sha512)-/)) { - findings.push(new Finding( - Type.INVALID_KEYWORD, - 'Did you forget to surround "' + value + '" with single-ticks?', - Severity.SYNTAX, directive, value)); + for (const cspChecked of parsedCsps) { + for (const [directive, directiveValues] of Object.entries( + cspChecked.directives)) { + if (directiveValues === undefined) { continue; } - - // Continue, if the value doesn't start with single tick. - // All CSP keywords start with a single tick. - if (!value.startsWith('\'')) { - continue; - } - - if (directive === csp.Directive.REQUIRE_TRUSTED_TYPES_FOR) { - // Continue, if it's an allowed Trusted Types sink. - if (value === csp.TrustedTypesSink.SCRIPT) { + for (const value of directiveValues) { + // Check if single ticks have been forgotten. + if (keywordsNoTicks.some((k) => k === value) || + value.startsWith('nonce-') || + value.match(/^(sha256|sha384|sha512)-/)) { + findings.push(new Finding( + Type.INVALID_KEYWORD, + 'Did you forget to surround "' + value + '" with single-ticks?', + Severity.SYNTAX, directive, value)); continue; } - } else if (directive === csp.Directive.TRUSTED_TYPES) { - // Continue, if it's an allowed Trusted Types keyword. - if (value === '\'allow-duplicates\'' || value === '\'none\'') { + + // Continue, if the value doesn't start with single tick. + // All CSP keywords start with a single tick. + if (!value.startsWith('\'')) { continue; } - } else { - // Continue, if it's a valid keyword. - if (csp.isKeyword(value) || csp.isHash(value) || csp.isNonce(value)) { - continue; + + if (directive === csp.Directive.REQUIRE_TRUSTED_TYPES_FOR) { + // Continue, if it's an allowed Trusted Types sink. + if (value === csp.TrustedTypesSink.SCRIPT) { + continue; + } + } else if (directive === csp.Directive.TRUSTED_TYPES) { + // Continue, if it's an allowed Trusted Types keyword. + if (value === '\'allow-duplicates\'' || value === '\'none\'') { + continue; + } + } else { + // Continue, if it's a valid keyword. + if (csp.isKeyword(value) || csp.isHash(value) || csp.isNonce(value)) { + continue; + } } - } - findings.push(new Finding( - Type.INVALID_KEYWORD, value + ' seems to be an invalid CSP keyword.', - Severity.SYNTAX, directive, value)); + findings.push(new Finding( + Type.INVALID_KEYWORD, value + ' seems to be an invalid CSP keyword.', + Severity.SYNTAX, directive, value)); + } } } diff --git a/checks/parser_checks_test.ts b/checks/parser_checks_test.ts index 0430141..692fb38 100644 --- a/checks/parser_checks_test.ts +++ b/checks/parser_checks_test.ts @@ -32,7 +32,7 @@ import * as parserChecks from './parser_checks'; * @param checkFunction check. */ function checkCsp(test: string, checkFunction: CheckerFunction): Finding[] { - const parsedCsp = (new CspParser(test)).csp; + const parsedCsp = (new CspParser(test)).csps; return checkFunction(parsedCsp); } diff --git a/checks/security_checks.ts b/checks/security_checks.ts index 1fd9dc4..a1b7a21 100644 --- a/checks/security_checks.ts +++ b/checks/security_checks.ts @@ -22,6 +22,7 @@ import * as flash from '../allowlist_bypasses/flash'; import * as jsonp from '../allowlist_bypasses/jsonp'; import * as csp from '../csp'; import {Csp, Directive, Keyword} from '../csp'; +import { EnforcedCsps } from '../enforced_csps'; import {Finding, Severity, Type} from '../finding'; import * as utils from '../utils'; @@ -53,18 +54,19 @@ export const URL_SCHEMES_CAUSING_XSS: string[] = ['data:', 'http:', 'https:']; * are active in a certain version of CSP (e.g. no unsafe-inline if a nonce * is present). */ -export function checkScriptUnsafeInline(effectiveCsp: Csp): Finding[] { - const directiveName = - effectiveCsp.getEffectiveDirective(Directive.SCRIPT_SRC); - const values: string[] = effectiveCsp.directives[directiveName] || []; - - // Check if unsafe-inline is present. - if (values.includes(Keyword.UNSAFE_INLINE)) { - return [new Finding( - Type.SCRIPT_UNSAFE_INLINE, - `'unsafe-inline' allows the execution of unsafe in-page scripts ` + - 'and event handlers.', - Severity.HIGH, directiveName, Keyword.UNSAFE_INLINE)]; +export function checkScriptUnsafeInline(effectiveCsp: EnforcedCsps): Finding[] { + for (const cspChecked of effectiveCsp) { + const directiveName = cspChecked.getEffectiveDirective(Directive.SCRIPT_SRC); + const values: string[] = cspChecked.directives[directiveName] || []; + + // Check if unsafe-inline is present. + if (values.includes(Keyword.UNSAFE_INLINE)) { + return [new Finding( + Type.SCRIPT_UNSAFE_INLINE, + `'unsafe-inline' allows the execution of unsafe in-page scripts ` + + 'and event handlers.', + Severity.HIGH, directiveName, Keyword.UNSAFE_INLINE)]; + } } return []; @@ -80,19 +82,20 @@ export function checkScriptUnsafeInline(effectiveCsp: Csp): Finding[] { * * @param parsedCsp Parsed CSP. */ -export function checkScriptUnsafeEval(parsedCsp: Csp): Finding[] { - const directiveName = parsedCsp.getEffectiveDirective(Directive.SCRIPT_SRC); - const values: string[] = parsedCsp.directives[directiveName] || []; - - // Check if unsafe-eval is present. - if (values.includes(Keyword.UNSAFE_EVAL)) { - return [new Finding( - Type.SCRIPT_UNSAFE_EVAL, - `'unsafe-eval' allows the execution of code injected into DOM APIs ` + - 'such as eval().', - Severity.MEDIUM_MAYBE, directiveName, Keyword.UNSAFE_EVAL)]; +export function checkScriptUnsafeEval(parsedCsps: EnforcedCsps): Finding[] { + for (const cspChecked of parsedCsps) { + const directiveName = cspChecked.getEffectiveDirective(Directive.SCRIPT_SRC); + const values: string[] = cspChecked.directives[directiveName] || []; + + // Check if unsafe-eval is present. + if (values.includes(Keyword.UNSAFE_EVAL)) { + return [new Finding( + Type.SCRIPT_UNSAFE_EVAL, + `'unsafe-eval' allows the execution of code injected into DOM APIs ` + + 'such as eval().', + Severity.MEDIUM_MAYBE, directiveName, Keyword.UNSAFE_EVAL)]; + } } - return []; } @@ -106,20 +109,22 @@ export function checkScriptUnsafeEval(parsedCsp: Csp): Finding[] { * * @param parsedCsp Parsed CSP. */ -export function checkPlainUrlSchemes(parsedCsp: Csp): Finding[] { +export function checkPlainUrlSchemes(parsedCsps: EnforcedCsps): Finding[] { const violations: Finding[] = []; - const directivesToCheck = - parsedCsp.getEffectiveDirectives(DIRECTIVES_CAUSING_XSS); - for (const directive of directivesToCheck) { - const values = parsedCsp.directives[directive] || []; - for (const value of values) { - if (URL_SCHEMES_CAUSING_XSS.includes(value)) { - violations.push(new Finding( - Type.PLAIN_URL_SCHEMES, - value + ' URI in ' + directive + ' allows the execution of ' + - 'unsafe scripts.', - Severity.HIGH, directive, value)); + for (const cspChecked of parsedCsps) { + const directivesToCheck = cspChecked.getEffectiveDirectives(DIRECTIVES_CAUSING_XSS); + + for (const directive of directivesToCheck) { + const values = cspChecked.directives[directive] || []; + for (const value of values) { + if (URL_SCHEMES_CAUSING_XSS.includes(value)) { + violations.push(new Finding( + Type.PLAIN_URL_SCHEMES, + value + ' URI in ' + directive + ' allows the execution of ' + + 'unsafe scripts.', + Severity.HIGH, directive, value)); + } } } } @@ -137,20 +142,22 @@ export function checkPlainUrlSchemes(parsedCsp: Csp): Finding[] { * * @param parsedCsp Parsed CSP. */ -export function checkWildcards(parsedCsp: Csp): Finding[] { +export function checkWildcards(parsedCsps: EnforcedCsps): Finding[] { const violations: Finding[] = []; - const directivesToCheck = - parsedCsp.getEffectiveDirectives(DIRECTIVES_CAUSING_XSS); - - for (const directive of directivesToCheck) { - const values = parsedCsp.directives[directive] || []; - for (const value of values) { - const url = utils.getSchemeFreeUrl(value); - if (url === '*') { - violations.push(new Finding( - Type.PLAIN_WILDCARD, directive + ` should not allow '*' as source`, - Severity.HIGH, directive, value)); - continue; + + for (const cspChecked of parsedCsps) { + const directivesToCheck = cspChecked.getEffectiveDirectives(DIRECTIVES_CAUSING_XSS); + + for (const directive of directivesToCheck) { + const values = cspChecked.directives[directive] || []; + for (const value of values) { + const url = utils.getSchemeFreeUrl(value); + if (url === '*') { + violations.push(new Finding( + Type.PLAIN_WILDCARD, directive + ` should not allow '*' as source`, + Severity.HIGH, directive, value)); + continue; + } } } } @@ -162,13 +169,17 @@ export function checkWildcards(parsedCsp: Csp): Finding[] { * Checks if object-src is restricted to none either directly or via a * default-src. */ -export function checkMissingObjectSrcDirective(parsedCsp: Csp): Finding[] { +export function checkMissingObjectSrcDirective(parsedCsps: EnforcedCsps): Finding[] { let objectRestrictions: string[]|undefined = []; - if (Directive.OBJECT_SRC in parsedCsp.directives) { - objectRestrictions = parsedCsp.directives[Directive.OBJECT_SRC]; - } else if (Directive.DEFAULT_SRC in parsedCsp.directives) { - objectRestrictions = parsedCsp.directives[Directive.DEFAULT_SRC]; + + for (const cspChecked of parsedCsps) { + if (Directive.OBJECT_SRC in cspChecked.directives) { + objectRestrictions = cspChecked.directives[Directive.OBJECT_SRC]; + } else if (Directive.DEFAULT_SRC in cspChecked.directives) { + objectRestrictions = cspChecked.directives[Directive.DEFAULT_SRC]; + } } + if (objectRestrictions !== undefined && objectRestrictions.length >= 1) { return []; } @@ -181,10 +192,12 @@ export function checkMissingObjectSrcDirective(parsedCsp: Csp): Finding[] { /** * Checks if script-src is restricted either directly or via a default-src. */ -export function checkMissingScriptSrcDirective(parsedCsp: Csp): Finding[] { - if (Directive.SCRIPT_SRC in parsedCsp.directives || - Directive.DEFAULT_SRC in parsedCsp.directives) { - return []; +export function checkMissingScriptSrcDirective(parsedCsps: EnforcedCsps): Finding[] { + for (const cspChecked of parsedCsps) { + if (Directive.SCRIPT_SRC in cspChecked.directives || + Directive.DEFAULT_SRC in cspChecked.directives) { + return []; + } } return [new Finding( Type.MISSING_DIRECTIVES, 'script-src directive is missing.', @@ -195,33 +208,43 @@ export function checkMissingScriptSrcDirective(parsedCsp: Csp): Finding[] { * Checks if the base-uri needs to be restricted and if so, whether it has been * restricted. */ -export function checkMissingBaseUriDirective(parsedCsp: Csp): Finding[] { - return checkMultipleMissingBaseUriDirective([parsedCsp]); -} +export function checkMissingBaseUriDirective(parsedCsps: EnforcedCsps): Finding[] { + const findings: Finding[] = []; -/** - * Checks if the base-uri needs to be restricted and if so, whether it has been - * restricted. - */ -export function checkMultipleMissingBaseUriDirective(parsedCsps: Csp[]): - Finding[] { // base-uri can be used to bypass nonce based CSPs and hash based CSPs that // use strict dynamic - const needsBaseUri = (csp: Csp) => - (csp.policyHasScriptNonces() || - (csp.policyHasScriptHashes() && csp.policyHasStrictDynamic())); - const hasBaseUri = (csp: Csp) => Directive.BASE_URI in csp.directives; - - if (parsedCsps.some(needsBaseUri) && !parsedCsps.some(hasBaseUri)) { - const description = 'Missing base-uri allows the injection of base tags. ' + + const needsBaseUri = ( + parsedCsps.policyHasScriptNonces() || + (parsedCsps.policyHasScriptHashes() && parsedCsps.policyHasStrictDynamic()) + ); + + if (needsBaseUri) { + for (const csp of parsedCsps) { + if (!(Directive.BASE_URI in csp.directives)) { + const description = 'Missing base-uri allows the injection of base tags. ' + 'They can be used to set the base URL for all relative (script) ' + 'URLs to an attacker controlled domain. ' + `Can you set it to 'none' or 'self'?`; - return [new Finding( - Type.MISSING_DIRECTIVES, description, Severity.HIGH, - Directive.BASE_URI)]; + findings.push(new Finding( + Type.MISSING_DIRECTIVES, description, Severity.HIGH, + Directive.BASE_URI)); + } + } } - return []; + + return findings; +} + +/** + * Checks if the base-uri needs to be restricted and if so, whether it has been + * restricted. + */ +export function checkMultipleMissingBaseUriDirective(parsedCsps: EnforcedCsps[]): Finding[] { + let findings: Finding[] = []; + for (const csp of parsedCsps) { + findings = findings.concat(checkMissingBaseUriDirective(csp)); + } + return findings; } @@ -234,11 +257,11 @@ export function checkMultipleMissingBaseUriDirective(parsedCsps: Csp[]): * * @param parsedCsp Parsed CSP. */ -export function checkMissingDirectives(parsedCsp: Csp): Finding[] { +export function checkMissingDirectives(parsedCsps: EnforcedCsps): Finding[] { return [ - ...checkMissingObjectSrcDirective(parsedCsp), - ...checkMissingScriptSrcDirective(parsedCsp), - ...checkMissingBaseUriDirective(parsedCsp), + ...checkMissingObjectSrcDirective(parsedCsps), + ...checkMissingScriptSrcDirective(parsedCsps), + ...checkMissingBaseUriDirective(parsedCsps), ]; } @@ -252,75 +275,76 @@ export function checkMissingDirectives(parsedCsp: Csp): Finding[] { * * @param parsedCsp Parsed CSP. */ -export function checkScriptAllowlistBypass(parsedCsp: Csp): Finding[] { +export function checkScriptAllowlistBypass(parsedCsps: EnforcedCsps): Finding[] { const violations: Finding[] = []; - const effectiveScriptSrcDirective = - parsedCsp.getEffectiveDirective(Directive.SCRIPT_SRC); - const scriptSrcValues = - parsedCsp.directives[effectiveScriptSrcDirective] || []; - if (scriptSrcValues.includes(Keyword.NONE)) { - return violations; - } - for (const value of scriptSrcValues) { - if (value === Keyword.SELF) { - violations.push(new Finding( - Type.SCRIPT_ALLOWLIST_BYPASS, - `'self' can be problematic if you host JSONP, AngularJS or user ` + - 'uploaded files.', - Severity.MEDIUM_MAYBE, effectiveScriptSrcDirective, value)); - continue; + for (const cspChecked of parsedCsps) { + const effectiveScriptSrcDirective = cspChecked.getEffectiveDirective(Directive.SCRIPT_SRC); + const scriptSrcValues = cspChecked.directives[effectiveScriptSrcDirective] || []; + if (scriptSrcValues.includes(Keyword.NONE)) { + return violations; } - // Ignore keywords, nonces and hashes (they start with a single quote). - if (value.startsWith('\'')) { - continue; - } + for (const value of scriptSrcValues) { + if (value === Keyword.SELF) { + violations.push(new Finding( + Type.SCRIPT_ALLOWLIST_BYPASS, + `'self' can be problematic if you host JSONP, AngularJS or user ` + + 'uploaded files.', + Severity.MEDIUM_MAYBE, effectiveScriptSrcDirective, value)); + continue; + } - // Ignore standalone schemes and things that don't look like URLs (no dot). - if (csp.isUrlScheme(value) || value.indexOf('.') === -1) { - continue; - } + // Ignore keywords, nonces and hashes (they start with a single quote). + if (value.startsWith('\'')) { + continue; + } - const url = '//' + utils.getSchemeFreeUrl(value); + // Ignore standalone schemes and things that don't look like URLs (no dot). + if (csp.isUrlScheme(value) || value.indexOf('.') === -1) { + continue; + } - const angularBypass = utils.matchWildcardUrls(url, angular.URLS); + const url = '//' + utils.getSchemeFreeUrl(value); - let jsonpBypass = utils.matchWildcardUrls(url, jsonp.URLS); + const angularBypass = utils.matchWildcardUrls(url, angular.URLS); - // Some JSONP bypasses only work in presence of unsafe-eval. - if (jsonpBypass) { - const evalRequired = jsonp.NEEDS_EVAL.includes(jsonpBypass.hostname); - const evalPresent = scriptSrcValues.includes(Keyword.UNSAFE_EVAL); - if (evalRequired && !evalPresent) { - jsonpBypass = null; - } - } + let jsonpBypass = utils.matchWildcardUrls(url, jsonp.URLS); - if (jsonpBypass || angularBypass) { - let bypassDomain = ''; - let bypassTxt = ''; + // Some JSONP bypasses only work in presence of unsafe-eval. if (jsonpBypass) { - bypassDomain = jsonpBypass.hostname; - bypassTxt = ' JSONP endpoints'; - } - if (angularBypass) { - bypassDomain = angularBypass.hostname; - bypassTxt += (bypassTxt.trim() === '') ? '' : ' and'; - bypassTxt += ' Angular libraries'; + const evalRequired = jsonp.NEEDS_EVAL.includes(jsonpBypass.hostname); + const evalPresent = scriptSrcValues.includes(Keyword.UNSAFE_EVAL); + if (evalRequired && !evalPresent) { + jsonpBypass = null; + } } - violations.push(new Finding( - Type.SCRIPT_ALLOWLIST_BYPASS, - bypassDomain + ' is known to host' + bypassTxt + - ' which allow to bypass this CSP.', - Severity.HIGH, effectiveScriptSrcDirective, value)); - } else { - violations.push(new Finding( - Type.SCRIPT_ALLOWLIST_BYPASS, - `No bypass found; make sure that this URL doesn't serve JSONP ` + - 'replies or Angular libraries.', - Severity.MEDIUM_MAYBE, effectiveScriptSrcDirective, value)); + if (jsonpBypass || angularBypass) { + let bypassDomain = ''; + let bypassTxt = ''; + if (jsonpBypass) { + bypassDomain = jsonpBypass.hostname; + bypassTxt = ' JSONP endpoints'; + } + if (angularBypass) { + bypassDomain = angularBypass.hostname; + bypassTxt += (bypassTxt.trim() === '') ? '' : ' and'; + bypassTxt += ' Angular libraries'; + } + + violations.push(new Finding( + Type.SCRIPT_ALLOWLIST_BYPASS, + bypassDomain + ' is known to host' + bypassTxt + + ' which allow to bypass this CSP.', + Severity.HIGH, effectiveScriptSrcDirective, value)); + } else { + violations.push(new Finding( + Type.SCRIPT_ALLOWLIST_BYPASS, + `No bypass found; make sure that this URL doesn't serve JSONP ` + + 'replies or Angular libraries.', + Severity.MEDIUM_MAYBE, effectiveScriptSrcDirective, value)); + } } } @@ -337,39 +361,40 @@ export function checkScriptAllowlistBypass(parsedCsp: Csp): Finding[] { * * @param parsedCsp Parsed CSP. */ -export function checkFlashObjectAllowlistBypass(parsedCsp: Csp): Finding[] { +export function checkFlashObjectAllowlistBypass(parsedCsps: EnforcedCsps): Finding[] { const violations = []; - const effectiveObjectSrcDirective = - parsedCsp.getEffectiveDirective(Directive.OBJECT_SRC); - const objectSrcValues = - parsedCsp.directives[effectiveObjectSrcDirective] || []; - - // If flash is not allowed in plugin-types, continue. - const pluginTypes = parsedCsp.directives[Directive.PLUGIN_TYPES]; - if (pluginTypes && !pluginTypes.includes('application/x-shockwave-flash')) { - return []; - } - for (const value of objectSrcValues) { - // Nothing to do here if 'none'. - if (value === Keyword.NONE) { + for (const cspChecked of parsedCsps) { + const effectiveObjectSrcDirective = cspChecked.getEffectiveDirective(Directive.OBJECT_SRC); + const objectSrcValues = cspChecked.directives[effectiveObjectSrcDirective] || []; + + // If flash is not allowed in plugin-types, continue. + const pluginTypes = cspChecked.directives[Directive.PLUGIN_TYPES]; + if (pluginTypes && !pluginTypes.includes('application/x-shockwave-flash')) { return []; } - const url = '//' + utils.getSchemeFreeUrl(value); - const flashBypass = utils.matchWildcardUrls(url, flash.URLS); + for (const value of objectSrcValues) { + // Nothing to do here if 'none'. + if (value === Keyword.NONE) { + return []; + } - if (flashBypass) { - violations.push(new Finding( - Type.OBJECT_ALLOWLIST_BYPASS, - flashBypass.hostname + - ' is known to host Flash files which allow to bypass this CSP.', - Severity.HIGH, effectiveObjectSrcDirective, value)); - } else if (effectiveObjectSrcDirective === Directive.OBJECT_SRC) { - violations.push(new Finding( - Type.OBJECT_ALLOWLIST_BYPASS, - `Can you restrict object-src to 'none' only?`, Severity.MEDIUM_MAYBE, - effectiveObjectSrcDirective, value)); + const url = '//' + utils.getSchemeFreeUrl(value); + const flashBypass = utils.matchWildcardUrls(url, flash.URLS); + + if (flashBypass) { + violations.push(new Finding( + Type.OBJECT_ALLOWLIST_BYPASS, + flashBypass.hostname + + ' is known to host Flash files which allow to bypass this CSP.', + Severity.HIGH, effectiveObjectSrcDirective, value)); + } else if (effectiveObjectSrcDirective === Directive.OBJECT_SRC) { + violations.push(new Finding( + Type.OBJECT_ALLOWLIST_BYPASS, + `Can you restrict object-src to 'none' only?`, Severity.MEDIUM_MAYBE, + effectiveObjectSrcDirective, value)); + } } } @@ -406,35 +431,37 @@ export function looksLikeIpAddress(maybeIp: string): boolean { * * @param parsedCsp Parsed CSP. */ -export function checkIpSource(parsedCsp: Csp): Finding[] { +export function checkIpSource(parsedCsps: EnforcedCsps): Finding[] { const violations: Finding[] = []; - // Function for checking if directive values contain IP addresses. - const checkIp = (directive: string, directiveValues: string[]) => { - for (const value of directiveValues) { - const host = utils.getHostname(value); - if (looksLikeIpAddress(host)) { - // Check if localhost. - // See 4.8 in https://www.w3.org/TR/CSP2/#match-source-expression - if (host === '127.0.0.1') { - violations.push(new Finding( - Type.IP_SOURCE, - directive + ' directive allows localhost as source. ' + - 'Please make sure to remove this in production environments.', - Severity.INFO, directive, value)); - } else { - violations.push(new Finding( - Type.IP_SOURCE, - directive + ' directive has an IP-Address as source: ' + host + - ' (will be ignored by browsers!). ', - Severity.INFO, directive, value)); + for (const cspChecked of parsedCsps) { + // Function for checking if directive values contain IP addresses. + const checkIp = (directive: string, directiveValues: string[]) => { + for (const value of directiveValues) { + const host = utils.getHostname(value); + if (looksLikeIpAddress(host)) { + // Check if localhost. + // See 4.8 in https://www.w3.org/TR/CSP2/#match-source-expression + if (host === '127.0.0.1') { + violations.push(new Finding( + Type.IP_SOURCE, + directive + ' directive allows localhost as source. ' + + 'Please make sure to remove this in production environments.', + Severity.INFO, directive, value)); + } else { + violations.push(new Finding( + Type.IP_SOURCE, + directive + ' directive has an IP-Address as source: ' + host + + ' (will be ignored by browsers!). ', + Severity.INFO, directive, value)); + } } } - } - }; + }; - // Apply check to values of all directives. - utils.applyCheckFunktionToDirectives(parsedCsp, checkIp); + // Apply check to values of all directives. + utils.applyCheckFunktionToDirectives(cspChecked, checkIp); + } return violations; } @@ -448,34 +475,36 @@ export function checkIpSource(parsedCsp: Csp): Finding[] { * * @param parsedCsp Parsed CSP. */ -export function checkDeprecatedDirective(parsedCsp: Csp): Finding[] { +export function checkDeprecatedDirective(parsedCsps: EnforcedCsps): Finding[] { const violations = []; - // More details: https://www.chromestatus.com/feature/5769374145183744 - if (Directive.REFLECTED_XSS in parsedCsp.directives) { - violations.push(new Finding( - Type.DEPRECATED_DIRECTIVE, - 'reflected-xss is deprecated since CSP2. ' + - 'Please, use the X-XSS-Protection header instead.', - Severity.INFO, Directive.REFLECTED_XSS)); - } + for (const cspChecked of parsedCsps) { + // More details: https://www.chromestatus.com/feature/5769374145183744 + if (Directive.REFLECTED_XSS in cspChecked.directives) { + violations.push(new Finding( + Type.DEPRECATED_DIRECTIVE, + 'reflected-xss is deprecated since CSP2. ' + + 'Please, use the X-XSS-Protection header instead.', + Severity.INFO, Directive.REFLECTED_XSS)); + } - // More details: https://www.chromestatus.com/feature/5680800376815616 - if (Directive.REFERRER in parsedCsp.directives) { - violations.push(new Finding( - Type.DEPRECATED_DIRECTIVE, - 'referrer is deprecated since CSP2. ' + - 'Please, use the Referrer-Policy header instead.', - Severity.INFO, Directive.REFERRER)); - } + // More details: https://www.chromestatus.com/feature/5680800376815616 + if (Directive.REFERRER in cspChecked.directives) { + violations.push(new Finding( + Type.DEPRECATED_DIRECTIVE, + 'referrer is deprecated since CSP2. ' + + 'Please, use the Referrer-Policy header instead.', + Severity.INFO, Directive.REFERRER)); + } - // More details: https://github.com/w3c/webappsec-csp/pull/327 - if (Directive.DISOWN_OPENER in parsedCsp.directives) { - violations.push(new Finding( - Type.DEPRECATED_DIRECTIVE, - 'disown-opener is deprecated since CSP3. ' + - 'Please, use the Cross Origin Opener Policy header instead.', - Severity.INFO, Directive.DISOWN_OPENER)); + // More details: https://github.com/w3c/webappsec-csp/pull/327 + if (Directive.DISOWN_OPENER in cspChecked.directives) { + violations.push(new Finding( + Type.DEPRECATED_DIRECTIVE, + 'disown-opener is deprecated since CSP3. ' + + 'Please, use the Cross Origin Opener Policy header instead.', + Severity.INFO, Directive.DISOWN_OPENER)); + } } return violations; } @@ -490,35 +519,37 @@ export function checkDeprecatedDirective(parsedCsp: Csp): Finding[] { * * @param parsedCsp Parsed CSP. */ -export function checkNonceLength(parsedCsp: Csp): Finding[] { +export function checkNonceLength(parsedCsps: EnforcedCsps): Finding[] { const noncePattern = new RegExp('^\'nonce-(.+)\'$'); const violations: Finding[] = []; - utils.applyCheckFunktionToDirectives( - parsedCsp, (directive, directiveValues) => { - for (const value of directiveValues) { - const match = value.match(noncePattern); - if (!match) { - continue; - } - // Not a nonce. - - const nonceValue = match[1]; - if (nonceValue.length < 8) { - violations.push(new Finding( - Type.NONCE_LENGTH, - 'Nonces should be at least 8 characters long.', Severity.MEDIUM, - directive, value)); - } - - if (!csp.isNonce(value, true)) { - violations.push(new Finding( - Type.NONCE_CHARSET, - 'Nonces should only use the base64 charset.', Severity.INFO, - directive, value)); + for (const cspChecked of parsedCsps) { + utils.applyCheckFunktionToDirectives( + cspChecked, (directive, directiveValues) => { + for (const value of directiveValues) { + const match = value.match(noncePattern); + if (!match) { + continue; + } + // Not a nonce. + + const nonceValue = match[1]; + if (nonceValue.length < 8) { + violations.push(new Finding( + Type.NONCE_LENGTH, + 'Nonces should be at least 8 characters long.', Severity.MEDIUM, + directive, value)); + } + + if (!csp.isNonce(value, true)) { + violations.push(new Finding( + Type.NONCE_CHARSET, + 'Nonces should only use the base64 charset.', Severity.INFO, + directive, value)); + } } - } - }); + }); + } return violations; } @@ -533,46 +564,72 @@ export function checkNonceLength(parsedCsp: Csp): Finding[] { * * @param parsedCsp Parsed CSP. */ -export function checkSrcHttp(parsedCsp: Csp): Finding[] { +export function checkSrcHttp(parsedCsps: EnforcedCsps): Finding[] { const violations: Finding[] = []; - utils.applyCheckFunktionToDirectives( - parsedCsp, (directive, directiveValues) => { + const directivesForcingHttps: string[] = []; + const directivesWithViolation: Record = {}; + + for (const cspChecked of parsedCsps) { + utils.applyCheckFunktionToDirectives( + cspChecked, (directive, directiveValues) => { for (const value of directiveValues) { - const description = directive === Directive.REPORT_URI ? - 'Use HTTPS to send violation reports securely.' : - 'Allow only resources downloaded over HTTPS.'; - if (value.startsWith('http://')) { - violations.push(new Finding( - Type.SRC_HTTP, description, Severity.MEDIUM, directive, value)); + if (value == 'https:') { + directivesForcingHttps.push(directive); + } else if (value.startsWith('http://')) { + // Check if reporting violations via http:// is allowed + if (directive === Directive.REPORT_URI) { + violations.push(new Finding( + Type.SRC_HTTP, 'Use HTTPS to send violation reports securely.', Severity.MEDIUM, Directive.REPORT_URI, value)); + } else { + directivesWithViolation[directive] = directivesWithViolation[directive] ? directivesWithViolation[directive] : []; + directivesWithViolation[directive].push(directive); + } } } }); + } + for (const directive in directivesWithViolation) { + for (const value in directivesWithViolation[directive]) { + if (directive in directivesForcingHttps) { + violations.push(new Finding( + Type.CONFLICTING_DIRECTIVES, 'Resources are only allowed over HTTPS but HTTP hosts are specified.', Severity.LOW, directive, value)); + } else { + violations.push(new Finding( + Type.SRC_HTTP, 'Allow only resources downloaded over HTTPS.', Severity.MEDIUM, directive, value)); + } + } + } + return violations; } /** * Checks if the policy has configured reporting in a robust manner. */ -export function checkHasConfiguredReporting(parsedCsp: Csp): Finding[] { - const reportUriValues: string[] = - parsedCsp.directives[Directive.REPORT_URI] || []; - if (reportUriValues.length > 0) { - return []; - } +export function checkHasConfiguredReporting(parsedCsps: EnforcedCsps): Finding[] { + const findings: Finding[] = []; - const reportToValues: string[] = - parsedCsp.directives[Directive.REPORT_TO] || []; - if (reportToValues.length > 0) { - return [new Finding( - Type.REPORT_TO_ONLY, - `This CSP policy only provides a reporting destination via the 'report-to' directive. This directive is only supported in Chromium-based browsers so it is recommended to also use a 'report-uri' directive.`, - Severity.INFO, Directive.REPORT_TO)]; + for (const cspChecked of parsedCsps) { + const reportUriValues: string[] = cspChecked.directives[Directive.REPORT_URI] || []; + if (reportUriValues.length > 0) { + continue; + } + + const reportToValues: string[] = cspChecked.directives[Directive.REPORT_TO] || []; + if (reportToValues.length > 0) { + findings.push(new Finding( + Type.REPORT_TO_ONLY, + `This CSP policy only provides a reporting destination via the 'report-to' directive. This directive is only supported in Chromium-based browsers so it is recommended to also use a 'report-uri' directive.`, + Severity.INFO, Directive.REPORT_TO)); + } else { + findings.push(new Finding( + Type.REPORTING_DESTINATION_MISSING, + 'This CSP policy does not configure a reporting destination. This makes it difficult to maintain the CSP policy over time and monitor for any breakages.', + Severity.INFO, Directive.REPORT_URI)); + } } - return [new Finding( - Type.REPORTING_DESTINATION_MISSING, - 'This CSP policy does not configure a reporting destination. This makes it difficult to maintain the CSP policy over time and monitor for any breakages.', - Severity.INFO, Directive.REPORT_URI)]; -} + return findings; +} \ No newline at end of file diff --git a/checks/security_checks_test.ts b/checks/security_checks_test.ts index 71a6098..2b6e5ce 100644 --- a/checks/security_checks_test.ts +++ b/checks/security_checks_test.ts @@ -19,7 +19,7 @@ import {Directive, Version} from '../csp'; - import {Finding, Severity} from '../finding'; + import {Finding, Severity, Type} from '../finding'; import {CspParser} from '../parser'; import {CheckerFunction} from './checker'; @@ -32,7 +32,7 @@ * @param checkFunction check. */ function checkCsp(test: string, checkFunction: CheckerFunction): Finding[] { - const parsedCsp = (new CspParser(test)).csp; + const parsedCsp = (new CspParser(test)).csps; return checkFunction(parsedCsp); } @@ -62,13 +62,13 @@ it('CheckScriptUnsafeInlineWithNonce', () => { const test = 'script-src \'unsafe-inline\' \'nonce-foobar\''; - const parsedCsp = (new CspParser(test)).csp; - - let effectiveCsp = parsedCsp.getEffectiveCsp(Version.CSP1); + const parsedCsp = (new CspParser(test)).csps; + + let effectiveCsp = parsedCsp.getEffectiveCsps(Version.CSP1); let violations = securityChecks.checkScriptUnsafeInline(effectiveCsp); expect(violations.length).toBe(1); - - effectiveCsp = parsedCsp.getEffectiveCsp(Version.CSP3); + + effectiveCsp = parsedCsp.getEffectiveCsps(Version.CSP3); violations = securityChecks.checkScriptUnsafeInline(effectiveCsp); expect(violations.length).toBe(0); }); @@ -424,6 +424,25 @@ expect(violations.every((v) => v.severity === Severity.MEDIUM)).toBeTrue(); }); + /** Tests for csp.securityChecks.checkSrcHttpWithHttps */ + it('CheckSrcHttp_whenMultipleReportingPolicies', () => { + const test = + 'script-src http://foo.bar https://test.com; report-uri http://test.com; report-uri https://test.com;'; + + const violations = checkCsp(test, securityChecks.checkSrcHttp); + expect(violations.length).toBe(2); + expect(violations.every((v) => v.severity === Severity.MEDIUM)).toBeTrue(); + }); + + /** Tests for csp.securityChecks.checkSrcHttps */ + it('CheckSrcHttp_whenReportingPolicyHttps', () => { + const test = + 'script-src http://foo.bar https://test.com; report-uri https://test.com'; + + const violations = checkCsp(test, securityChecks.checkSrcHttp); + expect(violations.length).toBe(1); + }); + /** Tests for csp.securityChecks.checkHasConfiguredReporting */ it('CheckHasConfiguredReporting_whenNoReporting', () => { const test = 'script-src \'nonce-aaaaaaaaaa\''; @@ -433,6 +452,7 @@ expect(violations.length).toBe(1); expect(violations[0].severity).toBe(Severity.INFO); + expect(violations[0].type).toBe(Type.REPORTING_DESTINATION_MISSING); expect(violations[0].directive).toBe('report-uri'); }); @@ -444,6 +464,7 @@ expect(violations.length).toBe(1); expect(violations[0].severity).toBe(Severity.INFO); + expect(violations[0].type).toBe(Type.REPORT_TO_ONLY); expect(violations[0].directive).toBe('report-to'); }); @@ -465,4 +486,38 @@ expect(violations.length).toBe(0); }); + + it('CheckHasConfiguredReporting_whenMultiplePolicies', () => { + const test = + 'script-src \'nonce-aaaaaaaaaa\'; report-uri url, style-src \'self\'; report-uri otheruri'; + + const violations = + checkCsp(test, securityChecks.checkHasConfiguredReporting); + + expect(violations.length).toBe(0); + }); + + it('CheckHasConfiguredReporting_whenMultiplePoliciesNoReporting', () => { + const test = + 'script-src \'nonce-aaaaaaaaaa\'; report-uri url, style-src \'self\''; + + const violations = checkCsp(test, securityChecks.checkHasConfiguredReporting); + + expect(violations.length).toBe(1); + expect(violations[0].severity).toBe(Severity.INFO); + expect(violations[0].type).toBe(Type.REPORTING_DESTINATION_MISSING); + expect(violations[0].directive).toBe('report-uri'); + }); + + it('CheckHasConfiguredReporting_whenMultiplePoliciesReportTo', () => { + const test = + 'script-src \'nonce-aaaaaaaaaa\'; report-uri url, style-src \'self\'; report-to test'; + + const violations = checkCsp(test, securityChecks.checkHasConfiguredReporting); + + expect(violations.length).toBe(1); + expect(violations[0].severity).toBe(Severity.INFO); + expect(violations[0].type).toBe(Type.REPORT_TO_ONLY); + expect(violations[0].directive).toBe('report-to'); + }); }); \ No newline at end of file diff --git a/checks/strictcsp_checks.ts b/checks/strictcsp_checks.ts index 15d5c37..d337d07 100644 --- a/checks/strictcsp_checks.ts +++ b/checks/strictcsp_checks.ts @@ -28,6 +28,7 @@ import * as csp from '../csp'; import {Csp, Keyword} from '../csp'; +import { EnforcedCsps } from '../enforced_csps'; import {Finding, Severity, Type} from '../finding'; @@ -40,23 +41,26 @@ import {Finding, Severity, Type} from '../finding'; * * @param parsedCsp A parsed csp. */ -export function checkStrictDynamic(parsedCsp: Csp): Finding[] { - const directiveName = - parsedCsp.getEffectiveDirective(csp.Directive.SCRIPT_SRC); - const values: string[] = parsedCsp.directives[directiveName] || []; - - const schemeOrHostPresent = values.some((v) => !v.startsWith('\'')); - - // Check if strict-dynamic is present in case a host/scheme allowlist is used. - if (schemeOrHostPresent && !values.includes(Keyword.STRICT_DYNAMIC)) { - return [new Finding( - Type.STRICT_DYNAMIC, - 'Host allowlists can frequently be bypassed. Consider using ' + - '\'strict-dynamic\' in combination with CSP nonces or hashes.', - Severity.STRICT_CSP, directiveName)]; +export function checkStrictDynamic(parsedCsps: EnforcedCsps): Finding[] { + const directiveName = parsedCsps.getEffectiveDirective(csp.Directive.SCRIPT_SRC); + const findings: Finding[] = []; + + for (const csp of parsedCsps) { + const values: string[] = csp.directives[directiveName] || []; + + const schemeOrHostPresent = values.some((v) => !v.startsWith('\'')); + + // Check if strict-dynamic is present in case a host/scheme allowlist is used. + if (schemeOrHostPresent && !values.includes(Keyword.STRICT_DYNAMIC)) { + findings.push(new Finding( + Type.STRICT_DYNAMIC, + 'Host allowlists can frequently be bypassed. Consider using ' + + '\'strict-dynamic\' in combination with CSP nonces or hashes.', + Severity.STRICT_CSP, directiveName)); + } } - return []; + return findings; } @@ -68,21 +72,24 @@ export function checkStrictDynamic(parsedCsp: Csp): Finding[] { * * @param parsedCsp A parsed csp. */ -export function checkStrictDynamicNotStandalone(parsedCsp: Csp): Finding[] { - const directiveName = - parsedCsp.getEffectiveDirective(csp.Directive.SCRIPT_SRC); - const values: string[] = parsedCsp.directives[directiveName] || []; - - if (values.includes(Keyword.STRICT_DYNAMIC) && - (!parsedCsp.policyHasScriptNonces() && - !parsedCsp.policyHasScriptHashes())) { - return [new Finding( - Type.STRICT_DYNAMIC_NOT_STANDALONE, - '\'strict-dynamic\' without a CSP nonce/hash will block all scripts.', - Severity.INFO, directiveName)]; +export function checkStrictDynamicNotStandalone(parsedCsps: EnforcedCsps): Finding[] { + const directiveName = parsedCsps.getEffectiveDirective(csp.Directive.SCRIPT_SRC); + const findings: Finding[] = []; + + for (const csp of parsedCsps) { + const values: string[] = csp.directives[directiveName] || []; + + if (values.includes(Keyword.STRICT_DYNAMIC) && + (!parsedCsps.policyHasScriptNonces() && + !parsedCsps.policyHasScriptHashes())) { + findings.push(new Finding( + Type.STRICT_DYNAMIC_NOT_STANDALONE, + '\'strict-dynamic\' without a CSP nonce/hash will block all scripts.', + Severity.INFO, directiveName)); + } } - return []; + return findings; } @@ -96,25 +103,28 @@ export function checkStrictDynamicNotStandalone(parsedCsp: Csp): Finding[] { * * @param parsedCsp A parsed csp. */ -export function checkUnsafeInlineFallback(parsedCsp: Csp): Finding[] { - if (!parsedCsp.policyHasScriptNonces() && - !parsedCsp.policyHasScriptHashes()) { +export function checkUnsafeInlineFallback(parsedCsps: EnforcedCsps): Finding[] { + if (!parsedCsps.policyHasScriptNonces() && + !parsedCsps.policyHasScriptHashes()) { return []; } - const directiveName = - parsedCsp.getEffectiveDirective(csp.Directive.SCRIPT_SRC); - const values: string[] = parsedCsp.directives[directiveName] || []; + const directiveName = parsedCsps.getEffectiveDirective(csp.Directive.SCRIPT_SRC); + const findings: Finding[] = []; - if (!values.includes(Keyword.UNSAFE_INLINE)) { - return [new Finding( - Type.UNSAFE_INLINE_FALLBACK, - 'Consider adding \'unsafe-inline\' (ignored by browsers supporting ' + - 'nonces/hashes) to be backward compatible with older browsers.', - Severity.STRICT_CSP, directiveName)]; + for (const csp of parsedCsps) { + const values: string[] = csp.directives[directiveName] || []; + + if (!values.includes(Keyword.UNSAFE_INLINE)) { + findings.push(new Finding( + Type.UNSAFE_INLINE_FALLBACK, + 'Consider adding \'unsafe-inline\' (ignored by browsers supporting ' + + 'nonces/hashes) to be backward compatible with older browsers.', + Severity.STRICT_CSP, directiveName)); + } } - return []; + return findings; } @@ -129,27 +139,30 @@ export function checkUnsafeInlineFallback(parsedCsp: Csp): Finding[] { * * @param parsedCsp A parsed csp. */ -export function checkAllowlistFallback(parsedCsp: Csp): Finding[] { - const directiveName = - parsedCsp.getEffectiveDirective(csp.Directive.SCRIPT_SRC); - const values: string[] = parsedCsp.directives[directiveName] || []; - - if (!values.includes(Keyword.STRICT_DYNAMIC)) { - return []; +export function checkAllowlistFallback(parsedCsps: EnforcedCsps): Finding[] { + const directiveName = parsedCsps.getEffectiveDirective(csp.Directive.SCRIPT_SRC); + const findings: Finding[] = []; + + for (const csp of parsedCsps) { + const values: string[] = csp.directives[directiveName] || []; + + if (!values.includes(Keyword.STRICT_DYNAMIC)) { + return []; + } + + // Check if there's already an allowlist (url scheme or url) + if (!values.some( + (v) => ['http:', 'https:', '*'].includes(v) || v.includes('.'))) { + findings.push(new Finding( + Type.ALLOWLIST_FALLBACK, + 'Consider adding https: and http: url schemes (ignored by browsers ' + + 'supporting \'strict-dynamic\') to be backward compatible with older ' + + 'browsers.', + Severity.STRICT_CSP, directiveName)); + } } - // Check if there's already an allowlist (url scheme or url) - if (!values.some( - (v) => ['http:', 'https:', '*'].includes(v) || v.includes('.'))) { - return [new Finding( - Type.ALLOWLIST_FALLBACK, - 'Consider adding https: and http: url schemes (ignored by browsers ' + - 'supporting \'strict-dynamic\') to be backward compatible with older ' + - 'browsers.', - Severity.STRICT_CSP, directiveName)]; - } - - return []; + return findings; } @@ -161,19 +174,21 @@ export function checkAllowlistFallback(parsedCsp: Csp): Finding[] { * * @param parsedCsp A parsed csp. */ -export function checkRequiresTrustedTypesForScripts(parsedCsp: Csp): Finding[] { - const directiveName = - parsedCsp.getEffectiveDirective(csp.Directive.REQUIRE_TRUSTED_TYPES_FOR); - const values: string[] = parsedCsp.directives[directiveName] || []; - - if (!values.includes(csp.TrustedTypesSink.SCRIPT)) { - return [new Finding( - Type.REQUIRE_TRUSTED_TYPES_FOR_SCRIPTS, - 'Consider requiring Trusted Types for scripts to lock down DOM XSS ' + - 'injection sinks. You can do this by adding ' + - '"require-trusted-types-for \'script\'" to your policy.', - Severity.INFO, csp.Directive.REQUIRE_TRUSTED_TYPES_FOR)]; +export function checkRequiresTrustedTypesForScripts(parsedCsps: EnforcedCsps): Finding[] { + + for (const cspChecked of parsedCsps) { + const directiveName = cspChecked.getEffectiveDirective(csp.Directive.REQUIRE_TRUSTED_TYPES_FOR); + const values: string[] = cspChecked.directives[directiveName] || []; + + if (values.includes(csp.TrustedTypesSink.SCRIPT)) { + return []; + } } - return []; + return [new Finding( + Type.REQUIRE_TRUSTED_TYPES_FOR_SCRIPTS, + 'Consider requiring Trusted Types for scripts to lock down DOM XSS ' + + 'injection sinks. You can do this by adding ' + + '"require-trusted-types-for \'script\'" to your policy.', + Severity.INFO, csp.Directive.REQUIRE_TRUSTED_TYPES_FOR)]; } diff --git a/checks/strictcsp_checks_test.ts b/checks/strictcsp_checks_test.ts index 056c6f0..fc524a8 100644 --- a/checks/strictcsp_checks_test.ts +++ b/checks/strictcsp_checks_test.ts @@ -31,7 +31,7 @@ import * as strictcspChecks from './strictcsp_checks'; * @param checkFunction check. */ function checkCsp(test: string, checkFunction: CheckerFunction): Finding[] { - const parsedCsp = (new CspParser(test)).csp; + const parsedCsp = (new CspParser(test)).csps; return checkFunction(parsedCsp); } diff --git a/csp_test.ts b/csp_test.ts index a7b1650..dd37ecb 100644 --- a/csp_test.ts +++ b/csp_test.ts @@ -29,7 +29,7 @@ describe('Test Csp', () => { 'https://example.com/foo.js foo.bar; ' + 'img-src \'self\' https: data: blob:; '; - const parsed = (new CspParser(testCsp)).csp; + const parsed = (new CspParser(testCsp)).csps[0]; expect(parsed.convertToString()).toBe(testCsp); }); @@ -37,7 +37,7 @@ describe('Test Csp', () => { const testCsp = 'default-src \'unsafe-inline\' \'strict-dynamic\' \'nonce-123\' ' + '\'sha256-foobar\' \'self\'; report-to foo.bar; worker-src *; manifest-src *'; - const parsed = (new CspParser(testCsp)).csp; + const parsed = (new CspParser(testCsp)).csps[0]; const effectiveCsp = parsed.getEffectiveCsp(Version.CSP1); expect(effectiveCsp.directives[Directive.DEFAULT_SRC]).toEqual([ @@ -52,7 +52,7 @@ describe('Test Csp', () => { const testCsp = 'default-src \'unsafe-inline\' \'strict-dynamic\' \'nonce-123\' ' + '\'sha256-foobar\' \'self\'; report-to foo.bar; worker-src *; manifest-src *'; - const parsed = (new CspParser(testCsp)).csp; + const parsed = (new CspParser(testCsp)).csps[0]; const effectiveCsp = parsed.getEffectiveCsp(Version.CSP2); expect(effectiveCsp.directives[Directive.DEFAULT_SRC]).toEqual([ @@ -67,7 +67,7 @@ describe('Test Csp', () => { const testCsp = 'default-src \'unsafe-inline\' \'strict-dynamic\' \'nonce-123\' ' + '\'sha256-foobar\' \'self\'; report-to foo.bar; worker-src *; manifest-src *'; - const parsed = (new CspParser(testCsp)).csp; + const parsed = (new CspParser(testCsp)).csps[0]; const effectiveCsp = parsed.getEffectiveCsp(Version.CSP3); expect(effectiveCsp.directives[Directive.DEFAULT_SRC]).toEqual([ @@ -81,7 +81,7 @@ describe('Test Csp', () => { it('GetEffectiveDirective', () => { const testCsp = 'default-src https:; script-src foo.bar'; - const parsed = (new CspParser(testCsp)).csp; + const parsed = (new CspParser(testCsp)).csps[0]; const script = parsed.getEffectiveDirective(Directive.SCRIPT_SRC); expect(script).toBe(Directive.SCRIPT_SRC); @@ -92,7 +92,7 @@ describe('Test Csp', () => { it('GetEffectiveDirectives', () => { const testCsp = 'default-src https:; script-src foo.bar'; - const parsed = (new CspParser(testCsp)).csp; + const parsed = (new CspParser(testCsp)).csps[0]; const directives = parsed.getEffectiveDirectives( [Directive.SCRIPT_SRC, Directive.STYLE_SRC]); @@ -102,7 +102,7 @@ describe('Test Csp', () => { it('PolicyHasScriptNoncesScriptSrcWithNonce', () => { const testCsp = 'default-src https:; script-src \'nonce-test123\''; - const parsed = (new CspParser(testCsp)).csp; + const parsed = (new CspParser(testCsp)).csps[0]; expect(parsed.policyHasScriptNonces()).toBeTrue(); }); @@ -111,7 +111,7 @@ describe('Test Csp', () => { it('PolicyHasScriptNoncesNoNonce', () => { const testCsp = 'default-src https: \'nonce-ignored\'; script-src nonce-invalid'; - const parsed = (new CspParser(testCsp)).csp; + const parsed = (new CspParser(testCsp)).csps[0]; expect(parsed.policyHasScriptNonces()).toBeFalse(); }); @@ -119,7 +119,7 @@ describe('Test Csp', () => { it('PolicyHasScriptHashesScriptSrcWithHash', () => { const testCsp = 'default-src https:; script-src \'sha256-asdfASDF\''; - const parsed = (new CspParser(testCsp)).csp; + const parsed = (new CspParser(testCsp)).csps[0]; expect(parsed.policyHasScriptHashes()).toBeTrue(); }); @@ -128,7 +128,7 @@ describe('Test Csp', () => { it('PolicyHasScriptHashesNoHash', () => { const testCsp = 'default-src https: \'nonce-ignored\'; script-src sha256-invalid'; - const parsed = (new CspParser(testCsp)).csp; + const parsed = (new CspParser(testCsp)).csps[0]; expect(parsed.policyHasScriptHashes()).toBeFalse(); }); @@ -136,7 +136,7 @@ describe('Test Csp', () => { it('PolicyHasStrictDynamicScriptSrcWithStrictDynamic', () => { const testCsp = 'default-src https:; script-src \'strict-dynamic\''; - const parsed = (new CspParser(testCsp)).csp; + const parsed = (new CspParser(testCsp)).csps[0]; expect(parsed.policyHasStrictDynamic()).toBeTrue(); }); @@ -144,7 +144,7 @@ describe('Test Csp', () => { it('PolicyHasStrictDynamicDefaultSrcWithStrictDynamic', () => { const testCsp = 'default-src https \'strict-dynamic\''; - const parsed = (new CspParser(testCsp)).csp; + const parsed = (new CspParser(testCsp)).csps[0]; expect(parsed.policyHasStrictDynamic()).toBeTrue(); }); @@ -152,7 +152,7 @@ describe('Test Csp', () => { it('PolicyHasStrictDynamicNoStrictDynamic', () => { const testCsp = 'default-src \'strict-dynamic\'; script-src foo.bar'; - const parsed = (new CspParser(testCsp)).csp; + const parsed = (new CspParser(testCsp)).csps[0]; expect(parsed.policyHasStrictDynamic()).toBeFalse(); }); @@ -224,7 +224,7 @@ describe('Test Csp', () => { it('ParseNavigateTo', () => { const testCsp = 'navigate-to \'self\'; script-src \'nonce-foo\''; - const parsed = (new CspParser(testCsp)).csp; + const parsed = (new CspParser(testCsp)).csps[0]; expect(parsed.policyHasStrictDynamic()).toBeFalse(); expect(parsed.policyHasScriptNonces()).toBeTrue(); @@ -232,7 +232,7 @@ describe('Test Csp', () => { it('ParseWebRtc', () => { const testCsp = 'web-rtc \'allow\'; script-src \'nonce-foo\''; - const parsed = (new CspParser(testCsp)).csp; + const parsed = (new CspParser(testCsp)).csps[0]; expect(parsed.policyHasStrictDynamic()).toBeFalse(); expect(parsed.policyHasScriptNonces()).toBeTrue(); diff --git a/enforced_csps.ts b/enforced_csps.ts new file mode 100644 index 0000000..e50b265 --- /dev/null +++ b/enforced_csps.ts @@ -0,0 +1,210 @@ +/** + * @fileoverview Enforce CSP definitions and helper functions. + * @author mnadeau@gosecure.net (Maxime Nadeau) + * + * @license + * Copyright 2016 Google Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +import {Csp, Directive, Keyword, Version, isHash, isNonce} from './csp'; +import { Finding, Severity, Type } from './finding'; + +/** + * Enforced Content Security Policy object. + * - https://www.w3.org/TR/CSP2/#enforcing-multiple-policies + */ +export class EnforcedCsps extends Array { + /** + * Clones a CSP object. + * @return clone of parsedCsp. + */ + clone(): EnforcedCsps { + const clone = new EnforcedCsps(); + for (const csp of this) { + clone.push(csp.clone()); + } + + return clone; + } + + /** + * Converts the enforced CSPs back into a string array. + * @return The list of CSP string. + */ + convertToStrings(): string[] { + let retString: string[] = []; + + for (const appliedCsp of this) { + retString.push(appliedCsp.convertToString()); + } + + return retString; + } + + /** + * Returns the passed directive if present in this CSP or default-src + * otherwise. + * @param directive The CSP directive to look for. + * @return The effective directive. + */ + getEffectiveDirective(directive: string): string { + // Look in each CSP to find the directive + for (const csp of this) { + if (directive in csp.directives) { + return directive; + } + } + + return Directive.DEFAULT_SRC; + } + + /** + * Returns CSP as it would be seen by a UA supporting a specific CSP version. + * @param cspVersion CSP. + * @param optFindings findings about ignored directive values will be added + * to this array, if passed. (e.g. CSP2 ignores 'unsafe-inline' in + * presence of a nonce or a hash) + * @return The effective CSP. + */ + getEffectiveCsps(cspVersion: Version, optFindings?: Finding[]): EnforcedCsps { + const findings = optFindings || []; + const effectiveCsps = this.clone(); + const directive = effectiveCsps.getEffectiveDirective(Directive.SCRIPT_SRC); + + for (let index = 0; index < effectiveCsps.length; index++) { + const values = this[index].directives[directive] || []; + const effectiveCspValues = effectiveCsps[index].directives[directive]; + + if (effectiveCspValues && + (effectiveCsps[index].policyHasScriptNonces() || + effectiveCsps[index].policyHasScriptHashes())) { + if (cspVersion >= Version.CSP2) { + // Ignore 'unsafe-inline' in CSP >= v2, if a nonce or a hash is present. + if (values.includes(Keyword.UNSAFE_INLINE)) { + arrayRemove(effectiveCspValues, Keyword.UNSAFE_INLINE); + findings.push(new Finding( + Type.IGNORED, + 'unsafe-inline is ignored if a nonce or a hash is present. ' + + '(CSP2 and above)', + Severity.NONE, directive, Keyword.UNSAFE_INLINE)); + } + } else { + // remove nonces and hashes (not supported in CSP < v2). + for (const value of values) { + if (value.startsWith('\'nonce-') || value.startsWith('\'sha')) { + arrayRemove(effectiveCspValues, value); + } + } + } + } + + if (effectiveCspValues && this[index].policyHasStrictDynamic()) { + // Ignore allowlist in CSP >= v3 in presence of 'strict-dynamic'. + if (cspVersion >= Version.CSP3) { + for (const value of values) { + // Because of 'strict-dynamic' all host-source and scheme-source + // expressions, as well as the "'unsafe-inline'" and "'self' + // keyword-sources will be ignored. + // https://w3c.github.io/webappsec-csp/#strict-dynamic-usage + if (!value.startsWith('\'') || value === Keyword.SELF || + value === Keyword.UNSAFE_INLINE) { + arrayRemove(effectiveCspValues, value); + findings.push(new Finding( + Type.IGNORED, + 'Because of strict-dynamic this entry is ignored in CSP3 and above', + Severity.NONE, directive, value)); + } + } + } else { + // strict-dynamic not supported. + arrayRemove(effectiveCspValues, Keyword.STRICT_DYNAMIC); + } + } + + if (cspVersion < Version.CSP3) { + // Remove CSP3 directives from pre-CSP3 policies. + // https://w3c.github.io/webappsec-csp/#changes-from-level-2 + delete effectiveCsps[index].directives[Directive.REPORT_TO]; + delete effectiveCsps[index].directives[Directive.WORKER_SRC]; + delete effectiveCsps[index].directives[Directive.MANIFEST_SRC]; + delete effectiveCsps[index].directives[Directive.TRUSTED_TYPES]; + delete effectiveCsps[index].directives[Directive.REQUIRE_TRUSTED_TYPES_FOR]; + } + } + + return effectiveCsps; + } + + /** + * Checks if this CSP is using nonces for scripts. + * @return true, if this CSP is using script nonces. + */ + policyHasScriptNonces(): boolean { + const directiveName = this.getEffectiveDirective(Directive.SCRIPT_SRC); + + for (const csp of this) { + const values = csp.directives[directiveName] || []; + if (values.some((val) => isNonce(val))) { + return true; + } + } + + return false; + } + + /** + * Checks if this CSP is using hashes for scripts. + * @return true, if this CSP is using script hashes. + */ + policyHasScriptHashes(): boolean { + const directiveName = this.getEffectiveDirective(Directive.SCRIPT_SRC); + + for (const csp of this) { + const values = csp.directives[directiveName] || []; + if (values.some((val) => isHash(val))) { + return true; + } + } + + return false; + } + + /** + * Checks if this CSP is using strict-dynamic. + * @return true, if this CSP is using CSP nonces. + */ + policyHasStrictDynamic(): boolean { + const directiveName = this.getEffectiveDirective(Directive.SCRIPT_SRC); + + for (const csp of this) { + const values = csp.directives[directiveName] || []; + if (values.includes(Keyword.STRICT_DYNAMIC)) { + return true; + } + } + + return false; + } +} + +/** + * Mutate the given array to remove the first instance of the given item + */ +function arrayRemove(arr: T[], item: T): void { + if (arr.includes(item)) { + const idx = arr.findIndex(elem => item === elem); + arr.splice(idx, 1); + } +} diff --git a/enforced_csps_test.ts b/enforced_csps_test.ts new file mode 100644 index 0000000..fc055a1 --- /dev/null +++ b/enforced_csps_test.ts @@ -0,0 +1,50 @@ +/** + * @fileoverview Tests for Enforced CSP object helper functions. + * @author mnadeau@gosecure.net (Maxime Nadeau) + * + * @license + * Copyright 2016 Google Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import 'jasmine'; + +import {EnforcedCsps} from './enforced_csps'; +import {CspParser} from './parser'; + +describe('Test enforced CSPs', () => { + it('ConvertToString with single CSP', () => { + const testCsp = 'default-src \'none\'; ' + + 'script-src \'nonce-unsafefoobar\' \'unsafe-eval\' \'unsafe-inline\' ' + + 'https://example.com/foo.js foo.bar; ' + + 'img-src \'self\' https: data: blob:; '; + + const parsed = (new CspParser(testCsp)).csps; + + let cspStrings: string[] = parsed.convertToStrings(); + expect(cspStrings[0]).toBe(testCsp); + }); + + it('ConvertToString with multiple CSPs', () => { + const testCsp1 = 'default-src \'self\' http://example.com http://example.net; ' + + 'connect-src \'none\'; '; + + const testCsp2 = 'connect-src http://example.com/; script-src http://example.com/; '; + + const parsed = (new CspParser([testCsp1, testCsp2])).csps; + + let cspStrings: string[] = parsed.convertToStrings(); + expect(cspStrings[0]).toBe(testCsp1); + expect(cspStrings[1]).toBe(testCsp2); + }); +}); diff --git a/evaluator.ts b/evaluator.ts index 7a44eab..c6edade 100644 --- a/evaluator.ts +++ b/evaluator.ts @@ -16,13 +16,14 @@ * limitations under the License. */ -import {CheckerFunction} from './checks/checker'; +import { CheckerFunction } from './checks/checker'; import * as parserChecks from './checks/parser_checks'; import * as securityChecks from './checks/security_checks'; import * as strictcspChecks from './checks/strictcsp_checks'; import * as csp from './csp'; -import {Csp, Version} from './csp'; -import {Finding} from './finding'; +import { Csp, Version } from './csp'; +import { EnforcedCsps } from './enforced_csps'; +import { Finding } from './finding'; @@ -33,7 +34,7 @@ import {Finding} from './finding'; */ export class CspEvaluator { version: Version; - csp: Csp; + csps: EnforcedCsps; /** * List of findings reported by checks. @@ -41,19 +42,19 @@ export class CspEvaluator { */ findings: Finding[] = []; /** - * @param parsedCsp A parsed Content Security Policy. + * @param parsedCsp A parsed list of Content Security Policy. * @param cspVersion CSP version to apply checks for. */ - constructor(parsedCsp: Csp, cspVersion?: Version) { + constructor(parsedCsps: EnforcedCsps, cspVersion?: Version) { /** * CSP version. */ this.version = cspVersion || csp.Version.CSP3; /** - * Parsed CSP. + * Parsed CSPs. */ - this.csp = parsedCsp; + this.csps = parsedCsps; } /** @@ -75,18 +76,18 @@ export class CspEvaluator { // supporting a specific version of CSP. // For example a browser supporting only CSP1 will ignore nonces and // therefore 'unsafe-inline' would not get ignored if a policy has nonces. - const effectiveCsp = this.csp.getEffectiveCsp(this.version, this.findings); + const effectiveCsps = this.csps.getEffectiveCsps(this.version, this.findings); // Checks independent of CSP version. if (parsedCspChecks) { for (const check of parsedCspChecks) { - this.findings = this.findings.concat(check(this.csp)); + this.findings = this.findings.concat(check(this.csps)); } } // Checks depenent on CSP version. for (const check of checks) { - this.findings = this.findings.concat(check(effectiveCsp)); + this.findings = this.findings.concat(check(effectiveCsps)); } return this.findings; diff --git a/evaluator_test.ts b/evaluator_test.ts index 14ba17d..8ac0016 100644 --- a/evaluator_test.ts +++ b/evaluator_test.ts @@ -22,26 +22,26 @@ import 'jasmine'; import {Csp} from './csp'; import {CspEvaluator} from './evaluator'; import {Finding, Severity, Type} from './finding'; +import { EnforcedCsps } from './enforced_csps'; describe('Test evaluator', () => { it('CspEvaluator', () => { - const fakeCsp = new Csp(); + const fakeCsp = new EnforcedCsps(); const evaluator = new CspEvaluator(fakeCsp); - expect(evaluator.csp).toBe(fakeCsp); + expect(evaluator.csps).toBe(fakeCsp); }); it('Evaluate', () => { - const fakeCsp = new (Csp)(); const fakeFinding = new (Finding)( - Type.UNKNOWN_DIRECTIVE, 'Fake description', Severity.MEDIUM, - 'fake-directive', 'fake-directive-value'); - const fakeVerifier = (parsedCsp: Csp) => { + Type.UNKNOWN_DIRECTIVE, 'Fake description', Severity.MEDIUM, 'fake-directive', 'fake-directive-value'); + + const fakeVerifier = (parsedCsp: EnforcedCsps) => { return [fakeFinding]; }; + const fakeCsp = new EnforcedCsps(); const evaluator = new (CspEvaluator)(fakeCsp); - const findings = - evaluator.evaluate([fakeVerifier, fakeVerifier], [fakeVerifier]); + const findings = evaluator.evaluate([fakeVerifier, fakeVerifier], [fakeVerifier]); const expectedFindings = [fakeFinding, fakeFinding, fakeFinding]; expect(findings).toEqual(expectedFindings); diff --git a/finding.ts b/finding.ts index e502a49..50cecb1 100644 --- a/finding.ts +++ b/finding.ts @@ -71,6 +71,8 @@ export enum Severity { HIGH_MAYBE = 40, STRICT_CSP = 45, MEDIUM_MAYBE = 50, + LOW = 70, + LOW_MAYBE = 80, INFO = 60, NONE = 100 } @@ -90,12 +92,15 @@ export enum Type { MISSING_DIRECTIVES = 300, SCRIPT_UNSAFE_INLINE, SCRIPT_UNSAFE_EVAL, + STYLE_UNSAFE_INLINE, + STYLE_UNSAFE_EVAL, PLAIN_URL_SCHEMES, PLAIN_WILDCARD, SCRIPT_ALLOWLIST_BYPASS, OBJECT_ALLOWLIST_BYPASS, NONCE_LENGTH, IP_SOURCE, + CONFLICTING_DIRECTIVES, DEPRECATED_DIRECTIVE, SRC_HTTP, diff --git a/package-lock.json b/package-lock.json index c00626c..6ef7de7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,124 +1,530 @@ { "name": "csp_evaluator", - "version": "1.0.4", - "lockfileVersion": 1, + "version": "2.0.0", + "lockfileVersion": 3, "requires": true, - "dependencies": { - "@types/jasmine": { - "version": "3.6.7", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.6.7.tgz", - "integrity": "sha512-8dtfiykrpe4Ysn6ONj0tOjmpDIh1vWxPk80eutSeWmyaJvAZXZ84219fS4gLrvz05eidhp7BP17WVQBaXHSyXQ==", + "packages": { + "": { + "name": "csp_evaluator", + "version": "2.0.0", + "license": "Apache-2.0", + "devDependencies": { + "@types/jasmine": "^5.1.4", + "jasmine": "^5.1.0", + "typescript": "^5.3.3" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@types/jasmine": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-5.1.4.tgz", + "integrity": "sha512-px7OMFO/ncXxixDe1zR13V1iycqWae0MxTaw62RpFlksUi5QuNWgQJFkTQjIOvrmutJbI7Fp2Y2N1F6D2R4G6w==", + "dev": true + }, + "node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" } }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "jasmine": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.7.0.tgz", - "integrity": "sha512-wlzGQ+cIFzMEsI+wDqmOwvnjTvolLFwlcpYLCqSPPH0prOQaW3P+IzMhHYn934l1imNvw07oCyX+vGUv3wmtSQ==", + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jasmine": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-5.1.0.tgz", + "integrity": "sha512-prmJlC1dbLhti4nE4XAPDWmfJesYO15sjGXVp7Cs7Ym5I9Xtwa/hUHxxJXjnpfLO72+ySttA0Ztf8g/RiVnUKw==", "dev": true, - "requires": { - "glob": "^7.1.6", - "jasmine-core": "~3.7.0" + "dependencies": { + "glob": "^10.2.2", + "jasmine-core": "~5.1.0" + }, + "bin": { + "jasmine": "bin/jasmine.js" } }, - "jasmine-core": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.7.1.tgz", - "integrity": "sha512-DH3oYDS/AUvvr22+xUBW62m1Xoy7tUlY1tsxKEJvl5JeJ7q8zd1K5bUwiOxdH+erj6l2vAMM3hV25Xs9/WrmuQ==", + "node_modules/jasmine-core": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.1.1.tgz", + "integrity": "sha512-UrzO3fL7nnxlQXlvTynNAenL+21oUQRlzqQFsA2U11ryb4+NLOCOePZ70PTojEaUKhiFugh7dG0Q+I58xlPdWg==", "dev": true }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/lru-cache": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", "dev": true, - "requires": { - "brace-expansion": "^1.1.7" + "engines": { + "node": "14 || >=16.14" } }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, - "requires": { - "wrappy": "1" + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dev": true, + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "typescript": { + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", "version": "4.2.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz", - "integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } } } } diff --git a/package.json b/package.json index 843e626..ba6222c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "csp_evaluator", - "version": "1.0.4", + "version": "2.0.0", "description": "Evaluate Content Security Policies for a wide range of bypasses and weaknesses", "main": "dist/evaluator.js", "keywords": [ @@ -21,8 +21,8 @@ "test": "tsc && npx jasmine --config=jasmine.json" }, "devDependencies": { - "@types/jasmine": "^3.6.7", - "jasmine": "^3.7.0", - "typescript": "^4.2.3" + "@types/jasmine": "^5.1.4", + "jasmine": "^5.1.0", + "typescript": "^5.3.3" } } diff --git a/parser.ts b/parser.ts index 584f954..449b3ea 100644 --- a/parser.ts +++ b/parser.ts @@ -17,6 +17,7 @@ */ import * as csp from './csp'; +import * as enforcedCsps from './enforced_csps'; @@ -25,41 +26,77 @@ import * as csp from './csp'; * @unrestricted */ export class CspParser { - csp: csp.Csp; + csps: enforcedCsps.EnforcedCsps; + /** * @param unparsedCsp A Content Security Policy as string. */ - constructor(unparsedCsp: string) { + constructor(unparsedCsps: string | string[]) { /** * Parsed CSP */ - this.csp = new csp.Csp(); + this.csps = new enforcedCsps.EnforcedCsps(); + + if (!Array.isArray(unparsedCsps)) { + unparsedCsps = [ (unparsedCsps as string) ]; + } - this.parse(unparsedCsp); + this.parse(unparsedCsps); } /** * Parses a CSP from a string. * @param unparsedCsp CSP as string. */ - parse(unparsedCsp: string): csp.Csp { - // Reset the internal state: - this.csp = new csp.Csp(); + parse(unparsedCspList: string[]): enforcedCsps.EnforcedCsps { + const splitCspList: string[] = []; + unparsedCspList.forEach(policy => { + // For each token returned by splitting list on commas: + const policiesList: string[] = policy.split(','); + + // Let policy be the result of parsing token, with a source of source, and disposition of disposition. + const filteredPoliciesList = policiesList.map(function (el, index) { + // If policy’s directive set is empty, continue. + if (el.trim() !== "") { + return el; + } + }); + + // Append policy to policies. + splitCspList.push(...policiesList); + }); + + splitCspList.forEach(currentCsp => { + this.csps.push(this.parseCsp(currentCsp)); + }); - // Split CSP into directive tokens. + return this.csps; + } + + + parseCsp(unparsedCsp: string): csp.Csp { + let retCsp: csp.Csp = new csp.Csp(); + + // For each token returned by strictly splitting serialized on the U+003B SEMICOLON character (;): const directiveTokens = unparsedCsp.split(';'); for (let i = 0; i < directiveTokens.length; i++) { + // Strip leading and trailing ASCII whitespace from token. const directiveToken = directiveTokens[i].trim(); - // Split directive tokens into directive name and directive values. + // If token is an empty string, or if token is not an ASCII string, continue. + if (directiveToken === "" || !/^[\x00-\xFF]*$/.test(directiveToken)) { + continue; + } + + // Let directive name be the result of collecting a sequence of code points from token which are not ASCII whitespace. + // Let directive value be the result of splitting token on ASCII whitespace. const directiveParts = directiveToken.match(/\S+/g); if (Array.isArray(directiveParts)) { + // Set directive name to be the result of running ASCII lowercase on directive name. const directiveName = directiveParts[0].toLowerCase(); - // If the set of directives already contains a directive whose name is a - // case insensitive match for directive name, ignore this instance of - // the directive and continue to the next token. - if (directiveName in this.csp.directives) { + // If policy’s directive set contains a directive whose name is directive name, continue. + if (directiveName in retCsp.directives) { continue; } @@ -69,16 +106,20 @@ export class CspParser { const directiveValues: string[] = []; for (let directiveValue, j = 1; (directiveValue = directiveParts[j]); j++) { + // Let directive be a new directive whose name is directive name, and value is directive value. directiveValue = normalizeDirectiveValue(directiveValue); if (!directiveValues.includes(directiveValue)) { directiveValues.push(directiveValue); } } - this.csp.directives[directiveName] = directiveValues; + + // Append directive to policy’s directive set. + retCsp.directives[directiveName] = directiveValues; } } - return this.csp; + // Return policy. + return retCsp; } } diff --git a/parser_test.ts b/parser_test.ts index 2a05673..02814d9 100644 --- a/parser_test.ts +++ b/parser_test.ts @@ -37,7 +37,7 @@ describe('Test parser', () => { 'report-uri /csp/test'; const parser = new (CspParser)(validCsp); - const parsedCsp = parser.csp; + const parsedCsp = parser.csps[0]; // check directives const directives = Object.keys(parsedCsp.directives); @@ -91,7 +91,7 @@ describe('Test parser', () => { 'OBJECT-src foo.bar;'; const parser = new (CspParser)(validCsp); - const parsedCsp = parser.csp; + const parsedCsp = parser.csps[0]; // check directives const directives = Object.keys(parsedCsp.directives); @@ -114,7 +114,7 @@ describe('Test parser', () => { 'img-src \'sElf\' HTTPS: Example.com/CaseSensitive;'; const parser = new (CspParser)(validCsp); - const parsedCsp = parser.csp; + const parsedCsp = parser.csps[0]; // check directives const directives = Object.keys(parsedCsp.directives); @@ -141,4 +141,218 @@ describe('Test parser', () => { expect(TEST_ONLY.normalizeDirectiveValue('example.com/TEST')) .toBe('example.com/TEST'); }); + + it('CspParserMultipleDirectives', () => { + const validCsp1 = // Test policy with different features from CSP2. + 'default-src \'none\';' + + 'script-src \'nonce-unsafefoobar\' \'unsafe-eval\' \'unsafe-inline\' \n' + + 'https://example.com/foo.js foo.bar; ' + + 'object-src \'none\';' + + 'img-src \'self\' https: data: blob:;' + + 'style-src \'self\' \'unsafe-inline\' \'sha256-1DCfk1NYWuHMfoobarfoobar=\';' + + 'font-src *;' + + 'child-src *.example.com:9090;' + + 'upgrade-insecure-requests;\n' + + 'report-uri /csp/test'; + + const validCsp2 = // Test policy with different features from CSP2. + 'default-src \'nonce-foobar\';' + + 'script-src https:'; + + const parser = new (CspParser)([validCsp1, validCsp2]); + const parsedCsp = parser.csps; + + // check directives + const directives = Object.keys(parsedCsp[0].directives); + const expectedDirectives = [ + 'default-src', 'script-src', 'object-src', 'img-src', 'style-src', + 'font-src', 'child-src', 'upgrade-insecure-requests', 'report-uri' + ]; + expect(expectedDirectives) + .toEqual(jasmine.arrayWithExactContents(directives)); + + const directives2 = Object.keys(parsedCsp[1].directives); + const expectedDirectives2 = [ + 'default-src', 'script-src' + ]; + expect(expectedDirectives2) + .toEqual(jasmine.arrayWithExactContents(directives2)); + + // check directive values + expect(['\'none\'']) + .toEqual(jasmine.arrayWithExactContents( + parsedCsp[0].directives['default-src'] as string[])); + + expect([ + '\'nonce-unsafefoobar\'', '\'unsafe-eval\'', '\'unsafe-inline\'', + 'https://example.com/foo.js', 'foo.bar' + ]) + .toEqual(jasmine.arrayWithExactContents( + parsedCsp[0].directives['script-src'] as string[])); + + expect(['\'none\'']) + .toEqual(jasmine.arrayWithExactContents( + parsedCsp[0].directives['object-src'] as string[])); + + expect(['\'self\'', 'https:', 'data:', 'blob:']) + .toEqual(jasmine.arrayWithExactContents( + parsedCsp[0].directives['img-src'] as string[])); + + expect([ + '\'self\'', '\'unsafe-inline\'', '\'sha256-1DCfk1NYWuHMfoobarfoobar=\'' + ]) + .toEqual(jasmine.arrayWithExactContents( + parsedCsp[0].directives['style-src'] as string[])); + + expect(['*']).toEqual(jasmine.arrayWithExactContents( + parsedCsp[0].directives['font-src'] as string[])); + + expect(['*.example.com:9090']) + .toEqual(jasmine.arrayWithExactContents( + parsedCsp[0].directives['child-src'] as string[])); + + expect([]).toEqual(jasmine.arrayWithExactContents( + parsedCsp[0].directives['upgrade-insecure-requests'] as string[])); + + expect(['/csp/test']) + .toEqual(jasmine.arrayWithExactContents( + parsedCsp[0].directives['report-uri'] as string[])); + + expect(['\'nonce-foobar\'']) + .toEqual(jasmine.arrayWithExactContents( + parsedCsp[1].directives['default-src'] as string[])); + + expect(['https:']) + .toEqual(jasmine.arrayWithExactContents( + parsedCsp[1].directives['script-src'] as string[])); + }); + + it('CspParserMultipleDirectivesRFC2616', () => { + const validCsp = // Test policy with different features from CSP2. + 'default-src \'none\', default-src \'nonce-foobar\';' + + 'script-src \'nonce-unsafefoobar\' \'unsafe-eval\' \'unsafe-inline\' \n' + + 'https://example.com/foo.js foo.bar, script-src https:; ' + + 'object-src \'none\';' + + 'img-src \'self\' https: data: blob:;' + + 'style-src \'self\' \'unsafe-inline\' \'sha256-1DCfk1NYWuHMfoobarfoobar=\';' + + 'font-src *;' + + 'child-src *.example.com:9090;' + + 'upgrade-insecure-requests;\n' + + 'report-uri /csp/test'; + + const parser = new (CspParser)([validCsp]); + const parsedCsp = parser.csps; + + // check directives + const directives = Object.keys(parsedCsp[0].directives); + const expectedDirectives = [ + 'default-src' + ]; + expect(expectedDirectives) + .toEqual(jasmine.arrayWithExactContents(directives)); + + const directives2 = Object.keys(parsedCsp[1].directives); + const expectedDirectives2 = [ + 'default-src', 'script-src' + ]; + expect(expectedDirectives2) + .toEqual(jasmine.arrayWithExactContents(directives2)); + + const directives3 = Object.keys(parsedCsp[2].directives); + const expectedDirectives3 = [ + 'script-src', 'object-src', 'img-src', 'style-src', + 'font-src', 'child-src', 'upgrade-insecure-requests', 'report-uri' + ]; + expect(expectedDirectives3) + .toEqual(jasmine.arrayWithExactContents(directives3)); + + // check directive values + expect(['\'none\'']) + .toEqual(jasmine.arrayWithExactContents( + parsedCsp[0].directives['default-src'] as string[])); + + expect(['\'nonce-foobar\'']) + .toEqual(jasmine.arrayWithExactContents( + parsedCsp[1].directives['default-src'] as string[])); + + expect([ + '\'nonce-unsafefoobar\'', '\'unsafe-eval\'', '\'unsafe-inline\'', + 'https://example.com/foo.js', 'foo.bar' + ]) + .toEqual(jasmine.arrayWithExactContents( + parsedCsp[1].directives['script-src'] as string[])); + + expect(['https:']) + .toEqual(jasmine.arrayWithExactContents( + parsedCsp[2].directives['script-src'] as string[])); + + expect(['\'none\'']) + .toEqual(jasmine.arrayWithExactContents( + parsedCsp[2].directives['object-src'] as string[])); + + expect(['\'self\'', 'https:', 'data:', 'blob:']) + .toEqual(jasmine.arrayWithExactContents( + parsedCsp[2].directives['img-src'] as string[])); + expect([ + '\'self\'', '\'unsafe-inline\'', '\'sha256-1DCfk1NYWuHMfoobarfoobar=\'' + ]) + .toEqual(jasmine.arrayWithExactContents( + parsedCsp[2].directives['style-src'] as string[])); + + expect(['*']).toEqual(jasmine.arrayWithExactContents( + parsedCsp[2].directives['font-src'] as string[])); + + expect(['*.example.com:9090']) + .toEqual(jasmine.arrayWithExactContents( + parsedCsp[2].directives['child-src'] as string[])); + + expect([]).toEqual(jasmine.arrayWithExactContents( + parsedCsp[2].directives['upgrade-insecure-requests'] as string[])); + + expect(['/csp/test']) + .toEqual(jasmine.arrayWithExactContents( + parsedCsp[2].directives['report-uri'] as string[])); + }); + + it('ParseMultipleDirectivesSimple', () => { + const testCsp1 = 'default-src \'self\' http://example.com http://example.net; ' + + 'connect-src \'none\'; '; + + const testCsp2 = 'connect-src http://example.com/; script-src http://example.com/; '; + + const parsed = (new CspParser([testCsp1, testCsp2])).csps; + expect(parsed.length).toBe(2); + + // check directives + const directives1 = Object.keys(parsed[0].directives); + const expectedDirectives1 = [ + 'default-src', 'connect-src' + ]; + expect(expectedDirectives1) + .toEqual(jasmine.arrayWithExactContents(directives1)); + + const directives2 = Object.keys(parsed[1].directives); + const expectedDirectives2 = [ + 'connect-src', 'script-src' + ]; + expect(expectedDirectives2) + .toEqual(jasmine.arrayWithExactContents(directives2)); + + // check directive values + expect(['\'self\'', 'http://example.com', 'http://example.net']) + .toEqual(jasmine.arrayWithExactContents( + parsed[0].directives['default-src'] as string[])); + + expect(['\'none\'']) + .toEqual(jasmine.arrayWithExactContents( + parsed[0].directives['connect-src'] as string[])); + + expect(['http://example.com/']) + .toEqual(jasmine.arrayWithExactContents( + parsed[1].directives['connect-src'] as string[])); + + expect(['http://example.com/']) + .toEqual(jasmine.arrayWithExactContents( + parsed[1].directives['script-src'] as string[])); + }); }); From 1cae6004f1bfaba1f250c9d5a98800987f05fdf1 Mon Sep 17 00:00:00 2001 From: MaxNad Date: Fri, 5 Jan 2024 16:01:33 -0500 Subject: [PATCH 03/12] Remove lighthouse for now --- lighthouse/lighthouse_checks.ts | 143 ------- lighthouse/lighthouse_checks_test.ts | 538 --------------------------- 2 files changed, 681 deletions(-) delete mode 100644 lighthouse/lighthouse_checks.ts delete mode 100644 lighthouse/lighthouse_checks_test.ts diff --git a/lighthouse/lighthouse_checks.ts b/lighthouse/lighthouse_checks.ts deleted file mode 100644 index 58cd97a..0000000 --- a/lighthouse/lighthouse_checks.ts +++ /dev/null @@ -1,143 +0,0 @@ -/** - * @fileoverview CSP checks as used by Lighthouse. These checks tend to be a - * stricter subset of the other checks defined in this project. - */ - -import {CheckerFunction} from '../checks/checker'; -import {checkInvalidKeyword, checkMissingSemicolon, checkUnknownDirective} from '../checks/parser_checks'; -import {checkDeprecatedDirective, checkMissingObjectSrcDirective, checkMissingScriptSrcDirective, checkMultipleMissingBaseUriDirective, checkNonceLength, checkPlainUrlSchemes, checkScriptUnsafeInline, checkWildcards} from '../checks/security_checks'; -import {checkAllowlistFallback, checkStrictDynamic, checkUnsafeInlineFallback} from '../checks/strictcsp_checks'; -import {Csp, Directive, Version} from '../csp'; -import {Finding} from '../finding'; - -interface Equalable { - equals(a: unknown): boolean; -} - -function arrayContains(arr: T[], elem: T) { - return arr.some(e => e.equals(elem)); -} - -/** - * Computes the intersection of all of the given sets using the `equals(...)` - * method to compare items. - */ -function setIntersection(sets: T[][]): T[] { - const intersection: T[] = []; - if (sets.length === 0) { - return intersection; - } - const firstSet = sets[0]; - for (const elem of firstSet) { - if (sets.every(set => arrayContains(set, elem))) { - intersection.push(elem); - } - } - return intersection; -} - -/** - * Computes the union of all of the given sets using the `equals(...)` method to - * compare items. - */ -function setUnion(sets: T[][]): T[] { - const union: T[] = []; - for (const set of sets) { - for (const elem of set) { - if (!arrayContains(union, elem)) { - union.push(elem); - } - } - } - return union; -} - -/** - * Checks if *any* of the given policies pass the given checker. If at least one - * passes, returns no findings. Otherwise, returns the list of findings from the - * first one that had any findings. - */ -function atLeastOnePasses( - parsedCsps: Csp[], checker: CheckerFunction): Finding[] { - const findings: Finding[][] = []; - for (const parsedCsp of parsedCsps) { - findings.push(checker(parsedCsp)); - } - return setIntersection(findings); -} - -/** - * Checks if *any* of the given policies fail the given checker. Returns the - * list of findings from the one that had the most findings. - */ -function atLeastOneFails( - parsedCsps: Csp[], checker: CheckerFunction): Finding[] { - const findings: Finding[][] = []; - for (const parsedCsp of parsedCsps) { - findings.push(checker(parsedCsp)); - } - return setUnion(findings); -} - -/** - * Evaluate the given list of CSPs for checks that should cause Lighthouse to - * mark the CSP as failing. Returns only the first set of failures. - */ -export function evaluateForFailure(parsedCsps: Csp[]): Finding[] { - // Check #1 - const targetsXssFindings = [ - ...atLeastOnePasses(parsedCsps, checkMissingScriptSrcDirective), - ...atLeastOnePasses(parsedCsps, checkMissingObjectSrcDirective), - ...checkMultipleMissingBaseUriDirective(parsedCsps), - ]; - - // Check #2 - const effectiveCsps = - parsedCsps.map(csp => csp.getEffectiveCsp(Version.CSP3)); - const effectiveCspsWithScript = effectiveCsps.filter(csp => { - const directiveName = csp.getEffectiveDirective(Directive.SCRIPT_SRC); - return csp.directives[directiveName]; - }); - const robust = [ - ...atLeastOnePasses(effectiveCspsWithScript, checkStrictDynamic), - ...atLeastOnePasses(effectiveCspsWithScript, checkScriptUnsafeInline), - ...atLeastOnePasses(effectiveCsps, checkWildcards), - ...atLeastOnePasses(effectiveCsps, checkPlainUrlSchemes), - ]; - return [...targetsXssFindings, ...robust]; -} - -/** - * Evaluate the given list of CSPs for checks that should cause Lighthouse to - * mark the CSP as OK, but present a warning. Returns only the first set of - * failures. - */ -export function evaluateForWarnings(parsedCsps: Csp[]): Finding[] { - // Check #1 is implemented by Lighthouse directly - // Check #2 is no longer used in Lighthouse. - - // Check #3 - return [ - ...atLeastOneFails(parsedCsps, checkUnsafeInlineFallback), - ...atLeastOneFails(parsedCsps, checkAllowlistFallback) - ]; -} - -/** - * Evaluate the given list of CSPs for syntax errors. Returns a list of the same - * length as parsedCsps where each item in the list is the findings for the - * matching Csp. - */ -export function evaluateForSyntaxErrors(parsedCsps: Csp[]): Finding[][] { - // Check #4 - const allFindings: Finding[][] = []; - for (const csp of parsedCsps) { - const findings = [ - ...checkNonceLength(csp), ...checkUnknownDirective(csp), - ...checkDeprecatedDirective(csp), ...checkMissingSemicolon(csp), - ...checkInvalidKeyword(csp) - ]; - allFindings.push(findings); - } - return allFindings; -} diff --git a/lighthouse/lighthouse_checks_test.ts b/lighthouse/lighthouse_checks_test.ts deleted file mode 100644 index 99ee292..0000000 --- a/lighthouse/lighthouse_checks_test.ts +++ /dev/null @@ -1,538 +0,0 @@ -/** - * @fileoverview Tests for CSP Parser checks. - */ - - import 'jasmine'; - - import {Csp,} from '../csp'; - import {Severity} from '../finding'; - import {CspParser} from '../parser'; - - import * as lighthouseChecks from './lighthouse_checks'; - - function parsePolicies(policies: string[]): Csp[] { - return policies.map(p => (new CspParser(p)).csp); - } - - describe('Test evaluateForFailure', () => { - it('robust nonce-based policy', () => { - const test = - 'script-src \'nonce-aaaaaaaaaa\'; object-src \'none\'; base-uri \'none\''; - - const violations = - lighthouseChecks.evaluateForFailure(parsePolicies([test])); - - expect(violations.length).toBe(0); - }); - it('robust hash-based policy', () => { - const test = 'script-src \'sha256-aaaaaaaaaa\'; object-src \'none\''; - - const violations = - lighthouseChecks.evaluateForFailure(parsePolicies([test])); - - expect(violations.length).toBe(0); - }); - it('policy not attempt', () => { - const test = 'block-all-mixed-content'; - - const violations = - lighthouseChecks.evaluateForFailure(parsePolicies([test])); - - expect(violations.length).toBe(2); - expect(violations[0].severity).toBe(Severity.HIGH); - expect(violations[0].directive).toBe('script-src'); - expect(violations[0].description).toBe('script-src directive is missing.'); - expect(violations[1].severity).toBe(Severity.HIGH); - expect(violations[1].directive).toBe('object-src'); - expect(violations[1].description) - .toBe( - `Missing object-src allows the injection of plugins which can execute JavaScript. Can you set it to 'none'?`); - }); - it('policy not robust', () => { - const test = 'script-src *.google.com; object-src \'none\''; - - const violations = - lighthouseChecks.evaluateForFailure(parsePolicies([test])); - - expect(violations.length).toBe(1); - expect(violations[0].severity).toBe(Severity.STRICT_CSP); - expect(violations[0].directive).toBe('script-src'); - expect(violations[0].description) - .toBe( - `Host allowlists can frequently be bypassed. Consider using 'strict-dynamic' in combination with CSP nonces or hashes.`); - }); - it('robust policy and not robust policy', () => { - const policies = [ - 'script-src *.google.com; object-src \'none\'', - 'script-src \'nonce-aaaaaaaaaa\'; base-uri \'none\'' - ]; - - const violations = - lighthouseChecks.evaluateForFailure(parsePolicies(policies)); - - expect(violations.length).toBe(0); - }); - it('split across many policies', () => { - const policies = [ - 'object-src \'none\'', 'script-src \'nonce-aaaaaaaaaa\'', - 'base-uri \'none\'' - ]; - - const violations = - lighthouseChecks.evaluateForFailure(parsePolicies(policies)); - - expect(violations.length).toBe(0); - }); - it('split across many policies with default-src', () => { - const policies = ['default-src \'none\'', 'base-uri \'none\'']; - - const violations = - lighthouseChecks.evaluateForFailure(parsePolicies(policies)); - - expect(violations.length).toBe(0); - }); - it('split across many policies some mixed useless policies', () => { - const policies = [ - 'object-src \'none\'', 'script-src \'nonce-aaaaaaaaaa\'', - 'base-uri \'none\'', 'block-all-mixed-content' - ]; - - const violations = - lighthouseChecks.evaluateForFailure(parsePolicies(policies)); - - expect(violations.length).toBe(0); - }); - it('split across many policies with allowlist', () => { - const policies = [ - 'object-src \'none\'', 'script-src \'nonce-aaaaaaaaaa\'', - 'base-uri \'none\'', 'script-src *' - ]; - - const violations = - lighthouseChecks.evaluateForFailure(parsePolicies(policies)); - - expect(violations.length).toBe(0); - }); - - it('not robust and not attempt', () => { - const policies = ['block-all-mixed-content', 'script-src *.google.com']; - - const violations = - lighthouseChecks.evaluateForFailure(parsePolicies(policies)); - - expect(violations.length).toBe(2); - expect(violations[0].severity).toBe(Severity.HIGH); - expect(violations[0].directive).toBe('object-src'); - expect(violations[0].description) - .toBe( - `Missing object-src allows the injection of plugins which can execute JavaScript. Can you set it to 'none'?`); - expect(violations[1].severity).toBe(Severity.STRICT_CSP); - expect(violations[1].directive).toBe('script-src'); - expect(violations[1].description) - .toBe( - `Host allowlists can frequently be bypassed. Consider using \'strict-dynamic\' in combination with CSP nonces or hashes.`); - }); - it('robust check only CSPs with script-src', () => { - const policies = ['script-src https://example.com', 'object-src \'none\'']; - - const violations = - lighthouseChecks.evaluateForFailure(parsePolicies(policies)); - - expect(violations.length).toBe(1); - expect(violations[0].severity).toBe(Severity.STRICT_CSP); - expect(violations[0].directive).toBe('script-src'); - expect(violations[0].description) - .toBe( - `Host allowlists can frequently be bypassed. Consider using \'strict-dynamic\' in combination with CSP nonces or hashes.`); - }); - it('two not attempt', () => { - const policies = ['block-all-mixed-content', 'block-all-mixed-content']; - - const violations = - lighthouseChecks.evaluateForFailure(parsePolicies(policies)); - - expect(violations.length).toBe(2); - expect(violations[0].severity).toBe(Severity.HIGH); - expect(violations[0].directive).toBe('script-src'); - expect(violations[0].description).toBe('script-src directive is missing.'); - expect(violations[1].severity).toBe(Severity.HIGH); - expect(violations[1].directive).toBe('object-src'); - expect(violations[1].description) - .toBe( - `Missing object-src allows the injection of plugins which can execute JavaScript. Can you set it to 'none'?`); - }); - it('two not attempt somewhat', () => { - const policies = [ - 'block-all-mixed-content; object-src \'none\'', - 'block-all-mixed-content', - ]; - - const violations = - lighthouseChecks.evaluateForFailure(parsePolicies(policies)); - - expect(violations.length).toBe(1); - expect(violations[0].severity).toBe(Severity.HIGH); - expect(violations[0].directive).toBe('script-src'); - expect(violations[0].description).toBe('script-src directive is missing.'); - }); - it('base-uri split across many policies', () => { - const policies = [ - 'script-src \'nonce-aaaaaaaaaaa\'; object-src \'none\'', - 'base-uri \'none\'', - ]; - - const violations = - lighthouseChecks.evaluateForFailure(parsePolicies(policies)); - - expect(violations.length).toBe(0); - }); - it('base-uri not set', () => { - const policies = [ - 'script-src \'nonce-aaaaaaaaaaa\'; object-src \'none\'', - ]; - - const violations = - lighthouseChecks.evaluateForFailure(parsePolicies(policies)); - - expect(violations.length).toBe(1); - expect(violations[0].severity).toBe(Severity.HIGH); - expect(violations[0].directive).toBe('base-uri'); - expect(violations[0].description) - .toBe( - `Missing base-uri allows the injection of base tags. They can be used to set the base URL for all relative (script) URLs to an attacker controlled domain. Can you set it to 'none' or 'self'?`); - }); - it('base-uri not set in either policy', () => { - const policies = [ - 'script-src \'nonce-aaaaaaaaaaa\'; object-src \'none\'', - 'block-all-mixed-content' - ]; - - const violations = - lighthouseChecks.evaluateForFailure(parsePolicies(policies)); - - expect(violations.length).toBe(1); - expect(violations[0].severity).toBe(Severity.HIGH); - expect(violations[0].directive).toBe('base-uri'); - }); - it('check wildcards', () => { - const policies = ['script-src \'none\'; object-src *']; - - const violations = - lighthouseChecks.evaluateForFailure(parsePolicies(policies)); - - expect(violations.length).toBe(1); - expect(violations[0].severity).toBe(Severity.HIGH); - expect(violations[0].directive).toBe('object-src'); - expect(violations[0].description) - .toBe(`object-src should not allow '*' as source`); - }); - it('check wildcards on multiple', () => { - const policies = - ['script-src \'none\'; object-src *', 'object-src \'none\'']; - - const violations = - lighthouseChecks.evaluateForFailure(parsePolicies(policies)); - - expect(violations.length).toBe(0); - }); - it('check plain url schemes', () => { - const policies = [ - `script-src 'strict-dynamic' 'nonce-random123' 'unsafe-inline' https:; base-uri 'none'; object-src https:` - ]; - - const violations = - lighthouseChecks.evaluateForFailure(parsePolicies(policies)); - - expect(violations.length).toBe(1); - expect(violations[0].severity).toBe(Severity.HIGH); - expect(violations[0].directive).toBe('object-src'); - expect(violations[0].description) - .toBe( - `https: URI in object-src allows the execution of unsafe scripts.`); - }); - }); - describe('Test evaluateForWarnings', () => { - it('perfect', () => { - const test = - 'script-src \'nonce-aaaaaaaaaa\' \'unsafe-inline\' http: https:; report-uri url'; - - const violations = - lighthouseChecks.evaluateForWarnings(parsePolicies([test])); - - expect(violations.length).toBe(0); - }); - it('perfect except some failures', () => { - const policies = [ - 'script-src \'nonce-aaaaaaaaaa\' \'unsafe-inline\' http: https:; report-uri url; object-src \'none\'', - 'block-all-mixed-content' - ]; - - const violations = - lighthouseChecks.evaluateForWarnings(parsePolicies(policies)); - - expect(violations.length).toBe(0); - }); - it('a perfect policy and a policy that does not target', () => { - const policies = [ - 'script-src \'nonce-aaaaaaaaaa\' \'unsafe-inline\' http: https:; report-uri url; base-uri \'none\'; object-src \'none\'', - 'block-all-mixed-content' - ]; - - const violations = - lighthouseChecks.evaluateForWarnings(parsePolicies(policies)); - - expect(violations.length).toBe(0); - }); - it('perfect policy split into two', () => { - const policies = [ - 'script-src \'nonce-aaaaaaaaaa\' \'unsafe-inline\' http: https:; report-uri url; base-uri \'none\'; ', - 'block-all-mixed-content; object-src \'none\'' - ]; - - const violations = - lighthouseChecks.evaluateForWarnings(parsePolicies(policies)); - - expect(violations.length).toBe(0); - }); - it('perfect policy split into three', () => { - const policies = [ - 'script-src \'nonce-aaaaaaaaaa\' \'unsafe-inline\' http: https:; report-uri url; base-uri \'none\'; ', - 'block-all-mixed-content', 'object-src \'none\'' - ]; - - const violations = - lighthouseChecks.evaluateForWarnings(parsePolicies(policies)); - - expect(violations.length).toBe(0); - }); - it('no reporting and malformed', () => { - const test = 'script-src \'nonce-aaaaaaaaaa\'; unknown-directive'; - - const violations = - lighthouseChecks.evaluateForWarnings(parsePolicies([test])); - - expect(violations.length).toBe(1); - expect(violations[0].severity).toBe(Severity.STRICT_CSP); - expect(violations[0].directive).toBe('script-src'); - expect(violations[0].description) - .toBe( - 'Consider adding \'unsafe-inline\' (ignored by browsers supporting nonces/hashes) to be backward compatible with older browsers.'); - }); - it('missing unsafe-inline fallback', () => { - const test = 'script-src \'nonce-aaaaaaaaaa\'; report-uri url'; - - const violations = - lighthouseChecks.evaluateForWarnings(parsePolicies([test])); - - expect(violations.length).toBe(1); - expect(violations[0].severity).toBe(Severity.STRICT_CSP); - expect(violations[0].directive).toBe('script-src'); - expect(violations[0].description) - .toBe( - 'Consider adding \'unsafe-inline\' (ignored by browsers supporting nonces/hashes) to be backward compatible with older browsers.'); - }); - it('missing allowlist fallback', () => { - const test = - 'script-src \'nonce-aaaaaaaaaa\' \'strict-dynamic\' \'unsafe-inline\'; report-uri url'; - - const violations = - lighthouseChecks.evaluateForWarnings(parsePolicies([test])); - - expect(violations.length).toBe(1); - expect(violations[0].severity).toBe(Severity.STRICT_CSP); - expect(violations[0].directive).toBe('script-src'); - expect(violations[0].description) - .toBe( - 'Consider adding https: and http: url schemes (ignored by browsers supporting \'strict-dynamic\') to be backward compatible with older browsers.'); - }); - it('missing semicolon', () => { - const test = - 'script-src \'nonce-aaaaaaaaa\' \'unsafe-inline\'; report-uri url object-src \'self\''; - - const violations = - lighthouseChecks.evaluateForWarnings(parsePolicies([test])); - - expect(violations.length).toBe(0); - }); - it('invalid keyword', () => { - const test = - 'script-src \'nonce-aaaaaaaaa\' \'invalid\' \'unsafe-inline\'; report-uri url'; - - const violations = - lighthouseChecks.evaluateForWarnings(parsePolicies([test])); - - expect(violations.length).toBe(0); - }); - it('perfect policy and invalid policy', () => { - const policies = [ - 'script-src \'nonce-aaaaaaaaaa\' \'unsafe-inline\' http: https:; report-uri url; base-uri \'none\'; object-src \'none\'', - 'unknown' - ]; - - const violations = - lighthouseChecks.evaluateForWarnings(parsePolicies(policies)); - - expect(violations.length).toBe(0); - }); - it('reporting on the wrong policy', () => { - const policies = [ - 'script-src \'nonce-aaaaaaaaaa\' \'unsafe-inline\' http: https:', - 'block-all-mixed-content; report-uri url' - ]; - - const violations = - lighthouseChecks.evaluateForWarnings(parsePolicies(policies)); - - expect(violations.length).toBe(0); - }); - it('missing unsafe-inline fallback split over two policies', () => { - const policies = [ - 'script-src \'nonce-aaaaaaaaaa\'', - 'block-all-mixed-content; report-uri url' - ]; - - const violations = - lighthouseChecks.evaluateForWarnings(parsePolicies(policies)); - - expect(violations.length).toBe(1); - expect(violations[0].severity).toBe(Severity.STRICT_CSP); - expect(violations[0].directive).toBe('script-src'); - expect(violations[0].description) - .toBe( - 'Consider adding \'unsafe-inline\' (ignored by browsers supporting nonces/hashes) to be backward compatible with older browsers.'); - }); - it('strict-dynamic with no fallback in any policy', () => { - const policies = [ - 'script-src \'nonce-aaaaaaaaaa\' \'strict-dynamic\'', - 'block-all-mixed-content; report-uri url' - ]; - - const violations = - lighthouseChecks.evaluateForWarnings(parsePolicies(policies)); - - expect(violations.length).toBe(2); - expect(violations[0].severity).toBe(Severity.STRICT_CSP); - expect(violations[0].directive).toBe('script-src'); - expect(violations[0].description) - .toBe( - 'Consider adding \'unsafe-inline\' (ignored by browsers supporting nonces/hashes) to be backward compatible with older browsers.'); - expect(violations[1].severity).toBe(Severity.STRICT_CSP); - expect(violations[1].directive).toBe('script-src'); - expect(violations[1].description) - .toBe( - 'Consider adding https: and http: url schemes (ignored by browsers supporting \'strict-dynamic\') to be backward compatible with older browsers.'); - }); - }); - describe('Test evaluateForSyntaxErrors', () => { - it('whenPerfectPolicies', () => { - const policies = [ - 'script-src \'nonce-aaaaaaaaaa\' \'unsafe-inline\' http: https:', - 'block-all-mixed-content; report-uri url' - ]; - - const violations = - lighthouseChecks.evaluateForSyntaxErrors(parsePolicies(policies)); - - expect(violations.length).toBe(2); - expect(violations[0].length).toBe(0); - expect(violations[1].length).toBe(0); - }); - it('whenShortNonce', () => { - const test = 'script-src \'nonce-a\' \'unsafe-inline\'; report-uri url'; - - const violations = - lighthouseChecks.evaluateForSyntaxErrors(parsePolicies([test])); - - expect(violations.length).toBe(1); - expect(violations[0].length).toBe(1); - expect(violations[0][0].severity).toBe(Severity.MEDIUM); - expect(violations[0][0].directive).toBe('script-src'); - expect(violations[0][0].description) - .toBe('Nonces should be at least 8 characters long.'); - }); - it('whenUnknownDirective', () => { - const test = - 'script-src \'nonce-aaaaaaaaa\' \'unsafe-inline\'; report-uri url; unknown'; - - const violations = - lighthouseChecks.evaluateForSyntaxErrors(parsePolicies([test])); - - expect(violations.length).toBe(1); - expect(violations[0].length).toBe(1); - expect(violations[0][0].severity).toBe(Severity.SYNTAX); - expect(violations[0][0].directive).toBe('unknown'); - expect(violations[0][0].description) - .toBe('Directive "unknown" is not a known CSP directive.'); - }); - it('whenDeprecatedDirective', () => { - const test = - 'script-src \'nonce-aaaaaaaaa\' \'unsafe-inline\'; report-uri url; reflected-xss foo'; - - const violations = - lighthouseChecks.evaluateForSyntaxErrors(parsePolicies([test])); - - expect(violations.length).toBe(1); - expect(violations[0].length).toBe(1); - expect(violations[0][0].severity).toBe(Severity.INFO); - expect(violations[0][0].directive).toBe('reflected-xss'); - expect(violations[0][0].description) - .toBe( - 'reflected-xss is deprecated since CSP2. Please, use the X-XSS-Protection header instead.'); - }); - it('whenMissingSemicolon', () => { - const test = - 'script-src \'nonce-aaaaaaaaa\' \'unsafe-inline\'; report-uri url object-src \'none\''; - - const violations = - lighthouseChecks.evaluateForSyntaxErrors(parsePolicies([test])); - - expect(violations.length).toBe(1); - expect(violations[0].length).toBe(1); - expect(violations[0][0].severity).toBe(Severity.SYNTAX); - expect(violations[0][0].directive).toBe('report-uri'); - expect(violations[0][0].description) - .toBe( - 'Did you forget the semicolon? "object-src" seems to be a directive, not a value.'); - }); - it('whenInvalidKeyword', () => { - const test = - 'script-src \'nonce-aaaaaaaaa\' \'unsafe-inline\'; object-src \'invalid\''; - - const violations = - lighthouseChecks.evaluateForSyntaxErrors(parsePolicies([test])); - - expect(violations.length).toBe(1); - expect(violations[0].length).toBe(1); - expect(violations[0][0].severity).toBe(Severity.SYNTAX); - expect(violations[0][0].directive).toBe('object-src'); - expect(violations[0][0].description) - .toBe('\'invalid\' seems to be an invalid CSP keyword.'); - }); - it('manyPolicies', () => { - const policies = [ - 'object-src \'invalid\'', 'script-src \'none\'', - 'script-src \'nonce-short\' default-src \'none\'' - ]; - - const violations = - lighthouseChecks.evaluateForSyntaxErrors(parsePolicies(policies)); - - expect(violations.length).toBe(3); - expect(violations[0].length).toBe(1); - expect(violations[0][0].severity).toBe(Severity.SYNTAX); - expect(violations[0][0].directive).toBe('object-src'); - expect(violations[0][0].description) - .toBe('\'invalid\' seems to be an invalid CSP keyword.'); - expect(violations[1].length).toBe(0); - expect(violations[2].length).toBe(2); - expect(violations[2][0].severity).toBe(Severity.MEDIUM); - expect(violations[2][0].directive).toBe('script-src'); - expect(violations[2][0].description) - .toBe('Nonces should be at least 8 characters long.'); - expect(violations[2][1].severity).toBe(Severity.SYNTAX); - expect(violations[2][1].directive).toBe('script-src'); - expect(violations[2][1].description) - .toBe( - 'Did you forget the semicolon? "default-src" seems to be a directive, not a value.'); - }); - }); \ No newline at end of file From 9368c87707544db58ddf496a51cd151cea49af4e Mon Sep 17 00:00:00 2001 From: MaxNad Date: Fri, 5 Jan 2024 16:16:10 -0500 Subject: [PATCH 04/12] Lint the code --- .eslintignore | 3 + .eslintrc | 12 + README.md | 1 - checks/checker.ts | 1 - checks/parser_checks.ts | 17 +- checks/security_checks.ts | 9 +- checks/strictcsp_checks.ts | 19 +- enforced_csps.ts | 2 +- evaluator.ts | 5 +- evaluator_test.ts | 5 +- package-lock.json | 1512 ++++++++++++++++++++++++++++++++++++ package.json | 6 +- parser.ts | 18 +- 13 files changed, 1561 insertions(+), 49 deletions(-) create mode 100644 .eslintignore create mode 100644 .eslintrc diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..d664296 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,3 @@ +node_modules +dist +*_test.ts \ No newline at end of file diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..11643ef --- /dev/null +++ b/.eslintrc @@ -0,0 +1,12 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "plugins": [ + "@typescript-eslint" + ], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" + ] +} diff --git a/README.md b/README.md index a0b1b95..c334962 100644 --- a/README.md +++ b/README.md @@ -58,4 +58,3 @@ import {CspParser} from "csp_evaluator/dist/parser.js"; const parsed = new CspParser(["script-src https://google.com"]).csps; console.log(new CspEvaluator(parsed).evaluate()); ``` - diff --git a/checks/checker.ts b/checks/checker.ts index 55b017a..7e8d22d 100644 --- a/checks/checker.ts +++ b/checks/checker.ts @@ -2,7 +2,6 @@ * @fileoverview Shared interfaces for functions that check CSP policies. */ -import {Csp} from '../csp'; import { EnforcedCsps } from '../enforced_csps'; import {Finding} from '../finding'; diff --git a/checks/parser_checks.ts b/checks/parser_checks.ts index 816d746..93572e2 100644 --- a/checks/parser_checks.ts +++ b/checks/parser_checks.ts @@ -19,11 +19,10 @@ * limitations under the License. */ -import * as csp from '../csp'; -import {Csp, Keyword} from '../csp'; +import { Directive, Keyword, TrustedTypesSink, isDirective, isHash, isKeyword, isNonce } from '../csp'; import { EnforcedCsps } from '../enforced_csps'; -import {Finding, Severity, Type} from '../finding'; +import { Finding, Severity, Type } from '../finding'; /** @@ -39,7 +38,7 @@ export function checkUnknownDirective(parsedCsps: EnforcedCsps): Finding[] { for (const cspChecked of parsedCsps) { for (const directive of Object.keys(cspChecked.directives)) { - if (csp.isDirective(directive)) { + if (isDirective(directive)) { // Directive is known. continue; } @@ -82,7 +81,7 @@ export function checkMissingSemicolon(parsedCsps: EnforcedCsps): Finding[] { for (const value of directiveValues) { // If we find a known directive inside a directive value, it is very // likely that a semicolon was forgoten. - if (csp.isDirective(value)) { + if (isDirective(value)) { findings.push(new Finding( Type.MISSING_SEMICOLON, 'Did you forget the semicolon? ' + @@ -134,19 +133,19 @@ export function checkInvalidKeyword(parsedCsps: EnforcedCsps): Finding[] { continue; } - if (directive === csp.Directive.REQUIRE_TRUSTED_TYPES_FOR) { + if (directive === Directive.REQUIRE_TRUSTED_TYPES_FOR) { // Continue, if it's an allowed Trusted Types sink. - if (value === csp.TrustedTypesSink.SCRIPT) { + if (value === TrustedTypesSink.SCRIPT) { continue; } - } else if (directive === csp.Directive.TRUSTED_TYPES) { + } else if (directive === Directive.TRUSTED_TYPES) { // Continue, if it's an allowed Trusted Types keyword. if (value === '\'allow-duplicates\'' || value === '\'none\'') { continue; } } else { // Continue, if it's a valid keyword. - if (csp.isKeyword(value) || csp.isHash(value) || csp.isNonce(value)) { + if (isKeyword(value) || isHash(value) || isNonce(value)) { continue; } } diff --git a/checks/security_checks.ts b/checks/security_checks.ts index a1b7a21..6e55d7e 100644 --- a/checks/security_checks.ts +++ b/checks/security_checks.ts @@ -20,10 +20,9 @@ import * as angular from '../allowlist_bypasses/angular'; import * as flash from '../allowlist_bypasses/flash'; import * as jsonp from '../allowlist_bypasses/jsonp'; -import * as csp from '../csp'; -import {Csp, Directive, Keyword} from '../csp'; +import { Directive, Keyword, isNonce, isUrlScheme } from '../csp'; import { EnforcedCsps } from '../enforced_csps'; -import {Finding, Severity, Type} from '../finding'; +import { Finding, Severity, Type } from '../finding'; import * as utils from '../utils'; @@ -301,7 +300,7 @@ export function checkScriptAllowlistBypass(parsedCsps: EnforcedCsps): Finding[] } // Ignore standalone schemes and things that don't look like URLs (no dot). - if (csp.isUrlScheme(value) || value.indexOf('.') === -1) { + if (isUrlScheme(value) || value.indexOf('.') === -1) { continue; } @@ -541,7 +540,7 @@ export function checkNonceLength(parsedCsps: EnforcedCsps): Finding[] { directive, value)); } - if (!csp.isNonce(value, true)) { + if (!isNonce(value, true)) { violations.push(new Finding( Type.NONCE_CHARSET, 'Nonces should only use the base64 charset.', Severity.INFO, diff --git a/checks/strictcsp_checks.ts b/checks/strictcsp_checks.ts index d337d07..1cb2b52 100644 --- a/checks/strictcsp_checks.ts +++ b/checks/strictcsp_checks.ts @@ -26,11 +26,10 @@ * limitations under the License. */ -import * as csp from '../csp'; -import {Csp, Keyword} from '../csp'; +import { Directive, Keyword, TrustedTypesSink } from '../csp'; import { EnforcedCsps } from '../enforced_csps'; -import {Finding, Severity, Type} from '../finding'; +import { Finding, Severity, Type } from '../finding'; /** @@ -42,7 +41,7 @@ import {Finding, Severity, Type} from '../finding'; * @param parsedCsp A parsed csp. */ export function checkStrictDynamic(parsedCsps: EnforcedCsps): Finding[] { - const directiveName = parsedCsps.getEffectiveDirective(csp.Directive.SCRIPT_SRC); + const directiveName = parsedCsps.getEffectiveDirective(Directive.SCRIPT_SRC); const findings: Finding[] = []; for (const csp of parsedCsps) { @@ -73,7 +72,7 @@ export function checkStrictDynamic(parsedCsps: EnforcedCsps): Finding[] { * @param parsedCsp A parsed csp. */ export function checkStrictDynamicNotStandalone(parsedCsps: EnforcedCsps): Finding[] { - const directiveName = parsedCsps.getEffectiveDirective(csp.Directive.SCRIPT_SRC); + const directiveName = parsedCsps.getEffectiveDirective(Directive.SCRIPT_SRC); const findings: Finding[] = []; for (const csp of parsedCsps) { @@ -109,7 +108,7 @@ export function checkUnsafeInlineFallback(parsedCsps: EnforcedCsps): Finding[] { return []; } - const directiveName = parsedCsps.getEffectiveDirective(csp.Directive.SCRIPT_SRC); + const directiveName = parsedCsps.getEffectiveDirective(Directive.SCRIPT_SRC); const findings: Finding[] = []; for (const csp of parsedCsps) { @@ -140,7 +139,7 @@ export function checkUnsafeInlineFallback(parsedCsps: EnforcedCsps): Finding[] { * @param parsedCsp A parsed csp. */ export function checkAllowlistFallback(parsedCsps: EnforcedCsps): Finding[] { - const directiveName = parsedCsps.getEffectiveDirective(csp.Directive.SCRIPT_SRC); + const directiveName = parsedCsps.getEffectiveDirective(Directive.SCRIPT_SRC); const findings: Finding[] = []; for (const csp of parsedCsps) { @@ -177,10 +176,10 @@ export function checkAllowlistFallback(parsedCsps: EnforcedCsps): Finding[] { export function checkRequiresTrustedTypesForScripts(parsedCsps: EnforcedCsps): Finding[] { for (const cspChecked of parsedCsps) { - const directiveName = cspChecked.getEffectiveDirective(csp.Directive.REQUIRE_TRUSTED_TYPES_FOR); + const directiveName = cspChecked.getEffectiveDirective(Directive.REQUIRE_TRUSTED_TYPES_FOR); const values: string[] = cspChecked.directives[directiveName] || []; - if (values.includes(csp.TrustedTypesSink.SCRIPT)) { + if (values.includes(TrustedTypesSink.SCRIPT)) { return []; } } @@ -190,5 +189,5 @@ export function checkRequiresTrustedTypesForScripts(parsedCsps: EnforcedCsps): F 'Consider requiring Trusted Types for scripts to lock down DOM XSS ' + 'injection sinks. You can do this by adding ' + '"require-trusted-types-for \'script\'" to your policy.', - Severity.INFO, csp.Directive.REQUIRE_TRUSTED_TYPES_FOR)]; + Severity.INFO, Directive.REQUIRE_TRUSTED_TYPES_FOR)]; } diff --git a/enforced_csps.ts b/enforced_csps.ts index e50b265..ca4c212 100644 --- a/enforced_csps.ts +++ b/enforced_csps.ts @@ -44,7 +44,7 @@ export class EnforcedCsps extends Array { * @return The list of CSP string. */ convertToStrings(): string[] { - let retString: string[] = []; + const retString: string[] = []; for (const appliedCsp of this) { retString.push(appliedCsp.convertToString()); diff --git a/evaluator.ts b/evaluator.ts index c6edade..398ead2 100644 --- a/evaluator.ts +++ b/evaluator.ts @@ -20,8 +20,7 @@ import { CheckerFunction } from './checks/checker'; import * as parserChecks from './checks/parser_checks'; import * as securityChecks from './checks/security_checks'; import * as strictcspChecks from './checks/strictcsp_checks'; -import * as csp from './csp'; -import { Csp, Version } from './csp'; +import { Version } from './csp'; import { EnforcedCsps } from './enforced_csps'; import { Finding } from './finding'; @@ -49,7 +48,7 @@ export class CspEvaluator { /** * CSP version. */ - this.version = cspVersion || csp.Version.CSP3; + this.version = cspVersion || Version.CSP3; /** * Parsed CSPs. diff --git a/evaluator_test.ts b/evaluator_test.ts index 8ac0016..afff674 100644 --- a/evaluator_test.ts +++ b/evaluator_test.ts @@ -19,9 +19,8 @@ import 'jasmine'; -import {Csp} from './csp'; -import {CspEvaluator} from './evaluator'; -import {Finding, Severity, Type} from './finding'; +import { CspEvaluator } from './evaluator'; +import { Finding, Severity, Type } from './finding'; import { EnforcedCsps } from './enforced_csps'; describe('Test evaluator', () => { diff --git a/package-lock.json b/package-lock.json index 6ef7de7..12fd177 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,10 +10,155 @@ "license": "Apache-2.0", "devDependencies": { "@types/jasmine": "^5.1.4", + "@typescript-eslint/eslint-plugin": "^6.17.0", + "@typescript-eslint/parser": "^6.17.0", + "eslint": "^8.56.0", "jasmine": "^5.1.0", "typescript": "^5.3.3" } }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "dev": true + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -31,6 +176,41 @@ "node": ">=12" } }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -47,6 +227,251 @@ "integrity": "sha512-px7OMFO/ncXxixDe1zR13V1iycqWae0MxTaw62RpFlksUi5QuNWgQJFkTQjIOvrmutJbI7Fp2Y2N1F6D2R4G6w==", "dev": true }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", + "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.17.0.tgz", + "integrity": "sha512-Vih/4xLXmY7V490dGwBQJTpIZxH4ZFH6eCVmQ4RFkB+wmaCTDAx4dtgoWwMNGKLkqRY1L6rPqzEbjorRnDo4rQ==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.17.0", + "@typescript-eslint/type-utils": "6.17.0", + "@typescript-eslint/utils": "6.17.0", + "@typescript-eslint/visitor-keys": "6.17.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.17.0.tgz", + "integrity": "sha512-C4bBaX2orvhK+LlwrY8oWGmSl4WolCfYm513gEccdWZj0CwGadbIADb0FtVEcI+WzUyjyoBj2JRP8g25E6IB8A==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.17.0", + "@typescript-eslint/types": "6.17.0", + "@typescript-eslint/typescript-estree": "6.17.0", + "@typescript-eslint/visitor-keys": "6.17.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.17.0.tgz", + "integrity": "sha512-RX7a8lwgOi7am0k17NUO0+ZmMOX4PpjLtLRgLmT1d3lBYdWH4ssBUbwdmc5pdRX8rXon8v9x8vaoOSpkHfcXGA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.17.0", + "@typescript-eslint/visitor-keys": "6.17.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.17.0.tgz", + "integrity": "sha512-hDXcWmnbtn4P2B37ka3nil3yi3VCQO2QEB9gBiHJmQp5wmyQWqnjA85+ZcE8c4FqnaB6lBwMrPkgd4aBYz3iNg==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.17.0", + "@typescript-eslint/utils": "6.17.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.17.0.tgz", + "integrity": "sha512-qRKs9tvc3a4RBcL/9PXtKSehI/q8wuU9xYJxe97WFxnzH8NWWtcW3ffNS+EWg8uPvIerhjsEZ+rHtDqOCiH57A==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.17.0.tgz", + "integrity": "sha512-gVQe+SLdNPfjlJn5VNGhlOhrXz4cajwFd5kAgWtZ9dCZf4XJf8xmgCTLIqec7aha3JwgLI2CK6GY1043FRxZwg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.17.0", + "@typescript-eslint/visitor-keys": "6.17.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.17.0.tgz", + "integrity": "sha512-LofsSPjN/ITNkzV47hxas2JCsNCEnGhVvocfyOcLzT9c/tSZE7SfhS/iWtzP1lKNOEfLhRTZz6xqI8N2RzweSQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.17.0", + "@typescript-eslint/types": "6.17.0", + "@typescript-eslint/typescript-estree": "6.17.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.17.0.tgz", + "integrity": "sha512-H6VwB/k3IuIeQOyYczyyKN8wH6ed8EwliaYHLxOIhyF0dYEIsN8+Bk3GE19qafeMKyZJJHP8+O1HiFhFLUNKSg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.17.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/ansi-regex": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", @@ -71,6 +496,21 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -86,6 +526,58 @@ "balanced-match": "^1.0.0" } }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -104,6 +596,12 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -118,6 +616,53 @@ "node": ">= 8" } }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -130,6 +675,318 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", + "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, "node_modules/foreground-child": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", @@ -146,6 +1003,12 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, "node_modules/glob": { "version": "10.3.10", "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", @@ -168,6 +1031,127 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ignore": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -177,6 +1161,36 @@ "node": ">=8" } }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -220,6 +1234,79 @@ "integrity": "sha512-UrzO3fL7nnxlQXlvTynNAenL+21oUQRlzqQFsA2U11ryb4+NLOCOePZ70PTojEaUKhiFugh7dG0Q+I58xlPdWg==", "dev": true }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, "node_modules/lru-cache": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", @@ -229,6 +1316,28 @@ "node": "14 || >=16.14" } }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/minimatch": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", @@ -253,6 +1362,104 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -278,6 +1485,191 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -311,6 +1703,15 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -407,6 +1808,84 @@ "node": ">=8" } }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/typescript": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", @@ -420,6 +1899,15 @@ "node": ">=14.17" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -525,6 +2013,30 @@ "engines": { "node": ">=8" } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index ba6222c..81df946 100644 --- a/package.json +++ b/package.json @@ -18,10 +18,14 @@ "url": "https://github.com/google/csp-evaluator" }, "scripts": { - "test": "tsc && npx jasmine --config=jasmine.json" + "test": "tsc && npx jasmine --config=jasmine.json", + "lint": "eslint . --ext .ts" }, "devDependencies": { "@types/jasmine": "^5.1.4", + "@typescript-eslint/eslint-plugin": "^6.17.0", + "@typescript-eslint/parser": "^6.17.0", + "eslint": "^8.56.0", "jasmine": "^5.1.0", "typescript": "^5.3.3" } diff --git a/parser.ts b/parser.ts index 449b3ea..0c31a2d 100644 --- a/parser.ts +++ b/parser.ts @@ -19,8 +19,6 @@ import * as csp from './csp'; import * as enforcedCsps from './enforced_csps'; - - /** * A class to hold a parser for CSP in string format. * @unrestricted @@ -54,14 +52,6 @@ export class CspParser { // For each token returned by splitting list on commas: const policiesList: string[] = policy.split(','); - // Let policy be the result of parsing token, with a source of source, and disposition of disposition. - const filteredPoliciesList = policiesList.map(function (el, index) { - // If policy’s directive set is empty, continue. - if (el.trim() !== "") { - return el; - } - }); - // Append policy to policies. splitCspList.push(...policiesList); }); @@ -73,9 +63,8 @@ export class CspParser { return this.csps; } - parseCsp(unparsedCsp: string): csp.Csp { - let retCsp: csp.Csp = new csp.Csp(); + const retCsp: csp.Csp = new csp.Csp(); // For each token returned by strictly splitting serialized on the U+003B SEMICOLON character (;): const directiveTokens = unparsedCsp.split(';'); @@ -84,9 +73,11 @@ export class CspParser { const directiveToken = directiveTokens[i].trim(); // If token is an empty string, or if token is not an ASCII string, continue. + /* eslint-disable no-control-regex */ if (directiveToken === "" || !/^[\x00-\xFF]*$/.test(directiveToken)) { continue; } + /* eslint-enable no-control-regex */ // Let directive name be the result of collecting a sequence of code points from token which are not ASCII whitespace. // Let directive value be the result of splitting token on ASCII whitespace. @@ -100,9 +91,6 @@ export class CspParser { continue; } - if (!csp.isDirective(directiveName)) { - } - const directiveValues: string[] = []; for (let directiveValue, j = 1; (directiveValue = directiveParts[j]); j++) { From 4b57ca0c156c3f39a44c6cad3c0fd875a8777869 Mon Sep 17 00:00:00 2001 From: MaxNad Date: Fri, 5 Jan 2024 16:28:58 -0500 Subject: [PATCH 05/12] More linting --- checks/checker.ts | 2 +- checks/parser_checks_test.ts | 6 +++--- checks/security_checks_test.ts | 8 ++++---- checks/strictcsp_checks_test.ts | 6 +++--- csp.ts | 2 +- csp_test.ts | 4 ++-- enforced_csps.ts | 2 +- enforced_csps_test.ts | 4 ++-- finding_test.ts | 4 ++-- parser.ts | 16 ++++++++-------- parser_test.ts | 2 +- utils.ts | 4 ++-- utils_test.ts | 2 +- 13 files changed, 31 insertions(+), 31 deletions(-) diff --git a/checks/checker.ts b/checks/checker.ts index 7e8d22d..4b9704b 100644 --- a/checks/checker.ts +++ b/checks/checker.ts @@ -3,7 +3,7 @@ */ import { EnforcedCsps } from '../enforced_csps'; -import {Finding} from '../finding'; +import { Finding } from '../finding'; /** * A function that checks a list of Csps for problems and returns an unordered diff --git a/checks/parser_checks_test.ts b/checks/parser_checks_test.ts index 692fb38..47fba05 100644 --- a/checks/parser_checks_test.ts +++ b/checks/parser_checks_test.ts @@ -19,10 +19,10 @@ import 'jasmine'; -import {Finding, Severity} from '../finding'; -import {CspParser} from '../parser'; +import { Finding, Severity } from '../finding'; +import { CspParser } from '../parser'; -import {CheckerFunction} from './checker'; +import { CheckerFunction } from './checker'; import * as parserChecks from './parser_checks'; /** diff --git a/checks/security_checks_test.ts b/checks/security_checks_test.ts index 2b6e5ce..57ed79d 100644 --- a/checks/security_checks_test.ts +++ b/checks/security_checks_test.ts @@ -18,11 +18,11 @@ */ - import {Directive, Version} from '../csp'; - import {Finding, Severity, Type} from '../finding'; - import {CspParser} from '../parser'; + import { Directive, Version } from '../csp'; + import { Finding, Severity, Type } from '../finding'; + import { CspParser } from '../parser'; - import {CheckerFunction} from './checker'; + import { CheckerFunction } from './checker'; import * as securityChecks from './security_checks'; /** diff --git a/checks/strictcsp_checks_test.ts b/checks/strictcsp_checks_test.ts index fc524a8..bb97d3a 100644 --- a/checks/strictcsp_checks_test.ts +++ b/checks/strictcsp_checks_test.ts @@ -17,10 +17,10 @@ * @author lwe@google.com (Lukas Weichselbaum) */ -import {Finding, Severity} from '../finding'; -import {CspParser} from '../parser'; +import { Finding, Severity } from '../finding'; +import { CspParser } from '../parser'; -import {CheckerFunction} from './checker'; +import { CheckerFunction } from './checker'; import * as strictcspChecks from './strictcsp_checks'; diff --git a/csp.ts b/csp.ts index 0e1daf6..7fd6077 100644 --- a/csp.ts +++ b/csp.ts @@ -18,7 +18,7 @@ */ -import {Finding, Severity, Type} from './finding'; +import { Finding, Severity, Type } from './finding'; /** * Content Security Policy object. diff --git a/csp_test.ts b/csp_test.ts index dd37ecb..73ec121 100644 --- a/csp_test.ts +++ b/csp_test.ts @@ -19,8 +19,8 @@ import 'jasmine'; -import {Directive, isDirective, isHash, isKeyword, isNonce, isUrlScheme, Keyword, Version} from './csp'; -import {CspParser} from './parser'; +import { Directive, isDirective, isHash, isKeyword, isNonce, isUrlScheme, Keyword, Version } from './csp'; +import { CspParser } from './parser'; describe('Test Csp', () => { it('ConvertToString', () => { diff --git a/enforced_csps.ts b/enforced_csps.ts index ca4c212..96ebf19 100644 --- a/enforced_csps.ts +++ b/enforced_csps.ts @@ -18,7 +18,7 @@ */ -import {Csp, Directive, Keyword, Version, isHash, isNonce} from './csp'; +import { Csp, Directive, Keyword, Version, isHash, isNonce } from './csp'; import { Finding, Severity, Type } from './finding'; /** diff --git a/enforced_csps_test.ts b/enforced_csps_test.ts index fc055a1..a538c04 100644 --- a/enforced_csps_test.ts +++ b/enforced_csps_test.ts @@ -19,8 +19,8 @@ import 'jasmine'; -import {EnforcedCsps} from './enforced_csps'; -import {CspParser} from './parser'; +import { EnforcedCsps } from './enforced_csps'; +import { CspParser } from './parser'; describe('Test enforced CSPs', () => { it('ConvertToString with single CSP', () => { diff --git a/finding_test.ts b/finding_test.ts index 303ebea..25ea2be 100644 --- a/finding_test.ts +++ b/finding_test.ts @@ -19,8 +19,8 @@ import 'jasmine'; -import {Directive, Keyword} from './csp'; -import {Finding, Severity, Type} from './finding'; +import { Directive, Keyword } from './csp'; +import { Finding, Severity, Type } from './finding'; describe('Test finding', () => { diff --git a/parser.ts b/parser.ts index 0c31a2d..8752427 100644 --- a/parser.ts +++ b/parser.ts @@ -16,15 +16,15 @@ * @author lwe@google.com (Lukas Weichselbaum) */ -import * as csp from './csp'; -import * as enforcedCsps from './enforced_csps'; +import { Csp, isKeyword, isUrlScheme } from './csp'; +import { EnforcedCsps } from './enforced_csps'; /** * A class to hold a parser for CSP in string format. * @unrestricted */ export class CspParser { - csps: enforcedCsps.EnforcedCsps; + csps: EnforcedCsps; /** * @param unparsedCsp A Content Security Policy as string. @@ -33,7 +33,7 @@ export class CspParser { /** * Parsed CSP */ - this.csps = new enforcedCsps.EnforcedCsps(); + this.csps = new EnforcedCsps(); if (!Array.isArray(unparsedCsps)) { unparsedCsps = [ (unparsedCsps as string) ]; @@ -46,7 +46,7 @@ export class CspParser { * Parses a CSP from a string. * @param unparsedCsp CSP as string. */ - parse(unparsedCspList: string[]): enforcedCsps.EnforcedCsps { + parse(unparsedCspList: string[]): EnforcedCsps { const splitCspList: string[] = []; unparsedCspList.forEach(policy => { // For each token returned by splitting list on commas: @@ -63,8 +63,8 @@ export class CspParser { return this.csps; } - parseCsp(unparsedCsp: string): csp.Csp { - const retCsp: csp.Csp = new csp.Csp(); + parseCsp(unparsedCsp: string): Csp { + const retCsp: Csp = new Csp(); // For each token returned by strictly splitting serialized on the U+003B SEMICOLON character (;): const directiveTokens = unparsedCsp.split(';'); @@ -120,7 +120,7 @@ export class CspParser { function normalizeDirectiveValue(directiveValue: string): string { directiveValue = directiveValue.trim(); const directiveValueLower = directiveValue.toLowerCase(); - if (csp.isKeyword(directiveValueLower) || csp.isUrlScheme(directiveValue)) { + if (isKeyword(directiveValueLower) || isUrlScheme(directiveValue)) { return directiveValueLower; } return directiveValue; diff --git a/parser_test.ts b/parser_test.ts index 02814d9..390bce5 100644 --- a/parser_test.ts +++ b/parser_test.ts @@ -19,7 +19,7 @@ import 'jasmine'; -import {CspParser, TEST_ONLY} from './parser'; +import { CspParser, TEST_ONLY } from './parser'; describe('Test parser', () => { diff --git a/utils.ts b/utils.ts index b572b1f..8c2123c 100644 --- a/utils.ts +++ b/utils.ts @@ -18,7 +18,7 @@ */ -import * as csp from './csp'; +import { Csp } from './csp'; /** @@ -133,7 +133,7 @@ export function matchWildcardUrls( * should get applied on directive values. */ export function applyCheckFunktionToDirectives( - parsedCsp: csp.Csp, + parsedCsp: Csp, check: (directive: string, directiveValues: string[]) => void, ) { const directiveNames = Object.keys(parsedCsp.directives); diff --git a/utils_test.ts b/utils_test.ts index 26306eb..300eb4a 100644 --- a/utils_test.ts +++ b/utils_test.ts @@ -18,7 +18,7 @@ import 'jasmine'; -import {getHostname, getSchemeFreeUrl, matchWildcardUrls} from './utils'; +import { getHostname, getSchemeFreeUrl, matchWildcardUrls } from './utils'; const TEST_BYPASSES = [ 'https://googletagmanager.com/gtm/js', 'https://www.google.com/jsapi', From 6353430761992f7783ebb4df8e5f71b8963673f8 Mon Sep 17 00:00:00 2001 From: MaxNad Date: Fri, 5 Jan 2024 16:34:46 -0500 Subject: [PATCH 06/12] Added checks for wasm-unsafe-eval --- checks/security_checks.ts | 28 ++++++++++++++++++++++++++++ checks/security_checks_test.ts | 22 ++++++++++++++++++++++ finding.ts | 1 + 3 files changed, 51 insertions(+) diff --git a/checks/security_checks.ts b/checks/security_checks.ts index 6e55d7e..3c17ce2 100644 --- a/checks/security_checks.ts +++ b/checks/security_checks.ts @@ -71,6 +71,34 @@ export function checkScriptUnsafeInline(effectiveCsp: EnforcedCsps): Finding[] { return []; } +/** + * Checks if passed csp allows web assembly eval in scripts. + * Findings of this check have a medium severity and are FP free. + * + * Example policy where this check would trigger: + * script-src 'wasm-unsafe-eval' + * + * @param parsedCsp Parsed CSP. + */ +export function checkScriptWasmUnsafeEval(parsedCsps: EnforcedCsps): Finding[] { + const findings: Finding[] = []; + + for (const cspChecked of parsedCsps) { + const directiveName = cspChecked.getEffectiveDirective(Directive.SCRIPT_SRC); + const values: string[] = cspChecked.directives[directiveName] || []; + + // Check if wasm-unsafe-eval is present. + if (values.includes(Keyword.WASM_UNSAFE_EVAL)) { + findings.push(new Finding( + Type.SCRIPT_WASM_UNSAFE_EVAL, + `'wasm-unsafe-eval' allows the execution of web assembly code injected in functions ` + + 'such as WebAssembly.compile().', + Severity.MEDIUM_MAYBE, directiveName, Keyword.UNSAFE_EVAL)); + } + } + + return findings; +} /** * Checks if passed csp allows eval in scripts. diff --git a/checks/security_checks_test.ts b/checks/security_checks_test.ts index 57ed79d..7cd4e1d 100644 --- a/checks/security_checks_test.ts +++ b/checks/security_checks_test.ts @@ -72,6 +72,28 @@ violations = securityChecks.checkScriptUnsafeInline(effectiveCsp); expect(violations.length).toBe(0); }); + + /** Tests for csp.securityChecks.checkScriptWasmUnsafeEval */ + it('CheckScriptWasmUnsafeEvalInScriptSrc', () => { + const test = 'default-src https:; script-src \'wasm-unsafe-eval\''; + + const violations = checkCsp(test, securityChecks.checkScriptWasmUnsafeEval); + expect(violations.length).toBe(1); + expect(violations[0].severity).toBe(Severity.MEDIUM_MAYBE); + expect(violations[0].type).toBe(Type.SCRIPT_WASM_UNSAFE_EVAL); + expect(violations[0].directive).toBe('script-src'); + }); + + /** Tests for csp.securityChecks.checkScriptWasmUnsafeEval */ + it('CheckScriptWasmUnsafeEvalInDefaultSrc', () => { + const test = 'default-src \'wasm-unsafe-eval\''; + + const violations = checkCsp(test, securityChecks.checkScriptWasmUnsafeEval); + expect(violations.length).toBe(1); + expect(violations[0].severity).toBe(Severity.MEDIUM_MAYBE); + expect(violations[0].type).toBe(Type.SCRIPT_WASM_UNSAFE_EVAL); + expect(violations[0].directive).toBe('default-src'); + }); /** Tests for csp.securityChecks.checkScriptUnsafeEval */ it('CheckScriptUnsafeEvalInScriptSrc', () => { diff --git a/finding.ts b/finding.ts index 50cecb1..d3203ae 100644 --- a/finding.ts +++ b/finding.ts @@ -94,6 +94,7 @@ export enum Type { SCRIPT_UNSAFE_EVAL, STYLE_UNSAFE_INLINE, STYLE_UNSAFE_EVAL, + SCRIPT_WASM_UNSAFE_EVAL, PLAIN_URL_SCHEMES, PLAIN_WILDCARD, SCRIPT_ALLOWLIST_BYPASS, From 4210b6ba7272407423c2eb5508c9634c8e08f64d Mon Sep 17 00:00:00 2001 From: MaxNad Date: Fri, 5 Jan 2024 16:46:28 -0500 Subject: [PATCH 07/12] Remove invalid hosts from bypass list --- allowlist_bypasses/angular.ts | 9 --------- allowlist_bypasses/flash.ts | 1 - allowlist_bypasses/json/angular.json | 9 --------- allowlist_bypasses/json/flash.json | 1 - allowlist_bypasses/json/jsonp.json | 3 --- allowlist_bypasses/jsonp.ts | 3 --- 6 files changed, 26 deletions(-) diff --git a/allowlist_bypasses/angular.ts b/allowlist_bypasses/angular.ts index 2eb45c0..99c7a83 100644 --- a/allowlist_bypasses/angular.ts +++ b/allowlist_bypasses/angular.ts @@ -32,38 +32,29 @@ export const URLS: string[] = [ '//yastatic.net/angularjs/1.2.23/angular.min.js', '//yuedust.yuedu.126.net/js/components/angular/angular.js', '//art.jobs.netease.com/script/angular.js', - '//csu-c45.kxcdn.com/angular/angular.js', '//elysiumwebsite.s3.amazonaws.com/uploads/blog-media/rockstar/angular.min.js', '//inno.blob.core.windows.net/new/libs/AngularJS/1.2.1/angular.min.js', '//gift-talk.kakao.com/public/javascripts/angular.min.js', '//ajax.googleapis.com/ajax/libs/angularjs/1.2.0rc1/angular-route.min.js', - '//master-sumok.ru/vendors/angular/angular-cookies.js', '//ayicommon-a.akamaihd.net/static/vendor/angular-1.4.2.min.js', - '//pangxiehaitao.com/framework/angular-1.3.9/angular-animate.min.js', '//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.16/angular.min.js', '//96fe3ee995e96e922b6b-d10c35bd0a0de2c718b252bc575fdb73.ssl.cf1.rackcdn.com/angular.js', '//oss.maxcdn.com/angularjs/1.2.20/angular.min.js', - '//reports.zemanta.com/smedia/common/angularjs/1.2.11/angular.js', '//cdn.shopify.com/s/files/1/0225/6463/t/1/assets/angular-animate.min.js', '//parademanagement.com.s3-website-ap-southeast-1.amazonaws.com/js/angular.min.js', '//cdn.jsdelivr.net/angularjs/1.1.2/angular.min.js', '//eb2883ede55c53e09fd5-9c145fb03d93709ea57875d307e2d82e.ssl.cf3.rackcdn.com/components/angular-resource.min.js', - '//andors-trail.googlecode.com/git/AndorsTrailEdit/lib/angular.min.js', '//cdn.walkme.com/General/EnvironmentTests/angular/angular.min.js', '//laundrymail.com/angular/angular.js', '//s3-eu-west-1.amazonaws.com/staticancpa/js/angular-cookies.min.js', '//collade.demo.stswp.com/js/vendor/angular.min.js', '//mrfishie.github.io/sailor/bower_components/angular/angular.min.js', - '//askgithub.com/static/js/angular.min.js', '//services.amazon.com/solution-providers/assets/vendor/angular-cookies.min.js', '//raw.githubusercontent.com/angular/code.angularjs.org/master/1.0.7/angular-resource.js', '//prb-resume.appspot.com/bower_components/angular-animate/angular-animate.js', - '//dl.dropboxusercontent.com/u/30877786/angular.min.js', '//static.tumblr.com/x5qdx0r/nPOnngtff/angular-resource.min_1_.js', - '//storage.googleapis.com/assets-prod.urbansitter.net/us-sym/assets/vendor/angular-sanitize/angular-sanitize.min.js', '//twitter.github.io/labella.js/bower_components/angular/angular.min.js', '//cdn2-casinoroom.global.ssl.fastly.net/js/lib/angular-animate.min.js', '//www.adobe.com/devnet-apps/flashshowcase/lib/angular/angular.1.1.5.min.js', - '//eternal-sunset.herokuapp.com/bower_components/angular/angular.js', '//cdn.bootcss.com/angular.js/1.2.0/angular.min.js' ]; diff --git a/allowlist_bypasses/flash.ts b/allowlist_bypasses/flash.ts index 58ca769..e080a12 100644 --- a/allowlist_bypasses/flash.ts +++ b/allowlist_bypasses/flash.ts @@ -25,6 +25,5 @@ * paths on these domains that would allow a bypass. */ export const URLS: string[] = [ - '//vk.com/swf/video.swf', '//ajax.googleapis.com/ajax/libs/yui/2.8.0r4/build/charts/assets/charts.swf' ]; diff --git a/allowlist_bypasses/json/angular.json b/allowlist_bypasses/json/angular.json index 98fb36f..35c25bc 100644 --- a/allowlist_bypasses/json/angular.json +++ b/allowlist_bypasses/json/angular.json @@ -7,39 +7,30 @@ "//yastatic.net/angularjs/1.2.23/angular.min.js", "//yuedust.yuedu.126.net/js/components/angular/angular.js", "//art.jobs.netease.com/script/angular.js", - "//csu-c45.kxcdn.com/angular/angular.js", "//elysiumwebsite.s3.amazonaws.com/uploads/blog-media/rockstar/angular.min.js", "//inno.blob.core.windows.net/new/libs/AngularJS/1.2.1/angular.min.js", "//gift-talk.kakao.com/public/javascripts/angular.min.js", "//ajax.googleapis.com/ajax/libs/angularjs/1.2.0rc1/angular-route.min.js", - "//master-sumok.ru/vendors/angular/angular-cookies.js", "//ayicommon-a.akamaihd.net/static/vendor/angular-1.4.2.min.js", - "//pangxiehaitao.com/framework/angular-1.3.9/angular-animate.min.js", "//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.16/angular.min.js", "//96fe3ee995e96e922b6b-d10c35bd0a0de2c718b252bc575fdb73.ssl.cf1.rackcdn.com/angular.js", "//oss.maxcdn.com/angularjs/1.2.20/angular.min.js", - "//reports.zemanta.com/smedia/common/angularjs/1.2.11/angular.js", "//cdn.shopify.com/s/files/1/0225/6463/t/1/assets/angular-animate.min.js", "//parademanagement.com.s3-website-ap-southeast-1.amazonaws.com/js/angular.min.js", "//cdn.jsdelivr.net/angularjs/1.1.2/angular.min.js", "//eb2883ede55c53e09fd5-9c145fb03d93709ea57875d307e2d82e.ssl.cf3.rackcdn.com/components/angular-resource.min.js", - "//andors-trail.googlecode.com/git/AndorsTrailEdit/lib/angular.min.js", "//cdn.walkme.com/General/EnvironmentTests/angular/angular.min.js", "//laundrymail.com/angular/angular.js", "//s3-eu-west-1.amazonaws.com/staticancpa/js/angular-cookies.min.js", "//collade.demo.stswp.com/js/vendor/angular.min.js", "//mrfishie.github.io/sailor/bower_components/angular/angular.min.js", - "//askgithub.com/static/js/angular.min.js", "//services.amazon.com/solution-providers/assets/vendor/angular-cookies.min.js", "//raw.githubusercontent.com/angular/code.angularjs.org/master/1.0.7/angular-resource.js", "//prb-resume.appspot.com/bower_components/angular-animate/angular-animate.js", - "//dl.dropboxusercontent.com/u/30877786/angular.min.js", "//static.tumblr.com/x5qdx0r/nPOnngtff/angular-resource.min_1_.js", - "//storage.googleapis.com/assets-prod.urbansitter.net/us-sym/assets/vendor/angular-sanitize/angular-sanitize.min.js", "//twitter.github.io/labella.js/bower_components/angular/angular.min.js", "//cdn2-casinoroom.global.ssl.fastly.net/js/lib/angular-animate.min.js", "//www.adobe.com/devnet-apps/flashshowcase/lib/angular/angular.1.1.5.min.js", - "//eternal-sunset.herokuapp.com/bower_components/angular/angular.js", "//cdn.bootcss.com/angular.js/1.2.0/angular.min.js" ] } diff --git a/allowlist_bypasses/json/flash.json b/allowlist_bypasses/json/flash.json index 1a8a7c5..0f41206 100644 --- a/allowlist_bypasses/json/flash.json +++ b/allowlist_bypasses/json/flash.json @@ -1,6 +1,5 @@ {"urls": [ - "//vk.com/swf/video.swf", "//ajax.googleapis.com/ajax/libs/yui/2.8.0r4/build/charts/assets/charts.swf" ] } diff --git a/allowlist_bypasses/json/jsonp.json b/allowlist_bypasses/json/jsonp.json index d069eb6..17ac33e 100644 --- a/allowlist_bypasses/json/jsonp.json +++ b/allowlist_bypasses/json/jsonp.json @@ -37,18 +37,15 @@ "//mc.yandex.ru/watch/24306916/1", "//share.yandex.net/counter/gpp/", "//ok.go.mail.ru/lady_on_lady_recipes_r.json", - "//d1f69o4buvlrj5.cloudfront.net/__efa_15_1_ornpba.xekq.arg/optout_check", "//www.googletagmanager.com/gtm/js", "//api.vk.com/method/wall.get", "//www.sharethis.com/get-publisher-info.php", "//google.ru/maps/vt", "//pro.netrox.sc/oapi/h_checksite.ashx", "//vimeo.com/api/oembed.json/", - "//de.blog.newrelic.com/wp-admin/admin-ajax.php", "//ajax.googleapis.com/ajax/services/search/news", "//ssl.google-analytics.com/gtm/js", "//pubsub.pubnub.com/subscribe/demo/hello_world/", - "//pass.yandex.ua/services", "//id.rambler.ru/script/topline_info.js", "//m.addthis.com/live/red_lojson/100eng.json", "//passport.ngs.ru/ajax/check", diff --git a/allowlist_bypasses/jsonp.ts b/allowlist_bypasses/jsonp.ts index 5caa7ad..a65712b 100644 --- a/allowlist_bypasses/jsonp.ts +++ b/allowlist_bypasses/jsonp.ts @@ -70,18 +70,15 @@ export const URLS: string[] = [ '//mc.yandex.ru/watch/24306916/1', '//share.yandex.net/counter/gpp/', '//ok.go.mail.ru/lady_on_lady_recipes_r.json', - '//d1f69o4buvlrj5.cloudfront.net/__efa_15_1_ornpba.xekq.arg/optout_check', '//www.googletagmanager.com/gtm/js', '//api.vk.com/method/wall.get', '//www.sharethis.com/get-publisher-info.php', '//google.ru/maps/vt', '//pro.netrox.sc/oapi/h_checksite.ashx', '//vimeo.com/api/oembed.json/', - '//de.blog.newrelic.com/wp-admin/admin-ajax.php', '//ajax.googleapis.com/ajax/services/search/news', '//ssl.google-analytics.com/gtm/js', '//pubsub.pubnub.com/subscribe/demo/hello_world/', - '//pass.yandex.ua/services', '//id.rambler.ru/script/topline_info.js', '//m.addthis.com/live/red_lojson/100eng.json', '//passport.ngs.ru/ajax/check', From 3ee78f580689ff9fce3648c0a41795ab30eb881b Mon Sep 17 00:00:00 2001 From: MaxNad Date: Fri, 5 Jan 2024 17:16:15 -0500 Subject: [PATCH 08/12] Added checks for style-src and form-action --- checks/security_checks.ts | 102 +++++++++++++++++++++++++- checks/security_checks_test.ts | 129 ++++++++++++++++++++++++++++++--- 2 files changed, 218 insertions(+), 13 deletions(-) diff --git a/checks/security_checks.ts b/checks/security_checks.ts index 3c17ce2..2811553 100644 --- a/checks/security_checks.ts +++ b/checks/security_checks.ts @@ -54,23 +54,59 @@ export const URL_SCHEMES_CAUSING_XSS: string[] = ['data:', 'http:', 'https:']; * is present). */ export function checkScriptUnsafeInline(effectiveCsp: EnforcedCsps): Finding[] { + const findings: Finding[] = []; + for (const cspChecked of effectiveCsp) { const directiveName = cspChecked.getEffectiveDirective(Directive.SCRIPT_SRC); const values: string[] = cspChecked.directives[directiveName] || []; // Check if unsafe-inline is present. if (values.includes(Keyword.UNSAFE_INLINE)) { - return [new Finding( + findings.push(new Finding( Type.SCRIPT_UNSAFE_INLINE, `'unsafe-inline' allows the execution of unsafe in-page scripts ` + 'and event handlers.', - Severity.HIGH, directiveName, Keyword.UNSAFE_INLINE)]; + Severity.HIGH, directiveName, Keyword.UNSAFE_INLINE)); } } - return []; + return findings; +} + + +/** + * Checks if passed csp allows inline styles. + * Findings of this check are critical and FP free. + * unsafe-inline is ignored in the presence of a nonce or a hash. This check + * does not account for this and therefore the effectiveCsp needs to be passed. + * + * Example policy where this check would trigger: + * style-src 'unsafe-inline' + * + * @param effectiveCsp A parsed csp that only contains values which + * are active in a certain version of CSP (e.g. no unsafe-inline if a nonce + * is present). + */ +export function checkStyleUnsafeInline(effectiveCsp: EnforcedCsps): Finding[] { + const findings: Finding[] = []; + + for (const cspChecked of effectiveCsp) { + const directiveName = cspChecked.getEffectiveDirective(Directive.STYLE_SRC); + const values: string[] = cspChecked.directives[directiveName] || []; + + // Check if unsafe-inline is present. + if (values.includes(Keyword.UNSAFE_INLINE)) { + findings.push(new Finding( + Type.STYLE_UNSAFE_INLINE, + 'Unsafe inline stylesheet are allowed. This could help social engineering attacks in case of content injection.', + Severity.MEDIUM, directiveName, Keyword.UNSAFE_INLINE)); + } + } + + return findings; } + /** * Checks if passed csp allows web assembly eval in scripts. * Findings of this check have a medium severity and are FP free. @@ -160,6 +196,34 @@ export function checkPlainUrlSchemes(parsedCsps: EnforcedCsps): Finding[] { } +/** + * Checks if plain URL schemes (e.g. http:) are allowed in the form-action directive. + * Findings of this check have a low severity and are FP free. + * + * Example policy where this check would trigger: + * form-action https: http: + * + * @param parsedCsp Parsed CSP. + */ +export function checkPlainUrlSchemesInFormActions(parsedCsps: EnforcedCsps): Finding[] { + const violations: Finding[] = []; + + for (const cspChecked of parsedCsps) { + const values = cspChecked.directives[Directive.FORM_ACTION] || []; + for (const value of values) { + if (value == 'https:' || value == 'http:') { + violations.push(new Finding( + Type.PLAIN_URL_SCHEMES, + 'Form actions only has protocol restrictions and still allows arbitrary hosts. This could help social engineering attacks in case of content injection.', + Severity.LOW, Directive.FORM_ACTION, value)); + } + } + } + + return violations; +} + + /** * Checks if csp contains wildcards in sensitive directives. * Findings of this check have a high severity and are FP free. @@ -231,6 +295,36 @@ export function checkMissingScriptSrcDirective(parsedCsps: EnforcedCsps): Findin Severity.HIGH, Directive.SCRIPT_SRC)]; } +/** + * Checks if style-src is restricted either directly or via a default-src. + */ +export function checkMissingStyleSrcDirective(parsedCsps: EnforcedCsps): Finding[] { + for (const cspChecked of parsedCsps) { + if (Directive.STYLE_SRC in cspChecked.directives || + Directive.DEFAULT_SRC in cspChecked.directives) { + return []; + } + } + return [new Finding( + Type.MISSING_DIRECTIVES, 'No stylesheet restrictions are present. This could help social engineering attacks in case of content injection.', + Severity.LOW, Directive.STYLE_SRC)]; +} + + +/** + * Checks if form-action is restricted. + */ +export function checkMissingFormActionDirective(parsedCsps: EnforcedCsps): Finding[] { + for (const cspChecked of parsedCsps) { + if (Directive.FORM_ACTION in cspChecked.directives) { + return []; + } + } + return [new Finding( + Type.MISSING_DIRECTIVES, 'No form action restrictions are present. This could help social engineering attacks in case of content injection.', + Severity.LOW, Directive.FORM_ACTION)]; +} + /** * Checks if the base-uri needs to be restricted and if so, whether it has been * restricted. @@ -289,6 +383,8 @@ export function checkMissingDirectives(parsedCsps: EnforcedCsps): Finding[] { ...checkMissingObjectSrcDirective(parsedCsps), ...checkMissingScriptSrcDirective(parsedCsps), ...checkMissingBaseUriDirective(parsedCsps), + ...checkMissingStyleSrcDirective(parsedCsps), + ...checkMissingFormActionDirective(parsedCsps) ]; } diff --git a/checks/security_checks_test.ts b/checks/security_checks_test.ts index 7cd4e1d..e0177f7 100644 --- a/checks/security_checks_test.ts +++ b/checks/security_checks_test.ts @@ -44,6 +44,8 @@ const violations = checkCsp(test, securityChecks.checkScriptUnsafeInline); expect(violations.length).toBe(1); expect(violations[0].severity).toBe(Severity.HIGH); + expect(violations[0].type).toBe(Type.SCRIPT_UNSAFE_INLINE); + expect(violations[0].directive).toBe('script-src'); }); it('CheckScriptUnsafeInlineInDefaultSrc', () => { @@ -51,6 +53,9 @@ const violations = checkCsp(test, securityChecks.checkScriptUnsafeInline); expect(violations.length).toBe(1); + expect(violations[0].severity).toBe(Severity.HIGH); + expect(violations[0].type).toBe(Type.SCRIPT_UNSAFE_INLINE); + expect(violations[0].directive).toBe('default-src'); }); it('CheckScriptUnsafeInlineInDefaultSrcAndNotInScriptSrc', () => { @@ -73,6 +78,48 @@ expect(violations.length).toBe(0); }); + /** Tests for csp.securityChecks.checkStyleUnsafeInline */ + it('CheckStyleUnsafeInlineInStyleSrc', () => { + const test = 'default-src https:; style-src \'unsafe-inline\''; + + const violations = checkCsp(test, securityChecks.checkStyleUnsafeInline); + expect(violations.length).toBe(1); + expect(violations[0].severity).toBe(Severity.MEDIUM); + expect(violations[0].type).toBe(Type.STYLE_UNSAFE_INLINE); + expect(violations[0].directive).toBe('style-src'); + }); + + it('CheckStyleUnsafeInlineInDefaultSrc', () => { + const test = 'default-src \'unsafe-inline\'; script-src \'self\''; + + const violations = checkCsp(test, securityChecks.checkStyleUnsafeInline); + expect(violations.length).toBe(1); + expect(violations[0].severity).toBe(Severity.MEDIUM); + expect(violations[0].type).toBe(Type.STYLE_UNSAFE_INLINE); + expect(violations[0].directive).toBe('default-src'); + }); + + it('CheckStyleUnsafeInlineInDefaultSrcAndNotInScriptSrc', () => { + const test = 'default-src \'unsafe-inline\'; style-src https:'; + + const violations = checkCsp(test, securityChecks.checkStyleUnsafeInline); + expect(violations.length).toBe(0); + }); + + // TODO: Fix the hardcoded script references in the getEffectiveCsps + //it('CheckStyleUnsafeInlineWithNonce', () => { + // const test = 'style-src \'unsafe-inline\' \'nonce-foobar\''; + // const parsedCsp = (new CspParser(test)).csps; + // + // let effectiveCsp = parsedCsp.getEffectiveCsps(Version.CSP1); + // let violations = securityChecks.checkStyleUnsafeInline(effectiveCsp); + // expect(violations.length).toBe(1); + // + // effectiveCsp = parsedCsp.getEffectiveCsps(Version.CSP3); + // violations = securityChecks.checkStyleUnsafeInline(effectiveCsp); + // expect(violations.length).toBe(0); + //}); + /** Tests for csp.securityChecks.checkScriptWasmUnsafeEval */ it('CheckScriptWasmUnsafeEvalInScriptSrc', () => { const test = 'default-src https:; script-src \'wasm-unsafe-eval\''; @@ -155,6 +202,29 @@ expect(violations.length).toBe(0); }); + /** Tests for csp.securityChecks.checkPlainUrlSchemesInFormActions */ + it('checkPlainUrlSchemesInFormActions', () => { + const test = 'form-action http: https:'; + + const violations = checkCsp(test, securityChecks.checkPlainUrlSchemesInFormActions); + expect(violations.length).toBe(2); + expect(violations[0].severity).toBe(Severity.LOW); + expect(violations[0].type).toBe(Type.PLAIN_URL_SCHEMES); + expect(violations[0].directive).toBe('form-action'); + expect(violations[1].severity).toBe(Severity.LOW); + expect(violations[1].type).toBe(Type.PLAIN_URL_SCHEMES); + expect(violations[1].directive).toBe('form-action'); + }); + + it('checkPlainUrlSchemesInFormActionsOK', () => { + const test = + 'default-src https:; object-src \'none\'; script-src \'none\'; ' + + 'base-uri \'none\'; form-action \'none\''; + + const violations = checkCsp(test, securityChecks.checkPlainUrlSchemesInFormActions); + expect(violations.length).toBe(0); + }); + /** Tests for csp.securityChecks.checkWildcards */ it('CheckWildcardsInScriptSrc', () => { const test = 'script-src * http://* //*'; @@ -196,69 +266,108 @@ /** Tests for csp.securityChecks.checkMissingDirectives */ it('CheckMissingDirectivesMissingObjectSrc', () => { - const test = 'script-src \'none\''; + const test = 'script-src \'none\'; style-src \'none\'; form-action \'self\''; const violations = checkCsp(test, securityChecks.checkMissingDirectives); expect(violations.length).toBe(1); expect(violations[0].severity).toBe(Severity.HIGH); + expect(violations[0].type).toBe(Type.MISSING_DIRECTIVES); + expect(violations[0].directive).toBe('object-src'); }); it('CheckMissingDirectivesMissingScriptSrc', () => { - const test = 'object-src \'none\''; + const test = 'object-src \'none\'; style-src \'none\'; form-action \'self\''; const violations = checkCsp(test, securityChecks.checkMissingDirectives); expect(violations.length).toBe(1); expect(violations[0].severity).toBe(Severity.HIGH); + expect(violations[0].type).toBe(Type.MISSING_DIRECTIVES); + expect(violations[0].directive).toBe('script-src'); + }); + + it('CheckMissingDirectivesMissingStyleSrc', () => { + const test = 'script-src \'none\'; object-src \'none\'; form-action \'self\''; + + const violations = checkCsp(test, securityChecks.checkMissingDirectives); + expect(violations.length).toBe(1); + expect(violations[0].severity).toBe(Severity.LOW); + expect(violations[0].type).toBe(Type.MISSING_DIRECTIVES); + expect(violations[0].directive).toBe('style-src'); + }); + + it('CheckMissingDirectivesMissingFormAction', () => { + const test = 'script-src \'none\'; object-src \'none\'; style-src \'none\''; + + const violations = checkCsp(test, securityChecks.checkMissingDirectives); + expect(violations.length).toBe(1); + expect(violations[0].severity).toBe(Severity.LOW); + expect(violations[0].type).toBe(Type.MISSING_DIRECTIVES); + expect(violations[0].directive).toBe('form-action'); }); it('CheckMissingDirectivesObjectSrcSelf', () => { - const test = 'object-src \'self\''; + const test = 'object-src \'self\'; style-src \'none\'; form-action \'self\''; const violations = checkCsp(test, securityChecks.checkMissingDirectives); expect(violations.length).toBe(1); expect(violations[0].severity).toBe(Severity.HIGH); + expect(violations[0].type).toBe(Type.MISSING_DIRECTIVES); + expect(violations[0].directive).toBe('script-src'); }); it('CheckMissingDirectivesMissingBaseUriInNonceCsp', () => { - const test = 'script-src \'nonce-123\'; object-src \'none\''; + const test = 'script-src \'nonce-123\'; object-src \'none\'; style-src \'none\'; form-action \'self\''; const violations = checkCsp(test, securityChecks.checkMissingDirectives); expect(violations.length).toBe(1); expect(violations[0].severity).toBe(Severity.HIGH); + expect(violations[0].type).toBe(Type.MISSING_DIRECTIVES); + expect(violations[0].directive).toBe('base-uri'); }); it('CheckMissingDirectivesMissingBaseUriInHashWStrictDynamicCsp', () => { const test = - 'script-src \'sha256-123456\' \'strict-dynamic\'; object-src \'none\''; + 'script-src \'sha256-123456\' \'strict-dynamic\'; object-src \'none\'; style-src \'none\'; form-action \'self\''; const violations = checkCsp(test, securityChecks.checkMissingDirectives); expect(violations.length).toBe(1); expect(violations[0].severity).toBe(Severity.HIGH); + expect(violations[0].type).toBe(Type.MISSING_DIRECTIVES); + expect(violations[0].directive).toBe('base-uri'); }); it('CheckMissingDirectivesMissingBaseUriInHashCsp', () => { - const test = 'script-src \'sha256-123456\'; object-src \'none\''; + const test = 'script-src \'sha256-123456\'; object-src \'none\'; style-src \'none\'; form-action \'self\''; const violations = checkCsp(test, securityChecks.checkMissingDirectives); expect(violations.length).toBe(0); }); - it('CheckMissingDirectivesScriptAndObjectSrcSet', () => { - const test = 'script-src \'none\'; object-src \'none\''; + it('CheckMissingDirectivesAllSetExplicit', () => { + const test = 'script-src \'none\'; object-src \'none\'; style-src \'none\'; form-action \'self\''; const violations = checkCsp(test, securityChecks.checkMissingDirectives); expect(violations.length).toBe(0); }); it('CheckMissingDirectivesDefaultSrcSet', () => { - const test = 'default-src https:;'; + const test = 'default-src https:; form-action \'self\''; const violations = checkCsp(test, securityChecks.checkMissingDirectives); expect(violations.length).toBe(0); }); + it('CheckMissingDirectivesDefaultSrcSetNoFormAction', () => { + const test = 'default-src https:'; + + const violations = checkCsp(test, securityChecks.checkMissingDirectives); + expect(violations.length).toBe(1); + expect(violations[0].type).toBe(Type.MISSING_DIRECTIVES); + expect(violations[0].directive).toBe('form-action'); + }); + it('CheckMissingDirectivesDefaultSrcSetToNone', () => { - const test = 'default-src \'none\';'; + const test = 'default-src \'none\'; form-action \'self\''; const violations = checkCsp(test, securityChecks.checkMissingDirectives); expect(violations.length).toBe(0); From a9b98973b2db2461e8d364657d12cdf9ba231c87 Mon Sep 17 00:00:00 2001 From: MaxNad Date: Mon, 8 Jan 2024 15:13:26 -0500 Subject: [PATCH 09/12] Made the module more backward compatible --- README.md | 2 +- checks/checker.ts | 4 +- checks/parser_checks.ts | 23 +- checks/parser_checks_test.ts | 2 +- checks/security_checks.ts | 185 +++++---- checks/security_checks_test.ts | 8 +- checks/strictcsp_checks.ts | 37 +- checks/strictcsp_checks_test.ts | 2 +- csp.ts | 204 ++++++---- csp_test.ts | 60 +-- enforced_csps.ts | 210 ----------- enforced_csps_test.ts | 50 --- evaluator.ts | 13 +- evaluator_test.ts | 10 +- lighthouse/lighthouse_checks.ts | 151 ++++++++ lighthouse/lighthouse_checks_test.ts | 538 +++++++++++++++++++++++++++ parser.ts | 37 +- parser_test.ts | 243 ++++++------ utils.ts | 13 +- utils_test.ts | 28 +- 20 files changed, 1157 insertions(+), 663 deletions(-) delete mode 100644 enforced_csps.ts delete mode 100644 enforced_csps_test.ts create mode 100644 lighthouse/lighthouse_checks.ts create mode 100644 lighthouse/lighthouse_checks_test.ts diff --git a/README.md b/README.md index c334962..dcfe196 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,6 @@ npm install && npm test import {CspEvaluator} from "csp_evaluator/dist/evaluator.js"; import {CspParser} from "csp_evaluator/dist/parser.js"; -const parsed = new CspParser(["script-src https://google.com"]).csps; +const parsed = new CspParser("script-src https://google.com").csp; console.log(new CspEvaluator(parsed).evaluate()); ``` diff --git a/checks/checker.ts b/checks/checker.ts index 4b9704b..1729478 100644 --- a/checks/checker.ts +++ b/checks/checker.ts @@ -2,11 +2,11 @@ * @fileoverview Shared interfaces for functions that check CSP policies. */ -import { EnforcedCsps } from '../enforced_csps'; +import { Csp } from '../csp'; import { Finding } from '../finding'; /** * A function that checks a list of Csps for problems and returns an unordered * list of Findings. */ -export type CheckerFunction = (csps: EnforcedCsps) => Finding[]; +export type CheckerFunction = (csp: Csp) => Finding[]; diff --git a/checks/parser_checks.ts b/checks/parser_checks.ts index 93572e2..884eeaa 100644 --- a/checks/parser_checks.ts +++ b/checks/parser_checks.ts @@ -19,8 +19,7 @@ * limitations under the License. */ -import { Directive, Keyword, TrustedTypesSink, isDirective, isHash, isKeyword, isNonce } from '../csp'; -import { EnforcedCsps } from '../enforced_csps'; +import { Csp, Directive, Keyword, TrustedTypesSink, isDirective, isHash, isKeyword, isNonce } from '../csp'; import { Finding, Severity, Type } from '../finding'; @@ -33,11 +32,11 @@ import { Finding, Severity, Type } from '../finding'; * * @param parsedCsp A parsed csp. */ -export function checkUnknownDirective(parsedCsps: EnforcedCsps): Finding[] { +export function checkUnknownDirective(parsedCsps: Csp): Finding[] { const findings: Finding[] = []; - for (const cspChecked of parsedCsps) { - for (const directive of Object.keys(cspChecked.directives)) { + for (const currentCsp of parsedCsps.directives) { + for (const directive of Object.keys(currentCsp)) { if (isDirective(directive)) { // Directive is known. continue; @@ -69,12 +68,11 @@ export function checkUnknownDirective(parsedCsps: EnforcedCsps): Finding[] { * * @param parsedCsp A parsed csp. */ -export function checkMissingSemicolon(parsedCsps: EnforcedCsps): Finding[] { +export function checkMissingSemicolon(parsedCsps: Csp): Finding[] { const findings: Finding[] = []; - for (const cspChecked of parsedCsps) { - for (const [directive, directiveValues] of Object.entries( - cspChecked.directives)) { + for (const cspChecked of parsedCsps.directives) { + for (const [directive, directiveValues] of Object.entries(cspChecked)) { if (directiveValues === undefined) { continue; } @@ -104,14 +102,13 @@ export function checkMissingSemicolon(parsedCsps: EnforcedCsps): Finding[] { * * @param parsedCsp A parsed csp. */ -export function checkInvalidKeyword(parsedCsps: EnforcedCsps): Finding[] { +export function checkInvalidKeyword(parsedCsps: Csp): Finding[] { const findings: Finding[] = []; const keywordsNoTicks = Object.values(Keyword).map((k) => k.replace(/'/g, '')); - for (const cspChecked of parsedCsps) { - for (const [directive, directiveValues] of Object.entries( - cspChecked.directives)) { + for (const cspChecked of parsedCsps.directives) { + for (const [directive, directiveValues] of Object.entries(cspChecked)) { if (directiveValues === undefined) { continue; } diff --git a/checks/parser_checks_test.ts b/checks/parser_checks_test.ts index 47fba05..bcf7424 100644 --- a/checks/parser_checks_test.ts +++ b/checks/parser_checks_test.ts @@ -32,7 +32,7 @@ import * as parserChecks from './parser_checks'; * @param checkFunction check. */ function checkCsp(test: string, checkFunction: CheckerFunction): Finding[] { - const parsedCsp = (new CspParser(test)).csps; + const parsedCsp = (new CspParser(test)).csp; return checkFunction(parsedCsp); } diff --git a/checks/security_checks.ts b/checks/security_checks.ts index 2811553..dfa99a2 100644 --- a/checks/security_checks.ts +++ b/checks/security_checks.ts @@ -20,8 +20,7 @@ import * as angular from '../allowlist_bypasses/angular'; import * as flash from '../allowlist_bypasses/flash'; import * as jsonp from '../allowlist_bypasses/jsonp'; -import { Directive, Keyword, isNonce, isUrlScheme } from '../csp'; -import { EnforcedCsps } from '../enforced_csps'; +import { Csp, Directive, Keyword, isNonce, isUrlScheme } from '../csp'; import { Finding, Severity, Type } from '../finding'; import * as utils from '../utils'; @@ -53,12 +52,12 @@ export const URL_SCHEMES_CAUSING_XSS: string[] = ['data:', 'http:', 'https:']; * are active in a certain version of CSP (e.g. no unsafe-inline if a nonce * is present). */ -export function checkScriptUnsafeInline(effectiveCsp: EnforcedCsps): Finding[] { +export function checkScriptUnsafeInline(effectiveCsp: Csp): Finding[] { const findings: Finding[] = []; - for (const cspChecked of effectiveCsp) { - const directiveName = cspChecked.getEffectiveDirective(Directive.SCRIPT_SRC); - const values: string[] = cspChecked.directives[directiveName] || []; + const directiveName = effectiveCsp.getEffectiveDirective(Directive.SCRIPT_SRC); + for (const cspChecked of effectiveCsp.directives) { + const values: string[] = cspChecked[directiveName] || []; // Check if unsafe-inline is present. if (values.includes(Keyword.UNSAFE_INLINE)) { @@ -87,12 +86,12 @@ export function checkScriptUnsafeInline(effectiveCsp: EnforcedCsps): Finding[] { * are active in a certain version of CSP (e.g. no unsafe-inline if a nonce * is present). */ -export function checkStyleUnsafeInline(effectiveCsp: EnforcedCsps): Finding[] { +export function checkStyleUnsafeInline(effectiveCsp: Csp): Finding[] { const findings: Finding[] = []; - for (const cspChecked of effectiveCsp) { - const directiveName = cspChecked.getEffectiveDirective(Directive.STYLE_SRC); - const values: string[] = cspChecked.directives[directiveName] || []; + const directiveName = effectiveCsp.getEffectiveDirective(Directive.STYLE_SRC); + for (const cspChecked of effectiveCsp.directives) { + const values: string[] = cspChecked[directiveName] || []; // Check if unsafe-inline is present. if (values.includes(Keyword.UNSAFE_INLINE)) { @@ -116,12 +115,12 @@ export function checkStyleUnsafeInline(effectiveCsp: EnforcedCsps): Finding[] { * * @param parsedCsp Parsed CSP. */ -export function checkScriptWasmUnsafeEval(parsedCsps: EnforcedCsps): Finding[] { +export function checkScriptWasmUnsafeEval(parsedCsps: Csp): Finding[] { const findings: Finding[] = []; - for (const cspChecked of parsedCsps) { - const directiveName = cspChecked.getEffectiveDirective(Directive.SCRIPT_SRC); - const values: string[] = cspChecked.directives[directiveName] || []; + const directiveName = parsedCsps.getEffectiveDirective(Directive.SCRIPT_SRC); + for (const cspChecked of parsedCsps.directives) { + const values: string[] = cspChecked[directiveName] || []; // Check if wasm-unsafe-eval is present. if (values.includes(Keyword.WASM_UNSAFE_EVAL)) { @@ -145,10 +144,10 @@ export function checkScriptWasmUnsafeEval(parsedCsps: EnforcedCsps): Finding[] { * * @param parsedCsp Parsed CSP. */ -export function checkScriptUnsafeEval(parsedCsps: EnforcedCsps): Finding[] { - for (const cspChecked of parsedCsps) { - const directiveName = cspChecked.getEffectiveDirective(Directive.SCRIPT_SRC); - const values: string[] = cspChecked.directives[directiveName] || []; +export function checkScriptUnsafeEval(parsedCsps: Csp): Finding[] { + const directiveName = parsedCsps.getEffectiveDirective(Directive.SCRIPT_SRC); + for (const cspChecked of parsedCsps.directives) { + const values: string[] = cspChecked[directiveName] || []; // Check if unsafe-eval is present. if (values.includes(Keyword.UNSAFE_EVAL)) { @@ -172,14 +171,13 @@ export function checkScriptUnsafeEval(parsedCsps: EnforcedCsps): Finding[] { * * @param parsedCsp Parsed CSP. */ -export function checkPlainUrlSchemes(parsedCsps: EnforcedCsps): Finding[] { +export function checkPlainUrlSchemes(parsedCsps: Csp): Finding[] { const violations: Finding[] = []; - for (const cspChecked of parsedCsps) { - const directivesToCheck = cspChecked.getEffectiveDirectives(DIRECTIVES_CAUSING_XSS); - + const directivesToCheck = parsedCsps.getEffectiveDirectives(DIRECTIVES_CAUSING_XSS); + for (const cspChecked of parsedCsps.directives) { for (const directive of directivesToCheck) { - const values = cspChecked.directives[directive] || []; + const values = cspChecked[directive] || []; for (const value of values) { if (URL_SCHEMES_CAUSING_XSS.includes(value)) { violations.push(new Finding( @@ -205,11 +203,11 @@ export function checkPlainUrlSchemes(parsedCsps: EnforcedCsps): Finding[] { * * @param parsedCsp Parsed CSP. */ -export function checkPlainUrlSchemesInFormActions(parsedCsps: EnforcedCsps): Finding[] { +export function checkPlainUrlSchemesInFormActions(parsedCsps: Csp): Finding[] { const violations: Finding[] = []; - for (const cspChecked of parsedCsps) { - const values = cspChecked.directives[Directive.FORM_ACTION] || []; + for (const cspChecked of parsedCsps.directives) { + const values = cspChecked[Directive.FORM_ACTION] || []; for (const value of values) { if (value == 'https:' || value == 'http:') { violations.push(new Finding( @@ -233,14 +231,13 @@ export function checkPlainUrlSchemesInFormActions(parsedCsps: EnforcedCsps): Fin * * @param parsedCsp Parsed CSP. */ -export function checkWildcards(parsedCsps: EnforcedCsps): Finding[] { +export function checkWildcards(parsedCsps: Csp): Finding[] { const violations: Finding[] = []; - for (const cspChecked of parsedCsps) { - const directivesToCheck = cspChecked.getEffectiveDirectives(DIRECTIVES_CAUSING_XSS); - + const directivesToCheck = parsedCsps.getEffectiveDirectives(DIRECTIVES_CAUSING_XSS); + for (const cspChecked of parsedCsps.directives) { for (const directive of directivesToCheck) { - const values = cspChecked.directives[directive] || []; + const values = cspChecked[directive] || []; for (const value of values) { const url = utils.getSchemeFreeUrl(value); if (url === '*') { @@ -260,14 +257,14 @@ export function checkWildcards(parsedCsps: EnforcedCsps): Finding[] { * Checks if object-src is restricted to none either directly or via a * default-src. */ -export function checkMissingObjectSrcDirective(parsedCsps: EnforcedCsps): Finding[] { +export function checkMissingObjectSrcDirective(parsedCsps: Csp): Finding[] { let objectRestrictions: string[]|undefined = []; - for (const cspChecked of parsedCsps) { - if (Directive.OBJECT_SRC in cspChecked.directives) { - objectRestrictions = cspChecked.directives[Directive.OBJECT_SRC]; - } else if (Directive.DEFAULT_SRC in cspChecked.directives) { - objectRestrictions = cspChecked.directives[Directive.DEFAULT_SRC]; + for (const cspChecked of parsedCsps.directives) { + if (Directive.OBJECT_SRC in cspChecked) { + objectRestrictions = cspChecked[Directive.OBJECT_SRC]; + } else if (Directive.DEFAULT_SRC in cspChecked) { + objectRestrictions = cspChecked[Directive.DEFAULT_SRC]; } } @@ -283,10 +280,10 @@ export function checkMissingObjectSrcDirective(parsedCsps: EnforcedCsps): Findin /** * Checks if script-src is restricted either directly or via a default-src. */ -export function checkMissingScriptSrcDirective(parsedCsps: EnforcedCsps): Finding[] { - for (const cspChecked of parsedCsps) { - if (Directive.SCRIPT_SRC in cspChecked.directives || - Directive.DEFAULT_SRC in cspChecked.directives) { +export function checkMissingScriptSrcDirective(parsedCsps: Csp): Finding[] { + for (const cspChecked of parsedCsps.directives) { + if (Directive.SCRIPT_SRC in cspChecked || + Directive.DEFAULT_SRC in cspChecked) { return []; } } @@ -298,10 +295,10 @@ export function checkMissingScriptSrcDirective(parsedCsps: EnforcedCsps): Findin /** * Checks if style-src is restricted either directly or via a default-src. */ -export function checkMissingStyleSrcDirective(parsedCsps: EnforcedCsps): Finding[] { - for (const cspChecked of parsedCsps) { - if (Directive.STYLE_SRC in cspChecked.directives || - Directive.DEFAULT_SRC in cspChecked.directives) { +export function checkMissingStyleSrcDirective(parsedCsps: Csp): Finding[] { + for (const cspChecked of parsedCsps.directives) { + if (Directive.STYLE_SRC in cspChecked || + Directive.DEFAULT_SRC in cspChecked) { return []; } } @@ -314,9 +311,9 @@ export function checkMissingStyleSrcDirective(parsedCsps: EnforcedCsps): Finding /** * Checks if form-action is restricted. */ -export function checkMissingFormActionDirective(parsedCsps: EnforcedCsps): Finding[] { - for (const cspChecked of parsedCsps) { - if (Directive.FORM_ACTION in cspChecked.directives) { +export function checkMissingFormActionDirective(parsedCsps: Csp): Finding[] { + for (const cspChecked of parsedCsps.directives) { + if (Directive.FORM_ACTION in cspChecked) { return []; } } @@ -329,9 +326,7 @@ export function checkMissingFormActionDirective(parsedCsps: EnforcedCsps): Findi * Checks if the base-uri needs to be restricted and if so, whether it has been * restricted. */ -export function checkMissingBaseUriDirective(parsedCsps: EnforcedCsps): Finding[] { - const findings: Finding[] = []; - +export function checkMissingBaseUriDirective(parsedCsps: Csp): Finding[] { // base-uri can be used to bypass nonce based CSPs and hash based CSPs that // use strict dynamic const needsBaseUri = ( @@ -340,32 +335,26 @@ export function checkMissingBaseUriDirective(parsedCsps: EnforcedCsps): Finding[ ); if (needsBaseUri) { - for (const csp of parsedCsps) { - if (!(Directive.BASE_URI in csp.directives)) { - const description = 'Missing base-uri allows the injection of base tags. ' + - 'They can be used to set the base URL for all relative (script) ' + - 'URLs to an attacker controlled domain. ' + - `Can you set it to 'none' or 'self'?`; - findings.push(new Finding( - Type.MISSING_DIRECTIVES, description, Severity.HIGH, - Directive.BASE_URI)); + let hasBaseUri: boolean = false; + + for (const currentCsp of parsedCsps.directives) { + if (Directive.BASE_URI in currentCsp) { + hasBaseUri = true; } } - } - - return findings; -} -/** - * Checks if the base-uri needs to be restricted and if so, whether it has been - * restricted. - */ -export function checkMultipleMissingBaseUriDirective(parsedCsps: EnforcedCsps[]): Finding[] { - let findings: Finding[] = []; - for (const csp of parsedCsps) { - findings = findings.concat(checkMissingBaseUriDirective(csp)); + if (!hasBaseUri) { + const description = 'Missing base-uri allows the injection of base tags. ' + + 'They can be used to set the base URL for all relative (script) ' + + 'URLs to an attacker controlled domain. ' + + `Can you set it to 'none' or 'self'?`; + return [new Finding( + Type.MISSING_DIRECTIVES, description, Severity.HIGH, + Directive.BASE_URI)]; + } } - return findings; + + return []; } @@ -378,7 +367,7 @@ export function checkMultipleMissingBaseUriDirective(parsedCsps: EnforcedCsps[]) * * @param parsedCsp Parsed CSP. */ -export function checkMissingDirectives(parsedCsps: EnforcedCsps): Finding[] { +export function checkMissingDirectives(parsedCsps: Csp): Finding[] { return [ ...checkMissingObjectSrcDirective(parsedCsps), ...checkMissingScriptSrcDirective(parsedCsps), @@ -398,12 +387,12 @@ export function checkMissingDirectives(parsedCsps: EnforcedCsps): Finding[] { * * @param parsedCsp Parsed CSP. */ -export function checkScriptAllowlistBypass(parsedCsps: EnforcedCsps): Finding[] { +export function checkScriptAllowlistBypass(parsedCsps: Csp): Finding[] { const violations: Finding[] = []; - for (const cspChecked of parsedCsps) { - const effectiveScriptSrcDirective = cspChecked.getEffectiveDirective(Directive.SCRIPT_SRC); - const scriptSrcValues = cspChecked.directives[effectiveScriptSrcDirective] || []; + const effectiveScriptSrcDirective = parsedCsps.getEffectiveDirective(Directive.SCRIPT_SRC); + for (const cspChecked of parsedCsps.directives) { + const scriptSrcValues = cspChecked[effectiveScriptSrcDirective] || []; if (scriptSrcValues.includes(Keyword.NONE)) { return violations; } @@ -484,15 +473,15 @@ export function checkScriptAllowlistBypass(parsedCsps: EnforcedCsps): Finding[] * * @param parsedCsp Parsed CSP. */ -export function checkFlashObjectAllowlistBypass(parsedCsps: EnforcedCsps): Finding[] { +export function checkFlashObjectAllowlistBypass(parsedCsps: Csp): Finding[] { const violations = []; - for (const cspChecked of parsedCsps) { - const effectiveObjectSrcDirective = cspChecked.getEffectiveDirective(Directive.OBJECT_SRC); - const objectSrcValues = cspChecked.directives[effectiveObjectSrcDirective] || []; + const effectiveObjectSrcDirective = parsedCsps.getEffectiveDirective(Directive.OBJECT_SRC); + for (const cspChecked of parsedCsps.directives) { + const objectSrcValues = cspChecked[effectiveObjectSrcDirective] || []; // If flash is not allowed in plugin-types, continue. - const pluginTypes = cspChecked.directives[Directive.PLUGIN_TYPES]; + const pluginTypes = cspChecked[Directive.PLUGIN_TYPES]; if (pluginTypes && !pluginTypes.includes('application/x-shockwave-flash')) { return []; } @@ -554,10 +543,10 @@ export function looksLikeIpAddress(maybeIp: string): boolean { * * @param parsedCsp Parsed CSP. */ -export function checkIpSource(parsedCsps: EnforcedCsps): Finding[] { +export function checkIpSource(parsedCsps: Csp): Finding[] { const violations: Finding[] = []; - for (const cspChecked of parsedCsps) { + for (const cspChecked of parsedCsps.directives) { // Function for checking if directive values contain IP addresses. const checkIp = (directive: string, directiveValues: string[]) => { for (const value of directiveValues) { @@ -598,12 +587,12 @@ export function checkIpSource(parsedCsps: EnforcedCsps): Finding[] { * * @param parsedCsp Parsed CSP. */ -export function checkDeprecatedDirective(parsedCsps: EnforcedCsps): Finding[] { +export function checkDeprecatedDirective(parsedCsps: Csp): Finding[] { const violations = []; - for (const cspChecked of parsedCsps) { + for (const cspChecked of parsedCsps.directives) { // More details: https://www.chromestatus.com/feature/5769374145183744 - if (Directive.REFLECTED_XSS in cspChecked.directives) { + if (Directive.REFLECTED_XSS in cspChecked) { violations.push(new Finding( Type.DEPRECATED_DIRECTIVE, 'reflected-xss is deprecated since CSP2. ' + @@ -612,7 +601,7 @@ export function checkDeprecatedDirective(parsedCsps: EnforcedCsps): Finding[] { } // More details: https://www.chromestatus.com/feature/5680800376815616 - if (Directive.REFERRER in cspChecked.directives) { + if (Directive.REFERRER in cspChecked) { violations.push(new Finding( Type.DEPRECATED_DIRECTIVE, 'referrer is deprecated since CSP2. ' + @@ -621,7 +610,7 @@ export function checkDeprecatedDirective(parsedCsps: EnforcedCsps): Finding[] { } // More details: https://github.com/w3c/webappsec-csp/pull/327 - if (Directive.DISOWN_OPENER in cspChecked.directives) { + if (Directive.DISOWN_OPENER in cspChecked) { violations.push(new Finding( Type.DEPRECATED_DIRECTIVE, 'disown-opener is deprecated since CSP3. ' + @@ -642,11 +631,11 @@ export function checkDeprecatedDirective(parsedCsps: EnforcedCsps): Finding[] { * * @param parsedCsp Parsed CSP. */ -export function checkNonceLength(parsedCsps: EnforcedCsps): Finding[] { +export function checkNonceLength(parsedCsps: Csp): Finding[] { const noncePattern = new RegExp('^\'nonce-(.+)\'$'); const violations: Finding[] = []; - for (const cspChecked of parsedCsps) { + for (const cspChecked of parsedCsps.directives) { utils.applyCheckFunktionToDirectives( cspChecked, (directive, directiveValues) => { for (const value of directiveValues) { @@ -687,13 +676,13 @@ export function checkNonceLength(parsedCsps: EnforcedCsps): Finding[] { * * @param parsedCsp Parsed CSP. */ -export function checkSrcHttp(parsedCsps: EnforcedCsps): Finding[] { +export function checkSrcHttp(parsedCsps: Csp): Finding[] { const violations: Finding[] = []; const directivesForcingHttps: string[] = []; const directivesWithViolation: Record = {}; - for (const cspChecked of parsedCsps) { + for (const cspChecked of parsedCsps.directives) { utils.applyCheckFunktionToDirectives( cspChecked, (directive, directiveValues) => { for (const value of directiveValues) { @@ -731,16 +720,16 @@ export function checkSrcHttp(parsedCsps: EnforcedCsps): Finding[] { /** * Checks if the policy has configured reporting in a robust manner. */ -export function checkHasConfiguredReporting(parsedCsps: EnforcedCsps): Finding[] { +export function checkHasConfiguredReporting(parsedCsps: Csp): Finding[] { const findings: Finding[] = []; - for (const cspChecked of parsedCsps) { - const reportUriValues: string[] = cspChecked.directives[Directive.REPORT_URI] || []; + for (const cspChecked of parsedCsps.directives) { + const reportUriValues: string[] = cspChecked[Directive.REPORT_URI] || []; if (reportUriValues.length > 0) { continue; } - const reportToValues: string[] = cspChecked.directives[Directive.REPORT_TO] || []; + const reportToValues: string[] = cspChecked[Directive.REPORT_TO] || []; if (reportToValues.length > 0) { findings.push(new Finding( Type.REPORT_TO_ONLY, diff --git a/checks/security_checks_test.ts b/checks/security_checks_test.ts index e0177f7..c06280d 100644 --- a/checks/security_checks_test.ts +++ b/checks/security_checks_test.ts @@ -32,7 +32,7 @@ * @param checkFunction check. */ function checkCsp(test: string, checkFunction: CheckerFunction): Finding[] { - const parsedCsp = (new CspParser(test)).csps; + const parsedCsp = (new CspParser(test)).csp; return checkFunction(parsedCsp); } @@ -67,13 +67,13 @@ it('CheckScriptUnsafeInlineWithNonce', () => { const test = 'script-src \'unsafe-inline\' \'nonce-foobar\''; - const parsedCsp = (new CspParser(test)).csps; + const parsedCsp = (new CspParser(test)).csp; - let effectiveCsp = parsedCsp.getEffectiveCsps(Version.CSP1); + let effectiveCsp = parsedCsp.getEffectiveCsp(Version.CSP1); let violations = securityChecks.checkScriptUnsafeInline(effectiveCsp); expect(violations.length).toBe(1); - effectiveCsp = parsedCsp.getEffectiveCsps(Version.CSP3); + effectiveCsp = parsedCsp.getEffectiveCsp(Version.CSP3); violations = securityChecks.checkScriptUnsafeInline(effectiveCsp); expect(violations.length).toBe(0); }); diff --git a/checks/strictcsp_checks.ts b/checks/strictcsp_checks.ts index 1cb2b52..94a9222 100644 --- a/checks/strictcsp_checks.ts +++ b/checks/strictcsp_checks.ts @@ -26,9 +26,7 @@ * limitations under the License. */ -import { Directive, Keyword, TrustedTypesSink } from '../csp'; -import { EnforcedCsps } from '../enforced_csps'; - +import { Csp, Directive, Keyword, TrustedTypesSink } from '../csp'; import { Finding, Severity, Type } from '../finding'; @@ -40,12 +38,12 @@ import { Finding, Severity, Type } from '../finding'; * * @param parsedCsp A parsed csp. */ -export function checkStrictDynamic(parsedCsps: EnforcedCsps): Finding[] { +export function checkStrictDynamic(parsedCsps: Csp): Finding[] { const directiveName = parsedCsps.getEffectiveDirective(Directive.SCRIPT_SRC); const findings: Finding[] = []; - for (const csp of parsedCsps) { - const values: string[] = csp.directives[directiveName] || []; + for (const currentCsp of parsedCsps.directives) { + const values: string[] = currentCsp[directiveName] || []; const schemeOrHostPresent = values.some((v) => !v.startsWith('\'')); @@ -71,12 +69,12 @@ export function checkStrictDynamic(parsedCsps: EnforcedCsps): Finding[] { * * @param parsedCsp A parsed csp. */ -export function checkStrictDynamicNotStandalone(parsedCsps: EnforcedCsps): Finding[] { +export function checkStrictDynamicNotStandalone(parsedCsps: Csp): Finding[] { const directiveName = parsedCsps.getEffectiveDirective(Directive.SCRIPT_SRC); const findings: Finding[] = []; - for (const csp of parsedCsps) { - const values: string[] = csp.directives[directiveName] || []; + for (const currentCsp of parsedCsps.directives) { + const values: string[] = currentCsp[directiveName] || []; if (values.includes(Keyword.STRICT_DYNAMIC) && (!parsedCsps.policyHasScriptNonces() && @@ -102,7 +100,7 @@ export function checkStrictDynamicNotStandalone(parsedCsps: EnforcedCsps): Findi * * @param parsedCsp A parsed csp. */ -export function checkUnsafeInlineFallback(parsedCsps: EnforcedCsps): Finding[] { +export function checkUnsafeInlineFallback(parsedCsps: Csp): Finding[] { if (!parsedCsps.policyHasScriptNonces() && !parsedCsps.policyHasScriptHashes()) { return []; @@ -111,8 +109,8 @@ export function checkUnsafeInlineFallback(parsedCsps: EnforcedCsps): Finding[] { const directiveName = parsedCsps.getEffectiveDirective(Directive.SCRIPT_SRC); const findings: Finding[] = []; - for (const csp of parsedCsps) { - const values: string[] = csp.directives[directiveName] || []; + for (const currentCsp of parsedCsps.directives) { + const values: string[] = currentCsp[directiveName] || []; if (!values.includes(Keyword.UNSAFE_INLINE)) { findings.push(new Finding( @@ -138,12 +136,12 @@ export function checkUnsafeInlineFallback(parsedCsps: EnforcedCsps): Finding[] { * * @param parsedCsp A parsed csp. */ -export function checkAllowlistFallback(parsedCsps: EnforcedCsps): Finding[] { +export function checkAllowlistFallback(parsedCsps: Csp): Finding[] { const directiveName = parsedCsps.getEffectiveDirective(Directive.SCRIPT_SRC); const findings: Finding[] = []; - for (const csp of parsedCsps) { - const values: string[] = csp.directives[directiveName] || []; + for (const currentCsp of parsedCsps.directives) { + const values: string[] = currentCsp[directiveName] || []; if (!values.includes(Keyword.STRICT_DYNAMIC)) { return []; @@ -173,11 +171,10 @@ export function checkAllowlistFallback(parsedCsps: EnforcedCsps): Finding[] { * * @param parsedCsp A parsed csp. */ -export function checkRequiresTrustedTypesForScripts(parsedCsps: EnforcedCsps): Finding[] { - - for (const cspChecked of parsedCsps) { - const directiveName = cspChecked.getEffectiveDirective(Directive.REQUIRE_TRUSTED_TYPES_FOR); - const values: string[] = cspChecked.directives[directiveName] || []; +export function checkRequiresTrustedTypesForScripts(parsedCsps: Csp): Finding[] { + const directiveName = parsedCsps.getEffectiveDirective(Directive.REQUIRE_TRUSTED_TYPES_FOR); + for (const cspChecked of parsedCsps.directives) { + const values: string[] = cspChecked[directiveName] || []; if (values.includes(TrustedTypesSink.SCRIPT)) { return []; diff --git a/checks/strictcsp_checks_test.ts b/checks/strictcsp_checks_test.ts index bb97d3a..1ec965a 100644 --- a/checks/strictcsp_checks_test.ts +++ b/checks/strictcsp_checks_test.ts @@ -31,7 +31,7 @@ import * as strictcspChecks from './strictcsp_checks'; * @param checkFunction check. */ function checkCsp(test: string, checkFunction: CheckerFunction): Finding[] { - const parsedCsp = (new CspParser(test)).csps; + const parsedCsp = (new CspParser(test)).csp; return checkFunction(parsedCsp); } diff --git a/csp.ts b/csp.ts index 7fd6077..d0e4bc2 100644 --- a/csp.ts +++ b/csp.ts @@ -27,7 +27,7 @@ import { Finding, Severity, Type } from './finding'; * - https://www.w3.org/TR/upgrade-insecure-requests/ */ export class Csp { - directives: Record = {}; + directives: Record[] = []; /** * Clones a CSP object. @@ -35,12 +35,18 @@ export class Csp { */ clone(): Csp { const clone = new Csp(); - for (const [directive, directiveValues] of Object.entries( - this.directives)) { - if (directiveValues) { - clone.directives[directive] = [...directiveValues]; - } + for (const currentDirective of this.directives) { + const directiveClone: Record = {}; + + for (const [directiveName, directiveValues] of Object.entries(currentDirective)) { + if (directiveValues) { + directiveClone[directiveName] = [...directiveValues]; + } + } + + clone.directives.push(directiveClone); } + return clone; } @@ -49,21 +55,25 @@ export class Csp { * @return CSP string. */ convertToString(): string { - let cspString = ''; - - for (const [directive, directiveValues] of Object.entries( - this.directives)) { - cspString += directive; - if (directiveValues !== undefined) { - for (let value, i = 0; (value = directiveValues[i]); i++) { - cspString += ' '; - cspString += value; + const cspStrings: string[] = []; + + for (const currentDirective of this.directives) { + let cspString: string = ''; + for (const [directive, directiveValues] of Object.entries(currentDirective)) { + cspString += directive; + if (directiveValues !== undefined) { + for (let value, i = 0; (value = directiveValues[i]); i++) { + cspString += ' '; + cspString += value; + } + } + cspString += '; '; } - } - cspString += '; '; + + cspStrings.push(cspString.trim()); } - return cspString; + return cspStrings.join(', '); } /** @@ -78,63 +88,66 @@ export class Csp { const findings = optFindings || []; const effectiveCsp = this.clone(); const directive = effectiveCsp.getEffectiveDirective(Directive.SCRIPT_SRC); - const values = this.directives[directive] || []; - const effectiveCspValues = effectiveCsp.directives[directive]; - - if (effectiveCspValues && - (effectiveCsp.policyHasScriptNonces() || - effectiveCsp.policyHasScriptHashes())) { - if (cspVersion >= Version.CSP2) { - // Ignore 'unsafe-inline' in CSP >= v2, if a nonce or a hash is present. - if (values.includes(Keyword.UNSAFE_INLINE)) { - arrayRemove(effectiveCspValues, Keyword.UNSAFE_INLINE); - findings.push(new Finding( - Type.IGNORED, - 'unsafe-inline is ignored if a nonce or a hash is present. ' + - '(CSP2 and above)', - Severity.NONE, directive, Keyword.UNSAFE_INLINE)); - } - } else { - // remove nonces and hashes (not supported in CSP < v2). - for (const value of values) { - if (value.startsWith('\'nonce-') || value.startsWith('\'sha')) { - arrayRemove(effectiveCspValues, value); - } + + for (let index = 0; index < effectiveCsp.directives.length; index++) { + const values = this.directives[index][directive] || []; + const effectiveCspValues = effectiveCsp.directives[index][directive]; + + if (effectiveCspValues && + (effectiveCsp.policyHasScriptNonces() || + effectiveCsp.policyHasScriptHashes())) { + if (cspVersion >= Version.CSP2) { + // Ignore 'unsafe-inline' in CSP >= v2, if a nonce or a hash is present. + if (values.includes(Keyword.UNSAFE_INLINE)) { + arrayRemove(effectiveCspValues, Keyword.UNSAFE_INLINE); + findings.push(new Finding( + Type.IGNORED, + 'unsafe-inline is ignored if a nonce or a hash is present. ' + + '(CSP2 and above)', + Severity.NONE, directive, Keyword.UNSAFE_INLINE)); + } + } else { + // remove nonces and hashes (not supported in CSP < v2). + for (const value of values) { + if (value.startsWith('\'nonce-') || value.startsWith('\'sha')) { + arrayRemove(effectiveCspValues, value); + } + } + } } - } - } - if (effectiveCspValues && this.policyHasStrictDynamic()) { - // Ignore allowlist in CSP >= v3 in presence of 'strict-dynamic'. - if (cspVersion >= Version.CSP3) { - for (const value of values) { - // Because of 'strict-dynamic' all host-source and scheme-source - // expressions, as well as the "'unsafe-inline'" and "'self' - // keyword-sources will be ignored. - // https://w3c.github.io/webappsec-csp/#strict-dynamic-usage - if (!value.startsWith('\'') || value === Keyword.SELF || - value === Keyword.UNSAFE_INLINE) { - arrayRemove(effectiveCspValues, value); - findings.push(new Finding( - Type.IGNORED, - 'Because of strict-dynamic this entry is ignored in CSP3 and above', - Severity.NONE, directive, value)); - } + if (effectiveCspValues && this.policyHasStrictDynamic()) { + // Ignore allowlist in CSP >= v3 in presence of 'strict-dynamic'. + if (cspVersion >= Version.CSP3) { + for (const value of values) { + // Because of 'strict-dynamic' all host-source and scheme-source + // expressions, as well as the "'unsafe-inline'" and "'self' + // keyword-sources will be ignored. + // https://w3c.github.io/webappsec-csp/#strict-dynamic-usage + if (!value.startsWith('\'') || value === Keyword.SELF || + value === Keyword.UNSAFE_INLINE) { + arrayRemove(effectiveCspValues, value); + findings.push(new Finding( + Type.IGNORED, + 'Because of strict-dynamic this entry is ignored in CSP3 and above', + Severity.NONE, directive, value)); + } + } + } else { + // strict-dynamic not supported. + arrayRemove(effectiveCspValues, Keyword.STRICT_DYNAMIC); + } } - } else { - // strict-dynamic not supported. - arrayRemove(effectiveCspValues, Keyword.STRICT_DYNAMIC); - } - } - if (cspVersion < Version.CSP3) { - // Remove CSP3 directives from pre-CSP3 policies. - // https://w3c.github.io/webappsec-csp/#changes-from-level-2 - delete effectiveCsp.directives[Directive.REPORT_TO]; - delete effectiveCsp.directives[Directive.WORKER_SRC]; - delete effectiveCsp.directives[Directive.MANIFEST_SRC]; - delete effectiveCsp.directives[Directive.TRUSTED_TYPES]; - delete effectiveCsp.directives[Directive.REQUIRE_TRUSTED_TYPES_FOR]; + if (cspVersion < Version.CSP3) { + // Remove CSP3 directives from pre-CSP3 policies. + // https://w3c.github.io/webappsec-csp/#changes-from-level-2 + delete effectiveCsp.directives[index][Directive.REPORT_TO]; + delete effectiveCsp.directives[index][Directive.WORKER_SRC]; + delete effectiveCsp.directives[index][Directive.MANIFEST_SRC]; + delete effectiveCsp.directives[index][Directive.TRUSTED_TYPES]; + delete effectiveCsp.directives[index][Directive.REQUIRE_TRUSTED_TYPES_FOR]; + } } return effectiveCsp; @@ -147,13 +160,19 @@ export class Csp { * @return The effective directive. */ getEffectiveDirective(directive: string): string { - // Only fetch directives default to default-src. - if (!(directive in this.directives) && - FETCH_DIRECTIVES.includes(directive as Directive)) { - return Directive.DEFAULT_SRC; + // Directive doesn't default to default-src + if (!FETCH_DIRECTIVES.includes(directive as Directive)) { + return directive; + } + + // Look in each CSP to find the directive + for (const currentCsp of this.directives) { + if (directive in currentCsp) { + return directive; + } } - return directive; + return Directive.DEFAULT_SRC; } /** @@ -174,8 +193,15 @@ export class Csp { */ policyHasScriptNonces(): boolean { const directiveName = this.getEffectiveDirective(Directive.SCRIPT_SRC); - const values = this.directives[directiveName] || []; - return values.some((val) => isNonce(val)); + + for (const currentDirective of this.directives) { + const values = currentDirective[directiveName] || []; + if (values.some((val) => isNonce(val))) { + return true; + } + } + + return false; } /** @@ -184,8 +210,15 @@ export class Csp { */ policyHasScriptHashes(): boolean { const directiveName = this.getEffectiveDirective(Directive.SCRIPT_SRC); - const values = this.directives[directiveName] || []; - return values.some((val) => isHash(val)); + + for (const currentDirective of this.directives) { + const values = currentDirective[directiveName] || []; + if (values.some((val) => isHash(val))) { + return true; + } + } + + return false; } /** @@ -194,8 +227,15 @@ export class Csp { */ policyHasStrictDynamic(): boolean { const directiveName = this.getEffectiveDirective(Directive.SCRIPT_SRC); - const values = this.directives[directiveName] || []; - return values.includes(Keyword.STRICT_DYNAMIC); + + for (const currentDirective of this.directives) { + const values = currentDirective[directiveName] || []; + if (values.includes(Keyword.STRICT_DYNAMIC)) { + return true; + } + } + + return false; } } diff --git a/csp_test.ts b/csp_test.ts index 73ec121..4a1146c 100644 --- a/csp_test.ts +++ b/csp_test.ts @@ -27,20 +27,36 @@ describe('Test Csp', () => { const testCsp = 'default-src \'none\'; ' + 'script-src \'nonce-unsafefoobar\' \'unsafe-eval\' \'unsafe-inline\' ' + 'https://example.com/foo.js foo.bar; ' + - 'img-src \'self\' https: data: blob:; '; + 'img-src \'self\' https: data: blob:;'; - const parsed = (new CspParser(testCsp)).csps[0]; + const parsed = (new CspParser(testCsp)).csp; expect(parsed.convertToString()).toBe(testCsp); }); + it('ConvertToString with multiple CSPs', () => { + const testCsp1 = 'default-src \'self\' http://example.com http://example.net; ' + + 'connect-src \'none\'; '; + + const testCsp2 = 'connect-src http://example.com/; script-src http://example.com/; '; + + const expectedCsp = 'default-src \'self\' http://example.com http://example.net; ' + + 'connect-src \'none\';, connect-src http://example.com/; ' + + 'script-src http://example.com/;'; + + const parsed = (new CspParser([testCsp1, testCsp2])).csp; + + let cspStrings: string = parsed.convertToString(); + expect(cspStrings).toBe(expectedCsp); + }); + it('GetEffectiveCspVersion1', () => { const testCsp = 'default-src \'unsafe-inline\' \'strict-dynamic\' \'nonce-123\' ' + '\'sha256-foobar\' \'self\'; report-to foo.bar; worker-src *; manifest-src *'; - const parsed = (new CspParser(testCsp)).csps[0]; + const parsed = (new CspParser(testCsp)).csp; const effectiveCsp = parsed.getEffectiveCsp(Version.CSP1); - expect(effectiveCsp.directives[Directive.DEFAULT_SRC]).toEqual([ + expect(effectiveCsp.directives[0][Directive.DEFAULT_SRC]).toEqual([ '\'unsafe-inline\'', '\'self\'' ]); expect(effectiveCsp.hasOwnProperty(Directive.REPORT_TO)).toBeFalse(); @@ -52,10 +68,10 @@ describe('Test Csp', () => { const testCsp = 'default-src \'unsafe-inline\' \'strict-dynamic\' \'nonce-123\' ' + '\'sha256-foobar\' \'self\'; report-to foo.bar; worker-src *; manifest-src *'; - const parsed = (new CspParser(testCsp)).csps[0]; + const parsed = (new CspParser(testCsp)).csp; const effectiveCsp = parsed.getEffectiveCsp(Version.CSP2); - expect(effectiveCsp.directives[Directive.DEFAULT_SRC]).toEqual([ + expect(effectiveCsp.directives[0][Directive.DEFAULT_SRC]).toEqual([ '\'nonce-123\'', '\'sha256-foobar\'', '\'self\'' ]); expect(effectiveCsp.hasOwnProperty(Directive.REPORT_TO)).toBeFalse(); @@ -67,21 +83,21 @@ describe('Test Csp', () => { const testCsp = 'default-src \'unsafe-inline\' \'strict-dynamic\' \'nonce-123\' ' + '\'sha256-foobar\' \'self\'; report-to foo.bar; worker-src *; manifest-src *'; - const parsed = (new CspParser(testCsp)).csps[0]; + const parsed = (new CspParser(testCsp)).csp; const effectiveCsp = parsed.getEffectiveCsp(Version.CSP3); - expect(effectiveCsp.directives[Directive.DEFAULT_SRC]).toEqual([ + expect(effectiveCsp.directives[0][Directive.DEFAULT_SRC]).toEqual([ '\'strict-dynamic\'', '\'nonce-123\'', '\'sha256-foobar\'' ]); - expect(effectiveCsp.directives[Directive.REPORT_TO]).toEqual(['foo.bar']); - expect(effectiveCsp.directives[Directive.WORKER_SRC]).toEqual(['*']); - expect(effectiveCsp.directives[Directive.MANIFEST_SRC]).toEqual(['*']); + expect(effectiveCsp.directives[0][Directive.REPORT_TO]).toEqual(['foo.bar']); + expect(effectiveCsp.directives[0][Directive.WORKER_SRC]).toEqual(['*']); + expect(effectiveCsp.directives[0][Directive.MANIFEST_SRC]).toEqual(['*']); }); it('GetEffectiveDirective', () => { const testCsp = 'default-src https:; script-src foo.bar'; - const parsed = (new CspParser(testCsp)).csps[0]; + const parsed = (new CspParser(testCsp)).csp; const script = parsed.getEffectiveDirective(Directive.SCRIPT_SRC); expect(script).toBe(Directive.SCRIPT_SRC); @@ -92,7 +108,7 @@ describe('Test Csp', () => { it('GetEffectiveDirectives', () => { const testCsp = 'default-src https:; script-src foo.bar'; - const parsed = (new CspParser(testCsp)).csps[0]; + const parsed = (new CspParser(testCsp)).csp; const directives = parsed.getEffectiveDirectives( [Directive.SCRIPT_SRC, Directive.STYLE_SRC]); @@ -102,7 +118,7 @@ describe('Test Csp', () => { it('PolicyHasScriptNoncesScriptSrcWithNonce', () => { const testCsp = 'default-src https:; script-src \'nonce-test123\''; - const parsed = (new CspParser(testCsp)).csps[0]; + const parsed = (new CspParser(testCsp)).csp; expect(parsed.policyHasScriptNonces()).toBeTrue(); }); @@ -111,7 +127,7 @@ describe('Test Csp', () => { it('PolicyHasScriptNoncesNoNonce', () => { const testCsp = 'default-src https: \'nonce-ignored\'; script-src nonce-invalid'; - const parsed = (new CspParser(testCsp)).csps[0]; + const parsed = (new CspParser(testCsp)).csp; expect(parsed.policyHasScriptNonces()).toBeFalse(); }); @@ -119,7 +135,7 @@ describe('Test Csp', () => { it('PolicyHasScriptHashesScriptSrcWithHash', () => { const testCsp = 'default-src https:; script-src \'sha256-asdfASDF\''; - const parsed = (new CspParser(testCsp)).csps[0]; + const parsed = (new CspParser(testCsp)).csp; expect(parsed.policyHasScriptHashes()).toBeTrue(); }); @@ -128,7 +144,7 @@ describe('Test Csp', () => { it('PolicyHasScriptHashesNoHash', () => { const testCsp = 'default-src https: \'nonce-ignored\'; script-src sha256-invalid'; - const parsed = (new CspParser(testCsp)).csps[0]; + const parsed = (new CspParser(testCsp)).csp; expect(parsed.policyHasScriptHashes()).toBeFalse(); }); @@ -136,7 +152,7 @@ describe('Test Csp', () => { it('PolicyHasStrictDynamicScriptSrcWithStrictDynamic', () => { const testCsp = 'default-src https:; script-src \'strict-dynamic\''; - const parsed = (new CspParser(testCsp)).csps[0]; + const parsed = (new CspParser(testCsp)).csp; expect(parsed.policyHasStrictDynamic()).toBeTrue(); }); @@ -144,7 +160,7 @@ describe('Test Csp', () => { it('PolicyHasStrictDynamicDefaultSrcWithStrictDynamic', () => { const testCsp = 'default-src https \'strict-dynamic\''; - const parsed = (new CspParser(testCsp)).csps[0]; + const parsed = (new CspParser(testCsp)).csp; expect(parsed.policyHasStrictDynamic()).toBeTrue(); }); @@ -152,7 +168,7 @@ describe('Test Csp', () => { it('PolicyHasStrictDynamicNoStrictDynamic', () => { const testCsp = 'default-src \'strict-dynamic\'; script-src foo.bar'; - const parsed = (new CspParser(testCsp)).csps[0]; + const parsed = (new CspParser(testCsp)).csp; expect(parsed.policyHasStrictDynamic()).toBeFalse(); }); @@ -224,7 +240,7 @@ describe('Test Csp', () => { it('ParseNavigateTo', () => { const testCsp = 'navigate-to \'self\'; script-src \'nonce-foo\''; - const parsed = (new CspParser(testCsp)).csps[0]; + const parsed = (new CspParser(testCsp)).csp; expect(parsed.policyHasStrictDynamic()).toBeFalse(); expect(parsed.policyHasScriptNonces()).toBeTrue(); @@ -232,7 +248,7 @@ describe('Test Csp', () => { it('ParseWebRtc', () => { const testCsp = 'web-rtc \'allow\'; script-src \'nonce-foo\''; - const parsed = (new CspParser(testCsp)).csps[0]; + const parsed = (new CspParser(testCsp)).csp; expect(parsed.policyHasStrictDynamic()).toBeFalse(); expect(parsed.policyHasScriptNonces()).toBeTrue(); diff --git a/enforced_csps.ts b/enforced_csps.ts deleted file mode 100644 index 96ebf19..0000000 --- a/enforced_csps.ts +++ /dev/null @@ -1,210 +0,0 @@ -/** - * @fileoverview Enforce CSP definitions and helper functions. - * @author mnadeau@gosecure.net (Maxime Nadeau) - * - * @license - * Copyright 2016 Google Inc. All rights reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -import { Csp, Directive, Keyword, Version, isHash, isNonce } from './csp'; -import { Finding, Severity, Type } from './finding'; - -/** - * Enforced Content Security Policy object. - * - https://www.w3.org/TR/CSP2/#enforcing-multiple-policies - */ -export class EnforcedCsps extends Array { - /** - * Clones a CSP object. - * @return clone of parsedCsp. - */ - clone(): EnforcedCsps { - const clone = new EnforcedCsps(); - for (const csp of this) { - clone.push(csp.clone()); - } - - return clone; - } - - /** - * Converts the enforced CSPs back into a string array. - * @return The list of CSP string. - */ - convertToStrings(): string[] { - const retString: string[] = []; - - for (const appliedCsp of this) { - retString.push(appliedCsp.convertToString()); - } - - return retString; - } - - /** - * Returns the passed directive if present in this CSP or default-src - * otherwise. - * @param directive The CSP directive to look for. - * @return The effective directive. - */ - getEffectiveDirective(directive: string): string { - // Look in each CSP to find the directive - for (const csp of this) { - if (directive in csp.directives) { - return directive; - } - } - - return Directive.DEFAULT_SRC; - } - - /** - * Returns CSP as it would be seen by a UA supporting a specific CSP version. - * @param cspVersion CSP. - * @param optFindings findings about ignored directive values will be added - * to this array, if passed. (e.g. CSP2 ignores 'unsafe-inline' in - * presence of a nonce or a hash) - * @return The effective CSP. - */ - getEffectiveCsps(cspVersion: Version, optFindings?: Finding[]): EnforcedCsps { - const findings = optFindings || []; - const effectiveCsps = this.clone(); - const directive = effectiveCsps.getEffectiveDirective(Directive.SCRIPT_SRC); - - for (let index = 0; index < effectiveCsps.length; index++) { - const values = this[index].directives[directive] || []; - const effectiveCspValues = effectiveCsps[index].directives[directive]; - - if (effectiveCspValues && - (effectiveCsps[index].policyHasScriptNonces() || - effectiveCsps[index].policyHasScriptHashes())) { - if (cspVersion >= Version.CSP2) { - // Ignore 'unsafe-inline' in CSP >= v2, if a nonce or a hash is present. - if (values.includes(Keyword.UNSAFE_INLINE)) { - arrayRemove(effectiveCspValues, Keyword.UNSAFE_INLINE); - findings.push(new Finding( - Type.IGNORED, - 'unsafe-inline is ignored if a nonce or a hash is present. ' + - '(CSP2 and above)', - Severity.NONE, directive, Keyword.UNSAFE_INLINE)); - } - } else { - // remove nonces and hashes (not supported in CSP < v2). - for (const value of values) { - if (value.startsWith('\'nonce-') || value.startsWith('\'sha')) { - arrayRemove(effectiveCspValues, value); - } - } - } - } - - if (effectiveCspValues && this[index].policyHasStrictDynamic()) { - // Ignore allowlist in CSP >= v3 in presence of 'strict-dynamic'. - if (cspVersion >= Version.CSP3) { - for (const value of values) { - // Because of 'strict-dynamic' all host-source and scheme-source - // expressions, as well as the "'unsafe-inline'" and "'self' - // keyword-sources will be ignored. - // https://w3c.github.io/webappsec-csp/#strict-dynamic-usage - if (!value.startsWith('\'') || value === Keyword.SELF || - value === Keyword.UNSAFE_INLINE) { - arrayRemove(effectiveCspValues, value); - findings.push(new Finding( - Type.IGNORED, - 'Because of strict-dynamic this entry is ignored in CSP3 and above', - Severity.NONE, directive, value)); - } - } - } else { - // strict-dynamic not supported. - arrayRemove(effectiveCspValues, Keyword.STRICT_DYNAMIC); - } - } - - if (cspVersion < Version.CSP3) { - // Remove CSP3 directives from pre-CSP3 policies. - // https://w3c.github.io/webappsec-csp/#changes-from-level-2 - delete effectiveCsps[index].directives[Directive.REPORT_TO]; - delete effectiveCsps[index].directives[Directive.WORKER_SRC]; - delete effectiveCsps[index].directives[Directive.MANIFEST_SRC]; - delete effectiveCsps[index].directives[Directive.TRUSTED_TYPES]; - delete effectiveCsps[index].directives[Directive.REQUIRE_TRUSTED_TYPES_FOR]; - } - } - - return effectiveCsps; - } - - /** - * Checks if this CSP is using nonces for scripts. - * @return true, if this CSP is using script nonces. - */ - policyHasScriptNonces(): boolean { - const directiveName = this.getEffectiveDirective(Directive.SCRIPT_SRC); - - for (const csp of this) { - const values = csp.directives[directiveName] || []; - if (values.some((val) => isNonce(val))) { - return true; - } - } - - return false; - } - - /** - * Checks if this CSP is using hashes for scripts. - * @return true, if this CSP is using script hashes. - */ - policyHasScriptHashes(): boolean { - const directiveName = this.getEffectiveDirective(Directive.SCRIPT_SRC); - - for (const csp of this) { - const values = csp.directives[directiveName] || []; - if (values.some((val) => isHash(val))) { - return true; - } - } - - return false; - } - - /** - * Checks if this CSP is using strict-dynamic. - * @return true, if this CSP is using CSP nonces. - */ - policyHasStrictDynamic(): boolean { - const directiveName = this.getEffectiveDirective(Directive.SCRIPT_SRC); - - for (const csp of this) { - const values = csp.directives[directiveName] || []; - if (values.includes(Keyword.STRICT_DYNAMIC)) { - return true; - } - } - - return false; - } -} - -/** - * Mutate the given array to remove the first instance of the given item - */ -function arrayRemove(arr: T[], item: T): void { - if (arr.includes(item)) { - const idx = arr.findIndex(elem => item === elem); - arr.splice(idx, 1); - } -} diff --git a/enforced_csps_test.ts b/enforced_csps_test.ts deleted file mode 100644 index a538c04..0000000 --- a/enforced_csps_test.ts +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @fileoverview Tests for Enforced CSP object helper functions. - * @author mnadeau@gosecure.net (Maxime Nadeau) - * - * @license - * Copyright 2016 Google Inc. All rights reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'jasmine'; - -import { EnforcedCsps } from './enforced_csps'; -import { CspParser } from './parser'; - -describe('Test enforced CSPs', () => { - it('ConvertToString with single CSP', () => { - const testCsp = 'default-src \'none\'; ' + - 'script-src \'nonce-unsafefoobar\' \'unsafe-eval\' \'unsafe-inline\' ' + - 'https://example.com/foo.js foo.bar; ' + - 'img-src \'self\' https: data: blob:; '; - - const parsed = (new CspParser(testCsp)).csps; - - let cspStrings: string[] = parsed.convertToStrings(); - expect(cspStrings[0]).toBe(testCsp); - }); - - it('ConvertToString with multiple CSPs', () => { - const testCsp1 = 'default-src \'self\' http://example.com http://example.net; ' + - 'connect-src \'none\'; '; - - const testCsp2 = 'connect-src http://example.com/; script-src http://example.com/; '; - - const parsed = (new CspParser([testCsp1, testCsp2])).csps; - - let cspStrings: string[] = parsed.convertToStrings(); - expect(cspStrings[0]).toBe(testCsp1); - expect(cspStrings[1]).toBe(testCsp2); - }); -}); diff --git a/evaluator.ts b/evaluator.ts index 398ead2..027c71f 100644 --- a/evaluator.ts +++ b/evaluator.ts @@ -20,8 +20,7 @@ import { CheckerFunction } from './checks/checker'; import * as parserChecks from './checks/parser_checks'; import * as securityChecks from './checks/security_checks'; import * as strictcspChecks from './checks/strictcsp_checks'; -import { Version } from './csp'; -import { EnforcedCsps } from './enforced_csps'; +import { Csp, Version } from './csp'; import { Finding } from './finding'; @@ -33,7 +32,7 @@ import { Finding } from './finding'; */ export class CspEvaluator { version: Version; - csps: EnforcedCsps; + csp: Csp; /** * List of findings reported by checks. @@ -44,7 +43,7 @@ export class CspEvaluator { * @param parsedCsp A parsed list of Content Security Policy. * @param cspVersion CSP version to apply checks for. */ - constructor(parsedCsps: EnforcedCsps, cspVersion?: Version) { + constructor(parsedCsps: Csp, cspVersion?: Version) { /** * CSP version. */ @@ -53,7 +52,7 @@ export class CspEvaluator { /** * Parsed CSPs. */ - this.csps = parsedCsps; + this.csp = parsedCsps; } /** @@ -75,12 +74,12 @@ export class CspEvaluator { // supporting a specific version of CSP. // For example a browser supporting only CSP1 will ignore nonces and // therefore 'unsafe-inline' would not get ignored if a policy has nonces. - const effectiveCsps = this.csps.getEffectiveCsps(this.version, this.findings); + const effectiveCsps = this.csp.getEffectiveCsp(this.version, this.findings); // Checks independent of CSP version. if (parsedCspChecks) { for (const check of parsedCspChecks) { - this.findings = this.findings.concat(check(this.csps)); + this.findings = this.findings.concat(check(this.csp)); } } diff --git a/evaluator_test.ts b/evaluator_test.ts index afff674..ae82dde 100644 --- a/evaluator_test.ts +++ b/evaluator_test.ts @@ -21,24 +21,24 @@ import 'jasmine'; import { CspEvaluator } from './evaluator'; import { Finding, Severity, Type } from './finding'; -import { EnforcedCsps } from './enforced_csps'; +import { Csp } from './csp'; describe('Test evaluator', () => { it('CspEvaluator', () => { - const fakeCsp = new EnforcedCsps(); + const fakeCsp = new Csp(); const evaluator = new CspEvaluator(fakeCsp); - expect(evaluator.csps).toBe(fakeCsp); + expect(evaluator.csp).toBe(fakeCsp); }); it('Evaluate', () => { const fakeFinding = new (Finding)( Type.UNKNOWN_DIRECTIVE, 'Fake description', Severity.MEDIUM, 'fake-directive', 'fake-directive-value'); - const fakeVerifier = (parsedCsp: EnforcedCsps) => { + const fakeVerifier = (parsedCsp: Csp) => { return [fakeFinding]; }; - const fakeCsp = new EnforcedCsps(); + const fakeCsp = new Csp(); const evaluator = new (CspEvaluator)(fakeCsp); const findings = evaluator.evaluate([fakeVerifier, fakeVerifier], [fakeVerifier]); diff --git a/lighthouse/lighthouse_checks.ts b/lighthouse/lighthouse_checks.ts new file mode 100644 index 0000000..62beb2c --- /dev/null +++ b/lighthouse/lighthouse_checks.ts @@ -0,0 +1,151 @@ +/** + * @fileoverview CSP checks as used by Lighthouse. These checks tend to be a + * stricter subset of the other checks defined in this project. + */ + +import { CheckerFunction } from '../checks/checker'; +import { checkInvalidKeyword, checkMissingSemicolon, checkUnknownDirective } from '../checks/parser_checks'; +import { checkDeprecatedDirective, checkMissingBaseUriDirective, checkMissingObjectSrcDirective, checkMissingScriptSrcDirective, checkNonceLength, checkPlainUrlSchemes, checkScriptUnsafeInline, checkWildcards } from '../checks/security_checks'; +import { checkAllowlistFallback, checkStrictDynamic, checkUnsafeInlineFallback } from '../checks/strictcsp_checks'; +import { Csp, Directive, Version } from '../csp'; +import { Finding } from '../finding'; +import { CspParser } from '../parser'; + +interface Equalable { + equals(a: unknown): boolean; +} + +function arrayContains(arr: T[], elem: T) { + return arr.some(e => e.equals(elem)); +} + +/** + * Computes the intersection of all of the given sets using the `equals(...)` + * method to compare items. + */ +function setIntersection(sets: T[][]): T[] { + const intersection: T[] = []; + if (sets.length === 0) { + return intersection; + } + const firstSet = sets[0]; + for (const elem of firstSet) { + if (sets.every(set => arrayContains(set, elem))) { + intersection.push(elem); + } + } + return intersection; +} + +/** + * Computes the union of all of the given sets using the `equals(...)` method to + * compare items. + */ +function setUnion(sets: T[][]): T[] { + const union: T[] = []; + for (const set of sets) { + for (const elem of set) { + if (!arrayContains(union, elem)) { + union.push(elem); + } + } + } + return union; +} + +/** + * Checks if *any* of the given policies pass the given checker. If at least one + * passes, returns no findings. Otherwise, returns the list of findings from the + * first one that had any findings. + */ +function atLeastOnePasses( + parsedCsps: Csp[], checker: CheckerFunction): Finding[] { + const findings: Finding[][] = []; + for (const parsedCsp of parsedCsps) { + findings.push(checker(parsedCsp)); + } + return setIntersection(findings); +} + +/** + * Checks if *any* of the given policies fail the given checker. Returns the + * list of findings from the one that had the most findings. + */ +function atLeastOneFails( + parsedCsps: Csp[], checker: CheckerFunction): Finding[] { + const findings: Finding[][] = []; + for (const parsedCsp of parsedCsps) { + findings.push(checker(parsedCsp)); + } + return setUnion(findings); +} + +/** + * Evaluate the given list of CSPs for checks that should cause Lighthouse to + * mark the CSP as failing. Returns only the first set of failures. + */ +export function evaluateForFailure(parsedCsps: Csp[]): Finding[] { + const mergedCspString: string[] = []; + for (const csp of parsedCsps) { + mergedCspString.push(csp.convertToString()); + } + const mergedCsp: Csp = (new CspParser(mergedCspString)).csp; + + // Check #1 + const targetsXssFindings = [ + ...atLeastOnePasses(parsedCsps, checkMissingScriptSrcDirective), + ...atLeastOnePasses(parsedCsps, checkMissingObjectSrcDirective), + ...checkMissingBaseUriDirective(mergedCsp), + ]; + + // Check #2 + const effectiveCsps = parsedCsps.map(csp => csp.getEffectiveCsp(Version.CSP3)); + const effectiveCspsWithScript = effectiveCsps.filter(csp => { + const directiveName = csp.getEffectiveDirective(Directive.SCRIPT_SRC); + for (const currentCsp of csp.directives) { + return currentCsp[directiveName]; + } + }); + const robust = [ + ...atLeastOnePasses(effectiveCspsWithScript, checkStrictDynamic), + ...atLeastOnePasses(effectiveCspsWithScript, checkScriptUnsafeInline), + ...atLeastOnePasses(effectiveCsps, checkWildcards), + ...atLeastOnePasses(effectiveCsps, checkPlainUrlSchemes), + ]; + return [...targetsXssFindings, ...robust]; +} + +/** + * Evaluate the given list of CSPs for checks that should cause Lighthouse to + * mark the CSP as OK, but present a warning. Returns only the first set of + * failures. + */ +export function evaluateForWarnings(parsedCsps: Csp[]): Finding[] { + // Check #1 is implemented by Lighthouse directly + // Check #2 is no longer used in Lighthouse. + + // Check #3 + return [ + ...atLeastOneFails(parsedCsps, checkUnsafeInlineFallback), + ...atLeastOneFails(parsedCsps, checkAllowlistFallback) + ]; +} + +/** + * Evaluate the given list of CSPs for syntax errors. Returns a list of the same + * length as parsedCsps where each item in the list is the findings for the + * matching Csp. + */ +export function evaluateForSyntaxErrors(parsedCsps: Csp[]): Finding[][] { + // Check #4 + const allFindings: Finding[][] = []; + for (const csp of parsedCsps) { + const findings = [ + ...checkNonceLength(csp), ...checkUnknownDirective(csp), + ...checkDeprecatedDirective(csp), ...checkMissingSemicolon(csp), + ...checkInvalidKeyword(csp) + ]; + allFindings.push(findings); + } + return allFindings; +} diff --git a/lighthouse/lighthouse_checks_test.ts b/lighthouse/lighthouse_checks_test.ts new file mode 100644 index 0000000..99ee292 --- /dev/null +++ b/lighthouse/lighthouse_checks_test.ts @@ -0,0 +1,538 @@ +/** + * @fileoverview Tests for CSP Parser checks. + */ + + import 'jasmine'; + + import {Csp,} from '../csp'; + import {Severity} from '../finding'; + import {CspParser} from '../parser'; + + import * as lighthouseChecks from './lighthouse_checks'; + + function parsePolicies(policies: string[]): Csp[] { + return policies.map(p => (new CspParser(p)).csp); + } + + describe('Test evaluateForFailure', () => { + it('robust nonce-based policy', () => { + const test = + 'script-src \'nonce-aaaaaaaaaa\'; object-src \'none\'; base-uri \'none\''; + + const violations = + lighthouseChecks.evaluateForFailure(parsePolicies([test])); + + expect(violations.length).toBe(0); + }); + it('robust hash-based policy', () => { + const test = 'script-src \'sha256-aaaaaaaaaa\'; object-src \'none\''; + + const violations = + lighthouseChecks.evaluateForFailure(parsePolicies([test])); + + expect(violations.length).toBe(0); + }); + it('policy not attempt', () => { + const test = 'block-all-mixed-content'; + + const violations = + lighthouseChecks.evaluateForFailure(parsePolicies([test])); + + expect(violations.length).toBe(2); + expect(violations[0].severity).toBe(Severity.HIGH); + expect(violations[0].directive).toBe('script-src'); + expect(violations[0].description).toBe('script-src directive is missing.'); + expect(violations[1].severity).toBe(Severity.HIGH); + expect(violations[1].directive).toBe('object-src'); + expect(violations[1].description) + .toBe( + `Missing object-src allows the injection of plugins which can execute JavaScript. Can you set it to 'none'?`); + }); + it('policy not robust', () => { + const test = 'script-src *.google.com; object-src \'none\''; + + const violations = + lighthouseChecks.evaluateForFailure(parsePolicies([test])); + + expect(violations.length).toBe(1); + expect(violations[0].severity).toBe(Severity.STRICT_CSP); + expect(violations[0].directive).toBe('script-src'); + expect(violations[0].description) + .toBe( + `Host allowlists can frequently be bypassed. Consider using 'strict-dynamic' in combination with CSP nonces or hashes.`); + }); + it('robust policy and not robust policy', () => { + const policies = [ + 'script-src *.google.com; object-src \'none\'', + 'script-src \'nonce-aaaaaaaaaa\'; base-uri \'none\'' + ]; + + const violations = + lighthouseChecks.evaluateForFailure(parsePolicies(policies)); + + expect(violations.length).toBe(0); + }); + it('split across many policies', () => { + const policies = [ + 'object-src \'none\'', 'script-src \'nonce-aaaaaaaaaa\'', + 'base-uri \'none\'' + ]; + + const violations = + lighthouseChecks.evaluateForFailure(parsePolicies(policies)); + + expect(violations.length).toBe(0); + }); + it('split across many policies with default-src', () => { + const policies = ['default-src \'none\'', 'base-uri \'none\'']; + + const violations = + lighthouseChecks.evaluateForFailure(parsePolicies(policies)); + + expect(violations.length).toBe(0); + }); + it('split across many policies some mixed useless policies', () => { + const policies = [ + 'object-src \'none\'', 'script-src \'nonce-aaaaaaaaaa\'', + 'base-uri \'none\'', 'block-all-mixed-content' + ]; + + const violations = + lighthouseChecks.evaluateForFailure(parsePolicies(policies)); + + expect(violations.length).toBe(0); + }); + it('split across many policies with allowlist', () => { + const policies = [ + 'object-src \'none\'', 'script-src \'nonce-aaaaaaaaaa\'', + 'base-uri \'none\'', 'script-src *' + ]; + + const violations = + lighthouseChecks.evaluateForFailure(parsePolicies(policies)); + + expect(violations.length).toBe(0); + }); + + it('not robust and not attempt', () => { + const policies = ['block-all-mixed-content', 'script-src *.google.com']; + + const violations = + lighthouseChecks.evaluateForFailure(parsePolicies(policies)); + + expect(violations.length).toBe(2); + expect(violations[0].severity).toBe(Severity.HIGH); + expect(violations[0].directive).toBe('object-src'); + expect(violations[0].description) + .toBe( + `Missing object-src allows the injection of plugins which can execute JavaScript. Can you set it to 'none'?`); + expect(violations[1].severity).toBe(Severity.STRICT_CSP); + expect(violations[1].directive).toBe('script-src'); + expect(violations[1].description) + .toBe( + `Host allowlists can frequently be bypassed. Consider using \'strict-dynamic\' in combination with CSP nonces or hashes.`); + }); + it('robust check only CSPs with script-src', () => { + const policies = ['script-src https://example.com', 'object-src \'none\'']; + + const violations = + lighthouseChecks.evaluateForFailure(parsePolicies(policies)); + + expect(violations.length).toBe(1); + expect(violations[0].severity).toBe(Severity.STRICT_CSP); + expect(violations[0].directive).toBe('script-src'); + expect(violations[0].description) + .toBe( + `Host allowlists can frequently be bypassed. Consider using \'strict-dynamic\' in combination with CSP nonces or hashes.`); + }); + it('two not attempt', () => { + const policies = ['block-all-mixed-content', 'block-all-mixed-content']; + + const violations = + lighthouseChecks.evaluateForFailure(parsePolicies(policies)); + + expect(violations.length).toBe(2); + expect(violations[0].severity).toBe(Severity.HIGH); + expect(violations[0].directive).toBe('script-src'); + expect(violations[0].description).toBe('script-src directive is missing.'); + expect(violations[1].severity).toBe(Severity.HIGH); + expect(violations[1].directive).toBe('object-src'); + expect(violations[1].description) + .toBe( + `Missing object-src allows the injection of plugins which can execute JavaScript. Can you set it to 'none'?`); + }); + it('two not attempt somewhat', () => { + const policies = [ + 'block-all-mixed-content; object-src \'none\'', + 'block-all-mixed-content', + ]; + + const violations = + lighthouseChecks.evaluateForFailure(parsePolicies(policies)); + + expect(violations.length).toBe(1); + expect(violations[0].severity).toBe(Severity.HIGH); + expect(violations[0].directive).toBe('script-src'); + expect(violations[0].description).toBe('script-src directive is missing.'); + }); + it('base-uri split across many policies', () => { + const policies = [ + 'script-src \'nonce-aaaaaaaaaaa\'; object-src \'none\'', + 'base-uri \'none\'', + ]; + + const violations = + lighthouseChecks.evaluateForFailure(parsePolicies(policies)); + + expect(violations.length).toBe(0); + }); + it('base-uri not set', () => { + const policies = [ + 'script-src \'nonce-aaaaaaaaaaa\'; object-src \'none\'', + ]; + + const violations = + lighthouseChecks.evaluateForFailure(parsePolicies(policies)); + + expect(violations.length).toBe(1); + expect(violations[0].severity).toBe(Severity.HIGH); + expect(violations[0].directive).toBe('base-uri'); + expect(violations[0].description) + .toBe( + `Missing base-uri allows the injection of base tags. They can be used to set the base URL for all relative (script) URLs to an attacker controlled domain. Can you set it to 'none' or 'self'?`); + }); + it('base-uri not set in either policy', () => { + const policies = [ + 'script-src \'nonce-aaaaaaaaaaa\'; object-src \'none\'', + 'block-all-mixed-content' + ]; + + const violations = + lighthouseChecks.evaluateForFailure(parsePolicies(policies)); + + expect(violations.length).toBe(1); + expect(violations[0].severity).toBe(Severity.HIGH); + expect(violations[0].directive).toBe('base-uri'); + }); + it('check wildcards', () => { + const policies = ['script-src \'none\'; object-src *']; + + const violations = + lighthouseChecks.evaluateForFailure(parsePolicies(policies)); + + expect(violations.length).toBe(1); + expect(violations[0].severity).toBe(Severity.HIGH); + expect(violations[0].directive).toBe('object-src'); + expect(violations[0].description) + .toBe(`object-src should not allow '*' as source`); + }); + it('check wildcards on multiple', () => { + const policies = + ['script-src \'none\'; object-src *', 'object-src \'none\'']; + + const violations = + lighthouseChecks.evaluateForFailure(parsePolicies(policies)); + + expect(violations.length).toBe(0); + }); + it('check plain url schemes', () => { + const policies = [ + `script-src 'strict-dynamic' 'nonce-random123' 'unsafe-inline' https:; base-uri 'none'; object-src https:` + ]; + + const violations = + lighthouseChecks.evaluateForFailure(parsePolicies(policies)); + + expect(violations.length).toBe(1); + expect(violations[0].severity).toBe(Severity.HIGH); + expect(violations[0].directive).toBe('object-src'); + expect(violations[0].description) + .toBe( + `https: URI in object-src allows the execution of unsafe scripts.`); + }); + }); + describe('Test evaluateForWarnings', () => { + it('perfect', () => { + const test = + 'script-src \'nonce-aaaaaaaaaa\' \'unsafe-inline\' http: https:; report-uri url'; + + const violations = + lighthouseChecks.evaluateForWarnings(parsePolicies([test])); + + expect(violations.length).toBe(0); + }); + it('perfect except some failures', () => { + const policies = [ + 'script-src \'nonce-aaaaaaaaaa\' \'unsafe-inline\' http: https:; report-uri url; object-src \'none\'', + 'block-all-mixed-content' + ]; + + const violations = + lighthouseChecks.evaluateForWarnings(parsePolicies(policies)); + + expect(violations.length).toBe(0); + }); + it('a perfect policy and a policy that does not target', () => { + const policies = [ + 'script-src \'nonce-aaaaaaaaaa\' \'unsafe-inline\' http: https:; report-uri url; base-uri \'none\'; object-src \'none\'', + 'block-all-mixed-content' + ]; + + const violations = + lighthouseChecks.evaluateForWarnings(parsePolicies(policies)); + + expect(violations.length).toBe(0); + }); + it('perfect policy split into two', () => { + const policies = [ + 'script-src \'nonce-aaaaaaaaaa\' \'unsafe-inline\' http: https:; report-uri url; base-uri \'none\'; ', + 'block-all-mixed-content; object-src \'none\'' + ]; + + const violations = + lighthouseChecks.evaluateForWarnings(parsePolicies(policies)); + + expect(violations.length).toBe(0); + }); + it('perfect policy split into three', () => { + const policies = [ + 'script-src \'nonce-aaaaaaaaaa\' \'unsafe-inline\' http: https:; report-uri url; base-uri \'none\'; ', + 'block-all-mixed-content', 'object-src \'none\'' + ]; + + const violations = + lighthouseChecks.evaluateForWarnings(parsePolicies(policies)); + + expect(violations.length).toBe(0); + }); + it('no reporting and malformed', () => { + const test = 'script-src \'nonce-aaaaaaaaaa\'; unknown-directive'; + + const violations = + lighthouseChecks.evaluateForWarnings(parsePolicies([test])); + + expect(violations.length).toBe(1); + expect(violations[0].severity).toBe(Severity.STRICT_CSP); + expect(violations[0].directive).toBe('script-src'); + expect(violations[0].description) + .toBe( + 'Consider adding \'unsafe-inline\' (ignored by browsers supporting nonces/hashes) to be backward compatible with older browsers.'); + }); + it('missing unsafe-inline fallback', () => { + const test = 'script-src \'nonce-aaaaaaaaaa\'; report-uri url'; + + const violations = + lighthouseChecks.evaluateForWarnings(parsePolicies([test])); + + expect(violations.length).toBe(1); + expect(violations[0].severity).toBe(Severity.STRICT_CSP); + expect(violations[0].directive).toBe('script-src'); + expect(violations[0].description) + .toBe( + 'Consider adding \'unsafe-inline\' (ignored by browsers supporting nonces/hashes) to be backward compatible with older browsers.'); + }); + it('missing allowlist fallback', () => { + const test = + 'script-src \'nonce-aaaaaaaaaa\' \'strict-dynamic\' \'unsafe-inline\'; report-uri url'; + + const violations = + lighthouseChecks.evaluateForWarnings(parsePolicies([test])); + + expect(violations.length).toBe(1); + expect(violations[0].severity).toBe(Severity.STRICT_CSP); + expect(violations[0].directive).toBe('script-src'); + expect(violations[0].description) + .toBe( + 'Consider adding https: and http: url schemes (ignored by browsers supporting \'strict-dynamic\') to be backward compatible with older browsers.'); + }); + it('missing semicolon', () => { + const test = + 'script-src \'nonce-aaaaaaaaa\' \'unsafe-inline\'; report-uri url object-src \'self\''; + + const violations = + lighthouseChecks.evaluateForWarnings(parsePolicies([test])); + + expect(violations.length).toBe(0); + }); + it('invalid keyword', () => { + const test = + 'script-src \'nonce-aaaaaaaaa\' \'invalid\' \'unsafe-inline\'; report-uri url'; + + const violations = + lighthouseChecks.evaluateForWarnings(parsePolicies([test])); + + expect(violations.length).toBe(0); + }); + it('perfect policy and invalid policy', () => { + const policies = [ + 'script-src \'nonce-aaaaaaaaaa\' \'unsafe-inline\' http: https:; report-uri url; base-uri \'none\'; object-src \'none\'', + 'unknown' + ]; + + const violations = + lighthouseChecks.evaluateForWarnings(parsePolicies(policies)); + + expect(violations.length).toBe(0); + }); + it('reporting on the wrong policy', () => { + const policies = [ + 'script-src \'nonce-aaaaaaaaaa\' \'unsafe-inline\' http: https:', + 'block-all-mixed-content; report-uri url' + ]; + + const violations = + lighthouseChecks.evaluateForWarnings(parsePolicies(policies)); + + expect(violations.length).toBe(0); + }); + it('missing unsafe-inline fallback split over two policies', () => { + const policies = [ + 'script-src \'nonce-aaaaaaaaaa\'', + 'block-all-mixed-content; report-uri url' + ]; + + const violations = + lighthouseChecks.evaluateForWarnings(parsePolicies(policies)); + + expect(violations.length).toBe(1); + expect(violations[0].severity).toBe(Severity.STRICT_CSP); + expect(violations[0].directive).toBe('script-src'); + expect(violations[0].description) + .toBe( + 'Consider adding \'unsafe-inline\' (ignored by browsers supporting nonces/hashes) to be backward compatible with older browsers.'); + }); + it('strict-dynamic with no fallback in any policy', () => { + const policies = [ + 'script-src \'nonce-aaaaaaaaaa\' \'strict-dynamic\'', + 'block-all-mixed-content; report-uri url' + ]; + + const violations = + lighthouseChecks.evaluateForWarnings(parsePolicies(policies)); + + expect(violations.length).toBe(2); + expect(violations[0].severity).toBe(Severity.STRICT_CSP); + expect(violations[0].directive).toBe('script-src'); + expect(violations[0].description) + .toBe( + 'Consider adding \'unsafe-inline\' (ignored by browsers supporting nonces/hashes) to be backward compatible with older browsers.'); + expect(violations[1].severity).toBe(Severity.STRICT_CSP); + expect(violations[1].directive).toBe('script-src'); + expect(violations[1].description) + .toBe( + 'Consider adding https: and http: url schemes (ignored by browsers supporting \'strict-dynamic\') to be backward compatible with older browsers.'); + }); + }); + describe('Test evaluateForSyntaxErrors', () => { + it('whenPerfectPolicies', () => { + const policies = [ + 'script-src \'nonce-aaaaaaaaaa\' \'unsafe-inline\' http: https:', + 'block-all-mixed-content; report-uri url' + ]; + + const violations = + lighthouseChecks.evaluateForSyntaxErrors(parsePolicies(policies)); + + expect(violations.length).toBe(2); + expect(violations[0].length).toBe(0); + expect(violations[1].length).toBe(0); + }); + it('whenShortNonce', () => { + const test = 'script-src \'nonce-a\' \'unsafe-inline\'; report-uri url'; + + const violations = + lighthouseChecks.evaluateForSyntaxErrors(parsePolicies([test])); + + expect(violations.length).toBe(1); + expect(violations[0].length).toBe(1); + expect(violations[0][0].severity).toBe(Severity.MEDIUM); + expect(violations[0][0].directive).toBe('script-src'); + expect(violations[0][0].description) + .toBe('Nonces should be at least 8 characters long.'); + }); + it('whenUnknownDirective', () => { + const test = + 'script-src \'nonce-aaaaaaaaa\' \'unsafe-inline\'; report-uri url; unknown'; + + const violations = + lighthouseChecks.evaluateForSyntaxErrors(parsePolicies([test])); + + expect(violations.length).toBe(1); + expect(violations[0].length).toBe(1); + expect(violations[0][0].severity).toBe(Severity.SYNTAX); + expect(violations[0][0].directive).toBe('unknown'); + expect(violations[0][0].description) + .toBe('Directive "unknown" is not a known CSP directive.'); + }); + it('whenDeprecatedDirective', () => { + const test = + 'script-src \'nonce-aaaaaaaaa\' \'unsafe-inline\'; report-uri url; reflected-xss foo'; + + const violations = + lighthouseChecks.evaluateForSyntaxErrors(parsePolicies([test])); + + expect(violations.length).toBe(1); + expect(violations[0].length).toBe(1); + expect(violations[0][0].severity).toBe(Severity.INFO); + expect(violations[0][0].directive).toBe('reflected-xss'); + expect(violations[0][0].description) + .toBe( + 'reflected-xss is deprecated since CSP2. Please, use the X-XSS-Protection header instead.'); + }); + it('whenMissingSemicolon', () => { + const test = + 'script-src \'nonce-aaaaaaaaa\' \'unsafe-inline\'; report-uri url object-src \'none\''; + + const violations = + lighthouseChecks.evaluateForSyntaxErrors(parsePolicies([test])); + + expect(violations.length).toBe(1); + expect(violations[0].length).toBe(1); + expect(violations[0][0].severity).toBe(Severity.SYNTAX); + expect(violations[0][0].directive).toBe('report-uri'); + expect(violations[0][0].description) + .toBe( + 'Did you forget the semicolon? "object-src" seems to be a directive, not a value.'); + }); + it('whenInvalidKeyword', () => { + const test = + 'script-src \'nonce-aaaaaaaaa\' \'unsafe-inline\'; object-src \'invalid\''; + + const violations = + lighthouseChecks.evaluateForSyntaxErrors(parsePolicies([test])); + + expect(violations.length).toBe(1); + expect(violations[0].length).toBe(1); + expect(violations[0][0].severity).toBe(Severity.SYNTAX); + expect(violations[0][0].directive).toBe('object-src'); + expect(violations[0][0].description) + .toBe('\'invalid\' seems to be an invalid CSP keyword.'); + }); + it('manyPolicies', () => { + const policies = [ + 'object-src \'invalid\'', 'script-src \'none\'', + 'script-src \'nonce-short\' default-src \'none\'' + ]; + + const violations = + lighthouseChecks.evaluateForSyntaxErrors(parsePolicies(policies)); + + expect(violations.length).toBe(3); + expect(violations[0].length).toBe(1); + expect(violations[0][0].severity).toBe(Severity.SYNTAX); + expect(violations[0][0].directive).toBe('object-src'); + expect(violations[0][0].description) + .toBe('\'invalid\' seems to be an invalid CSP keyword.'); + expect(violations[1].length).toBe(0); + expect(violations[2].length).toBe(2); + expect(violations[2][0].severity).toBe(Severity.MEDIUM); + expect(violations[2][0].directive).toBe('script-src'); + expect(violations[2][0].description) + .toBe('Nonces should be at least 8 characters long.'); + expect(violations[2][1].severity).toBe(Severity.SYNTAX); + expect(violations[2][1].directive).toBe('script-src'); + expect(violations[2][1].description) + .toBe( + 'Did you forget the semicolon? "default-src" seems to be a directive, not a value.'); + }); + }); \ No newline at end of file diff --git a/parser.ts b/parser.ts index 8752427..de4ee51 100644 --- a/parser.ts +++ b/parser.ts @@ -17,14 +17,14 @@ */ import { Csp, isKeyword, isUrlScheme } from './csp'; -import { EnforcedCsps } from './enforced_csps'; +import { mergeCspHeaders } from './utils'; /** * A class to hold a parser for CSP in string format. * @unrestricted */ export class CspParser { - csps: EnforcedCsps; + csp: Csp; /** * @param unparsedCsp A Content Security Policy as string. @@ -33,10 +33,10 @@ export class CspParser { /** * Parsed CSP */ - this.csps = new EnforcedCsps(); + this.csp = new Csp(); - if (!Array.isArray(unparsedCsps)) { - unparsedCsps = [ (unparsedCsps as string) ]; + if (Array.isArray(unparsedCsps)) { + unparsedCsps = mergeCspHeaders(unparsedCsps); } this.parse(unparsedCsps); @@ -46,25 +46,16 @@ export class CspParser { * Parses a CSP from a string. * @param unparsedCsp CSP as string. */ - parse(unparsedCspList: string[]): EnforcedCsps { - const splitCspList: string[] = []; - unparsedCspList.forEach(policy => { - // For each token returned by splitting list on commas: - const policiesList: string[] = policy.split(','); - - // Append policy to policies. - splitCspList.push(...policiesList); + parse(unparsedCsp: string): Csp { + unparsedCsp.split(', ').forEach(currentCsp => { + this.csp.directives.push(this.parseCsp(currentCsp)); }); - splitCspList.forEach(currentCsp => { - this.csps.push(this.parseCsp(currentCsp)); - }); - - return this.csps; + return this.csp; } - parseCsp(unparsedCsp: string): Csp { - const retCsp: Csp = new Csp(); + parseCsp(unparsedCsp: string): Record { + const retCspDirectives: Record = {}; // For each token returned by strictly splitting serialized on the U+003B SEMICOLON character (;): const directiveTokens = unparsedCsp.split(';'); @@ -87,7 +78,7 @@ export class CspParser { const directiveName = directiveParts[0].toLowerCase(); // If policy’s directive set contains a directive whose name is directive name, continue. - if (directiveName in retCsp.directives) { + if (directiveName in retCspDirectives) { continue; } @@ -102,12 +93,12 @@ export class CspParser { } // Append directive to policy’s directive set. - retCsp.directives[directiveName] = directiveValues; + retCspDirectives[directiveName] = directiveValues; } } // Return policy. - return retCsp; + return retCspDirectives; } } diff --git a/parser_test.ts b/parser_test.ts index 390bce5..a83fe9a 100644 --- a/parser_test.ts +++ b/parser_test.ts @@ -19,7 +19,7 @@ import 'jasmine'; -import { CspParser, TEST_ONLY } from './parser'; +import {CspParser, TEST_ONLY} from './parser'; describe('Test parser', () => { @@ -37,10 +37,10 @@ describe('Test parser', () => { 'report-uri /csp/test'; const parser = new (CspParser)(validCsp); - const parsedCsp = parser.csps[0]; + const parsedCsp = parser.csp; // check directives - const directives = Object.keys(parsedCsp.directives); + const directives = Object.keys(parsedCsp.directives[0]); const expectedDirectives = [ 'default-src', 'script-src', 'object-src', 'img-src', 'style-src', 'font-src', 'child-src', 'upgrade-insecure-requests', 'report-uri' @@ -51,37 +51,37 @@ describe('Test parser', () => { // check directive values expect(['\'none\'']) .toEqual(jasmine.arrayWithExactContents( - parsedCsp.directives['default-src'] as string[])); + parsedCsp.directives[0]['default-src'] as string[])); expect([ '\'nonce-unsafefoobar\'', '\'unsafe-eval\'', '\'unsafe-inline\'', 'https://example.com/foo.js', 'foo.bar' ]) .toEqual(jasmine.arrayWithExactContents( - parsedCsp.directives['script-src'] as string[])); + parsedCsp.directives[0]['script-src'] as string[])); expect(['\'none\'']) .toEqual(jasmine.arrayWithExactContents( - parsedCsp.directives['object-src'] as string[])); + parsedCsp.directives[0]['object-src'] as string[])); expect(['\'self\'', 'https:', 'data:', 'blob:']) .toEqual(jasmine.arrayWithExactContents( - parsedCsp.directives['img-src'] as string[])); + parsedCsp.directives[0]['img-src'] as string[])); expect([ '\'self\'', '\'unsafe-inline\'', '\'sha256-1DCfk1NYWuHMfoobarfoobar=\'' ]) .toEqual(jasmine.arrayWithExactContents( - parsedCsp.directives['style-src'] as string[])); + parsedCsp.directives[0]['style-src'] as string[])); expect(['*']).toEqual(jasmine.arrayWithExactContents( - parsedCsp.directives['font-src'] as string[])); + parsedCsp.directives[0]['font-src'] as string[])); expect(['*.example.com:9090']) .toEqual(jasmine.arrayWithExactContents( - parsedCsp.directives['child-src'] as string[])); + parsedCsp.directives[0]['child-src'] as string[])); expect([]).toEqual(jasmine.arrayWithExactContents( - parsedCsp.directives['upgrade-insecure-requests'] as string[])); + parsedCsp.directives[0]['upgrade-insecure-requests'] as string[])); expect(['/csp/test']) .toEqual(jasmine.arrayWithExactContents( - parsedCsp.directives['report-uri'] as string[])); + parsedCsp.directives[0]['report-uri'] as string[])); }); it('CspParserDuplicateDirectives', () => { @@ -91,10 +91,10 @@ describe('Test parser', () => { 'OBJECT-src foo.bar;'; const parser = new (CspParser)(validCsp); - const parsedCsp = parser.csps[0]; + const parsedCsp = parser.csp; // check directives - const directives = Object.keys(parsedCsp.directives); + const directives = Object.keys(parsedCsp.directives[0]); const expectedDirectives = ['default-src', 'object-src']; expect(expectedDirectives) .toEqual(jasmine.arrayWithExactContents(directives)); @@ -102,10 +102,10 @@ describe('Test parser', () => { // check directive values expect(['\'none\'']) .toEqual(jasmine.arrayWithExactContents( - parsedCsp.directives['default-src'] as string[])); + parsedCsp.directives[0]['default-src'] as string[])); expect(['\'none\'']) .toEqual(jasmine.arrayWithExactContents( - parsedCsp.directives['object-src'] as string[])); + parsedCsp.directives[0]['object-src'] as string[])); }); it('CspParserMixedCaseKeywords', () => { @@ -114,10 +114,10 @@ describe('Test parser', () => { 'img-src \'sElf\' HTTPS: Example.com/CaseSensitive;'; const parser = new (CspParser)(validCsp); - const parsedCsp = parser.csps[0]; + const parsedCsp = parser.csp; // check directives - const directives = Object.keys(parsedCsp.directives); + const directives = Object.keys(parsedCsp.directives[0]); const expectedDirectives = ['default-src', 'img-src']; expect(expectedDirectives) .toEqual(jasmine.arrayWithExactContents(directives)); @@ -125,10 +125,10 @@ describe('Test parser', () => { // check directive values expect(['\'none\'']) .toEqual(jasmine.arrayWithExactContents( - parsedCsp.directives['default-src'] as string[])); + parsedCsp.directives[0]['default-src'] as string[])); expect(['\'self\'', 'https:', 'Example.com/CaseSensitive']) .toEqual(jasmine.arrayWithExactContents( - parsedCsp.directives['img-src'] as string[])); + parsedCsp.directives[0]['img-src'] as string[])); }); it('NormalizeDirectiveValue', () => { @@ -142,89 +142,44 @@ describe('Test parser', () => { .toBe('example.com/TEST'); }); - it('CspParserMultipleDirectives', () => { - const validCsp1 = // Test policy with different features from CSP2. - 'default-src \'none\';' + - 'script-src \'nonce-unsafefoobar\' \'unsafe-eval\' \'unsafe-inline\' \n' + - 'https://example.com/foo.js foo.bar; ' + - 'object-src \'none\';' + - 'img-src \'self\' https: data: blob:;' + - 'style-src \'self\' \'unsafe-inline\' \'sha256-1DCfk1NYWuHMfoobarfoobar=\';' + - 'font-src *;' + - 'child-src *.example.com:9090;' + - 'upgrade-insecure-requests;\n' + - 'report-uri /csp/test'; + it('ParseMultipleDirectivesSimple', () => { + const testCsp1 = 'default-src \'self\' http://example.com http://example.net; ' + + 'connect-src \'none\'; '; - const validCsp2 = // Test policy with different features from CSP2. - 'default-src \'nonce-foobar\';' + - 'script-src https:'; + const testCsp2 = 'connect-src http://example.com/; script-src http://example.com/; '; - const parser = new (CspParser)([validCsp1, validCsp2]); - const parsedCsp = parser.csps; + const parsed = (new CspParser([testCsp1, testCsp2])).csp; // check directives - const directives = Object.keys(parsedCsp[0].directives); - const expectedDirectives = [ - 'default-src', 'script-src', 'object-src', 'img-src', 'style-src', - 'font-src', 'child-src', 'upgrade-insecure-requests', 'report-uri' + const directives1 = Object.keys(parsed.directives[0]); + const expectedDirectives1 = [ + 'default-src', 'connect-src' ]; - expect(expectedDirectives) - .toEqual(jasmine.arrayWithExactContents(directives)); - const directives2 = Object.keys(parsedCsp[1].directives); + const directives2 = Object.keys(parsed.directives[1]); const expectedDirectives2 = [ - 'default-src', 'script-src' + 'connect-src', 'script-src' ]; - expect(expectedDirectives2) - .toEqual(jasmine.arrayWithExactContents(directives2)); + + expect(expectedDirectives1).toEqual(jasmine.arrayWithExactContents(directives1)); + expect(expectedDirectives2).toEqual(jasmine.arrayWithExactContents(directives2)); // check directive values - expect(['\'none\'']) - .toEqual(jasmine.arrayWithExactContents( - parsedCsp[0].directives['default-src'] as string[])); - - expect([ - '\'nonce-unsafefoobar\'', '\'unsafe-eval\'', '\'unsafe-inline\'', - 'https://example.com/foo.js', 'foo.bar' - ]) + expect(['\'self\'', 'http://example.com', 'http://example.net']) .toEqual(jasmine.arrayWithExactContents( - parsedCsp[0].directives['script-src'] as string[])); + parsed.directives[0]['default-src'] as string[])); expect(['\'none\'']) .toEqual(jasmine.arrayWithExactContents( - parsedCsp[0].directives['object-src'] as string[])); - - expect(['\'self\'', 'https:', 'data:', 'blob:']) - .toEqual(jasmine.arrayWithExactContents( - parsedCsp[0].directives['img-src'] as string[])); - - expect([ - '\'self\'', '\'unsafe-inline\'', '\'sha256-1DCfk1NYWuHMfoobarfoobar=\'' - ]) - .toEqual(jasmine.arrayWithExactContents( - parsedCsp[0].directives['style-src'] as string[])); - - expect(['*']).toEqual(jasmine.arrayWithExactContents( - parsedCsp[0].directives['font-src'] as string[])); - - expect(['*.example.com:9090']) - .toEqual(jasmine.arrayWithExactContents( - parsedCsp[0].directives['child-src'] as string[])); - - expect([]).toEqual(jasmine.arrayWithExactContents( - parsedCsp[0].directives['upgrade-insecure-requests'] as string[])); + parsed.directives[0]['connect-src'] as string[])); - expect(['/csp/test']) - .toEqual(jasmine.arrayWithExactContents( - parsedCsp[0].directives['report-uri'] as string[])); - - expect(['\'nonce-foobar\'']) + expect(['http://example.com/']) .toEqual(jasmine.arrayWithExactContents( - parsedCsp[1].directives['default-src'] as string[])); + parsed.directives[1]['connect-src'] as string[])); - expect(['https:']) + expect(['http://example.com/']) .toEqual(jasmine.arrayWithExactContents( - parsedCsp[1].directives['script-src'] as string[])); + parsed.directives[1]['script-src'] as string[])); }); it('CspParserMultipleDirectivesRFC2616', () => { @@ -241,24 +196,24 @@ describe('Test parser', () => { 'report-uri /csp/test'; const parser = new (CspParser)([validCsp]); - const parsedCsp = parser.csps; + const parsedCsp = parser.csp; // check directives - const directives = Object.keys(parsedCsp[0].directives); + const directives = Object.keys(parsedCsp.directives[0]); const expectedDirectives = [ 'default-src' ]; expect(expectedDirectives) .toEqual(jasmine.arrayWithExactContents(directives)); - const directives2 = Object.keys(parsedCsp[1].directives); + const directives2 = Object.keys(parsedCsp.directives[1]); const expectedDirectives2 = [ 'default-src', 'script-src' ]; expect(expectedDirectives2) .toEqual(jasmine.arrayWithExactContents(directives2)); - const directives3 = Object.keys(parsedCsp[2].directives); + const directives3 = Object.keys(parsedCsp.directives[2]); const expectedDirectives3 = [ 'script-src', 'object-src', 'img-src', 'style-src', 'font-src', 'child-src', 'upgrade-insecure-requests', 'report-uri' @@ -269,90 +224,144 @@ describe('Test parser', () => { // check directive values expect(['\'none\'']) .toEqual(jasmine.arrayWithExactContents( - parsedCsp[0].directives['default-src'] as string[])); + parsedCsp.directives[0]['default-src'] as string[])); expect(['\'nonce-foobar\'']) .toEqual(jasmine.arrayWithExactContents( - parsedCsp[1].directives['default-src'] as string[])); + parsedCsp.directives[1]['default-src'] as string[])); expect([ '\'nonce-unsafefoobar\'', '\'unsafe-eval\'', '\'unsafe-inline\'', 'https://example.com/foo.js', 'foo.bar' ]) .toEqual(jasmine.arrayWithExactContents( - parsedCsp[1].directives['script-src'] as string[])); + parsedCsp.directives[1]['script-src'] as string[])); expect(['https:']) .toEqual(jasmine.arrayWithExactContents( - parsedCsp[2].directives['script-src'] as string[])); + parsedCsp.directives[2]['script-src'] as string[])); expect(['\'none\'']) .toEqual(jasmine.arrayWithExactContents( - parsedCsp[2].directives['object-src'] as string[])); + parsedCsp.directives[2]['object-src'] as string[])); expect(['\'self\'', 'https:', 'data:', 'blob:']) .toEqual(jasmine.arrayWithExactContents( - parsedCsp[2].directives['img-src'] as string[])); + parsedCsp.directives[2]['img-src'] as string[])); expect([ '\'self\'', '\'unsafe-inline\'', '\'sha256-1DCfk1NYWuHMfoobarfoobar=\'' ]) .toEqual(jasmine.arrayWithExactContents( - parsedCsp[2].directives['style-src'] as string[])); + parsedCsp.directives[2]['style-src'] as string[])); expect(['*']).toEqual(jasmine.arrayWithExactContents( - parsedCsp[2].directives['font-src'] as string[])); + parsedCsp.directives[2]['font-src'] as string[])); expect(['*.example.com:9090']) .toEqual(jasmine.arrayWithExactContents( - parsedCsp[2].directives['child-src'] as string[])); + parsedCsp.directives[2]['child-src'] as string[])); expect([]).toEqual(jasmine.arrayWithExactContents( - parsedCsp[2].directives['upgrade-insecure-requests'] as string[])); + parsedCsp.directives[2]['upgrade-insecure-requests'] as string[])); expect(['/csp/test']) .toEqual(jasmine.arrayWithExactContents( - parsedCsp[2].directives['report-uri'] as string[])); + parsedCsp.directives[2]['report-uri'] as string[])); }); - it('ParseMultipleDirectivesSimple', () => { - const testCsp1 = 'default-src \'self\' http://example.com http://example.net; ' + - 'connect-src \'none\'; '; + it('CspParserMultipleDirectivesMixed', () => { + const validCsp1 = // Test policy with different features from CSP2. + 'default-src \'none\';' + + 'script-src \'nonce-unsafefoobar\' \'unsafe-eval\' \'unsafe-inline\' \n' + + 'https://example.com/foo.js foo.bar; ' + + 'object-src \'none\';' + + 'img-src \'self\' https: data: blob:;' + + 'style-src \'self\' \'unsafe-inline\' \'sha256-1DCfk1NYWuHMfoobarfoobar=\';' + + 'font-src *;' + + 'child-src *.example.com:9090;' + + 'upgrade-insecure-requests;\n' + + 'report-uri /csp/test, default-src \'self\''; - const testCsp2 = 'connect-src http://example.com/; script-src http://example.com/; '; + const validCsp2 = // Test policy with different features from CSP2. + 'default-src \'nonce-foobar\';' + + 'script-src https:'; - const parsed = (new CspParser([testCsp1, testCsp2])).csps; - expect(parsed.length).toBe(2); + const parser = new (CspParser)([validCsp1, validCsp2]); + const parsedCsp = parser.csp; // check directives - const directives1 = Object.keys(parsed[0].directives); + const directives = Object.keys(parsedCsp.directives[0]); + const expectedDirectives = [ + 'default-src', 'script-src', 'object-src', 'img-src', 'style-src', + 'font-src', 'child-src', 'upgrade-insecure-requests', 'report-uri' + ]; + expect(expectedDirectives) + .toEqual(jasmine.arrayWithExactContents(directives)); + + const directives1 = Object.keys(parsedCsp.directives[1]); const expectedDirectives1 = [ - 'default-src', 'connect-src' + 'default-src' ]; expect(expectedDirectives1) .toEqual(jasmine.arrayWithExactContents(directives1)); - const directives2 = Object.keys(parsed[1].directives); + const directives2 = Object.keys(parsedCsp.directives[2]); const expectedDirectives2 = [ - 'connect-src', 'script-src' + 'default-src', 'script-src' ]; expect(expectedDirectives2) .toEqual(jasmine.arrayWithExactContents(directives2)); // check directive values - expect(['\'self\'', 'http://example.com', 'http://example.net']) + expect(['\'none\'']) .toEqual(jasmine.arrayWithExactContents( - parsed[0].directives['default-src'] as string[])); + parsedCsp.directives[0]['default-src'] as string[])); + + expect([ + '\'nonce-unsafefoobar\'', '\'unsafe-eval\'', '\'unsafe-inline\'', + 'https://example.com/foo.js', 'foo.bar' + ]) + .toEqual(jasmine.arrayWithExactContents( + parsedCsp.directives[0]['script-src'] as string[])); expect(['\'none\'']) .toEqual(jasmine.arrayWithExactContents( - parsed[0].directives['connect-src'] as string[])); + parsedCsp.directives[0]['object-src'] as string[])); - expect(['http://example.com/']) + expect(['\'self\'', 'https:', 'data:', 'blob:']) .toEqual(jasmine.arrayWithExactContents( - parsed[1].directives['connect-src'] as string[])); + parsedCsp.directives[0]['img-src'] as string[])); - expect(['http://example.com/']) + expect([ + '\'self\'', '\'unsafe-inline\'', '\'sha256-1DCfk1NYWuHMfoobarfoobar=\'' + ]) + .toEqual(jasmine.arrayWithExactContents( + parsedCsp.directives[0]['style-src'] as string[])); + + expect(['*']).toEqual(jasmine.arrayWithExactContents( + parsedCsp.directives[0]['font-src'] as string[])); + + expect(['*.example.com:9090']) + .toEqual(jasmine.arrayWithExactContents( + parsedCsp.directives[0]['child-src'] as string[])); + + expect([]).toEqual(jasmine.arrayWithExactContents( + parsedCsp.directives[0]['upgrade-insecure-requests'] as string[])); + + expect(['/csp/test']) + .toEqual(jasmine.arrayWithExactContents( + parsedCsp.directives[0]['report-uri'] as string[])); + + expect(['\'self\'']) + .toEqual(jasmine.arrayWithExactContents( + parsedCsp.directives[1]['default-src'] as string[])); + + expect(['\'nonce-foobar\'']) + .toEqual(jasmine.arrayWithExactContents( + parsedCsp.directives[2]['default-src'] as string[])); + + expect(['https:']) .toEqual(jasmine.arrayWithExactContents( - parsed[1].directives['script-src'] as string[])); + parsedCsp.directives[2]['script-src'] as string[])); }); }); diff --git a/utils.ts b/utils.ts index 8c2123c..36758a7 100644 --- a/utils.ts +++ b/utils.ts @@ -18,9 +18,6 @@ */ -import { Csp } from './csp'; - - /** * Removes scheme from url. * @param url Url. @@ -133,15 +130,19 @@ export function matchWildcardUrls( * should get applied on directive values. */ export function applyCheckFunktionToDirectives( - parsedCsp: Csp, + parsedCsp: Record, check: (directive: string, directiveValues: string[]) => void, ) { - const directiveNames = Object.keys(parsedCsp.directives); + const directiveNames = Object.keys(parsedCsp); for (const directive of directiveNames) { - const directiveValues = parsedCsp.directives[directive]; + const directiveValues = parsedCsp[directive]; if (directiveValues) { check(directive, directiveValues); } } } + +export function mergeCspHeaders(headerValues: string[]): string { + return headerValues.map(Function.prototype.call, String.prototype.trim).join(', '); +} \ No newline at end of file diff --git a/utils_test.ts b/utils_test.ts index 300eb4a..d123dae 100644 --- a/utils_test.ts +++ b/utils_test.ts @@ -18,7 +18,7 @@ import 'jasmine'; -import { getHostname, getSchemeFreeUrl, matchWildcardUrls } from './utils'; +import { getHostname, getSchemeFreeUrl, matchWildcardUrls, mergeCspHeaders } from './utils'; const TEST_BYPASSES = [ 'https://googletagmanager.com/gtm/js', 'https://www.google.com/jsapi', @@ -144,4 +144,30 @@ describe('Test Utils', () => { it('GetHostnameIPv6WithPartialProtocol', () => { expect(getHostname('//[::1]')).toBe('[::1]'); }); + + it('MergeCspHeadersSingleHeader', () => { + expect(mergeCspHeaders( + [ + 'default-src \'none\'; script-src https:;' + ] + )).toBe('default-src \'none\'; script-src https:;'); + }); + + it('MergeCspHeadersMultipleHeader', () => { + expect(mergeCspHeaders( + [ + 'default-src \'none\'; script-src https:', + 'default-src \'self\';' + ] + )).toBe('default-src \'none\'; script-src https:, default-src \'self\';'); + }); + + it('MergeCspHeadersMultipleHeaderWithSpaces', () => { + expect(mergeCspHeaders( + [ + 'default-src \'none\'; script-src https:; ', + 'default-src \'self\'; ' + ] + )).toBe('default-src \'none\'; script-src https:;, default-src \'self\';'); + }); }); From 3c846bce4591963cc80dbd13fea9005be70d39e8 Mon Sep 17 00:00:00 2001 From: MaxNad Date: Mon, 8 Jan 2024 15:14:30 -0500 Subject: [PATCH 10/12] Included gts linter --- .editorconfig | 8 + .eslintignore | 3 +- .eslintrc.json | 3 + .gitignore | 2 + package-lock.json | 2044 +++++++++++++++++++++++++++++++++++++++++---- package.json | 12 +- tsconfig.json | 19 +- 7 files changed, 1935 insertions(+), 156 deletions(-) create mode 100644 .editorconfig create mode 100644 .eslintrc.json diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..79fe802 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +insert_final_newline = true diff --git a/.eslintignore b/.eslintignore index d664296..e65f584 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,2 @@ -node_modules -dist +build/ *_test.ts \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..f95bb33 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/gts/" +} diff --git a/.gitignore b/.gitignore index 1a65c52..70e7aae 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ dest/ *.js dist +build +src node_modules \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 12fd177..c1395ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,11 +10,13 @@ "license": "Apache-2.0", "devDependencies": { "@types/jasmine": "^5.1.4", + "@types/node": "20.8.2", "@typescript-eslint/eslint-plugin": "^6.17.0", "@typescript-eslint/parser": "^6.17.0", "eslint": "^8.56.0", + "gts": "^5.2.0", "jasmine": "^5.1.0", - "typescript": "^5.3.3" + "typescript": "~5.2.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -26,6 +28,184 @@ "node": ">=0.10.0" } }, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -221,6 +401,18 @@ "node": ">=14" } }, + "node_modules/@pkgr/core": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.0.tgz", + "integrity": "sha512-Zwq5OCzuwJC2jwqmpEQt7Ds1DTi6BWSwoGkbb1n9pO3hzb35BoJELx7c0T23iDkBGkh2e7tvOtjF3tr3OaQHDQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/@types/jasmine": { "version": "5.1.4", "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-5.1.4.tgz", @@ -233,6 +425,24 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.8.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.2.tgz", + "integrity": "sha512-Vvycsc9FQdwhxE3y3DzeIxuEJbWGDsnrxvMADzTDF/lcdR9/K+AQIeAghTQsHtotg/q0j3WEOYS/jQgSdWue3w==", + "dev": true + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true + }, "node_modules/@types/semver": { "version": "7.5.6", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", @@ -240,16 +450,16 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.17.0.tgz", - "integrity": "sha512-Vih/4xLXmY7V490dGwBQJTpIZxH4ZFH6eCVmQ4RFkB+wmaCTDAx4dtgoWwMNGKLkqRY1L6rPqzEbjorRnDo4rQ==", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.18.0.tgz", + "integrity": "sha512-3lqEvQUdCozi6d1mddWqd+kf8KxmGq2Plzx36BlkjuQe3rSTm/O98cLf0A4uDO+a5N1KD2SeEEl6fW97YHY+6w==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.17.0", - "@typescript-eslint/type-utils": "6.17.0", - "@typescript-eslint/utils": "6.17.0", - "@typescript-eslint/visitor-keys": "6.17.0", + "@typescript-eslint/scope-manager": "6.18.0", + "@typescript-eslint/type-utils": "6.18.0", + "@typescript-eslint/utils": "6.18.0", + "@typescript-eslint/visitor-keys": "6.18.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -275,15 +485,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.17.0.tgz", - "integrity": "sha512-C4bBaX2orvhK+LlwrY8oWGmSl4WolCfYm513gEccdWZj0CwGadbIADb0FtVEcI+WzUyjyoBj2JRP8g25E6IB8A==", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.18.0.tgz", + "integrity": "sha512-v6uR68SFvqhNQT41frCMCQpsP+5vySy6IdgjlzUWoo7ALCnpaWYcz/Ij2k4L8cEsL0wkvOviCMpjmtRtHNOKzA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.17.0", - "@typescript-eslint/types": "6.17.0", - "@typescript-eslint/typescript-estree": "6.17.0", - "@typescript-eslint/visitor-keys": "6.17.0", + "@typescript-eslint/scope-manager": "6.18.0", + "@typescript-eslint/types": "6.18.0", + "@typescript-eslint/typescript-estree": "6.18.0", + "@typescript-eslint/visitor-keys": "6.18.0", "debug": "^4.3.4" }, "engines": { @@ -303,13 +513,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.17.0.tgz", - "integrity": "sha512-RX7a8lwgOi7am0k17NUO0+ZmMOX4PpjLtLRgLmT1d3lBYdWH4ssBUbwdmc5pdRX8rXon8v9x8vaoOSpkHfcXGA==", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.18.0.tgz", + "integrity": "sha512-o/UoDT2NgOJ2VfHpfr+KBY2ErWvCySNUIX/X7O9g8Zzt/tXdpfEU43qbNk8LVuWUT2E0ptzTWXh79i74PP0twA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.17.0", - "@typescript-eslint/visitor-keys": "6.17.0" + "@typescript-eslint/types": "6.18.0", + "@typescript-eslint/visitor-keys": "6.18.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -320,13 +530,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.17.0.tgz", - "integrity": "sha512-hDXcWmnbtn4P2B37ka3nil3yi3VCQO2QEB9gBiHJmQp5wmyQWqnjA85+ZcE8c4FqnaB6lBwMrPkgd4aBYz3iNg==", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.18.0.tgz", + "integrity": "sha512-ZeMtrXnGmTcHciJN1+u2CigWEEXgy1ufoxtWcHORt5kGvpjjIlK9MUhzHm4RM8iVy6dqSaZA/6PVkX6+r+ChjQ==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.17.0", - "@typescript-eslint/utils": "6.17.0", + "@typescript-eslint/typescript-estree": "6.18.0", + "@typescript-eslint/utils": "6.18.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -347,9 +557,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.17.0.tgz", - "integrity": "sha512-qRKs9tvc3a4RBcL/9PXtKSehI/q8wuU9xYJxe97WFxnzH8NWWtcW3ffNS+EWg8uPvIerhjsEZ+rHtDqOCiH57A==", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.18.0.tgz", + "integrity": "sha512-/RFVIccwkwSdW/1zeMx3hADShWbgBxBnV/qSrex6607isYjj05t36P6LyONgqdUrNLl5TYU8NIKdHUYpFvExkA==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -360,13 +570,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.17.0.tgz", - "integrity": "sha512-gVQe+SLdNPfjlJn5VNGhlOhrXz4cajwFd5kAgWtZ9dCZf4XJf8xmgCTLIqec7aha3JwgLI2CK6GY1043FRxZwg==", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.18.0.tgz", + "integrity": "sha512-klNvl+Ql4NsBNGB4W9TZ2Od03lm7aGvTbs0wYaFYsplVPhr+oeXjlPZCDI4U9jgJIDK38W1FKhacCFzCC+nbIg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.17.0", - "@typescript-eslint/visitor-keys": "6.17.0", + "@typescript-eslint/types": "6.18.0", + "@typescript-eslint/visitor-keys": "6.18.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -388,17 +598,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.17.0.tgz", - "integrity": "sha512-LofsSPjN/ITNkzV47hxas2JCsNCEnGhVvocfyOcLzT9c/tSZE7SfhS/iWtzP1lKNOEfLhRTZz6xqI8N2RzweSQ==", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.18.0.tgz", + "integrity": "sha512-wiKKCbUeDPGaYEYQh1S580dGxJ/V9HI7K5sbGAVklyf+o5g3O+adnS4UNJajplF4e7z2q0uVBaTdT/yLb4XAVA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.17.0", - "@typescript-eslint/types": "6.17.0", - "@typescript-eslint/typescript-estree": "6.17.0", + "@typescript-eslint/scope-manager": "6.18.0", + "@typescript-eslint/types": "6.18.0", + "@typescript-eslint/typescript-estree": "6.18.0", "semver": "^7.5.4" }, "engines": { @@ -413,12 +623,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.17.0.tgz", - "integrity": "sha512-H6VwB/k3IuIeQOyYczyyKN8wH6ed8EwliaYHLxOIhyF0dYEIsN8+Bk3GE19qafeMKyZJJHP8+O1HiFhFLUNKSg==", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.18.0.tgz", + "integrity": "sha512-1wetAlSZpewRDb2h9p/Q8kRjdGuqdTAQbkJIOUMLug2LBLG+QOjiWoSj6/3B/hA9/tVTFFdtiKvAYoYnSRW/RA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.17.0", + "@typescript-eslint/types": "6.18.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -472,6 +682,33 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-regex": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", @@ -511,6 +748,15 @@ "node": ">=8" } }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -547,6 +793,32 @@ "node": ">=6" } }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -578,6 +850,33 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -633,6 +932,40 @@ } } }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "dev": true, + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -675,6 +1008,15 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -742,44 +1084,58 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "node_modules/eslint-config-prettier": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", + "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "bin": { + "eslint-config-prettier": "bin/cli.js" }, - "funding": { - "url": "https://opencollective.com/eslint" + "peerDependencies": { + "eslint": ">=7.0.0" } }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "node_modules/eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", "dev": true, + "dependencies": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=8.10.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=4.19.1" } }, - "node_modules/eslint/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", "dev": true, + "dependencies": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, "engines": { - "node": ">=8" + "node": ">=8.10.0" + }, + "peerDependencies": { + "eslint": ">=5.16.0" } }, - "node_modules/eslint/node_modules/brace-expansion": { + "node_modules/eslint-plugin-node/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", @@ -789,7 +1145,7 @@ "concat-map": "0.0.1" } }, - "node_modules/eslint/node_modules/minimatch": { + "node_modules/eslint-plugin-node/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", @@ -801,27 +1157,52 @@ "node": "*" } }, - "node_modules/eslint/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/eslint-plugin-node/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.0.tgz", + "integrity": "sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.1" + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.5" }, "engines": { - "node": ">=8" + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } } }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -830,7 +1211,103 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/esquery": { + "node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", @@ -872,12 +1349,61 @@ "node": ">=0.10.0" } }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", @@ -927,6 +1453,30 @@ "reusify": "^1.0.4" } }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -1009,88 +1559,523 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, - "node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/gts": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/gts/-/gts-5.2.0.tgz", + "integrity": "sha512-25qOnePUUX7upFc4ycqWersDBq+o1X6hXUTW56JOWCxPYKJXQ1RWzqT9q+2SU3LfPKJf+4sz4Dw3VT0p96Kv6g==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "5.62.0", + "@typescript-eslint/parser": "5.62.0", + "chalk": "^4.1.2", + "eslint": "8.50.0", + "eslint-config-prettier": "9.0.0", + "eslint-plugin-node": "11.1.0", + "eslint-plugin-prettier": "5.0.0", + "execa": "^5.0.0", + "inquirer": "^7.3.3", + "json5": "^2.1.3", + "meow": "^9.0.0", + "ncp": "^2.0.0", + "prettier": "3.0.3", + "rimraf": "3.0.2", + "write-file-atomic": "^4.0.0" + }, + "bin": { + "gts": "build/src/cli.js" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "typescript": ">=3" + } + }, + "node_modules/gts/node_modules/@eslint/js": { + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.50.0.tgz", + "integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/gts/node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/gts/node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/gts/node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/gts/node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/gts/node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/gts/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/gts/node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/gts/node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/gts/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/gts/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/gts/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/gts/node_modules/eslint": { + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.50.0.tgz", + "integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.50.0", + "@humanwhocodes/config-array": "^0.11.11", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/gts/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/gts/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/gts/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=8" } }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", "dev": true, "dependencies": { - "is-glob": "^4.0.3" + "function-bind": "^1.1.2" }, "engines": { - "node": ">=10.13.0" + "node": ">= 0.4" } }, - "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", "dev": true, "dependencies": { - "type-fest": "^0.20.2" + "lru-cache": "^6.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=10" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" + "yallist": "^4.0.0" }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, "node_modules/ignore": { @@ -1127,6 +2112,15 @@ "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1143,6 +2137,89 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "node_modules/inquirer": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.19", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/inquirer/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/inquirer/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1191,6 +2268,27 @@ "node": ">=8" } }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -1234,6 +2332,12 @@ "integrity": "sha512-UrzO3fL7nnxlQXlvTynNAenL+21oUQRlzqQFsA2U11ryb4+NLOCOePZ70PTojEaUKhiFugh7dG0Q+I58xlPdWg==", "dev": true }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -1252,6 +2356,12 @@ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -1264,6 +2374,18 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -1273,6 +2395,15 @@ "json-buffer": "3.0.1" } }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -1286,6 +2417,12 @@ "node": ">= 0.8.0" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -1301,6 +2438,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -1316,6 +2459,62 @@ "node": "14 || >=16.14" } }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", + "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "dev": true, + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -1338,6 +2537,24 @@ "node": ">=8.6" } }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/minimatch": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", @@ -1353,6 +2570,20 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/minipass": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", @@ -1368,12 +2599,60 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "node_modules/ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", + "dev": true, + "bin": { + "ncp": "bin/ncp" + } + }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -1383,6 +2662,21 @@ "wrappy": "1" } }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -1400,6 +2694,15 @@ "node": ">= 0.8.0" } }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -1430,6 +2733,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -1442,6 +2754,24 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -1469,6 +2799,12 @@ "node": ">=8" } }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, "node_modules/path-scurry": { "version": "1.10.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", @@ -1515,6 +2851,33 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -1544,6 +2907,186 @@ } ] }, + "node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-pkg/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -1553,6 +3096,25 @@ "node": ">=4" } }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -1620,6 +3182,15 @@ "node": "*" } }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -1643,6 +3214,24 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -1712,6 +3301,38 @@ "node": ">=8" } }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", + "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", + "dev": true + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -1808,6 +3429,27 @@ "node": ">=8" } }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -1832,12 +3474,64 @@ "node": ">=8" } }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/synckit": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", + "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/synckit/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -1850,6 +3544,15 @@ "node": ">=8.0" } }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/ts-api-utils": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", @@ -1862,6 +3565,27 @@ "typescript": ">=4.2.0" } }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -1887,9 +3611,9 @@ } }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -1908,6 +3632,16 @@ "punycode": "^2.1.0" } }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -2020,12 +3754,40 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 81df946..a8ad6c6 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,13 @@ }, "scripts": { "test": "tsc && npx jasmine --config=jasmine.json", - "lint": "eslint . --ext .ts" + "lint": "gts lint", + "clean": "gts clean", + "compile": "tsc", + "fix": "gts fix", + "prepare": "npm run compile", + "pretest": "npm run compile", + "posttest": "npm run lint" }, "devDependencies": { "@types/jasmine": "^5.1.4", @@ -27,6 +33,8 @@ "@typescript-eslint/parser": "^6.17.0", "eslint": "^8.56.0", "jasmine": "^5.1.0", - "typescript": "^5.3.3" + "typescript": "~5.2.0", + "gts": "^5.2.0", + "@types/node": "20.8.2" } } diff --git a/tsconfig.json b/tsconfig.json index 845fb17..d1646f0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,14 +1,11 @@ { + "extends": "./node_modules/gts/tsconfig-google.json", "compilerOptions": { - "target": "ES2020", - "module": "commonjs", - "declaration": true, - "declarationMap": true, - "sourceMap": true, - "outDir": "dist", - "removeComments": true, - "strict": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true - } + "rootDir": ".", + "outDir": "build" + }, + "include": [ + "src/**/*.ts", + "test/**/*.ts" + ] } From b8dd94f838e0ed0a9c0e23a9e616a65a990afb6f Mon Sep 17 00:00:00 2001 From: MaxNad Date: Mon, 8 Jan 2024 16:01:04 -0500 Subject: [PATCH 11/12] Linted using gts --- .eslintignore | 2 +- .eslintrc | 8 +- .gitignore | 2 - LICENSE | 324 +- jasmine.json | 4 +- package-lock.json | 7590 ++++++++--------- package.json | 5 +- .../allowlist_bypasses}/angular.ts | 3 +- .../allowlist_bypasses}/flash.ts | 3 +- .../allowlist_bypasses}/json/angular.json | 0 .../allowlist_bypasses}/json/flash.json | 0 .../allowlist_bypasses}/json/jsonp.json | 0 .../allowlist_bypasses}/jsonp.ts | 15 +- {checks => src/checks}/checker.ts | 4 +- {checks => src/checks}/parser_checks.ts | 89 +- {checks => src/checks}/security_checks.ts | 473 +- {checks => src/checks}/strictcsp_checks.ts | 107 +- csp.ts => src/csp.ts | 288 +- evaluator.ts => src/evaluator.ts | 35 +- finding.ts => src/finding.ts | 27 +- .../lighthouse}/lighthouse_checks.ts | 58 +- parser.ts => src/parser.ts | 17 +- utils.ts => src/utils.ts | 37 +- {checks => test/checks}/parser_checks_test.ts | 8 +- .../checks}/security_checks_test.ts | 10 +- .../checks}/strictcsp_checks_test.ts | 17 +- csp_test.ts => test/csp_test.ts | 4 +- evaluator_test.ts => test/evaluator_test.ts | 6 +- finding_test.ts => test/finding_test.ts | 4 +- .../lighthouse}/lighthouse_checks_test.ts | 8 +- parser_test.ts => test/parser_test.ts | 4 +- utils_test.ts => test/utils_test.ts | 2 +- 32 files changed, 4715 insertions(+), 4439 deletions(-) rename {allowlist_bypasses => src/allowlist_bypasses}/angular.ts (98%) rename {allowlist_bypasses => src/allowlist_bypasses}/flash.ts (98%) rename {allowlist_bypasses => src/allowlist_bypasses}/json/angular.json (100%) rename {allowlist_bypasses => src/allowlist_bypasses}/json/flash.json (100%) rename {allowlist_bypasses => src/allowlist_bypasses}/json/jsonp.json (100%) rename {allowlist_bypasses => src/allowlist_bypasses}/jsonp.ts (97%) rename {checks => src/checks}/checker.ts (78%) rename {checks => src/checks}/parser_checks.ts (72%) rename {checks => src/checks}/security_checks.ts (66%) rename {checks => src/checks}/strictcsp_checks.ts (71%) rename csp.ts => src/csp.ts (58%) rename evaluator.ts => src/evaluator.ts (80%) rename finding.ts => src/finding.ts (83%) rename {lighthouse => src/lighthouse}/lighthouse_checks.ts (75%) rename parser.ts => src/parser.ts (89%) rename utils.ts => src/utils.ts (84%) rename {checks => test/checks}/parser_checks_test.ts (93%) rename {checks => test/checks}/security_checks_test.ts (98%) rename {checks => test/checks}/strictcsp_checks_test.ts (86%) rename csp_test.ts => test/csp_test.ts (99%) rename evaluator_test.ts => test/evaluator_test.ts (91%) rename finding_test.ts => test/finding_test.ts (95%) rename {lighthouse => test/lighthouse}/lighthouse_checks_test.ts (99%) rename parser_test.ts => test/parser_test.ts (99%) rename utils_test.ts => test/utils_test.ts (99%) diff --git a/.eslintignore b/.eslintignore index e65f584..cded043 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1,2 @@ build/ -*_test.ts \ No newline at end of file +test/ \ No newline at end of file diff --git a/.eslintrc b/.eslintrc index 11643ef..4dee9b0 100644 --- a/.eslintrc +++ b/.eslintrc @@ -2,11 +2,11 @@ "root": true, "parser": "@typescript-eslint/parser", "plugins": [ - "@typescript-eslint" + "@typescript-eslint" ], "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended" + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" ] } diff --git a/.gitignore b/.gitignore index 70e7aae..914a919 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,4 @@ dest/ *.js -dist build -src node_modules \ No newline at end of file diff --git a/LICENSE b/LICENSE index 7a4a3ea..dfadee9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,191 +1,191 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. Copyright [yyyy] [name of copyright owner] @@ -193,7 +193,7 @@ you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/jasmine.json b/jasmine.json index 35e0adf..da2dbbe 100644 --- a/jasmine.json +++ b/jasmine.json @@ -1,7 +1,7 @@ { - "spec_dir": "dist", + "spec_dir": "build", "spec_files": [ - "**/*_test.js" + "test/**/*_test.js" ], "stopSpecOnExpectationFailure": false, "random": true diff --git a/package-lock.json b/package-lock.json index c1395ee..f4e9a5a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,3801 +4,3801 @@ "lockfileVersion": 3, "requires": true, "packages": { - "": { - "name": "csp_evaluator", - "version": "2.0.0", - "license": "Apache-2.0", - "devDependencies": { - "@types/jasmine": "^5.1.4", - "@types/node": "20.8.2", - "@typescript-eslint/eslint-plugin": "^6.17.0", - "@typescript-eslint/parser": "^6.17.0", - "eslint": "^8.56.0", - "gts": "^5.2.0", - "jasmine": "^5.1.0", - "typescript": "~5.2.0" - } - }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/code-frame/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", - "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/js": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", - "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", - "dev": true - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@pkgr/core": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.0.tgz", - "integrity": "sha512-Zwq5OCzuwJC2jwqmpEQt7Ds1DTi6BWSwoGkbb1n9pO3hzb35BoJELx7c0T23iDkBGkh2e7tvOtjF3tr3OaQHDQ==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, - "node_modules/@types/jasmine": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-5.1.4.tgz", - "integrity": "sha512-px7OMFO/ncXxixDe1zR13V1iycqWae0MxTaw62RpFlksUi5QuNWgQJFkTQjIOvrmutJbI7Fp2Y2N1F6D2R4G6w==", - "dev": true - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, - "node_modules/@types/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", - "dev": true - }, - "node_modules/@types/node": { - "version": "20.8.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.2.tgz", - "integrity": "sha512-Vvycsc9FQdwhxE3y3DzeIxuEJbWGDsnrxvMADzTDF/lcdR9/K+AQIeAghTQsHtotg/q0j3WEOYS/jQgSdWue3w==", - "dev": true - }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", - "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", - "dev": true - }, - "node_modules/@types/semver": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", - "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", - "dev": true - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.18.0.tgz", - "integrity": "sha512-3lqEvQUdCozi6d1mddWqd+kf8KxmGq2Plzx36BlkjuQe3rSTm/O98cLf0A4uDO+a5N1KD2SeEEl6fW97YHY+6w==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.18.0", - "@typescript-eslint/type-utils": "6.18.0", - "@typescript-eslint/utils": "6.18.0", - "@typescript-eslint/visitor-keys": "6.18.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.4", - "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.18.0.tgz", - "integrity": "sha512-v6uR68SFvqhNQT41frCMCQpsP+5vySy6IdgjlzUWoo7ALCnpaWYcz/Ij2k4L8cEsL0wkvOviCMpjmtRtHNOKzA==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "6.18.0", - "@typescript-eslint/types": "6.18.0", - "@typescript-eslint/typescript-estree": "6.18.0", - "@typescript-eslint/visitor-keys": "6.18.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.18.0.tgz", - "integrity": "sha512-o/UoDT2NgOJ2VfHpfr+KBY2ErWvCySNUIX/X7O9g8Zzt/tXdpfEU43qbNk8LVuWUT2E0ptzTWXh79i74PP0twA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.18.0", - "@typescript-eslint/visitor-keys": "6.18.0" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.18.0.tgz", - "integrity": "sha512-ZeMtrXnGmTcHciJN1+u2CigWEEXgy1ufoxtWcHORt5kGvpjjIlK9MUhzHm4RM8iVy6dqSaZA/6PVkX6+r+ChjQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "6.18.0", - "@typescript-eslint/utils": "6.18.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.18.0.tgz", - "integrity": "sha512-/RFVIccwkwSdW/1zeMx3hADShWbgBxBnV/qSrex6607isYjj05t36P6LyONgqdUrNLl5TYU8NIKdHUYpFvExkA==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.18.0.tgz", - "integrity": "sha512-klNvl+Ql4NsBNGB4W9TZ2Od03lm7aGvTbs0wYaFYsplVPhr+oeXjlPZCDI4U9jgJIDK38W1FKhacCFzCC+nbIg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.18.0", - "@typescript-eslint/visitor-keys": "6.18.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.18.0.tgz", - "integrity": "sha512-wiKKCbUeDPGaYEYQh1S580dGxJ/V9HI7K5sbGAVklyf+o5g3O+adnS4UNJajplF4e7z2q0uVBaTdT/yLb4XAVA==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.18.0", - "@typescript-eslint/types": "6.18.0", - "@typescript-eslint/typescript-estree": "6.18.0", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.18.0.tgz", - "integrity": "sha512-1wetAlSZpewRDb2h9p/Q8kRjdGuqdTAQbkJIOUMLug2LBLG+QOjiWoSj6/3B/hA9/tVTFFdtiKvAYoYnSRW/RA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.18.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, - "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chalk/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decamelize-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", - "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", - "dev": true, - "dependencies": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decamelize-keys/node_modules/map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", - "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.56.0", - "@humanwhocodes/config-array": "^0.11.13", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-prettier": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", - "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", - "dev": true, - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-plugin-es": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", - "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", - "dev": true, - "dependencies": { - "eslint-utils": "^2.0.0", - "regexpp": "^3.0.0" - }, - "engines": { - "node": ">=8.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=4.19.1" - } - }, - "node_modules/eslint-plugin-node": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", - "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", - "dev": true, - "dependencies": { - "eslint-plugin-es": "^3.0.0", - "eslint-utils": "^2.0.0", - "ignore": "^5.1.1", - "minimatch": "^3.0.4", - "resolve": "^1.10.1", - "semver": "^6.1.0" - }, - "engines": { - "node": ">=8.10.0" - }, - "peerDependencies": { - "eslint": ">=5.16.0" - } - }, - "node_modules/eslint-plugin-node/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint-plugin-node/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint-plugin-node/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-prettier": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.0.tgz", - "integrity": "sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==", - "dev": true, - "dependencies": { - "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.5" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/prettier" - }, - "peerDependencies": { - "@types/eslint": ">=8.0.0", - "eslint": ">=8.0.0", - "prettier": ">=3.0.0" - }, - "peerDependenciesMeta": { - "@types/eslint": { - "optional": true - }, - "eslint-config-prettier": { - "optional": true - } - } - }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^1.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/execa/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", - "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", - "dev": true - }, - "node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "node_modules/gts": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/gts/-/gts-5.2.0.tgz", - "integrity": "sha512-25qOnePUUX7upFc4ycqWersDBq+o1X6hXUTW56JOWCxPYKJXQ1RWzqT9q+2SU3LfPKJf+4sz4Dw3VT0p96Kv6g==", - "dev": true, - "dependencies": { - "@typescript-eslint/eslint-plugin": "5.62.0", - "@typescript-eslint/parser": "5.62.0", - "chalk": "^4.1.2", - "eslint": "8.50.0", - "eslint-config-prettier": "9.0.0", - "eslint-plugin-node": "11.1.0", - "eslint-plugin-prettier": "5.0.0", - "execa": "^5.0.0", - "inquirer": "^7.3.3", - "json5": "^2.1.3", - "meow": "^9.0.0", - "ncp": "^2.0.0", - "prettier": "3.0.3", - "rimraf": "3.0.2", - "write-file-atomic": "^4.0.0" - }, - "bin": { - "gts": "build/src/cli.js" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "typescript": ">=3" - } - }, - "node_modules/gts/node_modules/@eslint/js": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.50.0.tgz", - "integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/gts/node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", - "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/type-utils": "5.62.0", - "@typescript-eslint/utils": "5.62.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/gts/node_modules/@typescript-eslint/parser": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", - "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/gts/node_modules/@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/gts/node_modules/@typescript-eslint/type-utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", - "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "5.62.0", - "@typescript-eslint/utils": "5.62.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/gts/node_modules/@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/gts/node_modules/@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/gts/node_modules/@typescript-eslint/utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", - "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/gts/node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/gts/node_modules/@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/gts/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/gts/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/gts/node_modules/eslint": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.50.0.tgz", - "integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.50.0", - "@humanwhocodes/config-array": "^0.11.11", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/gts/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/gts/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/gts/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/hard-rejection": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", - "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ignore": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", - "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/inquirer": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", - "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", - "dev": true, - "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.19", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.6.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/inquirer/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/inquirer/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", - "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jasmine": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-5.1.0.tgz", - "integrity": "sha512-prmJlC1dbLhti4nE4XAPDWmfJesYO15sjGXVp7Cs7Ym5I9Xtwa/hUHxxJXjnpfLO72+ySttA0Ztf8g/RiVnUKw==", - "dev": true, - "dependencies": { - "glob": "^10.2.2", - "jasmine-core": "~5.1.0" - }, - "bin": { - "jasmine": "bin/jasmine.js" - } - }, - "node_modules/jasmine-core": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.1.1.tgz", - "integrity": "sha512-UrzO3fL7nnxlQXlvTynNAenL+21oUQRlzqQFsA2U11ryb4+NLOCOePZ70PTojEaUKhiFugh7dG0Q+I58xlPdWg==", - "dev": true - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/lru-cache": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", - "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } - }, - "node_modules/map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/meow": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", - "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", - "dev": true, - "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize": "^1.2.0", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/meow/node_modules/type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", - "dev": true, - "dependencies": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, - "node_modules/ncp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", - "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", - "dev": true, - "bin": { - "ncp": "bin/ncp" - } - }, - "node_modules/normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", - "dev": true, - "dependencies": { - "lru-cache": "^9.1.1 || ^10.0.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", - "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", - "dev": true, - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg-up/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg-up/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "node_modules/read-pkg/node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/read-pkg/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/restore-cursor/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", - "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", - "dev": true - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/synckit": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", - "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", - "dev": true, - "dependencies": { - "@pkgr/core": "^0.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, - "node_modules/synckit/node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true - }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ts-api-utils": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", - "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", - "dev": true, - "engines": { - "node": ">=16.13.0" - }, - "peerDependencies": { - "typescript": ">=4.2.0" - } - }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/write-file-atomic/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "": { + "name": "csp_evaluator", + "version": "2.0.0", + "license": "Apache-2.0", + "devDependencies": { + "@types/jasmine": "^5.1.4", + "@types/node": "20.8.2", + "@typescript-eslint/eslint-plugin": "^6.17.0", + "@typescript-eslint/parser": "^6.17.0", + "eslint": "^8.56.0", + "gts": "^5.2.0", + "jasmine": "^5.1.0", + "typescript": "~5.2.0" } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "dev": true + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.0.tgz", + "integrity": "sha512-Zwq5OCzuwJC2jwqmpEQt7Ds1DTi6BWSwoGkbb1n9pO3hzb35BoJELx7c0T23iDkBGkh2e7tvOtjF3tr3OaQHDQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@types/jasmine": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-5.1.4.tgz", + "integrity": "sha512-px7OMFO/ncXxixDe1zR13V1iycqWae0MxTaw62RpFlksUi5QuNWgQJFkTQjIOvrmutJbI7Fp2Y2N1F6D2R4G6w==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.8.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.2.tgz", + "integrity": "sha512-Vvycsc9FQdwhxE3y3DzeIxuEJbWGDsnrxvMADzTDF/lcdR9/K+AQIeAghTQsHtotg/q0j3WEOYS/jQgSdWue3w==", + "dev": true + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", + "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.18.0.tgz", + "integrity": "sha512-3lqEvQUdCozi6d1mddWqd+kf8KxmGq2Plzx36BlkjuQe3rSTm/O98cLf0A4uDO+a5N1KD2SeEEl6fW97YHY+6w==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.18.0", + "@typescript-eslint/type-utils": "6.18.0", + "@typescript-eslint/utils": "6.18.0", + "@typescript-eslint/visitor-keys": "6.18.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.18.0.tgz", + "integrity": "sha512-v6uR68SFvqhNQT41frCMCQpsP+5vySy6IdgjlzUWoo7ALCnpaWYcz/Ij2k4L8cEsL0wkvOviCMpjmtRtHNOKzA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.18.0", + "@typescript-eslint/types": "6.18.0", + "@typescript-eslint/typescript-estree": "6.18.0", + "@typescript-eslint/visitor-keys": "6.18.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.18.0.tgz", + "integrity": "sha512-o/UoDT2NgOJ2VfHpfr+KBY2ErWvCySNUIX/X7O9g8Zzt/tXdpfEU43qbNk8LVuWUT2E0ptzTWXh79i74PP0twA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.18.0", + "@typescript-eslint/visitor-keys": "6.18.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.18.0.tgz", + "integrity": "sha512-ZeMtrXnGmTcHciJN1+u2CigWEEXgy1ufoxtWcHORt5kGvpjjIlK9MUhzHm4RM8iVy6dqSaZA/6PVkX6+r+ChjQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.18.0", + "@typescript-eslint/utils": "6.18.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.18.0.tgz", + "integrity": "sha512-/RFVIccwkwSdW/1zeMx3hADShWbgBxBnV/qSrex6607isYjj05t36P6LyONgqdUrNLl5TYU8NIKdHUYpFvExkA==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.18.0.tgz", + "integrity": "sha512-klNvl+Ql4NsBNGB4W9TZ2Od03lm7aGvTbs0wYaFYsplVPhr+oeXjlPZCDI4U9jgJIDK38W1FKhacCFzCC+nbIg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.18.0", + "@typescript-eslint/visitor-keys": "6.18.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.18.0.tgz", + "integrity": "sha512-wiKKCbUeDPGaYEYQh1S580dGxJ/V9HI7K5sbGAVklyf+o5g3O+adnS4UNJajplF4e7z2q0uVBaTdT/yLb4XAVA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.18.0", + "@typescript-eslint/types": "6.18.0", + "@typescript-eslint/typescript-estree": "6.18.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.18.0.tgz", + "integrity": "sha512-1wetAlSZpewRDb2h9p/Q8kRjdGuqdTAQbkJIOUMLug2LBLG+QOjiWoSj6/3B/hA9/tVTFFdtiKvAYoYnSRW/RA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.18.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "dev": true, + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", + "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "dev": true, + "dependencies": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=4.19.1" + } + }, + "node_modules/eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "dev": true, + "dependencies": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "peerDependencies": { + "eslint": ">=5.16.0" + } + }, + "node_modules/eslint-plugin-node/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-node/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-node/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.0.tgz", + "integrity": "sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.5" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", + "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/gts": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/gts/-/gts-5.2.0.tgz", + "integrity": "sha512-25qOnePUUX7upFc4ycqWersDBq+o1X6hXUTW56JOWCxPYKJXQ1RWzqT9q+2SU3LfPKJf+4sz4Dw3VT0p96Kv6g==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "5.62.0", + "@typescript-eslint/parser": "5.62.0", + "chalk": "^4.1.2", + "eslint": "8.50.0", + "eslint-config-prettier": "9.0.0", + "eslint-plugin-node": "11.1.0", + "eslint-plugin-prettier": "5.0.0", + "execa": "^5.0.0", + "inquirer": "^7.3.3", + "json5": "^2.1.3", + "meow": "^9.0.0", + "ncp": "^2.0.0", + "prettier": "3.0.3", + "rimraf": "3.0.2", + "write-file-atomic": "^4.0.0" + }, + "bin": { + "gts": "build/src/cli.js" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "typescript": ">=3" + } + }, + "node_modules/gts/node_modules/@eslint/js": { + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.50.0.tgz", + "integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/gts/node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/gts/node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/gts/node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/gts/node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/gts/node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/gts/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/gts/node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/gts/node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/gts/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/gts/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/gts/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/gts/node_modules/eslint": { + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.50.0.tgz", + "integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.50.0", + "@humanwhocodes/config-array": "^0.11.11", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/gts/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/gts/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/gts/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/inquirer": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.19", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/inquirer/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/inquirer/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jasmine": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-5.1.0.tgz", + "integrity": "sha512-prmJlC1dbLhti4nE4XAPDWmfJesYO15sjGXVp7Cs7Ym5I9Xtwa/hUHxxJXjnpfLO72+ySttA0Ztf8g/RiVnUKw==", + "dev": true, + "dependencies": { + "glob": "^10.2.2", + "jasmine-core": "~5.1.0" + }, + "bin": { + "jasmine": "bin/jasmine.js" + } + }, + "node_modules/jasmine-core": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.1.1.tgz", + "integrity": "sha512-UrzO3fL7nnxlQXlvTynNAenL+21oUQRlzqQFsA2U11ryb4+NLOCOePZ70PTojEaUKhiFugh7dG0Q+I58xlPdWg==", + "dev": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", + "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "dev": true, + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "node_modules/ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", + "dev": true, + "bin": { + "ncp": "bin/ncp" + } + }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dev": true, + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-pkg/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", + "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", + "dev": true + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/synckit": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", + "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/synckit/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } } } diff --git a/package.json b/package.json index a8ad6c6..12788b5 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "csp_evaluator", "version": "2.0.0", "description": "Evaluate Content Security Policies for a wide range of bypasses and weaknesses", - "main": "dist/evaluator.js", + "main": "build/evaluator.js", "keywords": [ "csp", "content security policy", @@ -17,6 +17,9 @@ "type": "git", "url": "https://github.com/google/csp-evaluator" }, + "engines": { + "node": ">=10.0.0" + }, "scripts": { "test": "tsc && npx jasmine --config=jasmine.json", "lint": "gts lint", diff --git a/allowlist_bypasses/angular.ts b/src/allowlist_bypasses/angular.ts similarity index 98% rename from allowlist_bypasses/angular.ts rename to src/allowlist_bypasses/angular.ts index 99c7a83..1d2e4b5 100644 --- a/allowlist_bypasses/angular.ts +++ b/src/allowlist_bypasses/angular.ts @@ -17,7 +17,6 @@ * limitations under the License. */ - /** * Angular libraries on commonly allowlisted origins (e.g. CDNs) that would * allow a CSP bypass. @@ -56,5 +55,5 @@ export const URLS: string[] = [ '//twitter.github.io/labella.js/bower_components/angular/angular.min.js', '//cdn2-casinoroom.global.ssl.fastly.net/js/lib/angular-animate.min.js', '//www.adobe.com/devnet-apps/flashshowcase/lib/angular/angular.1.1.5.min.js', - '//cdn.bootcss.com/angular.js/1.2.0/angular.min.js' + '//cdn.bootcss.com/angular.js/1.2.0/angular.min.js', ]; diff --git a/allowlist_bypasses/flash.ts b/src/allowlist_bypasses/flash.ts similarity index 98% rename from allowlist_bypasses/flash.ts rename to src/allowlist_bypasses/flash.ts index e080a12..9088fd7 100644 --- a/allowlist_bypasses/flash.ts +++ b/src/allowlist_bypasses/flash.ts @@ -18,12 +18,11 @@ * limitations under the License. */ - /** * Domains that would allow a CSP bypass if allowlisted. * Only most common paths will be listed here. Hence there might still be other * paths on these domains that would allow a bypass. */ export const URLS: string[] = [ - '//ajax.googleapis.com/ajax/libs/yui/2.8.0r4/build/charts/assets/charts.swf' + '//ajax.googleapis.com/ajax/libs/yui/2.8.0r4/build/charts/assets/charts.swf', ]; diff --git a/allowlist_bypasses/json/angular.json b/src/allowlist_bypasses/json/angular.json similarity index 100% rename from allowlist_bypasses/json/angular.json rename to src/allowlist_bypasses/json/angular.json diff --git a/allowlist_bypasses/json/flash.json b/src/allowlist_bypasses/json/flash.json similarity index 100% rename from allowlist_bypasses/json/flash.json rename to src/allowlist_bypasses/json/flash.json diff --git a/allowlist_bypasses/json/jsonp.json b/src/allowlist_bypasses/json/jsonp.json similarity index 100% rename from allowlist_bypasses/json/jsonp.json rename to src/allowlist_bypasses/json/jsonp.json diff --git a/allowlist_bypasses/jsonp.ts b/src/allowlist_bypasses/jsonp.ts similarity index 97% rename from allowlist_bypasses/jsonp.ts rename to src/allowlist_bypasses/jsonp.ts index a65712b..a9dc4f2 100644 --- a/allowlist_bypasses/jsonp.ts +++ b/src/allowlist_bypasses/jsonp.ts @@ -23,19 +23,18 @@ * limitations under the License. */ - /** * Some JSONP-like bypasses only work if the CSP allows 'eval()'. */ export const NEEDS_EVAL: string[] = [ - 'googletagmanager.com', 'www.googletagmanager.com', - - 'www.googleadservices.com', 'google-analytics.com', - 'ssl.google-analytics.com', 'www.google-analytics.com' + 'googletagmanager.com', + 'www.googletagmanager.com', + 'www.googleadservices.com', + 'google-analytics.com', + 'ssl.google-analytics.com', + 'www.google-analytics.com', ]; - - /** * JSONP endpoints on commonly allowlisted origins (e.g. CDNs) that would allow * a CSP bypass. @@ -162,5 +161,5 @@ export const URLS: string[] = [ '//maps.google.ru/maps/vt', '//c1n2.hypercomments.com/stream/subscribe', '//rec.ydf.yandex.ru/cookie', - '//cdn.jsdelivr.net' + '//cdn.jsdelivr.net', ]; diff --git a/checks/checker.ts b/src/checks/checker.ts similarity index 78% rename from checks/checker.ts rename to src/checks/checker.ts index 1729478..9783cf9 100644 --- a/checks/checker.ts +++ b/src/checks/checker.ts @@ -2,8 +2,8 @@ * @fileoverview Shared interfaces for functions that check CSP policies. */ -import { Csp } from '../csp'; -import { Finding } from '../finding'; +import {Csp} from '../csp'; +import {Finding} from '../finding'; /** * A function that checks a list of Csps for problems and returns an unordered diff --git a/checks/parser_checks.ts b/src/checks/parser_checks.ts similarity index 72% rename from checks/parser_checks.ts rename to src/checks/parser_checks.ts index 884eeaa..ce73911 100644 --- a/checks/parser_checks.ts +++ b/src/checks/parser_checks.ts @@ -19,10 +19,18 @@ * limitations under the License. */ -import { Csp, Directive, Keyword, TrustedTypesSink, isDirective, isHash, isKeyword, isNonce } from '../csp'; - -import { Finding, Severity, Type } from '../finding'; - +import { + Csp, + Directive, + Keyword, + TrustedTypesSink, + isDirective, + isHash, + isKeyword, + isNonce, +} from '../csp'; + +import {Finding, Severity, Type} from '../finding'; /** * Checks if the csp contains invalid directives. @@ -41,16 +49,25 @@ export function checkUnknownDirective(parsedCsps: Csp): Finding[] { // Directive is known. continue; } - + if (directive.endsWith(':')) { - findings.push(new Finding( - Type.UNKNOWN_DIRECTIVE, 'CSP directives don\'t end with a colon.', - Severity.SYNTAX, directive)); + findings.push( + new Finding( + Type.UNKNOWN_DIRECTIVE, + "CSP directives don't end with a colon.", + Severity.SYNTAX, + directive + ) + ); } else { - findings.push(new Finding( + findings.push( + new Finding( Type.UNKNOWN_DIRECTIVE, 'Directive "' + directive + '" is not a known CSP directive.', - Severity.SYNTAX, directive)); + Severity.SYNTAX, + directive + ) + ); } } } @@ -58,7 +75,6 @@ export function checkUnknownDirective(parsedCsps: Csp): Finding[] { return findings; } - /** * Checks if semicolons are missing in the csp. * @@ -80,11 +96,18 @@ export function checkMissingSemicolon(parsedCsps: Csp): Finding[] { // If we find a known directive inside a directive value, it is very // likely that a semicolon was forgoten. if (isDirective(value)) { - findings.push(new Finding( + findings.push( + new Finding( Type.MISSING_SEMICOLON, 'Did you forget the semicolon? ' + - '"' + value + '" seems to be a directive, not a value.', - Severity.SYNTAX, directive, value)); + '"' + + value + + '" seems to be a directive, not a value.', + Severity.SYNTAX, + directive, + value + ) + ); } } } @@ -93,7 +116,6 @@ export function checkMissingSemicolon(parsedCsps: Csp): Finding[] { return findings; } - /** * Checks if csp contains invalid keywords. * @@ -104,8 +126,7 @@ export function checkMissingSemicolon(parsedCsps: Csp): Finding[] { */ export function checkInvalidKeyword(parsedCsps: Csp): Finding[] { const findings: Finding[] = []; - const keywordsNoTicks = - Object.values(Keyword).map((k) => k.replace(/'/g, '')); + const keywordsNoTicks = Object.values(Keyword).map(k => k.replace(/'/g, '')); for (const cspChecked of parsedCsps.directives) { for (const [directive, directiveValues] of Object.entries(cspChecked)) { @@ -114,19 +135,26 @@ export function checkInvalidKeyword(parsedCsps: Csp): Finding[] { } for (const value of directiveValues) { // Check if single ticks have been forgotten. - if (keywordsNoTicks.some((k) => k === value) || - value.startsWith('nonce-') || - value.match(/^(sha256|sha384|sha512)-/)) { - findings.push(new Finding( + if ( + keywordsNoTicks.some(k => k === value) || + value.startsWith('nonce-') || + value.match(/^(sha256|sha384|sha512)-/) + ) { + findings.push( + new Finding( Type.INVALID_KEYWORD, 'Did you forget to surround "' + value + '" with single-ticks?', - Severity.SYNTAX, directive, value)); + Severity.SYNTAX, + directive, + value + ) + ); continue; } // Continue, if the value doesn't start with single tick. // All CSP keywords start with a single tick. - if (!value.startsWith('\'')) { + if (!value.startsWith("'")) { continue; } @@ -137,7 +165,7 @@ export function checkInvalidKeyword(parsedCsps: Csp): Finding[] { } } else if (directive === Directive.TRUSTED_TYPES) { // Continue, if it's an allowed Trusted Types keyword. - if (value === '\'allow-duplicates\'' || value === '\'none\'') { + if (value === "'allow-duplicates'" || value === "'none'") { continue; } } else { @@ -147,13 +175,18 @@ export function checkInvalidKeyword(parsedCsps: Csp): Finding[] { } } - findings.push(new Finding( - Type.INVALID_KEYWORD, value + ' seems to be an invalid CSP keyword.', - Severity.SYNTAX, directive, value)); + findings.push( + new Finding( + Type.INVALID_KEYWORD, + value + ' seems to be an invalid CSP keyword.', + Severity.SYNTAX, + directive, + value + ) + ); } } } return findings; } - diff --git a/checks/security_checks.ts b/src/checks/security_checks.ts similarity index 66% rename from checks/security_checks.ts rename to src/checks/security_checks.ts index dfa99a2..107a70a 100644 --- a/checks/security_checks.ts +++ b/src/checks/security_checks.ts @@ -20,17 +20,19 @@ import * as angular from '../allowlist_bypasses/angular'; import * as flash from '../allowlist_bypasses/flash'; import * as jsonp from '../allowlist_bypasses/jsonp'; -import { Csp, Directive, Keyword, isNonce, isUrlScheme } from '../csp'; -import { Finding, Severity, Type } from '../finding'; +import {Csp, Directive, Keyword, isNonce, isUrlScheme} from '../csp'; +import {Finding, Severity, Type} from '../finding'; import * as utils from '../utils'; - /** * A list of CSP directives that can allow XSS vulnerabilities if they fail * validation. */ -export const DIRECTIVES_CAUSING_XSS: Directive[] = - [Directive.SCRIPT_SRC, Directive.OBJECT_SRC, Directive.BASE_URI]; +export const DIRECTIVES_CAUSING_XSS: Directive[] = [ + Directive.SCRIPT_SRC, + Directive.OBJECT_SRC, + Directive.BASE_URI, +]; /** * A list of URL schemes that can allow XSS vulnerabilities when requests to @@ -38,7 +40,6 @@ export const DIRECTIVES_CAUSING_XSS: Directive[] = */ export const URL_SCHEMES_CAUSING_XSS: string[] = ['data:', 'http:', 'https:']; - /** * Checks if passed csp allows inline scripts. * Findings of this check are critical and FP free. @@ -55,24 +56,30 @@ export const URL_SCHEMES_CAUSING_XSS: string[] = ['data:', 'http:', 'https:']; export function checkScriptUnsafeInline(effectiveCsp: Csp): Finding[] { const findings: Finding[] = []; - const directiveName = effectiveCsp.getEffectiveDirective(Directive.SCRIPT_SRC); + const directiveName = effectiveCsp.getEffectiveDirective( + Directive.SCRIPT_SRC + ); for (const cspChecked of effectiveCsp.directives) { const values: string[] = cspChecked[directiveName] || []; // Check if unsafe-inline is present. if (values.includes(Keyword.UNSAFE_INLINE)) { - findings.push(new Finding( + findings.push( + new Finding( Type.SCRIPT_UNSAFE_INLINE, - `'unsafe-inline' allows the execution of unsafe in-page scripts ` + - 'and event handlers.', - Severity.HIGH, directiveName, Keyword.UNSAFE_INLINE)); + "'unsafe-inline' allows the execution of unsafe in-page scripts " + + 'and event handlers.', + Severity.HIGH, + directiveName, + Keyword.UNSAFE_INLINE + ) + ); } } return findings; } - /** * Checks if passed csp allows inline styles. * Findings of this check are critical and FP free. @@ -95,17 +102,21 @@ export function checkStyleUnsafeInline(effectiveCsp: Csp): Finding[] { // Check if unsafe-inline is present. if (values.includes(Keyword.UNSAFE_INLINE)) { - findings.push(new Finding( + findings.push( + new Finding( Type.STYLE_UNSAFE_INLINE, 'Unsafe inline stylesheet are allowed. This could help social engineering attacks in case of content injection.', - Severity.MEDIUM, directiveName, Keyword.UNSAFE_INLINE)); + Severity.MEDIUM, + directiveName, + Keyword.UNSAFE_INLINE + ) + ); } } return findings; } - /** * Checks if passed csp allows web assembly eval in scripts. * Findings of this check have a medium severity and are FP free. @@ -124,11 +135,16 @@ export function checkScriptWasmUnsafeEval(parsedCsps: Csp): Finding[] { // Check if wasm-unsafe-eval is present. if (values.includes(Keyword.WASM_UNSAFE_EVAL)) { - findings.push(new Finding( + findings.push( + new Finding( Type.SCRIPT_WASM_UNSAFE_EVAL, - `'wasm-unsafe-eval' allows the execution of web assembly code injected in functions ` + - 'such as WebAssembly.compile().', - Severity.MEDIUM_MAYBE, directiveName, Keyword.UNSAFE_EVAL)); + "'wasm-unsafe-eval' allows the execution of web assembly code injected in functions " + + 'such as WebAssembly.compile().', + Severity.MEDIUM_MAYBE, + directiveName, + Keyword.UNSAFE_EVAL + ) + ); } } @@ -151,17 +167,21 @@ export function checkScriptUnsafeEval(parsedCsps: Csp): Finding[] { // Check if unsafe-eval is present. if (values.includes(Keyword.UNSAFE_EVAL)) { - return [new Finding( + return [ + new Finding( Type.SCRIPT_UNSAFE_EVAL, - `'unsafe-eval' allows the execution of code injected into DOM APIs ` + - 'such as eval().', - Severity.MEDIUM_MAYBE, directiveName, Keyword.UNSAFE_EVAL)]; + "'unsafe-eval' allows the execution of code injected into DOM APIs " + + 'such as eval().', + Severity.MEDIUM_MAYBE, + directiveName, + Keyword.UNSAFE_EVAL + ), + ]; } } return []; } - /** * Checks if plain URL schemes (e.g. http:) are allowed in sensitive directives. * Findings of this check have a high severity and are FP free. @@ -174,17 +194,27 @@ export function checkScriptUnsafeEval(parsedCsps: Csp): Finding[] { export function checkPlainUrlSchemes(parsedCsps: Csp): Finding[] { const violations: Finding[] = []; - const directivesToCheck = parsedCsps.getEffectiveDirectives(DIRECTIVES_CAUSING_XSS); + const directivesToCheck = parsedCsps.getEffectiveDirectives( + DIRECTIVES_CAUSING_XSS + ); for (const cspChecked of parsedCsps.directives) { for (const directive of directivesToCheck) { const values = cspChecked[directive] || []; for (const value of values) { if (URL_SCHEMES_CAUSING_XSS.includes(value)) { - violations.push(new Finding( + violations.push( + new Finding( Type.PLAIN_URL_SCHEMES, - value + ' URI in ' + directive + ' allows the execution of ' + - 'unsafe scripts.', - Severity.HIGH, directive, value)); + value + + ' URI in ' + + directive + + ' allows the execution of ' + + 'unsafe scripts.', + Severity.HIGH, + directive, + value + ) + ); } } } @@ -193,7 +223,6 @@ export function checkPlainUrlSchemes(parsedCsps: Csp): Finding[] { return violations; } - /** * Checks if plain URL schemes (e.g. http:) are allowed in the form-action directive. * Findings of this check have a low severity and are FP free. @@ -209,11 +238,16 @@ export function checkPlainUrlSchemesInFormActions(parsedCsps: Csp): Finding[] { for (const cspChecked of parsedCsps.directives) { const values = cspChecked[Directive.FORM_ACTION] || []; for (const value of values) { - if (value == 'https:' || value == 'http:') { - violations.push(new Finding( + if (value === 'https:' || value === 'http:') { + violations.push( + new Finding( Type.PLAIN_URL_SCHEMES, 'Form actions only has protocol restrictions and still allows arbitrary hosts. This could help social engineering attacks in case of content injection.', - Severity.LOW, Directive.FORM_ACTION, value)); + Severity.LOW, + Directive.FORM_ACTION, + value + ) + ); } } } @@ -221,7 +255,6 @@ export function checkPlainUrlSchemesInFormActions(parsedCsps: Csp): Finding[] { return violations; } - /** * Checks if csp contains wildcards in sensitive directives. * Findings of this check have a high severity and are FP free. @@ -234,16 +267,24 @@ export function checkPlainUrlSchemesInFormActions(parsedCsps: Csp): Finding[] { export function checkWildcards(parsedCsps: Csp): Finding[] { const violations: Finding[] = []; - const directivesToCheck = parsedCsps.getEffectiveDirectives(DIRECTIVES_CAUSING_XSS); + const directivesToCheck = parsedCsps.getEffectiveDirectives( + DIRECTIVES_CAUSING_XSS + ); for (const cspChecked of parsedCsps.directives) { for (const directive of directivesToCheck) { const values = cspChecked[directive] || []; for (const value of values) { const url = utils.getSchemeFreeUrl(value); if (url === '*') { - violations.push(new Finding( - Type.PLAIN_WILDCARD, directive + ` should not allow '*' as source`, - Severity.HIGH, directive, value)); + violations.push( + new Finding( + Type.PLAIN_WILDCARD, + directive + " should not allow '*' as source", + Severity.HIGH, + directive, + value + ) + ); continue; } } @@ -258,7 +299,7 @@ export function checkWildcards(parsedCsps: Csp): Finding[] { * default-src. */ export function checkMissingObjectSrcDirective(parsedCsps: Csp): Finding[] { - let objectRestrictions: string[]|undefined = []; + let objectRestrictions: string[] | undefined = []; for (const cspChecked of parsedCsps.directives) { if (Directive.OBJECT_SRC in cspChecked) { @@ -271,10 +312,14 @@ export function checkMissingObjectSrcDirective(parsedCsps: Csp): Finding[] { if (objectRestrictions !== undefined && objectRestrictions.length >= 1) { return []; } - return [new Finding( + return [ + new Finding( Type.MISSING_DIRECTIVES, - `Missing object-src allows the injection of plugins which can execute JavaScript. Can you set it to 'none'?`, - Severity.HIGH, Directive.OBJECT_SRC)]; + "Missing object-src allows the injection of plugins which can execute JavaScript. Can you set it to 'none'?", + Severity.HIGH, + Directive.OBJECT_SRC + ), + ]; } /** @@ -282,14 +327,21 @@ export function checkMissingObjectSrcDirective(parsedCsps: Csp): Finding[] { */ export function checkMissingScriptSrcDirective(parsedCsps: Csp): Finding[] { for (const cspChecked of parsedCsps.directives) { - if (Directive.SCRIPT_SRC in cspChecked || - Directive.DEFAULT_SRC in cspChecked) { + if ( + Directive.SCRIPT_SRC in cspChecked || + Directive.DEFAULT_SRC in cspChecked + ) { return []; } } - return [new Finding( - Type.MISSING_DIRECTIVES, 'script-src directive is missing.', - Severity.HIGH, Directive.SCRIPT_SRC)]; + return [ + new Finding( + Type.MISSING_DIRECTIVES, + 'script-src directive is missing.', + Severity.HIGH, + Directive.SCRIPT_SRC + ), + ]; } /** @@ -297,17 +349,23 @@ export function checkMissingScriptSrcDirective(parsedCsps: Csp): Finding[] { */ export function checkMissingStyleSrcDirective(parsedCsps: Csp): Finding[] { for (const cspChecked of parsedCsps.directives) { - if (Directive.STYLE_SRC in cspChecked || - Directive.DEFAULT_SRC in cspChecked) { + if ( + Directive.STYLE_SRC in cspChecked || + Directive.DEFAULT_SRC in cspChecked + ) { return []; } } - return [new Finding( - Type.MISSING_DIRECTIVES, 'No stylesheet restrictions are present. This could help social engineering attacks in case of content injection.', - Severity.LOW, Directive.STYLE_SRC)]; + return [ + new Finding( + Type.MISSING_DIRECTIVES, + 'No stylesheet restrictions are present. This could help social engineering attacks in case of content injection.', + Severity.LOW, + Directive.STYLE_SRC + ), + ]; } - /** * Checks if form-action is restricted. */ @@ -317,9 +375,14 @@ export function checkMissingFormActionDirective(parsedCsps: Csp): Finding[] { return []; } } - return [new Finding( - Type.MISSING_DIRECTIVES, 'No form action restrictions are present. This could help social engineering attacks in case of content injection.', - Severity.LOW, Directive.FORM_ACTION)]; + return [ + new Finding( + Type.MISSING_DIRECTIVES, + 'No form action restrictions are present. This could help social engineering attacks in case of content injection.', + Severity.LOW, + Directive.FORM_ACTION + ), + ]; } /** @@ -329,35 +392,39 @@ export function checkMissingFormActionDirective(parsedCsps: Csp): Finding[] { export function checkMissingBaseUriDirective(parsedCsps: Csp): Finding[] { // base-uri can be used to bypass nonce based CSPs and hash based CSPs that // use strict dynamic - const needsBaseUri = ( + const needsBaseUri = parsedCsps.policyHasScriptNonces() || - (parsedCsps.policyHasScriptHashes() && parsedCsps.policyHasStrictDynamic()) - ); + (parsedCsps.policyHasScriptHashes() && parsedCsps.policyHasStrictDynamic()); if (needsBaseUri) { let hasBaseUri: boolean = false; for (const currentCsp of parsedCsps.directives) { - if (Directive.BASE_URI in currentCsp) { + if (Directive.BASE_URI in currentCsp) { hasBaseUri = true; } } if (!hasBaseUri) { - const description = 'Missing base-uri allows the injection of base tags. ' + - 'They can be used to set the base URL for all relative (script) ' + - 'URLs to an attacker controlled domain. ' + - `Can you set it to 'none' or 'self'?`; - return [new Finding( - Type.MISSING_DIRECTIVES, description, Severity.HIGH, - Directive.BASE_URI)]; + const description = + 'Missing base-uri allows the injection of base tags. ' + + 'They can be used to set the base URL for all relative (script) ' + + 'URLs to an attacker controlled domain. ' + + "Can you set it to 'none' or 'self'?"; + return [ + new Finding( + Type.MISSING_DIRECTIVES, + description, + Severity.HIGH, + Directive.BASE_URI + ), + ]; } } - + return []; } - /** * Checks if all necessary directives for preventing XSS are set. * Findings of this check have a high severity and are FP free. @@ -373,11 +440,10 @@ export function checkMissingDirectives(parsedCsps: Csp): Finding[] { ...checkMissingScriptSrcDirective(parsedCsps), ...checkMissingBaseUriDirective(parsedCsps), ...checkMissingStyleSrcDirective(parsedCsps), - ...checkMissingFormActionDirective(parsedCsps) + ...checkMissingFormActionDirective(parsedCsps), ]; } - /** * Checks if allowlisted origins are bypassable by JSONP/Angular endpoints. * High severity findings of this check are FP free. @@ -390,7 +456,9 @@ export function checkMissingDirectives(parsedCsps: Csp): Finding[] { export function checkScriptAllowlistBypass(parsedCsps: Csp): Finding[] { const violations: Finding[] = []; - const effectiveScriptSrcDirective = parsedCsps.getEffectiveDirective(Directive.SCRIPT_SRC); + const effectiveScriptSrcDirective = parsedCsps.getEffectiveDirective( + Directive.SCRIPT_SRC + ); for (const cspChecked of parsedCsps.directives) { const scriptSrcValues = cspChecked[effectiveScriptSrcDirective] || []; if (scriptSrcValues.includes(Keyword.NONE)) { @@ -399,16 +467,21 @@ export function checkScriptAllowlistBypass(parsedCsps: Csp): Finding[] { for (const value of scriptSrcValues) { if (value === Keyword.SELF) { - violations.push(new Finding( + violations.push( + new Finding( Type.SCRIPT_ALLOWLIST_BYPASS, - `'self' can be problematic if you host JSONP, AngularJS or user ` + - 'uploaded files.', - Severity.MEDIUM_MAYBE, effectiveScriptSrcDirective, value)); + "'self' can be problematic if you host JSONP, AngularJS or user " + + 'uploaded files.', + Severity.MEDIUM_MAYBE, + effectiveScriptSrcDirective, + value + ) + ); continue; } // Ignore keywords, nonces and hashes (they start with a single quote). - if (value.startsWith('\'')) { + if (value.startsWith("'")) { continue; } @@ -441,21 +514,33 @@ export function checkScriptAllowlistBypass(parsedCsps: Csp): Finding[] { } if (angularBypass) { bypassDomain = angularBypass.hostname; - bypassTxt += (bypassTxt.trim() === '') ? '' : ' and'; + bypassTxt += bypassTxt.trim() === '' ? '' : ' and'; bypassTxt += ' Angular libraries'; } - violations.push(new Finding( + violations.push( + new Finding( Type.SCRIPT_ALLOWLIST_BYPASS, - bypassDomain + ' is known to host' + bypassTxt + - ' which allow to bypass this CSP.', - Severity.HIGH, effectiveScriptSrcDirective, value)); + bypassDomain + + ' is known to host' + + bypassTxt + + ' which allow to bypass this CSP.', + Severity.HIGH, + effectiveScriptSrcDirective, + value + ) + ); } else { - violations.push(new Finding( + violations.push( + new Finding( Type.SCRIPT_ALLOWLIST_BYPASS, - `No bypass found; make sure that this URL doesn't serve JSONP ` + - 'replies or Angular libraries.', - Severity.MEDIUM_MAYBE, effectiveScriptSrcDirective, value)); + "No bypass found; make sure that this URL doesn't serve JSONP " + + 'replies or Angular libraries.', + Severity.MEDIUM_MAYBE, + effectiveScriptSrcDirective, + value + ) + ); } } } @@ -463,7 +548,6 @@ export function checkScriptAllowlistBypass(parsedCsps: Csp): Finding[] { return violations; } - /** * Checks if allowlisted object-src origins are bypassable. * Findings of this check have a high severity and are FP free. @@ -476,7 +560,9 @@ export function checkScriptAllowlistBypass(parsedCsps: Csp): Finding[] { export function checkFlashObjectAllowlistBypass(parsedCsps: Csp): Finding[] { const violations = []; - const effectiveObjectSrcDirective = parsedCsps.getEffectiveDirective(Directive.OBJECT_SRC); + const effectiveObjectSrcDirective = parsedCsps.getEffectiveDirective( + Directive.OBJECT_SRC + ); for (const cspChecked of parsedCsps.directives) { const objectSrcValues = cspChecked[effectiveObjectSrcDirective] || []; @@ -496,16 +582,26 @@ export function checkFlashObjectAllowlistBypass(parsedCsps: Csp): Finding[] { const flashBypass = utils.matchWildcardUrls(url, flash.URLS); if (flashBypass) { - violations.push(new Finding( + violations.push( + new Finding( Type.OBJECT_ALLOWLIST_BYPASS, flashBypass.hostname + - ' is known to host Flash files which allow to bypass this CSP.', - Severity.HIGH, effectiveObjectSrcDirective, value)); + ' is known to host Flash files which allow to bypass this CSP.', + Severity.HIGH, + effectiveObjectSrcDirective, + value + ) + ); } else if (effectiveObjectSrcDirective === Directive.OBJECT_SRC) { - violations.push(new Finding( + violations.push( + new Finding( Type.OBJECT_ALLOWLIST_BYPASS, - `Can you restrict object-src to 'none' only?`, Severity.MEDIUM_MAYBE, - effectiveObjectSrcDirective, value)); + "Can you restrict object-src to 'none' only?", + Severity.MEDIUM_MAYBE, + effectiveObjectSrcDirective, + value + ) + ); } } } @@ -555,17 +651,30 @@ export function checkIpSource(parsedCsps: Csp): Finding[] { // Check if localhost. // See 4.8 in https://www.w3.org/TR/CSP2/#match-source-expression if (host === '127.0.0.1') { - violations.push(new Finding( + violations.push( + new Finding( Type.IP_SOURCE, - directive + ' directive allows localhost as source. ' + - 'Please make sure to remove this in production environments.', - Severity.INFO, directive, value)); + directive + + ' directive allows localhost as source. ' + + 'Please make sure to remove this in production environments.', + Severity.INFO, + directive, + value + ) + ); } else { - violations.push(new Finding( + violations.push( + new Finding( Type.IP_SOURCE, - directive + ' directive has an IP-Address as source: ' + host + - ' (will be ignored by browsers!). ', - Severity.INFO, directive, value)); + directive + + ' directive has an IP-Address as source: ' + + host + + ' (will be ignored by browsers!). ', + Severity.INFO, + directive, + value + ) + ); } } } @@ -577,7 +686,6 @@ export function checkIpSource(parsedCsps: Csp): Finding[] { return violations; } - /** * Checks if csp contains directives that are deprecated in CSP3. * Findings of this check are informal only and are FP free. @@ -593,35 +701,46 @@ export function checkDeprecatedDirective(parsedCsps: Csp): Finding[] { for (const cspChecked of parsedCsps.directives) { // More details: https://www.chromestatus.com/feature/5769374145183744 if (Directive.REFLECTED_XSS in cspChecked) { - violations.push(new Finding( + violations.push( + new Finding( Type.DEPRECATED_DIRECTIVE, 'reflected-xss is deprecated since CSP2. ' + - 'Please, use the X-XSS-Protection header instead.', - Severity.INFO, Directive.REFLECTED_XSS)); + 'Please, use the X-XSS-Protection header instead.', + Severity.INFO, + Directive.REFLECTED_XSS + ) + ); } // More details: https://www.chromestatus.com/feature/5680800376815616 if (Directive.REFERRER in cspChecked) { - violations.push(new Finding( + violations.push( + new Finding( Type.DEPRECATED_DIRECTIVE, 'referrer is deprecated since CSP2. ' + - 'Please, use the Referrer-Policy header instead.', - Severity.INFO, Directive.REFERRER)); + 'Please, use the Referrer-Policy header instead.', + Severity.INFO, + Directive.REFERRER + ) + ); } // More details: https://github.com/w3c/webappsec-csp/pull/327 if (Directive.DISOWN_OPENER in cspChecked) { - violations.push(new Finding( + violations.push( + new Finding( Type.DEPRECATED_DIRECTIVE, 'disown-opener is deprecated since CSP3. ' + - 'Please, use the Cross Origin Opener Policy header instead.', - Severity.INFO, Directive.DISOWN_OPENER)); + 'Please, use the Cross Origin Opener Policy header instead.', + Severity.INFO, + Directive.DISOWN_OPENER + ) + ); } } return violations; } - /** * Checks if csp nonce is at least 8 characters long. * Findings of this check are of medium severity and are FP free. @@ -632,41 +751,52 @@ export function checkDeprecatedDirective(parsedCsps: Csp): Finding[] { * @param parsedCsp Parsed CSP. */ export function checkNonceLength(parsedCsps: Csp): Finding[] { - const noncePattern = new RegExp('^\'nonce-(.+)\'$'); + const noncePattern = new RegExp("^'nonce-(.+)'$"); const violations: Finding[] = []; for (const cspChecked of parsedCsps.directives) { utils.applyCheckFunktionToDirectives( - cspChecked, (directive, directiveValues) => { - for (const value of directiveValues) { - const match = value.match(noncePattern); - if (!match) { - continue; - } - // Not a nonce. - - const nonceValue = match[1]; - if (nonceValue.length < 8) { - violations.push(new Finding( - Type.NONCE_LENGTH, - 'Nonces should be at least 8 characters long.', Severity.MEDIUM, - directive, value)); - } + cspChecked, + (directive, directiveValues) => { + for (const value of directiveValues) { + const match = value.match(noncePattern); + if (!match) { + continue; + } + // Not a nonce. + + const nonceValue = match[1]; + if (nonceValue.length < 8) { + violations.push( + new Finding( + Type.NONCE_LENGTH, + 'Nonces should be at least 8 characters long.', + Severity.MEDIUM, + directive, + value + ) + ); + } - if (!isNonce(value, true)) { - violations.push(new Finding( - Type.NONCE_CHARSET, - 'Nonces should only use the base64 charset.', Severity.INFO, - directive, value)); - } + if (!isNonce(value, true)) { + violations.push( + new Finding( + Type.NONCE_CHARSET, + 'Nonces should only use the base64 charset.', + Severity.INFO, + directive, + value + ) + ); } - }); + } + } + ); } return violations; } - /** * Checks if CSP allows sourcing from http:// * Findings of this check are of medium severity and are FP free. @@ -684,36 +814,63 @@ export function checkSrcHttp(parsedCsps: Csp): Finding[] { for (const cspChecked of parsedCsps.directives) { utils.applyCheckFunktionToDirectives( - cspChecked, (directive, directiveValues) => { + cspChecked, + (directive, directiveValues) => { for (const value of directiveValues) { - if (value == 'https:') { + if (value === 'https:') { directivesForcingHttps.push(directive); } else if (value.startsWith('http://')) { // Check if reporting violations via http:// is allowed if (directive === Directive.REPORT_URI) { - violations.push(new Finding( - Type.SRC_HTTP, 'Use HTTPS to send violation reports securely.', Severity.MEDIUM, Directive.REPORT_URI, value)); + violations.push( + new Finding( + Type.SRC_HTTP, + 'Use HTTPS to send violation reports securely.', + Severity.MEDIUM, + Directive.REPORT_URI, + value + ) + ); } else { - directivesWithViolation[directive] = directivesWithViolation[directive] ? directivesWithViolation[directive] : []; + directivesWithViolation[directive] = directivesWithViolation[ + directive + ] + ? directivesWithViolation[directive] + : []; directivesWithViolation[directive].push(directive); } } } - }); + } + ); } for (const directive in directivesWithViolation) { for (const value in directivesWithViolation[directive]) { if (directive in directivesForcingHttps) { - violations.push(new Finding( - Type.CONFLICTING_DIRECTIVES, 'Resources are only allowed over HTTPS but HTTP hosts are specified.', Severity.LOW, directive, value)); + violations.push( + new Finding( + Type.CONFLICTING_DIRECTIVES, + 'Resources are only allowed over HTTPS but HTTP hosts are specified.', + Severity.LOW, + directive, + value + ) + ); } else { - violations.push(new Finding( - Type.SRC_HTTP, 'Allow only resources downloaded over HTTPS.', Severity.MEDIUM, directive, value)); + violations.push( + new Finding( + Type.SRC_HTTP, + 'Allow only resources downloaded over HTTPS.', + Severity.MEDIUM, + directive, + value + ) + ); } } } - + return violations; } @@ -731,17 +888,25 @@ export function checkHasConfiguredReporting(parsedCsps: Csp): Finding[] { const reportToValues: string[] = cspChecked[Directive.REPORT_TO] || []; if (reportToValues.length > 0) { - findings.push(new Finding( + findings.push( + new Finding( Type.REPORT_TO_ONLY, - `This CSP policy only provides a reporting destination via the 'report-to' directive. This directive is only supported in Chromium-based browsers so it is recommended to also use a 'report-uri' directive.`, - Severity.INFO, Directive.REPORT_TO)); + "This CSP policy only provides a reporting destination via the 'report-to' directive. This directive is only supported in Chromium-based browsers so it is recommended to also use a 'report-uri' directive.", + Severity.INFO, + Directive.REPORT_TO + ) + ); } else { - findings.push(new Finding( + findings.push( + new Finding( Type.REPORTING_DESTINATION_MISSING, 'This CSP policy does not configure a reporting destination. This makes it difficult to maintain the CSP policy over time and monitor for any breakages.', - Severity.INFO, Directive.REPORT_URI)); + Severity.INFO, + Directive.REPORT_URI + ) + ); } } return findings; -} \ No newline at end of file +} diff --git a/checks/strictcsp_checks.ts b/src/checks/strictcsp_checks.ts similarity index 71% rename from checks/strictcsp_checks.ts rename to src/checks/strictcsp_checks.ts index 94a9222..bd7ffcc 100644 --- a/checks/strictcsp_checks.ts +++ b/src/checks/strictcsp_checks.ts @@ -26,9 +26,8 @@ * limitations under the License. */ -import { Csp, Directive, Keyword, TrustedTypesSink } from '../csp'; -import { Finding, Severity, Type } from '../finding'; - +import {Csp, Directive, Keyword, TrustedTypesSink} from '../csp'; +import {Finding, Severity, Type} from '../finding'; /** * Checks if 'strict-dynamic' is present. @@ -44,23 +43,26 @@ export function checkStrictDynamic(parsedCsps: Csp): Finding[] { for (const currentCsp of parsedCsps.directives) { const values: string[] = currentCsp[directiveName] || []; - - const schemeOrHostPresent = values.some((v) => !v.startsWith('\'')); - + + const schemeOrHostPresent = values.some(v => !v.startsWith("'")); + // Check if strict-dynamic is present in case a host/scheme allowlist is used. if (schemeOrHostPresent && !values.includes(Keyword.STRICT_DYNAMIC)) { - findings.push(new Finding( + findings.push( + new Finding( Type.STRICT_DYNAMIC, 'Host allowlists can frequently be bypassed. Consider using ' + - '\'strict-dynamic\' in combination with CSP nonces or hashes.', - Severity.STRICT_CSP, directiveName)); + "'strict-dynamic' in combination with CSP nonces or hashes.", + Severity.STRICT_CSP, + directiveName + ) + ); } } return findings; } - /** * Checks if 'strict-dynamic' is only used together with a nonce or a hash. * @@ -71,25 +73,25 @@ export function checkStrictDynamic(parsedCsps: Csp): Finding[] { */ export function checkStrictDynamicNotStandalone(parsedCsps: Csp): Finding[] { const directiveName = parsedCsps.getEffectiveDirective(Directive.SCRIPT_SRC); - const findings: Finding[] = []; - for (const currentCsp of parsedCsps.directives) { - const values: string[] = currentCsp[directiveName] || []; - - if (values.includes(Keyword.STRICT_DYNAMIC) && - (!parsedCsps.policyHasScriptNonces() && - !parsedCsps.policyHasScriptHashes())) { - findings.push(new Finding( - Type.STRICT_DYNAMIC_NOT_STANDALONE, - '\'strict-dynamic\' without a CSP nonce/hash will block all scripts.', - Severity.INFO, directiveName)); - } + if ( + parsedCsps.policyHasStrictDynamic() && + !parsedCsps.policyHasScriptNonces() && + !parsedCsps.policyHasScriptHashes() + ) { + return [ + new Finding( + Type.STRICT_DYNAMIC_NOT_STANDALONE, + "'strict-dynamic' without a CSP nonce/hash will block all scripts.", + Severity.INFO, + directiveName + ), + ]; } - return findings; + return []; } - /** * Checks if the policy has 'unsafe-inline' when a nonce or hash are present. * This will ensure backward compatibility to browser that don't support @@ -101,8 +103,10 @@ export function checkStrictDynamicNotStandalone(parsedCsps: Csp): Finding[] { * @param parsedCsp A parsed csp. */ export function checkUnsafeInlineFallback(parsedCsps: Csp): Finding[] { - if (!parsedCsps.policyHasScriptNonces() && - !parsedCsps.policyHasScriptHashes()) { + if ( + !parsedCsps.policyHasScriptNonces() && + !parsedCsps.policyHasScriptHashes() + ) { return []; } @@ -113,18 +117,21 @@ export function checkUnsafeInlineFallback(parsedCsps: Csp): Finding[] { const values: string[] = currentCsp[directiveName] || []; if (!values.includes(Keyword.UNSAFE_INLINE)) { - findings.push(new Finding( + findings.push( + new Finding( Type.UNSAFE_INLINE_FALLBACK, - 'Consider adding \'unsafe-inline\' (ignored by browsers supporting ' + - 'nonces/hashes) to be backward compatible with older browsers.', - Severity.STRICT_CSP, directiveName)); + "Consider adding 'unsafe-inline' (ignored by browsers supporting " + + 'nonces/hashes) to be backward compatible with older browsers.', + Severity.STRICT_CSP, + directiveName + ) + ); } } return findings; } - /** * Checks if the policy has an allowlist fallback (* or http: and https:) when * 'strict-dynamic' is present. @@ -148,21 +155,25 @@ export function checkAllowlistFallback(parsedCsps: Csp): Finding[] { } // Check if there's already an allowlist (url scheme or url) - if (!values.some( - (v) => ['http:', 'https:', '*'].includes(v) || v.includes('.'))) { - findings.push(new Finding( + if ( + !values.some(v => ['http:', 'https:', '*'].includes(v) || v.includes('.')) + ) { + findings.push( + new Finding( Type.ALLOWLIST_FALLBACK, 'Consider adding https: and http: url schemes (ignored by browsers ' + - 'supporting \'strict-dynamic\') to be backward compatible with older ' + - 'browsers.', - Severity.STRICT_CSP, directiveName)); + "supporting 'strict-dynamic') to be backward compatible with older " + + 'browsers.', + Severity.STRICT_CSP, + directiveName + ) + ); } } return findings; } - /** * Checks if the policy requires Trusted Types for scripts. * @@ -171,8 +182,12 @@ export function checkAllowlistFallback(parsedCsps: Csp): Finding[] { * * @param parsedCsp A parsed csp. */ -export function checkRequiresTrustedTypesForScripts(parsedCsps: Csp): Finding[] { - const directiveName = parsedCsps.getEffectiveDirective(Directive.REQUIRE_TRUSTED_TYPES_FOR); +export function checkRequiresTrustedTypesForScripts( + parsedCsps: Csp +): Finding[] { + const directiveName = parsedCsps.getEffectiveDirective( + Directive.REQUIRE_TRUSTED_TYPES_FOR + ); for (const cspChecked of parsedCsps.directives) { const values: string[] = cspChecked[directiveName] || []; @@ -181,10 +196,14 @@ export function checkRequiresTrustedTypesForScripts(parsedCsps: Csp): Finding[] } } - return [new Finding( + return [ + new Finding( Type.REQUIRE_TRUSTED_TYPES_FOR_SCRIPTS, 'Consider requiring Trusted Types for scripts to lock down DOM XSS ' + - 'injection sinks. You can do this by adding ' + - '"require-trusted-types-for \'script\'" to your policy.', - Severity.INFO, Directive.REQUIRE_TRUSTED_TYPES_FOR)]; + 'injection sinks. You can do this by adding ' + + '"require-trusted-types-for \'script\'" to your policy.', + Severity.INFO, + Directive.REQUIRE_TRUSTED_TYPES_FOR + ), + ]; } diff --git a/csp.ts b/src/csp.ts similarity index 58% rename from csp.ts rename to src/csp.ts index d0e4bc2..ee9b573 100644 --- a/csp.ts +++ b/src/csp.ts @@ -17,8 +17,7 @@ * limitations under the License. */ - -import { Finding, Severity, Type } from './finding'; +import {Finding, Severity, Type} from './finding'; /** * Content Security Policy object. @@ -27,7 +26,7 @@ import { Finding, Severity, Type } from './finding'; * - https://www.w3.org/TR/upgrade-insecure-requests/ */ export class Csp { - directives: Record[] = []; + directives: Record[] = []; /** * Clones a CSP object. @@ -36,15 +35,17 @@ export class Csp { clone(): Csp { const clone = new Csp(); for (const currentDirective of this.directives) { - const directiveClone: Record = {}; + const directiveClone: Record = {}; - for (const [directiveName, directiveValues] of Object.entries(currentDirective)) { - if (directiveValues) { - directiveClone[directiveName] = [...directiveValues]; - } + for (const [directiveName, directiveValues] of Object.entries( + currentDirective + )) { + if (directiveValues) { + directiveClone[directiveName] = [...directiveValues]; } + } - clone.directives.push(directiveClone); + clone.directives.push(directiveClone); } return clone; @@ -58,19 +59,21 @@ export class Csp { const cspStrings: string[] = []; for (const currentDirective of this.directives) { - let cspString: string = ''; - for (const [directive, directiveValues] of Object.entries(currentDirective)) { - cspString += directive; - if (directiveValues !== undefined) { - for (let value, i = 0; (value = directiveValues[i]); i++) { - cspString += ' '; - cspString += value; - } - } - cspString += '; '; + let cspString: string = ''; + for (const [directive, directiveValues] of Object.entries( + currentDirective + )) { + cspString += directive; + if (directiveValues !== undefined) { + for (let value, i = 0; (value = directiveValues[i]); i++) { + cspString += ' '; + cspString += value; + } } + cspString += '; '; + } - cspStrings.push(cspString.trim()); + cspStrings.push(cspString.trim()); } return cspStrings.join(', '); @@ -90,64 +93,81 @@ export class Csp { const directive = effectiveCsp.getEffectiveDirective(Directive.SCRIPT_SRC); for (let index = 0; index < effectiveCsp.directives.length; index++) { - const values = this.directives[index][directive] || []; - const effectiveCspValues = effectiveCsp.directives[index][directive]; - - if (effectiveCspValues && - (effectiveCsp.policyHasScriptNonces() || - effectiveCsp.policyHasScriptHashes())) { - if (cspVersion >= Version.CSP2) { - // Ignore 'unsafe-inline' in CSP >= v2, if a nonce or a hash is present. - if (values.includes(Keyword.UNSAFE_INLINE)) { - arrayRemove(effectiveCspValues, Keyword.UNSAFE_INLINE); - findings.push(new Finding( - Type.IGNORED, - 'unsafe-inline is ignored if a nonce or a hash is present. ' + - '(CSP2 and above)', - Severity.NONE, directive, Keyword.UNSAFE_INLINE)); - } - } else { - // remove nonces and hashes (not supported in CSP < v2). - for (const value of values) { - if (value.startsWith('\'nonce-') || value.startsWith('\'sha')) { - arrayRemove(effectiveCspValues, value); - } - } + const values = this.directives[index][directive] || []; + const effectiveCspValues = effectiveCsp.directives[index][directive]; + + if ( + effectiveCspValues && + (effectiveCsp.policyHasScriptNonces() || + effectiveCsp.policyHasScriptHashes()) + ) { + if (cspVersion >= Version.CSP2) { + // Ignore 'unsafe-inline' in CSP >= v2, if a nonce or a hash is present. + if (values.includes(Keyword.UNSAFE_INLINE)) { + arrayRemove(effectiveCspValues, Keyword.UNSAFE_INLINE); + findings.push( + new Finding( + Type.IGNORED, + 'unsafe-inline is ignored if a nonce or a hash is present. ' + + '(CSP2 and above)', + Severity.NONE, + directive, + Keyword.UNSAFE_INLINE + ) + ); + } + } else { + // remove nonces and hashes (not supported in CSP < v2). + for (const value of values) { + if (value.startsWith("'nonce-") || value.startsWith("'sha")) { + arrayRemove(effectiveCspValues, value); } + } } - - if (effectiveCspValues && this.policyHasStrictDynamic()) { - // Ignore allowlist in CSP >= v3 in presence of 'strict-dynamic'. - if (cspVersion >= Version.CSP3) { - for (const value of values) { - // Because of 'strict-dynamic' all host-source and scheme-source - // expressions, as well as the "'unsafe-inline'" and "'self' - // keyword-sources will be ignored. - // https://w3c.github.io/webappsec-csp/#strict-dynamic-usage - if (!value.startsWith('\'') || value === Keyword.SELF || - value === Keyword.UNSAFE_INLINE) { - arrayRemove(effectiveCspValues, value); - findings.push(new Finding( - Type.IGNORED, - 'Because of strict-dynamic this entry is ignored in CSP3 and above', - Severity.NONE, directive, value)); - } - } - } else { - // strict-dynamic not supported. - arrayRemove(effectiveCspValues, Keyword.STRICT_DYNAMIC); + } + + if (effectiveCspValues && this.policyHasStrictDynamic()) { + // Ignore allowlist in CSP >= v3 in presence of 'strict-dynamic'. + if (cspVersion >= Version.CSP3) { + for (const value of values) { + // Because of 'strict-dynamic' all host-source and scheme-source + // expressions, as well as the "'unsafe-inline'" and "'self' + // keyword-sources will be ignored. + // https://w3c.github.io/webappsec-csp/#strict-dynamic-usage + if ( + !value.startsWith("'") || + value === Keyword.SELF || + value === Keyword.UNSAFE_INLINE + ) { + arrayRemove(effectiveCspValues, value); + findings.push( + new Finding( + Type.IGNORED, + 'Because of strict-dynamic this entry is ignored in CSP3 and above', + Severity.NONE, + directive, + value + ) + ); } + } + } else { + // strict-dynamic not supported. + arrayRemove(effectiveCspValues, Keyword.STRICT_DYNAMIC); } - - if (cspVersion < Version.CSP3) { - // Remove CSP3 directives from pre-CSP3 policies. - // https://w3c.github.io/webappsec-csp/#changes-from-level-2 - delete effectiveCsp.directives[index][Directive.REPORT_TO]; - delete effectiveCsp.directives[index][Directive.WORKER_SRC]; - delete effectiveCsp.directives[index][Directive.MANIFEST_SRC]; - delete effectiveCsp.directives[index][Directive.TRUSTED_TYPES]; - delete effectiveCsp.directives[index][Directive.REQUIRE_TRUSTED_TYPES_FOR]; - } + } + + if (cspVersion < Version.CSP3) { + // Remove CSP3 directives from pre-CSP3 policies. + // https://w3c.github.io/webappsec-csp/#changes-from-level-2 + delete effectiveCsp.directives[index][Directive.REPORT_TO]; + delete effectiveCsp.directives[index][Directive.WORKER_SRC]; + delete effectiveCsp.directives[index][Directive.MANIFEST_SRC]; + delete effectiveCsp.directives[index][Directive.TRUSTED_TYPES]; + delete effectiveCsp.directives[index][ + Directive.REQUIRE_TRUSTED_TYPES_FOR + ]; + } } return effectiveCsp; @@ -162,14 +182,14 @@ export class Csp { getEffectiveDirective(directive: string): string { // Directive doesn't default to default-src if (!FETCH_DIRECTIVES.includes(directive as Directive)) { - return directive; + return directive; } // Look in each CSP to find the directive for (const currentCsp of this.directives) { - if (directive in currentCsp) { - return directive; - } + if (directive in currentCsp) { + return directive; + } } return Directive.DEFAULT_SRC; @@ -182,8 +202,9 @@ export class Csp { * @return The effective directives. */ getEffectiveDirectives(directives: string[]): string[] { - const effectiveDirectives = - new Set(directives.map((val) => this.getEffectiveDirective(val))); + const effectiveDirectives = new Set( + directives.map(val => this.getEffectiveDirective(val)) + ); return [...effectiveDirectives]; } @@ -194,11 +215,11 @@ export class Csp { policyHasScriptNonces(): boolean { const directiveName = this.getEffectiveDirective(Directive.SCRIPT_SRC); - for (const currentDirective of this.directives) { - const values = currentDirective[directiveName] || []; - if (values.some((val) => isNonce(val))) { - return true; - } + for (const currentDirective of this.directives) { + const values = currentDirective[directiveName] || []; + if (values.some(val => isNonce(val))) { + return true; + } } return false; @@ -211,11 +232,11 @@ export class Csp { policyHasScriptHashes(): boolean { const directiveName = this.getEffectiveDirective(Directive.SCRIPT_SRC); - for (const currentDirective of this.directives) { - const values = currentDirective[directiveName] || []; - if (values.some((val) => isHash(val))) { - return true; - } + for (const currentDirective of this.directives) { + const values = currentDirective[directiveName] || []; + if (values.some(val => isHash(val))) { + return true; + } } return false; @@ -228,45 +249,42 @@ export class Csp { policyHasStrictDynamic(): boolean { const directiveName = this.getEffectiveDirective(Directive.SCRIPT_SRC); - for (const currentDirective of this.directives) { - const values = currentDirective[directiveName] || []; - if (values.includes(Keyword.STRICT_DYNAMIC)) { - return true; - } + for (const currentDirective of this.directives) { + const values = currentDirective[directiveName] || []; + if (values.includes(Keyword.STRICT_DYNAMIC)) { + return true; + } } return false; } } - /** * CSP directive source keywords. */ export enum Keyword { - SELF = '\'self\'', - NONE = '\'none\'', - UNSAFE_INLINE = '\'unsafe-inline\'', - UNSAFE_EVAL = '\'unsafe-eval\'', - WASM_EVAL = '\'wasm-eval\'', - WASM_UNSAFE_EVAL = '\'wasm-unsafe-eval\'', - STRICT_DYNAMIC = '\'strict-dynamic\'', - UNSAFE_HASHED_ATTRIBUTES = '\'unsafe-hashed-attributes\'', - UNSAFE_HASHES = '\'unsafe-hashes\'', - REPORT_SAMPLE = '\'report-sample\'', - BLOCK = '\'block\'', - ALLOW = '\'allow\'', + SELF = "'self'", + NONE = "'none'", + UNSAFE_INLINE = "'unsafe-inline'", + UNSAFE_EVAL = "'unsafe-eval'", + WASM_EVAL = "'wasm-eval'", + WASM_UNSAFE_EVAL = "'wasm-unsafe-eval'", + STRICT_DYNAMIC = "'strict-dynamic'", + UNSAFE_HASHED_ATTRIBUTES = "'unsafe-hashed-attributes'", + UNSAFE_HASHES = "'unsafe-hashes'", + REPORT_SAMPLE = "'report-sample'", + BLOCK = "'block'", + ALLOW = "'allow'", } - /** * CSP directive source keywords. */ export enum TrustedTypesSink { - SCRIPT = '\'script\'', + SCRIPT = "'script'", } - /** * CSP v3 directives. * List of valid CSP directives: @@ -329,12 +347,22 @@ export enum Directive { * */ export const FETCH_DIRECTIVES: Directive[] = [ - Directive.CHILD_SRC, Directive.CONNECT_SRC, Directive.DEFAULT_SRC, - Directive.FONT_SRC, Directive.FRAME_SRC, Directive.IMG_SRC, - Directive.MANIFEST_SRC, Directive.MEDIA_SRC, Directive.OBJECT_SRC, - Directive.SCRIPT_SRC, Directive.SCRIPT_SRC_ATTR, Directive.SCRIPT_SRC_ELEM, - Directive.STYLE_SRC, Directive.STYLE_SRC_ATTR, Directive.STYLE_SRC_ELEM, - Directive.WORKER_SRC + Directive.CHILD_SRC, + Directive.CONNECT_SRC, + Directive.DEFAULT_SRC, + Directive.FONT_SRC, + Directive.FRAME_SRC, + Directive.IMG_SRC, + Directive.MANIFEST_SRC, + Directive.MEDIA_SRC, + Directive.OBJECT_SRC, + Directive.SCRIPT_SRC, + Directive.SCRIPT_SRC_ATTR, + Directive.SCRIPT_SRC_ELEM, + Directive.STYLE_SRC, + Directive.STYLE_SRC_ATTR, + Directive.STYLE_SRC_ELEM, + Directive.WORKER_SRC, ]; /** @@ -343,10 +371,9 @@ export const FETCH_DIRECTIVES: Directive[] = [ export enum Version { CSP1 = 1, CSP2, - CSP3 + CSP3, } - /** * Checks if a string is a valid CSP directive. * @param directive value to check. @@ -356,7 +383,6 @@ export function isDirective(directive: string): boolean { return Object.values(Directive).includes(directive as Directive); } - /** * Checks if a string is a valid CSP keyword. * @param keyword value to check. @@ -366,7 +392,6 @@ export function isKeyword(keyword: string): boolean { return Object.values(Keyword).includes(keyword as Keyword); } - /** * Checks if a string is a valid URL scheme. * Scheme part + ":" @@ -379,17 +404,15 @@ export function isUrlScheme(urlScheme: string): boolean { return pattern.test(urlScheme); } - /** * A regex pattern to check nonce prefix and Base64 formatting of a nonce value. */ -export const STRICT_NONCE_PATTERN = - new RegExp('^\'nonce-[a-zA-Z0-9+/_-]+[=]{0,2}\'$'); - +export const STRICT_NONCE_PATTERN = new RegExp( + "^'nonce-[a-zA-Z0-9+/_-]+[=]{0,2}'$" +); /** A regex pattern for checking if nonce prefix. */ -export const NONCE_PATTERN = new RegExp('^\'nonce-(.+)\'$'); - +export const NONCE_PATTERN = new RegExp("^'nonce-(.+)'$"); /** * Checks if a string is a valid CSP nonce. @@ -403,17 +426,15 @@ export function isNonce(nonce: string, strictCheck?: boolean): boolean { return pattern.test(nonce); } - /** * A regex pattern to check hash prefix and Base64 formatting of a hash value. */ -export const STRICT_HASH_PATTERN = - new RegExp('^\'(sha256|sha384|sha512)-[a-zA-Z0-9+/]+[=]{0,2}\'$'); - +export const STRICT_HASH_PATTERN = new RegExp( + "^'(sha256|sha384|sha512)-[a-zA-Z0-9+/]+[=]{0,2}'$" +); /** A regex pattern to check hash prefix. */ -export const HASH_PATTERN = new RegExp('^\'(sha256|sha384|sha512)-(.+)\'$'); - +export const HASH_PATTERN = new RegExp("^'(sha256|sha384|sha512)-(.+)'$"); /** * Checks if a string is a valid CSP hash. @@ -427,7 +448,6 @@ export function isHash(hash: string, strictCheck?: boolean): boolean { return pattern.test(hash); } - /** * Class to represent all generic CSP errors. */ diff --git a/evaluator.ts b/src/evaluator.ts similarity index 80% rename from evaluator.ts rename to src/evaluator.ts index 027c71f..e2eb038 100644 --- a/evaluator.ts +++ b/src/evaluator.ts @@ -16,14 +16,12 @@ * limitations under the License. */ -import { CheckerFunction } from './checks/checker'; +import {CheckerFunction} from './checks/checker'; import * as parserChecks from './checks/parser_checks'; import * as securityChecks from './checks/security_checks'; import * as strictcspChecks from './checks/strictcsp_checks'; -import { Csp, Version } from './csp'; -import { Finding } from './finding'; - - +import {Csp, Version} from './csp'; +import {Finding} from './finding'; /** * A class to hold a CSP Evaluator. @@ -65,8 +63,9 @@ export class CspEvaluator { * @export */ evaluate( - parsedCspChecks?: CheckerFunction[], - effectiveCspChecks?: CheckerFunction[]): Finding[] { + parsedCspChecks?: CheckerFunction[], + effectiveCspChecks?: CheckerFunction[] + ): Finding[] { this.findings = []; const checks = effectiveCspChecks || DEFAULT_CHECKS; @@ -92,22 +91,26 @@ export class CspEvaluator { } } - /** * Set of default checks to run. */ export const DEFAULT_CHECKS: CheckerFunction[] = [ - securityChecks.checkScriptUnsafeInline, securityChecks.checkScriptUnsafeEval, - securityChecks.checkPlainUrlSchemes, securityChecks.checkWildcards, + securityChecks.checkScriptUnsafeInline, + securityChecks.checkScriptUnsafeEval, + securityChecks.checkPlainUrlSchemes, + securityChecks.checkWildcards, securityChecks.checkMissingDirectives, securityChecks.checkScriptAllowlistBypass, - securityChecks.checkFlashObjectAllowlistBypass, securityChecks.checkIpSource, - securityChecks.checkNonceLength, securityChecks.checkSrcHttp, - securityChecks.checkDeprecatedDirective, parserChecks.checkUnknownDirective, - parserChecks.checkMissingSemicolon, parserChecks.checkInvalidKeyword + securityChecks.checkFlashObjectAllowlistBypass, + securityChecks.checkIpSource, + securityChecks.checkNonceLength, + securityChecks.checkSrcHttp, + securityChecks.checkDeprecatedDirective, + parserChecks.checkUnknownDirective, + parserChecks.checkMissingSemicolon, + parserChecks.checkInvalidKeyword, ]; - /** * Strict CSP and backward compatibility checks. */ @@ -116,5 +119,5 @@ export const STRICTCSP_CHECKS: CheckerFunction[] = [ strictcspChecks.checkStrictDynamicNotStandalone, strictcspChecks.checkUnsafeInlineFallback, strictcspChecks.checkAllowlistFallback, - strictcspChecks.checkRequiresTrustedTypesForScripts + strictcspChecks.checkRequiresTrustedTypesForScripts, ]; diff --git a/finding.ts b/src/finding.ts similarity index 83% rename from finding.ts rename to src/finding.ts index d3203ae..e09f471 100644 --- a/finding.ts +++ b/src/finding.ts @@ -16,7 +16,6 @@ * @author lwe@google.com (Lukas Weichselbaum) */ - /** * A CSP Finding is returned by a CSP check and can either reference a directive * value or a directive. If a directive value is referenced opt_index must be @@ -32,8 +31,12 @@ export class Finding { * @param value The directive value, if exists. */ constructor( - public type: Type, public description: string, public severity: Severity, - public directive: string, public value?: string) {} + public type: Type, + public description: string, + public severity: Severity, + public directive: string, + public value?: string + ) {} /** * Returns the highest severity of a list of findings. @@ -45,8 +48,8 @@ export class Finding { return Severity.NONE; } - const severities = findings.map((finding) => finding.severity); - const min = (prev: Severity, cur: Severity) => prev < cur ? prev : cur; + const severities = findings.map(finding => finding.severity); + const min = (prev: Severity, cur: Severity) => (prev < cur ? prev : cur); return severities.reduce(min, Severity.NONE); } @@ -54,13 +57,16 @@ export class Finding { if (!(obj instanceof Finding)) { return false; } - return obj.type === this.type && obj.description === this.description && - obj.severity === this.severity && obj.directive === this.directive && - obj.value === this.value; + return ( + obj.type === this.type && + obj.description === this.description && + obj.severity === this.severity && + obj.directive === this.directive && + obj.value === this.value + ); } } - /** * Finding severities. */ @@ -74,10 +80,9 @@ export enum Severity { LOW = 70, LOW_MAYBE = 80, INFO = 60, - NONE = 100 + NONE = 100, } - /** * Finding types for evluator checks. */ diff --git a/lighthouse/lighthouse_checks.ts b/src/lighthouse/lighthouse_checks.ts similarity index 75% rename from lighthouse/lighthouse_checks.ts rename to src/lighthouse/lighthouse_checks.ts index 62beb2c..5ce04fe 100644 --- a/lighthouse/lighthouse_checks.ts +++ b/src/lighthouse/lighthouse_checks.ts @@ -3,13 +3,30 @@ * stricter subset of the other checks defined in this project. */ -import { CheckerFunction } from '../checks/checker'; -import { checkInvalidKeyword, checkMissingSemicolon, checkUnknownDirective } from '../checks/parser_checks'; -import { checkDeprecatedDirective, checkMissingBaseUriDirective, checkMissingObjectSrcDirective, checkMissingScriptSrcDirective, checkNonceLength, checkPlainUrlSchemes, checkScriptUnsafeInline, checkWildcards } from '../checks/security_checks'; -import { checkAllowlistFallback, checkStrictDynamic, checkUnsafeInlineFallback } from '../checks/strictcsp_checks'; -import { Csp, Directive, Version } from '../csp'; -import { Finding } from '../finding'; -import { CspParser } from '../parser'; +import {CheckerFunction} from '../checks/checker'; +import { + checkInvalidKeyword, + checkMissingSemicolon, + checkUnknownDirective, +} from '../checks/parser_checks'; +import { + checkDeprecatedDirective, + checkMissingBaseUriDirective, + checkMissingObjectSrcDirective, + checkMissingScriptSrcDirective, + checkNonceLength, + checkPlainUrlSchemes, + checkScriptUnsafeInline, + checkWildcards, +} from '../checks/security_checks'; +import { + checkAllowlistFallback, + checkStrictDynamic, + checkUnsafeInlineFallback, +} from '../checks/strictcsp_checks'; +import {Csp, Directive, Version} from '../csp'; +import {Finding} from '../finding'; +import {CspParser} from '../parser'; interface Equalable { equals(a: unknown): boolean; @@ -59,7 +76,9 @@ function setUnion(sets: T[][]): T[] { * first one that had any findings. */ function atLeastOnePasses( - parsedCsps: Csp[], checker: CheckerFunction): Finding[] { + parsedCsps: Csp[], + checker: CheckerFunction +): Finding[] { const findings: Finding[][] = []; for (const parsedCsp of parsedCsps) { findings.push(checker(parsedCsp)); @@ -72,7 +91,9 @@ function atLeastOnePasses( * list of findings from the one that had the most findings. */ function atLeastOneFails( - parsedCsps: Csp[], checker: CheckerFunction): Finding[] { + parsedCsps: Csp[], + checker: CheckerFunction +): Finding[] { const findings: Finding[][] = []; for (const parsedCsp of parsedCsps) { findings.push(checker(parsedCsp)); @@ -89,7 +110,7 @@ export function evaluateForFailure(parsedCsps: Csp[]): Finding[] { for (const csp of parsedCsps) { mergedCspString.push(csp.convertToString()); } - const mergedCsp: Csp = (new CspParser(mergedCspString)).csp; + const mergedCsp: Csp = new CspParser(mergedCspString).csp; // Check #1 const targetsXssFindings = [ @@ -99,12 +120,15 @@ export function evaluateForFailure(parsedCsps: Csp[]): Finding[] { ]; // Check #2 - const effectiveCsps = parsedCsps.map(csp => csp.getEffectiveCsp(Version.CSP3)); + const effectiveCsps = parsedCsps.map(csp => + csp.getEffectiveCsp(Version.CSP3) + ); const effectiveCspsWithScript = effectiveCsps.filter(csp => { const directiveName = csp.getEffectiveDirective(Directive.SCRIPT_SRC); for (const currentCsp of csp.directives) { - return currentCsp[directiveName]; + return currentCsp[directiveName]; } + return false; }); const robust = [ ...atLeastOnePasses(effectiveCspsWithScript, checkStrictDynamic), @@ -127,7 +151,7 @@ export function evaluateForWarnings(parsedCsps: Csp[]): Finding[] { // Check #3 return [ ...atLeastOneFails(parsedCsps, checkUnsafeInlineFallback), - ...atLeastOneFails(parsedCsps, checkAllowlistFallback) + ...atLeastOneFails(parsedCsps, checkAllowlistFallback), ]; } @@ -141,9 +165,11 @@ export function evaluateForSyntaxErrors(parsedCsps: Csp[]): Finding[][] { const allFindings: Finding[][] = []; for (const csp of parsedCsps) { const findings = [ - ...checkNonceLength(csp), ...checkUnknownDirective(csp), - ...checkDeprecatedDirective(csp), ...checkMissingSemicolon(csp), - ...checkInvalidKeyword(csp) + ...checkNonceLength(csp), + ...checkUnknownDirective(csp), + ...checkDeprecatedDirective(csp), + ...checkMissingSemicolon(csp), + ...checkInvalidKeyword(csp), ]; allFindings.push(findings); } diff --git a/parser.ts b/src/parser.ts similarity index 89% rename from parser.ts rename to src/parser.ts index de4ee51..7ab9f8f 100644 --- a/parser.ts +++ b/src/parser.ts @@ -16,8 +16,8 @@ * @author lwe@google.com (Lukas Weichselbaum) */ -import { Csp, isKeyword, isUrlScheme } from './csp'; -import { mergeCspHeaders } from './utils'; +import {Csp, isKeyword, isUrlScheme} from './csp'; +import {mergeCspHeaders} from './utils'; /** * A class to hold a parser for CSP in string format. @@ -54,8 +54,8 @@ export class CspParser { return this.csp; } - parseCsp(unparsedCsp: string): Record { - const retCspDirectives: Record = {}; + parseCsp(unparsedCsp: string): Record { + const retCspDirectives: Record = {}; // For each token returned by strictly splitting serialized on the U+003B SEMICOLON character (;): const directiveTokens = unparsedCsp.split(';'); @@ -65,7 +65,7 @@ export class CspParser { // If token is an empty string, or if token is not an ASCII string, continue. /* eslint-disable no-control-regex */ - if (directiveToken === "" || !/^[\x00-\xFF]*$/.test(directiveToken)) { + if (directiveToken === '' || !/^[\x00-\xFF]*$/.test(directiveToken)) { continue; } /* eslint-enable no-control-regex */ @@ -83,8 +83,11 @@ export class CspParser { } const directiveValues: string[] = []; - for (let directiveValue, j = 1; (directiveValue = directiveParts[j]); - j++) { + for ( + let directiveValue, j = 1; + (directiveValue = directiveParts[j]); + j++ + ) { // Let directive be a new directive whose name is directive name, and value is directive value. directiveValue = normalizeDirectiveValue(directiveValue); if (!directiveValues.includes(directiveValue)) { diff --git a/utils.ts b/src/utils.ts similarity index 84% rename from utils.ts rename to src/utils.ts index 36758a7..8d92978 100644 --- a/utils.ts +++ b/src/utils.ts @@ -17,7 +17,6 @@ * limitations under the License. */ - /** * Removes scheme from url. * @param url Url. @@ -37,11 +36,11 @@ export function getSchemeFreeUrl(url: string): string { */ export function getHostname(url: string): string { const hostname = new URL( - 'https://' + - getSchemeFreeUrl(url) - .replace(':*', '') // Remove wildcard port - .replace('*', 'wildcard_placeholder')) - .hostname.replace('wildcard_placeholder', '*'); + 'https://' + + getSchemeFreeUrl(url) + .replace(':*', '') // Remove wildcard port + .replace('*', 'wildcard_placeholder') + ).hostname.replace('wildcard_placeholder', '*'); // Some browsers strip the brackets from IPv6 addresses when you access the // hostname. If the scheme free url starts with something that vaguely looks @@ -69,16 +68,21 @@ function setScheme(u: string): string { * @return First match found in url list, null otherwise. */ export function matchWildcardUrls( - cspUrlString: string, listOfUrlStrings: string[]): URL|null { + cspUrlString: string, + listOfUrlStrings: string[] +): URL | null { // non-Chromium browsers don't support wildcards in domain names. We work // around this by replacing the wildcard with `wildcard_placeholder` before // parsing the domain and using that as a magic string. This magic string is // encapsulated in this function such that callers of this function do not // have to worry about this detail. - const cspUrl = - new URL(setScheme(cspUrlString - .replace(':*', '') // Remove wildcard port - .replace('*', 'wildcard_placeholder'))); + const cspUrl = new URL( + setScheme( + cspUrlString + .replace(':*', '') // Remove wildcard port + .replace('*', 'wildcard_placeholder') + ) + ); const listOfUrls = listOfUrlStrings.map(u => new URL(setScheme(u))); const host = cspUrl.hostname.toLowerCase(); const hostHasWildcard = host.startsWith('wildcard_placeholder.'); @@ -122,7 +126,6 @@ export function matchWildcardUrls( return null; } - /** * Applies a check to all directive values of a csp. * @param parsedCsp Parsed CSP. @@ -130,8 +133,8 @@ export function matchWildcardUrls( * should get applied on directive values. */ export function applyCheckFunktionToDirectives( - parsedCsp: Record, - check: (directive: string, directiveValues: string[]) => void, + parsedCsp: Record, + check: (directive: string, directiveValues: string[]) => void ) { const directiveNames = Object.keys(parsedCsp); @@ -144,5 +147,7 @@ export function applyCheckFunktionToDirectives( } export function mergeCspHeaders(headerValues: string[]): string { - return headerValues.map(Function.prototype.call, String.prototype.trim).join(', '); -} \ No newline at end of file + return headerValues + .map(Function.prototype.call, String.prototype.trim) + .join(', '); +} diff --git a/checks/parser_checks_test.ts b/test/checks/parser_checks_test.ts similarity index 93% rename from checks/parser_checks_test.ts rename to test/checks/parser_checks_test.ts index bcf7424..55376d3 100644 --- a/checks/parser_checks_test.ts +++ b/test/checks/parser_checks_test.ts @@ -19,11 +19,11 @@ import 'jasmine'; -import { Finding, Severity } from '../finding'; -import { CspParser } from '../parser'; +import { Finding, Severity } from '../../src/finding'; +import { CspParser } from '../../src/parser'; -import { CheckerFunction } from './checker'; -import * as parserChecks from './parser_checks'; +import { CheckerFunction } from '../../src/checks/checker'; +import * as parserChecks from '../../src/checks/parser_checks'; /** * Runs a check on a CSP string. diff --git a/checks/security_checks_test.ts b/test/checks/security_checks_test.ts similarity index 98% rename from checks/security_checks_test.ts rename to test/checks/security_checks_test.ts index c06280d..353f2c8 100644 --- a/checks/security_checks_test.ts +++ b/test/checks/security_checks_test.ts @@ -18,12 +18,12 @@ */ - import { Directive, Version } from '../csp'; - import { Finding, Severity, Type } from '../finding'; - import { CspParser } from '../parser'; + import { Directive, Version } from '../../src/csp'; + import { Finding, Severity, Type } from '../../src/finding'; + import { CspParser } from '../../src/parser'; - import { CheckerFunction } from './checker'; - import * as securityChecks from './security_checks'; + import { CheckerFunction } from '../../src/checks/checker'; + import * as securityChecks from '../../src/checks/security_checks'; /** * Helper function for running a check on a CSP string. diff --git a/checks/strictcsp_checks_test.ts b/test/checks/strictcsp_checks_test.ts similarity index 86% rename from checks/strictcsp_checks_test.ts rename to test/checks/strictcsp_checks_test.ts index 1ec965a..8acb8d4 100644 --- a/checks/strictcsp_checks_test.ts +++ b/test/checks/strictcsp_checks_test.ts @@ -17,11 +17,11 @@ * @author lwe@google.com (Lukas Weichselbaum) */ -import { Finding, Severity } from '../finding'; -import { CspParser } from '../parser'; +import { Finding, Severity } from '../../src/finding'; +import { CspParser } from '../../src/parser'; -import { CheckerFunction } from './checker'; -import * as strictcspChecks from './strictcsp_checks'; +import { CheckerFunction } from '../../src/checks/checker'; +import * as strictcspChecks from '../../src/checks/strictcsp_checks'; /** @@ -49,8 +49,7 @@ describe('Test strictcsp checks', () => { it('CheckStrictDynamicNotStandalone', () => { const test = 'script-src \'strict-dynamic\''; - const violations = - checkCsp(test, strictcspChecks.checkStrictDynamicNotStandalone); + const violations = checkCsp(test, strictcspChecks.checkStrictDynamicNotStandalone); expect(violations[0].severity).toBe(Severity.INFO); }); @@ -58,7 +57,7 @@ describe('Test strictcsp checks', () => { const test = 'script-src \'strict-dynamic\' \'nonce-foobar\''; const violations = - checkCsp(test, strictcspChecks.checkStrictDynamicNotStandalone); + checkCsp(test, strictcspChecks.checkStrictDynamicNotStandalone); expect(violations.length).toBe(0); }); @@ -67,7 +66,7 @@ describe('Test strictcsp checks', () => { const test = 'script-src \'nonce-test\''; const violations = - checkCsp(test, strictcspChecks.checkUnsafeInlineFallback); + checkCsp(test, strictcspChecks.checkUnsafeInlineFallback); expect(violations.length).toBe(1); expect(violations[0].severity).toBe(Severity.STRICT_CSP); }); @@ -76,7 +75,7 @@ describe('Test strictcsp checks', () => { const test = 'script-src \'nonce-test\' \'unsafe-inline\''; const violations = - checkCsp(test, strictcspChecks.checkUnsafeInlineFallback); + checkCsp(test, strictcspChecks.checkUnsafeInlineFallback); expect(violations.length).toBe(0); }); diff --git a/csp_test.ts b/test/csp_test.ts similarity index 99% rename from csp_test.ts rename to test/csp_test.ts index 4a1146c..a47ed61 100644 --- a/csp_test.ts +++ b/test/csp_test.ts @@ -19,8 +19,8 @@ import 'jasmine'; -import { Directive, isDirective, isHash, isKeyword, isNonce, isUrlScheme, Keyword, Version } from './csp'; -import { CspParser } from './parser'; +import { Directive, isDirective, isHash, isKeyword, isNonce, isUrlScheme, Keyword, Version } from '../src/csp'; +import { CspParser } from '../src/parser'; describe('Test Csp', () => { it('ConvertToString', () => { diff --git a/evaluator_test.ts b/test/evaluator_test.ts similarity index 91% rename from evaluator_test.ts rename to test/evaluator_test.ts index ae82dde..0762a62 100644 --- a/evaluator_test.ts +++ b/test/evaluator_test.ts @@ -19,9 +19,9 @@ import 'jasmine'; -import { CspEvaluator } from './evaluator'; -import { Finding, Severity, Type } from './finding'; -import { Csp } from './csp'; +import { CspEvaluator } from '../src/evaluator'; +import { Finding, Severity, Type } from '../src/finding'; +import { Csp } from '../src/csp'; describe('Test evaluator', () => { it('CspEvaluator', () => { diff --git a/finding_test.ts b/test/finding_test.ts similarity index 95% rename from finding_test.ts rename to test/finding_test.ts index 25ea2be..a73c44f 100644 --- a/finding_test.ts +++ b/test/finding_test.ts @@ -19,8 +19,8 @@ import 'jasmine'; -import { Directive, Keyword } from './csp'; -import { Finding, Severity, Type } from './finding'; +import { Directive, Keyword } from '../src/csp'; +import { Finding, Severity, Type } from '../src/finding'; describe('Test finding', () => { diff --git a/lighthouse/lighthouse_checks_test.ts b/test/lighthouse/lighthouse_checks_test.ts similarity index 99% rename from lighthouse/lighthouse_checks_test.ts rename to test/lighthouse/lighthouse_checks_test.ts index 99ee292..67af64c 100644 --- a/lighthouse/lighthouse_checks_test.ts +++ b/test/lighthouse/lighthouse_checks_test.ts @@ -4,11 +4,11 @@ import 'jasmine'; - import {Csp,} from '../csp'; - import {Severity} from '../finding'; - import {CspParser} from '../parser'; + import {Csp,} from '../../src/csp'; + import {Severity} from '../../src/finding'; + import {CspParser} from '../../src/parser'; - import * as lighthouseChecks from './lighthouse_checks'; + import * as lighthouseChecks from '../../src/lighthouse/lighthouse_checks'; function parsePolicies(policies: string[]): Csp[] { return policies.map(p => (new CspParser(p)).csp); diff --git a/parser_test.ts b/test/parser_test.ts similarity index 99% rename from parser_test.ts rename to test/parser_test.ts index a83fe9a..6987fb9 100644 --- a/parser_test.ts +++ b/test/parser_test.ts @@ -19,7 +19,7 @@ import 'jasmine'; -import {CspParser, TEST_ONLY} from './parser'; +import {CspParser, TEST_ONLY} from '../src/parser'; describe('Test parser', () => { @@ -160,7 +160,7 @@ describe('Test parser', () => { const expectedDirectives2 = [ 'connect-src', 'script-src' ]; - + expect(expectedDirectives1).toEqual(jasmine.arrayWithExactContents(directives1)); expect(expectedDirectives2).toEqual(jasmine.arrayWithExactContents(directives2)); diff --git a/utils_test.ts b/test/utils_test.ts similarity index 99% rename from utils_test.ts rename to test/utils_test.ts index d123dae..f3f9e10 100644 --- a/utils_test.ts +++ b/test/utils_test.ts @@ -18,7 +18,7 @@ import 'jasmine'; -import { getHostname, getSchemeFreeUrl, matchWildcardUrls, mergeCspHeaders } from './utils'; +import { getHostname, getSchemeFreeUrl, matchWildcardUrls, mergeCspHeaders } from '../src/utils'; const TEST_BYPASSES = [ 'https://googletagmanager.com/gtm/js', 'https://www.google.com/jsapi', From 9431de1f0dc4cdd040caf613817bc904490534c4 Mon Sep 17 00:00:00 2001 From: MaxNad Date: Mon, 8 Jan 2024 16:09:26 -0500 Subject: [PATCH 12/12] Moved the artifacts back in dist for backward compatibility --- .eslintignore | 2 +- .gitignore | 2 +- jasmine.json | 2 +- package.json | 2 +- tsconfig.json | 15 ++++++++++++--- 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/.eslintignore b/.eslintignore index cded043..3466553 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1,2 @@ -build/ +dist/ test/ \ No newline at end of file diff --git a/.gitignore b/.gitignore index 914a919..1a65c52 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ dest/ *.js -build +dist node_modules \ No newline at end of file diff --git a/jasmine.json b/jasmine.json index da2dbbe..4539f7b 100644 --- a/jasmine.json +++ b/jasmine.json @@ -1,5 +1,5 @@ { - "spec_dir": "build", + "spec_dir": "dist", "spec_files": [ "test/**/*_test.js" ], diff --git a/package.json b/package.json index 12788b5..af7564f 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "csp_evaluator", "version": "2.0.0", "description": "Evaluate Content Security Policies for a wide range of bypasses and weaknesses", - "main": "build/evaluator.js", + "main": "dist/evaluator.js", "keywords": [ "csp", "content security policy", diff --git a/tsconfig.json b/tsconfig.json index d1646f0..a5e3444 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,20 @@ { "extends": "./node_modules/gts/tsconfig-google.json", "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "declaration": true, + "declarationMap": true, + "sourceMap": true, "rootDir": ".", - "outDir": "build" + "outDir": "dist", + "removeComments": true, + "strict": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true }, "include": [ "src/**/*.ts", - "test/**/*.ts" + "test/**/*.ts", ] -} +} \ No newline at end of file