fix: prevent asset conflicts between React and Grid.js versions

Add coexistence checks to all enqueue methods to prevent loading
both React and Grid.js assets simultaneously.

Changes:
- ReactAdmin.php: Only enqueue React assets when ?react=1
- Init.php: Skip Grid.js when React active on admin pages
- Form.php, Coupon.php, Access.php: Restore classic assets when ?react=0
- Customer.php, Product.php, License.php: Add coexistence checks

Now the toggle between Classic and React versions works correctly.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
dwindown
2026-04-18 17:02:14 +07:00
parent bd9cdac02e
commit e8fbfb14c1
74973 changed files with 6658406 additions and 71 deletions

4
node_modules/csp_evaluator/dist/checks/checker.d.ts generated vendored Normal file
View File

@@ -0,0 +1,4 @@
import { Csp } from '../csp';
import { Finding } from '../finding';
export declare type CheckerFunction = (csp: Csp) => Finding[];
//# sourceMappingURL=checker.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"checker.d.ts","sourceRoot":"","sources":["../../checks/checker.ts"],"names":[],"mappings":"AAIA,OAAO,EAAC,GAAG,EAAC,MAAM,QAAQ,CAAC;AAC3B,OAAO,EAAC,OAAO,EAAC,MAAM,YAAY,CAAC;AAMnC,oBAAY,eAAe,GAAG,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,EAAE,CAAC"}

3
node_modules/csp_evaluator/dist/checks/checker.js generated vendored Normal file
View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=checker.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"checker.js","sourceRoot":"","sources":["../../checks/checker.ts"],"names":[],"mappings":""}

View File

@@ -0,0 +1,6 @@
import { Csp } from '../csp';
import { Finding } from '../finding';
export declare function checkUnknownDirective(parsedCsp: Csp): Finding[];
export declare function checkMissingSemicolon(parsedCsp: Csp): Finding[];
export declare function checkInvalidKeyword(parsedCsp: Csp): Finding[];
//# sourceMappingURL=parser_checks.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"parser_checks.d.ts","sourceRoot":"","sources":["../../checks/parser_checks.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAC,GAAG,EAAU,MAAM,QAAQ,CAAC;AAEpC,OAAO,EAAC,OAAO,EAAiB,MAAM,YAAY,CAAC;AAWnD,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,EAAE,CAsB/D;AAYD,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,EAAE,CAsB/D;AAWD,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,EAAE,CAoD7D"}

View File

@@ -0,0 +1,96 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkInvalidKeyword = exports.checkMissingSemicolon = exports.checkUnknownDirective = void 0;
const csp = __importStar(require("../csp"));
const csp_1 = require("../csp");
const finding_1 = require("../finding");
function checkUnknownDirective(parsedCsp) {
const findings = [];
for (const directive of Object.keys(parsedCsp.directives)) {
if (csp.isDirective(directive)) {
continue;
}
if (directive.endsWith(':')) {
findings.push(new finding_1.Finding(finding_1.Type.UNKNOWN_DIRECTIVE, 'CSP directives don\'t end with a colon.', finding_1.Severity.SYNTAX, directive));
}
else {
findings.push(new finding_1.Finding(finding_1.Type.UNKNOWN_DIRECTIVE, 'Directive "' + directive + '" is not a known CSP directive.', finding_1.Severity.SYNTAX, directive));
}
}
return findings;
}
exports.checkUnknownDirective = checkUnknownDirective;
function checkMissingSemicolon(parsedCsp) {
const findings = [];
for (const [directive, directiveValues] of Object.entries(parsedCsp.directives)) {
if (directiveValues === undefined) {
continue;
}
for (const value of directiveValues) {
if (csp.isDirective(value)) {
findings.push(new finding_1.Finding(finding_1.Type.MISSING_SEMICOLON, 'Did you forget the semicolon? ' +
'"' + value + '" seems to be a directive, not a value.', finding_1.Severity.SYNTAX, directive, value));
}
}
}
return findings;
}
exports.checkMissingSemicolon = checkMissingSemicolon;
function checkInvalidKeyword(parsedCsp) {
const findings = [];
const keywordsNoTicks = Object.values(csp_1.Keyword).map((k) => k.replace(/'/g, ''));
for (const [directive, directiveValues] of Object.entries(parsedCsp.directives)) {
if (directiveValues === undefined) {
continue;
}
for (const value of directiveValues) {
if (keywordsNoTicks.some((k) => k === value) ||
value.startsWith('nonce-') ||
value.match(/^(sha256|sha384|sha512)-/)) {
findings.push(new finding_1.Finding(finding_1.Type.INVALID_KEYWORD, 'Did you forget to surround "' + value + '" with single-ticks?', finding_1.Severity.SYNTAX, directive, value));
continue;
}
if (!value.startsWith('\'')) {
continue;
}
if (directive === csp.Directive.REQUIRE_TRUSTED_TYPES_FOR) {
if (value === csp.TrustedTypesSink.SCRIPT) {
continue;
}
}
else if (directive === csp.Directive.TRUSTED_TYPES) {
if (value === '\'allow-duplicates\'' || value === '\'none\'') {
continue;
}
}
else {
if (csp.isKeyword(value) || csp.isHash(value) || csp.isNonce(value)) {
continue;
}
}
findings.push(new finding_1.Finding(finding_1.Type.INVALID_KEYWORD, value + ' seems to be an invalid CSP keyword.', finding_1.Severity.SYNTAX, directive, value));
}
}
return findings;
}
exports.checkInvalidKeyword = checkInvalidKeyword;
//# sourceMappingURL=parser_checks.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"parser_checks.js","sourceRoot":"","sources":["../../checks/parser_checks.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,2 @@
import 'jasmine';
//# sourceMappingURL=parser_checks_test.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"parser_checks_test.d.ts","sourceRoot":"","sources":["../../checks/parser_checks_test.ts"],"names":[],"mappings":"AAmBA,OAAO,SAAS,CAAC"}

View File

@@ -0,0 +1,71 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
require("jasmine");
const finding_1 = require("../finding");
const parser_1 = require("../parser");
const parserChecks = __importStar(require("./parser_checks"));
function checkCsp(test, checkFunction) {
const parsedCsp = (new parser_1.CspParser(test)).csp;
return checkFunction(parsedCsp);
}
describe('Test parser checks', () => {
it('CheckUnknownDirective', () => {
const test = 'foobar-src http:';
const violations = checkCsp(test, parserChecks.checkUnknownDirective);
expect(violations.length).toBe(1);
expect(violations[0].severity).toBe(finding_1.Severity.SYNTAX);
expect(violations[0].directive).toBe('foobar-src');
});
it('CheckMissingSemicolon', () => {
const test = 'default-src foo.bar script-src \'none\'';
const violations = checkCsp(test, parserChecks.checkMissingSemicolon);
expect(violations.length).toBe(1);
expect(violations[0].severity).toBe(finding_1.Severity.SYNTAX);
expect(violations[0].value).toBe('script-src');
});
it('CheckInvalidKeywordForgottenSingleTicks', () => {
const test = 'script-src strict-dynamic nonce-test sha256-asdf';
const violations = checkCsp(test, parserChecks.checkInvalidKeyword);
expect(violations.length).toBe(3);
expect(violations.every((v) => v.severity === finding_1.Severity.SYNTAX)).toBeTrue();
expect(violations.every((v) => v.description.includes('single-ticks')))
.toBeTrue();
});
it('CheckInvalidKeywordUnknownKeyword', () => {
const test = 'script-src \'foo-bar\'';
const violations = checkCsp(test, parserChecks.checkInvalidKeyword);
expect(violations.length).toBe(1);
expect(violations[0].severity).toBe(finding_1.Severity.SYNTAX);
expect(violations[0].value).toBe('\'foo-bar\'');
});
it('CheckInvalidKeywordAllowsRequireTrustedTypesForScript', () => {
const test = 'require-trusted-types-for \'script\'';
const violations = checkCsp(test, parserChecks.checkInvalidKeyword);
expect(violations.length).toBe(0);
});
it('CheckInvalidKeywordAllowsTrustedTypesAllowDuplicateKeyword', () => {
const test = 'trusted-types \'allow-duplicates\' policy1';
const violations = checkCsp(test, parserChecks.checkInvalidKeyword);
expect(violations.length).toBe(0);
});
});
//# sourceMappingURL=parser_checks_test.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"parser_checks_test.js","sourceRoot":"","sources":["../../checks/parser_checks_test.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,22 @@
import { Csp, Directive } from '../csp';
import { Finding } from '../finding';
export declare const DIRECTIVES_CAUSING_XSS: Directive[];
export declare const URL_SCHEMES_CAUSING_XSS: string[];
export declare function checkScriptUnsafeInline(effectiveCsp: Csp): Finding[];
export declare function checkScriptUnsafeEval(parsedCsp: Csp): Finding[];
export declare function checkPlainUrlSchemes(parsedCsp: Csp): Finding[];
export declare function checkWildcards(parsedCsp: Csp): Finding[];
export declare function checkMissingObjectSrcDirective(parsedCsp: Csp): Finding[];
export declare function checkMissingScriptSrcDirective(parsedCsp: Csp): Finding[];
export declare function checkMissingBaseUriDirective(parsedCsp: Csp): Finding[];
export declare function checkMultipleMissingBaseUriDirective(parsedCsps: Csp[]): Finding[];
export declare function checkMissingDirectives(parsedCsp: Csp): Finding[];
export declare function checkScriptAllowlistBypass(parsedCsp: Csp): Finding[];
export declare function checkFlashObjectAllowlistBypass(parsedCsp: Csp): Finding[];
export declare function looksLikeIpAddress(maybeIp: string): boolean;
export declare function checkIpSource(parsedCsp: Csp): Finding[];
export declare function checkDeprecatedDirective(parsedCsp: Csp): Finding[];
export declare function checkNonceLength(parsedCsp: Csp): Finding[];
export declare function checkSrcHttp(parsedCsp: Csp): Finding[];
export declare function checkHasConfiguredReporting(parsedCsp: Csp): Finding[];
//# sourceMappingURL=security_checks.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"security_checks.d.ts","sourceRoot":"","sources":["../../checks/security_checks.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAC,GAAG,EAAE,SAAS,EAAU,MAAM,QAAQ,CAAC;AAC/C,OAAO,EAAC,OAAO,EAAiB,MAAM,YAAY,CAAC;AAQnD,eAAO,MAAM,sBAAsB,EAAE,SAAS,EACsB,CAAC;AAMrE,eAAO,MAAM,uBAAuB,EAAE,MAAM,EAAiC,CAAC;AAgB9E,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,GAAG,GAAG,OAAO,EAAE,CAepE;AAYD,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,EAAE,CAc/D;AAYD,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,EAAE,CAmB9D;AAYD,wBAAgB,cAAc,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,EAAE,CAmBxD;AAMD,wBAAgB,8BAA8B,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,EAAE,CAcxE;AAKD,wBAAgB,8BAA8B,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,EAAE,CAQxE;AAMD,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,EAAE,CAEtE;AAMD,wBAAgB,oCAAoC,CAAC,UAAU,EAAE,GAAG,EAAE,GAClE,OAAO,EAAE,CAkBZ;AAYD,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,EAAE,CAMhE;AAYD,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,EAAE,CAyEpE;AAYD,wBAAgB,+BAA+B,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,EAAE,CAqCzE;AAOD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAc3D;AAWD,wBAAgB,aAAa,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,EAAE,CA8BvD;AAYD,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,EAAE,CA8BlE;AAYD,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,EAAE,CA+B1D;AAYD,wBAAgB,YAAY,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,EAAE,CAiBtD;AAKD,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,EAAE,CAoBrE"}

View File

@@ -0,0 +1,304 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkHasConfiguredReporting = exports.checkSrcHttp = exports.checkNonceLength = exports.checkDeprecatedDirective = exports.checkIpSource = exports.looksLikeIpAddress = exports.checkFlashObjectAllowlistBypass = exports.checkScriptAllowlistBypass = exports.checkMissingDirectives = exports.checkMultipleMissingBaseUriDirective = exports.checkMissingBaseUriDirective = exports.checkMissingScriptSrcDirective = exports.checkMissingObjectSrcDirective = exports.checkWildcards = exports.checkPlainUrlSchemes = exports.checkScriptUnsafeEval = exports.checkScriptUnsafeInline = exports.URL_SCHEMES_CAUSING_XSS = exports.DIRECTIVES_CAUSING_XSS = void 0;
const angular = __importStar(require("../allowlist_bypasses/angular"));
const flash = __importStar(require("../allowlist_bypasses/flash"));
const jsonp = __importStar(require("../allowlist_bypasses/jsonp"));
const csp = __importStar(require("../csp"));
const csp_1 = require("../csp");
const finding_1 = require("../finding");
const utils = __importStar(require("../utils"));
exports.DIRECTIVES_CAUSING_XSS = [csp_1.Directive.SCRIPT_SRC, csp_1.Directive.OBJECT_SRC, csp_1.Directive.BASE_URI];
exports.URL_SCHEMES_CAUSING_XSS = ['data:', 'http:', 'https:'];
function checkScriptUnsafeInline(effectiveCsp) {
const directiveName = effectiveCsp.getEffectiveDirective(csp_1.Directive.SCRIPT_SRC);
const values = effectiveCsp.directives[directiveName] || [];
if (values.includes(csp_1.Keyword.UNSAFE_INLINE)) {
return [new finding_1.Finding(finding_1.Type.SCRIPT_UNSAFE_INLINE, `'unsafe-inline' allows the execution of unsafe in-page scripts ` +
'and event handlers.', finding_1.Severity.HIGH, directiveName, csp_1.Keyword.UNSAFE_INLINE)];
}
return [];
}
exports.checkScriptUnsafeInline = checkScriptUnsafeInline;
function checkScriptUnsafeEval(parsedCsp) {
const directiveName = parsedCsp.getEffectiveDirective(csp_1.Directive.SCRIPT_SRC);
const values = parsedCsp.directives[directiveName] || [];
if (values.includes(csp_1.Keyword.UNSAFE_EVAL)) {
return [new finding_1.Finding(finding_1.Type.SCRIPT_UNSAFE_EVAL, `'unsafe-eval' allows the execution of code injected into DOM APIs ` +
'such as eval().', finding_1.Severity.MEDIUM_MAYBE, directiveName, csp_1.Keyword.UNSAFE_EVAL)];
}
return [];
}
exports.checkScriptUnsafeEval = checkScriptUnsafeEval;
function checkPlainUrlSchemes(parsedCsp) {
const violations = [];
const directivesToCheck = parsedCsp.getEffectiveDirectives(exports.DIRECTIVES_CAUSING_XSS);
for (const directive of directivesToCheck) {
const values = parsedCsp.directives[directive] || [];
for (const value of values) {
if (exports.URL_SCHEMES_CAUSING_XSS.includes(value)) {
violations.push(new finding_1.Finding(finding_1.Type.PLAIN_URL_SCHEMES, value + ' URI in ' + directive + ' allows the execution of ' +
'unsafe scripts.', finding_1.Severity.HIGH, directive, value));
}
}
}
return violations;
}
exports.checkPlainUrlSchemes = checkPlainUrlSchemes;
function checkWildcards(parsedCsp) {
const violations = [];
const directivesToCheck = parsedCsp.getEffectiveDirectives(exports.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_1.Finding(finding_1.Type.PLAIN_WILDCARD, directive + ` should not allow '*' as source`, finding_1.Severity.HIGH, directive, value));
continue;
}
}
}
return violations;
}
exports.checkWildcards = checkWildcards;
function checkMissingObjectSrcDirective(parsedCsp) {
let objectRestrictions = [];
if (csp_1.Directive.OBJECT_SRC in parsedCsp.directives) {
objectRestrictions = parsedCsp.directives[csp_1.Directive.OBJECT_SRC];
}
else if (csp_1.Directive.DEFAULT_SRC in parsedCsp.directives) {
objectRestrictions = parsedCsp.directives[csp_1.Directive.DEFAULT_SRC];
}
if (objectRestrictions !== undefined && objectRestrictions.length >= 1) {
return [];
}
return [new finding_1.Finding(finding_1.Type.MISSING_DIRECTIVES, `Missing object-src allows the injection of plugins which can execute JavaScript. Can you set it to 'none'?`, finding_1.Severity.HIGH, csp_1.Directive.OBJECT_SRC)];
}
exports.checkMissingObjectSrcDirective = checkMissingObjectSrcDirective;
function checkMissingScriptSrcDirective(parsedCsp) {
if (csp_1.Directive.SCRIPT_SRC in parsedCsp.directives ||
csp_1.Directive.DEFAULT_SRC in parsedCsp.directives) {
return [];
}
return [new finding_1.Finding(finding_1.Type.MISSING_DIRECTIVES, 'script-src directive is missing.', finding_1.Severity.HIGH, csp_1.Directive.SCRIPT_SRC)];
}
exports.checkMissingScriptSrcDirective = checkMissingScriptSrcDirective;
function checkMissingBaseUriDirective(parsedCsp) {
return checkMultipleMissingBaseUriDirective([parsedCsp]);
}
exports.checkMissingBaseUriDirective = checkMissingBaseUriDirective;
function checkMultipleMissingBaseUriDirective(parsedCsps) {
const needsBaseUri = (csp) => (csp.policyHasScriptNonces() ||
(csp.policyHasScriptHashes() && csp.policyHasStrictDynamic()));
const hasBaseUri = (csp) => csp_1.Directive.BASE_URI in csp.directives;
if (parsedCsps.some(needsBaseUri) && !parsedCsps.some(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_1.Finding(finding_1.Type.MISSING_DIRECTIVES, description, finding_1.Severity.HIGH, csp_1.Directive.BASE_URI)];
}
return [];
}
exports.checkMultipleMissingBaseUriDirective = checkMultipleMissingBaseUriDirective;
function checkMissingDirectives(parsedCsp) {
return [
...checkMissingObjectSrcDirective(parsedCsp),
...checkMissingScriptSrcDirective(parsedCsp),
...checkMissingBaseUriDirective(parsedCsp),
];
}
exports.checkMissingDirectives = checkMissingDirectives;
function checkScriptAllowlistBypass(parsedCsp) {
const violations = [];
const effectiveScriptSrcDirective = parsedCsp.getEffectiveDirective(csp_1.Directive.SCRIPT_SRC);
const scriptSrcValues = parsedCsp.directives[effectiveScriptSrcDirective] || [];
if (scriptSrcValues.includes(csp_1.Keyword.NONE)) {
return violations;
}
for (const value of scriptSrcValues) {
if (value === csp_1.Keyword.SELF) {
violations.push(new finding_1.Finding(finding_1.Type.SCRIPT_ALLOWLIST_BYPASS, `'self' can be problematic if you host JSONP, AngularJS or user ` +
'uploaded files.', finding_1.Severity.MEDIUM_MAYBE, effectiveScriptSrcDirective, value));
continue;
}
if (value.startsWith('\'')) {
continue;
}
if (csp.isUrlScheme(value) || value.indexOf('.') === -1) {
continue;
}
const url = '//' + utils.getSchemeFreeUrl(value);
const angularBypass = utils.matchWildcardUrls(url, angular.URLS);
let jsonpBypass = utils.matchWildcardUrls(url, jsonp.URLS);
if (jsonpBypass) {
const evalRequired = jsonp.NEEDS_EVAL.includes(jsonpBypass.hostname);
const evalPresent = scriptSrcValues.includes(csp_1.Keyword.UNSAFE_EVAL);
if (evalRequired && !evalPresent) {
jsonpBypass = null;
}
}
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_1.Finding(finding_1.Type.SCRIPT_ALLOWLIST_BYPASS, bypassDomain + ' is known to host' + bypassTxt +
' which allow to bypass this CSP.', finding_1.Severity.HIGH, effectiveScriptSrcDirective, value));
}
else {
violations.push(new finding_1.Finding(finding_1.Type.SCRIPT_ALLOWLIST_BYPASS, `No bypass found; make sure that this URL doesn't serve JSONP ` +
'replies or Angular libraries.', finding_1.Severity.MEDIUM_MAYBE, effectiveScriptSrcDirective, value));
}
}
return violations;
}
exports.checkScriptAllowlistBypass = checkScriptAllowlistBypass;
function checkFlashObjectAllowlistBypass(parsedCsp) {
const violations = [];
const effectiveObjectSrcDirective = parsedCsp.getEffectiveDirective(csp_1.Directive.OBJECT_SRC);
const objectSrcValues = parsedCsp.directives[effectiveObjectSrcDirective] || [];
const pluginTypes = parsedCsp.directives[csp_1.Directive.PLUGIN_TYPES];
if (pluginTypes && !pluginTypes.includes('application/x-shockwave-flash')) {
return [];
}
for (const value of objectSrcValues) {
if (value === csp_1.Keyword.NONE) {
return [];
}
const url = '//' + utils.getSchemeFreeUrl(value);
const flashBypass = utils.matchWildcardUrls(url, flash.URLS);
if (flashBypass) {
violations.push(new finding_1.Finding(finding_1.Type.OBJECT_ALLOWLIST_BYPASS, flashBypass.hostname +
' is known to host Flash files which allow to bypass this CSP.', finding_1.Severity.HIGH, effectiveObjectSrcDirective, value));
}
else if (effectiveObjectSrcDirective === csp_1.Directive.OBJECT_SRC) {
violations.push(new finding_1.Finding(finding_1.Type.OBJECT_ALLOWLIST_BYPASS, `Can you restrict object-src to 'none' only?`, finding_1.Severity.MEDIUM_MAYBE, effectiveObjectSrcDirective, value));
}
}
return violations;
}
exports.checkFlashObjectAllowlistBypass = checkFlashObjectAllowlistBypass;
function looksLikeIpAddress(maybeIp) {
if (maybeIp.startsWith('[') && maybeIp.endsWith(']')) {
return true;
}
if (/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/.test(maybeIp)) {
return true;
}
return false;
}
exports.looksLikeIpAddress = looksLikeIpAddress;
function checkIpSource(parsedCsp) {
const violations = [];
const checkIp = (directive, directiveValues) => {
for (const value of directiveValues) {
const host = utils.getHostname(value);
if (looksLikeIpAddress(host)) {
if (host === '127.0.0.1') {
violations.push(new finding_1.Finding(finding_1.Type.IP_SOURCE, directive + ' directive allows localhost as source. ' +
'Please make sure to remove this in production environments.', finding_1.Severity.INFO, directive, value));
}
else {
violations.push(new finding_1.Finding(finding_1.Type.IP_SOURCE, directive + ' directive has an IP-Address as source: ' + host +
' (will be ignored by browsers!). ', finding_1.Severity.INFO, directive, value));
}
}
}
};
utils.applyCheckFunktionToDirectives(parsedCsp, checkIp);
return violations;
}
exports.checkIpSource = checkIpSource;
function checkDeprecatedDirective(parsedCsp) {
const violations = [];
if (csp_1.Directive.REFLECTED_XSS in parsedCsp.directives) {
violations.push(new finding_1.Finding(finding_1.Type.DEPRECATED_DIRECTIVE, 'reflected-xss is deprecated since CSP2. ' +
'Please, use the X-XSS-Protection header instead.', finding_1.Severity.INFO, csp_1.Directive.REFLECTED_XSS));
}
if (csp_1.Directive.REFERRER in parsedCsp.directives) {
violations.push(new finding_1.Finding(finding_1.Type.DEPRECATED_DIRECTIVE, 'referrer is deprecated since CSP2. ' +
'Please, use the Referrer-Policy header instead.', finding_1.Severity.INFO, csp_1.Directive.REFERRER));
}
if (csp_1.Directive.DISOWN_OPENER in parsedCsp.directives) {
violations.push(new finding_1.Finding(finding_1.Type.DEPRECATED_DIRECTIVE, 'disown-opener is deprecated since CSP3. ' +
'Please, use the Cross Origin Opener Policy header instead.', finding_1.Severity.INFO, csp_1.Directive.DISOWN_OPENER));
}
return violations;
}
exports.checkDeprecatedDirective = checkDeprecatedDirective;
function checkNonceLength(parsedCsp) {
const noncePattern = new RegExp('^\'nonce-(.+)\'$');
const violations = [];
utils.applyCheckFunktionToDirectives(parsedCsp, (directive, directiveValues) => {
for (const value of directiveValues) {
const match = value.match(noncePattern);
if (!match) {
continue;
}
const nonceValue = match[1];
if (nonceValue.length < 8) {
violations.push(new finding_1.Finding(finding_1.Type.NONCE_LENGTH, 'Nonces should be at least 8 characters long.', finding_1.Severity.MEDIUM, directive, value));
}
if (!csp.isNonce(value, true)) {
violations.push(new finding_1.Finding(finding_1.Type.NONCE_CHARSET, 'Nonces should only use the base64 charset.', finding_1.Severity.INFO, directive, value));
}
}
});
return violations;
}
exports.checkNonceLength = checkNonceLength;
function checkSrcHttp(parsedCsp) {
const violations = [];
utils.applyCheckFunktionToDirectives(parsedCsp, (directive, directiveValues) => {
for (const value of directiveValues) {
const description = directive === csp_1.Directive.REPORT_URI ?
'Use HTTPS to send violation reports securely.' :
'Allow only resources downloaded over HTTPS.';
if (value.startsWith('http://')) {
violations.push(new finding_1.Finding(finding_1.Type.SRC_HTTP, description, finding_1.Severity.MEDIUM, directive, value));
}
}
});
return violations;
}
exports.checkSrcHttp = checkSrcHttp;
function checkHasConfiguredReporting(parsedCsp) {
const reportUriValues = parsedCsp.directives[csp_1.Directive.REPORT_URI] || [];
if (reportUriValues.length > 0) {
return [];
}
const reportToValues = parsedCsp.directives[csp_1.Directive.REPORT_TO] || [];
if (reportToValues.length > 0) {
return [new finding_1.Finding(finding_1.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.`, finding_1.Severity.INFO, csp_1.Directive.REPORT_TO)];
}
return [new finding_1.Finding(finding_1.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.', finding_1.Severity.INFO, csp_1.Directive.REPORT_URI)];
}
exports.checkHasConfiguredReporting = checkHasConfiguredReporting;
//# sourceMappingURL=security_checks.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=security_checks_test.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"security_checks_test.d.ts","sourceRoot":"","sources":["../../checks/security_checks_test.ts"],"names":[],"mappings":""}

View File

@@ -0,0 +1,329 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const csp_1 = require("../csp");
const finding_1 = require("../finding");
const parser_1 = require("../parser");
const securityChecks = __importStar(require("./security_checks"));
function checkCsp(test, checkFunction) {
const parsedCsp = (new parser_1.CspParser(test)).csp;
return checkFunction(parsedCsp);
}
describe('Test security checks', () => {
it('CheckScriptUnsafeInlineInScriptSrc', () => {
const test = 'default-src https:; script-src \'unsafe-inline\'';
const violations = checkCsp(test, securityChecks.checkScriptUnsafeInline);
expect(violations.length).toBe(1);
expect(violations[0].severity).toBe(finding_1.Severity.HIGH);
});
it('CheckScriptUnsafeInlineInDefaultSrc', () => {
const test = 'default-src \'unsafe-inline\'';
const violations = checkCsp(test, securityChecks.checkScriptUnsafeInline);
expect(violations.length).toBe(1);
});
it('CheckScriptUnsafeInlineInDefaultSrcAndNotInScriptSrc', () => {
const test = 'default-src \'unsafe-inline\'; script-src https:';
const violations = checkCsp(test, securityChecks.checkScriptUnsafeInline);
expect(violations.length).toBe(0);
});
it('CheckScriptUnsafeInlineWithNonce', () => {
const test = 'script-src \'unsafe-inline\' \'nonce-foobar\'';
const parsedCsp = (new parser_1.CspParser(test)).csp;
let effectiveCsp = parsedCsp.getEffectiveCsp(csp_1.Version.CSP1);
let violations = securityChecks.checkScriptUnsafeInline(effectiveCsp);
expect(violations.length).toBe(1);
effectiveCsp = parsedCsp.getEffectiveCsp(csp_1.Version.CSP3);
violations = securityChecks.checkScriptUnsafeInline(effectiveCsp);
expect(violations.length).toBe(0);
});
it('CheckScriptUnsafeEvalInScriptSrc', () => {
const test = 'default-src https:; script-src \'unsafe-eval\'';
const violations = checkCsp(test, securityChecks.checkScriptUnsafeEval);
expect(violations.length).toBe(1);
expect(violations[0].severity).toBe(finding_1.Severity.MEDIUM_MAYBE);
});
it('CheckScriptUnsafeEvalInDefaultSrc', () => {
const test = 'default-src \'unsafe-eval\'';
const violations = checkCsp(test, securityChecks.checkScriptUnsafeEval);
expect(violations.length).toBe(1);
});
it('CheckPlainUrlSchemesInScriptSrc', () => {
const test = 'script-src data: http: https: sthInvalid:';
const violations = checkCsp(test, securityChecks.checkPlainUrlSchemes);
expect(violations.length).toBe(3);
expect(violations.every((v) => v.severity === finding_1.Severity.HIGH)).toBeTrue();
});
it('CheckPlainUrlSchemesInObjectSrc', () => {
const test = 'object-src data: http: https: sthInvalid:';
const violations = checkCsp(test, securityChecks.checkPlainUrlSchemes);
expect(violations.length).toBe(3);
expect(violations.every((v) => v.severity === finding_1.Severity.HIGH)).toBeTrue();
});
it('CheckPlainUrlSchemesInBaseUri', () => {
const test = 'base-uri data: http: https: sthInvalid:';
const violations = checkCsp(test, securityChecks.checkPlainUrlSchemes);
expect(violations.length).toBe(3);
expect(violations.every((v) => v.severity === finding_1.Severity.HIGH)).toBeTrue();
});
it('CheckPlainUrlSchemesMixed', () => {
const test = 'default-src https:; object-src data: sthInvalid:';
const violations = checkCsp(test, securityChecks.checkPlainUrlSchemes);
expect(violations.length).toBe(2);
expect(violations.every((v) => v.severity === finding_1.Severity.HIGH)).toBeTrue();
expect(violations[0].directive).toBe(csp_1.Directive.DEFAULT_SRC);
expect(violations[1].directive).toBe(csp_1.Directive.OBJECT_SRC);
});
it('CheckPlainUrlSchemesDangerousDirectivesOK', () => {
const test = 'default-src https:; object-src \'none\'; script-src \'none\'; ' +
'base-uri \'none\'';
const violations = checkCsp(test, securityChecks.checkPlainUrlSchemes);
expect(violations.length).toBe(0);
});
it('CheckWildcardsInScriptSrc', () => {
const test = 'script-src * http://* //*';
const violations = checkCsp(test, securityChecks.checkWildcards);
expect(violations.length).toBe(3);
expect(violations.every((v) => v.severity === finding_1.Severity.HIGH)).toBeTrue();
});
it('CheckWildcardsInObjectSrc', () => {
const test = 'object-src * http://* //*';
const violations = checkCsp(test, securityChecks.checkWildcards);
expect(violations.length).toBe(3);
expect(violations.every((v) => v.severity === finding_1.Severity.HIGH)).toBeTrue();
});
it('CheckWildcardsInBaseUri', () => {
const test = 'base-uri * http://* //*';
const violations = checkCsp(test, securityChecks.checkWildcards);
expect(violations.length).toBe(3);
expect(violations.every((v) => v.severity === finding_1.Severity.HIGH)).toBeTrue();
});
it('CheckWildcardsSchemesMixed', () => {
const test = 'default-src *; object-src * ignore.me.com';
const violations = checkCsp(test, securityChecks.checkWildcards);
expect(violations.length).toBe(2);
expect(violations.every((v) => v.severity === finding_1.Severity.HIGH)).toBeTrue();
expect(violations[0].directive).toBe(csp_1.Directive.DEFAULT_SRC);
expect(violations[1].directive).toBe(csp_1.Directive.OBJECT_SRC);
});
it('CheckWildcardsDangerousDirectivesOK', () => {
const test = 'default-src *; object-src *.foo.bar; script-src \'none\'; ' +
'base-uri \'none\'';
const violations = checkCsp(test, securityChecks.checkWildcards);
expect(violations.length).toBe(0);
});
it('CheckMissingDirectivesMissingObjectSrc', () => {
const test = 'script-src \'none\'';
const violations = checkCsp(test, securityChecks.checkMissingDirectives);
expect(violations.length).toBe(1);
expect(violations[0].severity).toBe(finding_1.Severity.HIGH);
});
it('CheckMissingDirectivesMissingScriptSrc', () => {
const test = 'object-src \'none\'';
const violations = checkCsp(test, securityChecks.checkMissingDirectives);
expect(violations.length).toBe(1);
expect(violations[0].severity).toBe(finding_1.Severity.HIGH);
});
it('CheckMissingDirectivesObjectSrcSelf', () => {
const test = 'object-src \'self\'';
const violations = checkCsp(test, securityChecks.checkMissingDirectives);
expect(violations.length).toBe(1);
expect(violations[0].severity).toBe(finding_1.Severity.HIGH);
});
it('CheckMissingDirectivesMissingBaseUriInNonceCsp', () => {
const test = 'script-src \'nonce-123\'; object-src \'none\'';
const violations = checkCsp(test, securityChecks.checkMissingDirectives);
expect(violations.length).toBe(1);
expect(violations[0].severity).toBe(finding_1.Severity.HIGH);
});
it('CheckMissingDirectivesMissingBaseUriInHashWStrictDynamicCsp', () => {
const test = 'script-src \'sha256-123456\' \'strict-dynamic\'; object-src \'none\'';
const violations = checkCsp(test, securityChecks.checkMissingDirectives);
expect(violations.length).toBe(1);
expect(violations[0].severity).toBe(finding_1.Severity.HIGH);
});
it('CheckMissingDirectivesMissingBaseUriInHashCsp', () => {
const test = 'script-src \'sha256-123456\'; object-src \'none\'';
const violations = checkCsp(test, securityChecks.checkMissingDirectives);
expect(violations.length).toBe(0);
});
it('CheckMissingDirectivesScriptAndObjectSrcSet', () => {
const test = 'script-src \'none\'; object-src \'none\'';
const violations = checkCsp(test, securityChecks.checkMissingDirectives);
expect(violations.length).toBe(0);
});
it('CheckMissingDirectivesDefaultSrcSet', () => {
const test = 'default-src https:;';
const violations = checkCsp(test, securityChecks.checkMissingDirectives);
expect(violations.length).toBe(0);
});
it('CheckMissingDirectivesDefaultSrcSetToNone', () => {
const test = 'default-src \'none\';';
const violations = checkCsp(test, securityChecks.checkMissingDirectives);
expect(violations.length).toBe(0);
});
it('checkScriptAllowlistBypassJSONPBypass', () => {
const test = 'script-src *.google.com';
const violations = checkCsp(test, securityChecks.checkScriptAllowlistBypass);
expect(violations.length).toBe(1);
expect(violations[0].severity).toBe(finding_1.Severity.HIGH);
expect(violations[0].description.includes('www.google.com is known to host JSONP endpoints which'))
.toBeTrue();
});
it('checkScriptAllowlistBypassWithNoneAndJSONPBypass', () => {
const test = 'script-src *.google.com \'none\'';
const violations = checkCsp(test, securityChecks.checkScriptAllowlistBypass);
expect(violations.length).toBe(0);
});
it('checkScriptAllowlistBypassJSONPBypassEvalRequired', () => {
const test = 'script-src https://googletagmanager.com \'unsafe-eval\'';
const violations = checkCsp(test, securityChecks.checkScriptAllowlistBypass);
expect(violations.length).toBe(1);
expect(violations[0].severity).toBe(finding_1.Severity.HIGH);
});
it('checkScriptAllowlistBypassJSONPBypassEvalRequiredNotPresent', () => {
const test = 'script-src https://googletagmanager.com';
const violations = checkCsp(test, securityChecks.checkScriptAllowlistBypass);
expect(violations.length).toBe(1);
expect(violations[0].severity).toBe(finding_1.Severity.MEDIUM_MAYBE);
});
it('checkScriptAllowlistBypassAngularBypass', () => {
const test = 'script-src gstatic.com';
const violations = checkCsp(test, securityChecks.checkScriptAllowlistBypass);
expect(violations.length).toBe(1);
expect(violations[0].severity).toBe(finding_1.Severity.HIGH);
expect(violations[0].description.includes('gstatic.com is known to host Angular libraries which'))
.toBeTrue();
});
it('checkScriptAllowlistBypassNoBypassWarningOnly', () => {
const test = 'script-src foo.bar';
const violations = checkCsp(test, securityChecks.checkScriptAllowlistBypass);
expect(violations.length).toBe(1);
expect(violations[0].severity).toBe(finding_1.Severity.MEDIUM_MAYBE);
});
it('checkScriptAllowlistBypassNoBypassSelfWarningOnly', () => {
const test = 'script-src \'self\'';
const violations = checkCsp(test, securityChecks.checkScriptAllowlistBypass);
expect(violations.length).toBe(1);
expect(violations[0].severity).toBe(finding_1.Severity.MEDIUM_MAYBE);
});
it('checkFlashObjectAllowlistBypassFlashBypass', () => {
const test = 'object-src https://*.googleapis.com';
const violations = checkCsp(test, securityChecks.checkFlashObjectAllowlistBypass);
expect(violations.length).toBe(1);
expect(violations[0].severity).toBe(finding_1.Severity.HIGH);
});
it('checkFlashObjectAllowlistBypassNoFlashBypass', () => {
const test = 'object-src https://foo.bar';
const violations = checkCsp(test, securityChecks.checkFlashObjectAllowlistBypass);
expect(violations.length).toBe(1);
expect(violations[0].severity).toBe(finding_1.Severity.MEDIUM_MAYBE);
});
it('checkFlashObjectAllowlistBypassSelfAllowed', () => {
const test = 'object-src \'self\'';
const violations = checkCsp(test, securityChecks.checkFlashObjectAllowlistBypass);
expect(violations.length).toBe(1);
expect(violations[0].severity).toBe(finding_1.Severity.MEDIUM_MAYBE);
expect(violations[0].description)
.toBe('Can you restrict object-src to \'none\' only?');
});
it('CheckIpSource', () => {
const test = 'script-src 8.8.8.8; font-src //127.0.0.1 https://[::1] not.an.ip';
const violations = checkCsp(test, securityChecks.checkIpSource);
expect(violations.length).toBe(3);
expect(violations.every((v) => v.severity === finding_1.Severity.INFO)).toBeTrue();
});
it('LooksLikeIpAddressIPv4', () => {
expect(securityChecks.looksLikeIpAddress('8.8.8.8')).toBeTrue();
});
it('LooksLikeIpAddressIPv6', () => {
expect(securityChecks.looksLikeIpAddress('[::1]')).toBeTrue();
});
it('CheckDeprecatedDirectiveReportUriWithReportTo', () => {
const test = 'report-uri foo.bar/csp;report-to abc';
const violations = checkCsp(test, securityChecks.checkDeprecatedDirective);
expect(violations.length).toBe(0);
});
it('CheckDeprecatedDirectiveWithoutReportUriButWithReportTo', () => {
const test = 'report-to abc';
const violations = checkCsp(test, securityChecks.checkDeprecatedDirective);
expect(violations.length).toBe(0);
});
it('CheckDeprecatedDirectiveReflectedXss', () => {
const test = 'reflected-xss block';
const violations = checkCsp(test, securityChecks.checkDeprecatedDirective);
expect(violations.length).toBe(1);
expect(violations[0].severity).toBe(finding_1.Severity.INFO);
});
it('CheckDeprecatedDirectiveReferrer', () => {
const test = 'referrer origin';
const violations = checkCsp(test, securityChecks.checkDeprecatedDirective);
expect(violations.length).toBe(1);
expect(violations[0].severity).toBe(finding_1.Severity.INFO);
});
it('CheckNonceLengthWithLongNonce', () => {
const test = 'script-src \'nonce-veryLongRandomNonce\'';
const violations = checkCsp(test, securityChecks.checkNonceLength);
expect(violations.length).toBe(0);
});
it('CheckNonceLengthWithShortNonce', () => {
const test = 'script-src \'nonce-short\'';
const violations = checkCsp(test, securityChecks.checkNonceLength);
expect(violations.length).toBe(1);
expect(violations[0].severity).toBe(finding_1.Severity.MEDIUM);
});
it('CheckNonceLengthInvalidCharset', () => {
const test = 'script-src \'nonce-***notBase64***\'';
const violations = checkCsp(test, securityChecks.checkNonceLength);
expect(violations.length).toBe(1);
expect(violations[0].severity).toBe(finding_1.Severity.INFO);
});
it('CheckSrcHttp', () => {
const test = 'script-src http://foo.bar https://test.com; report-uri http://test.com';
const violations = checkCsp(test, securityChecks.checkSrcHttp);
expect(violations.length).toBe(2);
expect(violations.every((v) => v.severity === finding_1.Severity.MEDIUM)).toBeTrue();
});
it('CheckHasConfiguredReporting_whenNoReporting', () => {
const test = 'script-src \'nonce-aaaaaaaaaa\'';
const violations = checkCsp(test, securityChecks.checkHasConfiguredReporting);
expect(violations.length).toBe(1);
expect(violations[0].severity).toBe(finding_1.Severity.INFO);
expect(violations[0].directive).toBe('report-uri');
});
it('CheckHasConfiguredReporting_whenOnlyReportTo', () => {
const test = 'script-src \'nonce-aaaaaaaaaa\'; report-to name';
const violations = checkCsp(test, securityChecks.checkHasConfiguredReporting);
expect(violations.length).toBe(1);
expect(violations[0].severity).toBe(finding_1.Severity.INFO);
expect(violations[0].directive).toBe('report-to');
});
it('CheckHasConfiguredReporting_whenOnlyReportUri', () => {
const test = 'script-src \'nonce-aaaaaaaaaa\'; report-uri url';
const violations = checkCsp(test, securityChecks.checkHasConfiguredReporting);
expect(violations.length).toBe(0);
});
it('CheckHasConfiguredReporting_whenReportUriAndReportTo', () => {
const test = 'script-src \'nonce-aaaaaaaaaa\'; report-uri url; report-to name';
const violations = checkCsp(test, securityChecks.checkHasConfiguredReporting);
expect(violations.length).toBe(0);
});
});
//# sourceMappingURL=security_checks_test.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
import { Csp } from '../csp';
import { Finding } from '../finding';
export declare function checkStrictDynamic(parsedCsp: Csp): Finding[];
export declare function checkStrictDynamicNotStandalone(parsedCsp: Csp): Finding[];
export declare function checkUnsafeInlineFallback(parsedCsp: Csp): Finding[];
export declare function checkAllowlistFallback(parsedCsp: Csp): Finding[];
export declare function checkRequiresTrustedTypesForScripts(parsedCsp: Csp): Finding[];
//# sourceMappingURL=strictcsp_checks.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"strictcsp_checks.d.ts","sourceRoot":"","sources":["../../checks/strictcsp_checks.ts"],"names":[],"mappings":"AA6BA,OAAO,EAAC,GAAG,EAAU,MAAM,QAAQ,CAAC;AAEpC,OAAO,EAAC,OAAO,EAAiB,MAAM,YAAY,CAAC;AAWnD,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,EAAE,CAiB5D;AAWD,wBAAgB,+BAA+B,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,EAAE,CAezE;AAaD,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,EAAE,CAmBnE;AAcD,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,EAAE,CAqBhE;AAWD,wBAAgB,mCAAmC,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,EAAE,CAe7E"}

View File

@@ -0,0 +1,87 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkRequiresTrustedTypesForScripts = exports.checkAllowlistFallback = exports.checkUnsafeInlineFallback = exports.checkStrictDynamicNotStandalone = exports.checkStrictDynamic = void 0;
const csp = __importStar(require("../csp"));
const csp_1 = require("../csp");
const finding_1 = require("../finding");
function checkStrictDynamic(parsedCsp) {
const directiveName = parsedCsp.getEffectiveDirective(csp.Directive.SCRIPT_SRC);
const values = parsedCsp.directives[directiveName] || [];
const schemeOrHostPresent = values.some((v) => !v.startsWith('\''));
if (schemeOrHostPresent && !values.includes(csp_1.Keyword.STRICT_DYNAMIC)) {
return [new finding_1.Finding(finding_1.Type.STRICT_DYNAMIC, 'Host allowlists can frequently be bypassed. Consider using ' +
'\'strict-dynamic\' in combination with CSP nonces or hashes.', finding_1.Severity.STRICT_CSP, directiveName)];
}
return [];
}
exports.checkStrictDynamic = checkStrictDynamic;
function checkStrictDynamicNotStandalone(parsedCsp) {
const directiveName = parsedCsp.getEffectiveDirective(csp.Directive.SCRIPT_SRC);
const values = parsedCsp.directives[directiveName] || [];
if (values.includes(csp_1.Keyword.STRICT_DYNAMIC) &&
(!parsedCsp.policyHasScriptNonces() &&
!parsedCsp.policyHasScriptHashes())) {
return [new finding_1.Finding(finding_1.Type.STRICT_DYNAMIC_NOT_STANDALONE, '\'strict-dynamic\' without a CSP nonce/hash will block all scripts.', finding_1.Severity.INFO, directiveName)];
}
return [];
}
exports.checkStrictDynamicNotStandalone = checkStrictDynamicNotStandalone;
function checkUnsafeInlineFallback(parsedCsp) {
if (!parsedCsp.policyHasScriptNonces() &&
!parsedCsp.policyHasScriptHashes()) {
return [];
}
const directiveName = parsedCsp.getEffectiveDirective(csp.Directive.SCRIPT_SRC);
const values = parsedCsp.directives[directiveName] || [];
if (!values.includes(csp_1.Keyword.UNSAFE_INLINE)) {
return [new finding_1.Finding(finding_1.Type.UNSAFE_INLINE_FALLBACK, 'Consider adding \'unsafe-inline\' (ignored by browsers supporting ' +
'nonces/hashes) to be backward compatible with older browsers.', finding_1.Severity.STRICT_CSP, directiveName)];
}
return [];
}
exports.checkUnsafeInlineFallback = checkUnsafeInlineFallback;
function checkAllowlistFallback(parsedCsp) {
const directiveName = parsedCsp.getEffectiveDirective(csp.Directive.SCRIPT_SRC);
const values = parsedCsp.directives[directiveName] || [];
if (!values.includes(csp_1.Keyword.STRICT_DYNAMIC)) {
return [];
}
if (!values.some((v) => ['http:', 'https:', '*'].includes(v) || v.includes('.'))) {
return [new finding_1.Finding(finding_1.Type.ALLOWLIST_FALLBACK, 'Consider adding https: and http: url schemes (ignored by browsers ' +
'supporting \'strict-dynamic\') to be backward compatible with older ' +
'browsers.', finding_1.Severity.STRICT_CSP, directiveName)];
}
return [];
}
exports.checkAllowlistFallback = checkAllowlistFallback;
function checkRequiresTrustedTypesForScripts(parsedCsp) {
const directiveName = parsedCsp.getEffectiveDirective(csp.Directive.REQUIRE_TRUSTED_TYPES_FOR);
const values = parsedCsp.directives[directiveName] || [];
if (!values.includes(csp.TrustedTypesSink.SCRIPT)) {
return [new finding_1.Finding(finding_1.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.', finding_1.Severity.INFO, csp.Directive.REQUIRE_TRUSTED_TYPES_FOR)];
}
return [];
}
exports.checkRequiresTrustedTypesForScripts = checkRequiresTrustedTypesForScripts;
//# sourceMappingURL=strictcsp_checks.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"strictcsp_checks.js","sourceRoot":"","sources":["../../checks/strictcsp_checks.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=strictcsp_checks_test.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"strictcsp_checks_test.d.ts","sourceRoot":"","sources":["../../checks/strictcsp_checks_test.ts"],"names":[],"mappings":""}

View File

@@ -0,0 +1,79 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const finding_1 = require("../finding");
const parser_1 = require("../parser");
const strictcspChecks = __importStar(require("./strictcsp_checks"));
function checkCsp(test, checkFunction) {
const parsedCsp = (new parser_1.CspParser(test)).csp;
return checkFunction(parsedCsp);
}
describe('Test strictcsp checks', () => {
it('CheckStrictDynamic', () => {
const test = 'script-src foo.bar';
const violations = checkCsp(test, strictcspChecks.checkStrictDynamic);
expect(violations.length).toBe(1);
expect(violations[0].severity).toBe(finding_1.Severity.STRICT_CSP);
});
it('CheckStrictDynamicNotStandalone', () => {
const test = 'script-src \'strict-dynamic\'';
const violations = checkCsp(test, strictcspChecks.checkStrictDynamicNotStandalone);
expect(violations[0].severity).toBe(finding_1.Severity.INFO);
});
it('CheckStrictDynamicNotStandaloneDoesntFireIfNoncePresent', () => {
const test = 'script-src \'strict-dynamic\' \'nonce-foobar\'';
const violations = checkCsp(test, strictcspChecks.checkStrictDynamicNotStandalone);
expect(violations.length).toBe(0);
});
it('CheckUnsafeInlineFallback', () => {
const test = 'script-src \'nonce-test\'';
const violations = checkCsp(test, strictcspChecks.checkUnsafeInlineFallback);
expect(violations.length).toBe(1);
expect(violations[0].severity).toBe(finding_1.Severity.STRICT_CSP);
});
it('CheckUnsafeInlineFallbackDoesntFireIfFallbackPresent', () => {
const test = 'script-src \'nonce-test\' \'unsafe-inline\'';
const violations = checkCsp(test, strictcspChecks.checkUnsafeInlineFallback);
expect(violations.length).toBe(0);
});
it('checkAllowlistFallback', () => {
const test = 'script-src \'nonce-test\' \'strict-dynamic\'';
const violations = checkCsp(test, strictcspChecks.checkAllowlistFallback);
expect(violations.length).toBe(1);
expect(violations[0].severity).toBe(finding_1.Severity.STRICT_CSP);
});
it('checkAllowlistFallbackDoesntFireIfSchemeFallbackPresent', () => {
const test = 'script-src \'nonce-test\' \'strict-dynamic\' https:';
const violations = checkCsp(test, strictcspChecks.checkAllowlistFallback);
expect(violations.length).toBe(0);
});
it('checkAllowlistFallbackDoesntFireIfURLFallbackPresent', () => {
const test = 'script-src \'nonce-test\' \'strict-dynamic\' foo.bar';
const violations = checkCsp(test, strictcspChecks.checkAllowlistFallback);
expect(violations.length).toBe(0);
});
it('checkAllowlistFallbackDoesntFireInAbsenceOfStrictDynamic', () => {
const test = 'script-src \'nonce-test\'';
const violations = checkCsp(test, strictcspChecks.checkAllowlistFallback);
expect(violations.length).toBe(0);
});
});
//# sourceMappingURL=strictcsp_checks_test.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"strictcsp_checks_test.js","sourceRoot":"","sources":["../../checks/strictcsp_checks_test.ts"],"names":[],"mappings":"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"}