Next.js website for Rocky Mountain Vending company featuring: - Product catalog with Stripe integration - Service areas and parts pages - Admin dashboard with Clerk authentication - SEO optimized pages with JSON-LD structured data Co-authored-by: Cursor <cursoragent@cursor.com>
607 lines
20 KiB
Text
607 lines
20 KiB
Text
/**
|
|
* @fileoverview Collection of CSP evaluation checks.
|
|
* @author lwe@google.com (Lukas Weichselbaum)
|
|
*
|
|
* @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 * 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 {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.SCRIPT_SRC_ATTR, Directive.SCRIPT_SRC_ELEM,
|
|
Directive.OBJECT_SRC, Directive.BASE_URI
|
|
];
|
|
|
|
/**
|
|
* A list of URL schemes that can allow XSS vulnerabilities when requests to
|
|
* them are made.
|
|
*/
|
|
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.
|
|
* 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:
|
|
* script-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 checkScriptUnsafeInline(effectiveCsp: Csp): Finding[] {
|
|
const violations: Finding[] = [];
|
|
const directivesToCheck = effectiveCsp.getEffectiveDirectives([
|
|
Directive.SCRIPT_SRC, Directive.SCRIPT_SRC_ATTR, Directive.SCRIPT_SRC_ELEM
|
|
]);
|
|
|
|
for (const directive of directivesToCheck) {
|
|
const values = effectiveCsp.directives[directive] || [];
|
|
if (values.includes(Keyword.UNSAFE_INLINE)) {
|
|
violations.push(new Finding(
|
|
Type.SCRIPT_UNSAFE_INLINE,
|
|
`'unsafe-inline' allows the execution of unsafe in-page scripts ` +
|
|
'and event handlers.',
|
|
Severity.HIGH, directive, Keyword.UNSAFE_INLINE));
|
|
}
|
|
if (values.includes(Keyword.UNSAFE_HASHES)) {
|
|
violations.push(new Finding(
|
|
Type.SCRIPT_UNSAFE_HASHES,
|
|
`'unsafe-hashes', while safer than 'unsafe-inline', allows the execution of unsafe in-page scripts and event handlers as long as their hashes appear in the CSP. Please refactor them to no longer use inline scripts if possible.`,
|
|
Severity.MEDIUM_MAYBE, directive, Keyword.UNSAFE_HASHES));
|
|
}
|
|
}
|
|
|
|
return violations;
|
|
}
|
|
|
|
|
|
/**
|
|
* Checks if passed csp allows eval in scripts.
|
|
* Findings of this check have a medium severity and are FP free.
|
|
*
|
|
* Example policy where this check would trigger:
|
|
* script-src 'unsafe-eval'
|
|
*
|
|
* @param parsedCsp Parsed CSP.
|
|
*/
|
|
export function checkScriptUnsafeEval(parsedCsp: Csp): Finding[] {
|
|
const violations: Finding[] = [];
|
|
const directivesToCheck = parsedCsp.getEffectiveDirectives([
|
|
Directive.SCRIPT_SRC, Directive.SCRIPT_SRC_ATTR, Directive.SCRIPT_SRC_ELEM
|
|
]);
|
|
|
|
for (const directive of directivesToCheck) {
|
|
const values = parsedCsp.directives[directive] || [];
|
|
if (values.includes(Keyword.UNSAFE_EVAL)) {
|
|
violations.push(new Finding(
|
|
Type.SCRIPT_UNSAFE_EVAL,
|
|
`'unsafe-eval' allows the execution of code injected into DOM APIs ` +
|
|
'such as eval().',
|
|
Severity.MEDIUM_MAYBE, directive, Keyword.UNSAFE_EVAL));
|
|
}
|
|
}
|
|
|
|
return violations;
|
|
}
|
|
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* Example policy where this check would trigger:
|
|
* script-src https: http: data:
|
|
*
|
|
* @param parsedCsp Parsed CSP.
|
|
*/
|
|
export function checkPlainUrlSchemes(parsedCsp: Csp): 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));
|
|
}
|
|
}
|
|
}
|
|
|
|
return violations;
|
|
}
|
|
|
|
|
|
/**
|
|
* Checks if csp contains wildcards in sensitive directives.
|
|
* Findings of this check have a high severity and are FP free.
|
|
*
|
|
* Example policy where this check would trigger:
|
|
* script-src *
|
|
*
|
|
* @param parsedCsp Parsed CSP.
|
|
*/
|
|
export function checkWildcards(parsedCsp: Csp): 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
return violations;
|
|
}
|
|
|
|
/**
|
|
* Checks if object-src is restricted to none either directly or via a
|
|
* default-src.
|
|
*/
|
|
export function checkMissingObjectSrcDirective(parsedCsp: Csp): 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];
|
|
}
|
|
if (objectRestrictions !== undefined && objectRestrictions.length >= 1) {
|
|
return [];
|
|
}
|
|
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)];
|
|
}
|
|
|
|
/**
|
|
* 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 [];
|
|
}
|
|
return [new Finding(
|
|
Type.MISSING_DIRECTIVES, 'script-src directive is missing.',
|
|
Severity.HIGH, Directive.SCRIPT_SRC)];
|
|
}
|
|
|
|
/**
|
|
* 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]);
|
|
}
|
|
|
|
/**
|
|
* 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. ' +
|
|
'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.
|
|
*
|
|
* Example policy where this check would trigger:
|
|
* script-src 'none'
|
|
*
|
|
* @param parsedCsp Parsed CSP.
|
|
*/
|
|
export function checkMissingDirectives(parsedCsp: Csp): Finding[] {
|
|
return [
|
|
...checkMissingObjectSrcDirective(parsedCsp),
|
|
...checkMissingScriptSrcDirective(parsedCsp),
|
|
...checkMissingBaseUriDirective(parsedCsp),
|
|
];
|
|
}
|
|
|
|
|
|
/**
|
|
* Checks if allowlisted origins are bypassable by JSONP/Angular endpoints.
|
|
* High severity findings of this check are FP free.
|
|
*
|
|
* Example policy where this check would trigger:
|
|
* default-src 'none'; script-src www.google.com
|
|
*
|
|
* @param parsedCsp Parsed CSP.
|
|
*/
|
|
export function checkScriptAllowlistBypass(parsedCsp: Csp): Finding[] {
|
|
const violations: Finding[] = [];
|
|
parsedCsp
|
|
.getEffectiveDirectives([Directive.SCRIPT_SRC, Directive.SCRIPT_SRC_ELEM])
|
|
.forEach(effectiveScriptSrcDirective => {
|
|
const scriptSrcValues =
|
|
parsedCsp.directives[effectiveScriptSrcDirective] || [];
|
|
if (scriptSrcValues.includes(Keyword.NONE)) {
|
|
return;
|
|
}
|
|
|
|
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 keywords, nonces and hashes (they start with a single
|
|
// quote).
|
|
if (value.startsWith('\'')) {
|
|
continue;
|
|
}
|
|
|
|
// Ignore standalone schemes and things that don't look like URLs (no
|
|
// dot).
|
|
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);
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
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));
|
|
}
|
|
}
|
|
});
|
|
|
|
return violations;
|
|
}
|
|
|
|
|
|
/**
|
|
* Checks if allowlisted object-src origins are bypassable.
|
|
* Findings of this check have a high severity and are FP free.
|
|
*
|
|
* Example policy where this check would trigger:
|
|
* default-src 'none'; object-src ajax.googleapis.com
|
|
*
|
|
* @param parsedCsp Parsed CSP.
|
|
*/
|
|
export function checkFlashObjectAllowlistBypass(parsedCsp: Csp): 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) {
|
|
return [];
|
|
}
|
|
|
|
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));
|
|
}
|
|
}
|
|
|
|
return violations;
|
|
}
|
|
|
|
/**
|
|
* Returns whether the given string "looks" like an IP address. This function
|
|
* only uses basic heuristics and does not accept all valid IPs nor reject all
|
|
* invalid IPs.
|
|
*/
|
|
export function looksLikeIpAddress(maybeIp: string): boolean {
|
|
if (maybeIp.startsWith('[') && maybeIp.endsWith(']')) {
|
|
// Looks like an IPv6 address and not a hostname (though it may be some
|
|
// nonsense like `[foo]`)
|
|
return true;
|
|
}
|
|
if (/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/.test(maybeIp)) {
|
|
// Looks like an IPv4 address (though it may be something like
|
|
// `500.600.700.800`
|
|
return true;
|
|
}
|
|
// Won't match IP addresses encoded in other manners (eg octal or
|
|
// decimal)
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Checks if csp contains IP addresses.
|
|
* Findings of this check are informal only and are FP free.
|
|
*
|
|
* Example policy where this check would trigger:
|
|
* script-src 127.0.0.1
|
|
*
|
|
* @param parsedCsp Parsed CSP.
|
|
*/
|
|
export function checkIpSource(parsedCsp: Csp): 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));
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// Apply check to values of all directives.
|
|
utils.applyCheckFunktionToDirectives(parsedCsp, checkIp);
|
|
return violations;
|
|
}
|
|
|
|
|
|
/**
|
|
* Checks if csp contains directives that are deprecated in CSP3.
|
|
* Findings of this check are informal only and are FP free.
|
|
*
|
|
* Example policy where this check would trigger:
|
|
* report-uri foo.bar/csp
|
|
*
|
|
* @param parsedCsp Parsed CSP.
|
|
*/
|
|
export function checkDeprecatedDirective(parsedCsp: Csp): 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));
|
|
}
|
|
|
|
// 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://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://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/prefetch-src
|
|
if (Directive.PREFETCH_SRC in parsedCsp.directives) {
|
|
violations.push(new Finding(
|
|
Type.DEPRECATED_DIRECTIVE,
|
|
'prefetch-src is deprecated since CSP3. ' +
|
|
'Be aware that this feature may cease to work at any time.',
|
|
Severity.INFO, Directive.PREFETCH_SRC));
|
|
}
|
|
return violations;
|
|
}
|
|
|
|
|
|
/**
|
|
* Checks if csp nonce is at least 8 characters long.
|
|
* Findings of this check are of medium severity and are FP free.
|
|
*
|
|
* Example policy where this check would trigger:
|
|
* script-src 'nonce-short'
|
|
*
|
|
* @param parsedCsp Parsed CSP.
|
|
*/
|
|
export function checkNonceLength(parsedCsp: Csp): 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));
|
|
}
|
|
}
|
|
});
|
|
|
|
return violations;
|
|
}
|
|
|
|
|
|
/**
|
|
* Checks if CSP allows sourcing from http://
|
|
* Findings of this check are of medium severity and are FP free.
|
|
*
|
|
* Example policy where this check would trigger:
|
|
* report-uri http://foo.bar/csp
|
|
*
|
|
* @param parsedCsp Parsed CSP.
|
|
*/
|
|
export function checkSrcHttp(parsedCsp: Csp): Finding[] {
|
|
const violations: Finding[] = [];
|
|
|
|
utils.applyCheckFunktionToDirectives(
|
|
parsedCsp, (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));
|
|
}
|
|
}
|
|
});
|
|
|
|
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 [];
|
|
}
|
|
|
|
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)];
|
|
}
|
|
|
|
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)];
|
|
}
|