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>
319 lines
12 KiB
Text
319 lines
12 KiB
Text
/**
|
|
* @fileoverview Tests for CSP Defintions.
|
|
* @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 'jasmine';
|
|
|
|
import {Directive, isDirective, isHash, isKeyword, isNonce, isUrlScheme, Keyword, Version} from './csp';
|
|
import {Finding, Severity, Type} from './finding';
|
|
import {CspParser} from './parser';
|
|
|
|
describe('Test Csp', () => {
|
|
it('ConvertToString', () => {
|
|
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)).csp;
|
|
expect(parsed.convertToString()).toBe(testCsp);
|
|
});
|
|
|
|
it('GetEffectiveCspVersion1', () => {
|
|
const testCsp =
|
|
'default-src \'unsafe-inline\' \'strict-dynamic\' \'nonce-123\' ' +
|
|
'\'sha256-foobar\' \'self\'; report-to foo.bar; worker-src *; manifest-src *; script-src-attr \'self\'; script-src-elem \'self\'; style-src-attr \'self\'; style-src-elem \'self\';';
|
|
const parsed = (new CspParser(testCsp)).csp;
|
|
const findings: Finding[] = [];
|
|
const effectiveCsp = parsed.getEffectiveCsp(Version.CSP1, findings);
|
|
|
|
expect(findings.length)
|
|
.toBe(0); // No warnings about unsafe-inline being ignored in CSP 1.
|
|
expect(effectiveCsp.directives[Directive.DEFAULT_SRC]).toEqual([
|
|
'\'unsafe-inline\'', '\'self\''
|
|
]);
|
|
expect(effectiveCsp.directives.hasOwnProperty(Directive.REPORT_TO))
|
|
.toBeFalse();
|
|
expect(effectiveCsp.directives.hasOwnProperty(Directive.WORKER_SRC))
|
|
.toBeFalse();
|
|
expect(effectiveCsp.directives.hasOwnProperty(Directive.MANIFEST_SRC))
|
|
.toBeFalse();
|
|
});
|
|
|
|
it('GetEffectiveCspVersion2', () => {
|
|
const testCsp =
|
|
'default-src \'unsafe-inline\' \'strict-dynamic\' \'nonce-123\' ' +
|
|
'\'sha256-foobar\' \'self\'; report-to foo.bar; worker-src *; manifest-src *; script-src-attr \'self\'; script-src-elem \'self\'; style-src-attr \'self\'; style-src-elem \'self\';';
|
|
const parsed = (new CspParser(testCsp)).csp;
|
|
const findings: Finding[] = [];
|
|
const effectiveCsp = parsed.getEffectiveCsp(Version.CSP2, findings);
|
|
|
|
// Ignored messages are surfaced properly.
|
|
expect(findings.length).toBe(1); // Only nonces cause unsafe-inline to be
|
|
// ignored in CSP2, not strict-dynamic.
|
|
expect(findings[0].value).toBe('\'unsafe-inline\'');
|
|
expect(findings[0].type).toBe(Type.IGNORED);
|
|
expect(findings[0].severity).toBe(Severity.NONE);
|
|
expect(findings[0].directive).toBe(Directive.DEFAULT_SRC);
|
|
|
|
expect(effectiveCsp.directives[Directive.DEFAULT_SRC]).toEqual([
|
|
'\'nonce-123\'', '\'sha256-foobar\'', '\'self\''
|
|
]);
|
|
expect(effectiveCsp.directives.hasOwnProperty(Directive.REPORT_TO))
|
|
.toBeFalse();
|
|
expect(effectiveCsp.directives.hasOwnProperty(Directive.WORKER_SRC))
|
|
.toBeFalse();
|
|
expect(effectiveCsp.directives.hasOwnProperty(Directive.MANIFEST_SRC))
|
|
.toBeFalse();
|
|
expect(effectiveCsp.directives.hasOwnProperty(Directive.SCRIPT_SRC_ATTR))
|
|
.toBeFalse();
|
|
expect(effectiveCsp.directives.hasOwnProperty(Directive.SCRIPT_SRC_ELEM))
|
|
.toBeFalse();
|
|
expect(effectiveCsp.directives.hasOwnProperty(Directive.STYLE_SRC_ATTR))
|
|
.toBeFalse();
|
|
expect(effectiveCsp.directives.hasOwnProperty(Directive.STYLE_SRC_ELEM))
|
|
.toBeFalse();
|
|
});
|
|
|
|
it('GetEffectiveCspVersion3', () => {
|
|
const testCsp =
|
|
'default-src \'unsafe-inline\' \'strict-dynamic\' \'nonce-123\' ' +
|
|
'\'sha256-foobar\' \'self\'; report-to foo.bar; worker-src *; manifest-src *; script-src-attr \'self\'; script-src-elem \'self\'; style-src-attr \'self\'; style-src-elem \'self\';';
|
|
const parsed = (new CspParser(testCsp)).csp;
|
|
const findings: Finding[] = [];
|
|
const effectiveCsp = parsed.getEffectiveCsp(Version.CSP3, findings);
|
|
|
|
// Ignored messages are only on the default-src: Ignoring self and
|
|
// unsafe-inline because of strict-dynamic and the unsafe-inline because of
|
|
// the nonce.
|
|
expect(findings.length).toBe(3);
|
|
expect(findings.every(f => f.type === Type.IGNORED)).toBeTrue();
|
|
expect(findings.every(f => f.severity === Severity.NONE)).toBeTrue();
|
|
expect(findings.every(f => f.directive === Directive.DEFAULT_SRC))
|
|
.toBeTrue();
|
|
|
|
expect(effectiveCsp.directives[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[Directive.SCRIPT_SRC_ATTR]).toEqual([
|
|
'\'self\''
|
|
]);
|
|
expect(effectiveCsp.directives[Directive.SCRIPT_SRC_ELEM]).toEqual([
|
|
'\'self\''
|
|
]);
|
|
expect(effectiveCsp.directives[Directive.STYLE_SRC_ATTR]).toEqual([
|
|
'\'self\''
|
|
]);
|
|
expect(effectiveCsp.directives[Directive.STYLE_SRC_ELEM]).toEqual([
|
|
'\'self\''
|
|
]);
|
|
});
|
|
|
|
|
|
it('GetEffectiveDirective', () => {
|
|
const testCsp = 'default-src https:; script-src foo.bar';
|
|
const parsed = (new CspParser(testCsp)).csp;
|
|
|
|
const script = parsed.getEffectiveDirective(Directive.SCRIPT_SRC);
|
|
expect(script).toBe(Directive.SCRIPT_SRC);
|
|
const style = parsed.getEffectiveDirective(Directive.STYLE_SRC);
|
|
expect(style).toBe(Directive.DEFAULT_SRC);
|
|
});
|
|
|
|
|
|
it('GetEffectiveDirectives', () => {
|
|
const testCsp = 'default-src https:; script-src foo.bar';
|
|
const parsed = (new CspParser(testCsp)).csp;
|
|
|
|
const directives = parsed.getEffectiveDirectives(
|
|
[Directive.SCRIPT_SRC, Directive.STYLE_SRC]);
|
|
expect(directives).toEqual([Directive.SCRIPT_SRC, Directive.DEFAULT_SRC]);
|
|
});
|
|
|
|
it('GetEffectiveDirectives', () => {
|
|
const testCsp = 'default-src https:; style-src-elem foo.bar';
|
|
const parsed = (new CspParser(testCsp)).csp;
|
|
|
|
const directives = parsed.getEffectiveDirectives([
|
|
Directive.STYLE_SRC, Directive.STYLE_SRC_ATTR, Directive.STYLE_SRC_ELEM
|
|
]);
|
|
// Both style-src and style-src-attr default to default-src.
|
|
expect(directives).toEqual([
|
|
Directive.DEFAULT_SRC, Directive.STYLE_SRC_ELEM
|
|
]);
|
|
});
|
|
|
|
it('GetEffectiveDirectives', () => {
|
|
const testCsp =
|
|
'default-src https:; script-src foo.bar; script-src-elem bar.baz; style-src foo.bar; style-src-elem bar.baz';
|
|
const parsed = (new CspParser(testCsp)).csp;
|
|
|
|
const directives = parsed.getEffectiveDirectives([
|
|
Directive.SCRIPT_SRC, Directive.STYLE_SRC, Directive.SCRIPT_SRC_ATTR,
|
|
Directive.SCRIPT_SRC_ELEM, Directive.STYLE_SRC_ATTR,
|
|
Directive.STYLE_SRC_ELEM
|
|
]);
|
|
expect(directives).toEqual([
|
|
Directive.SCRIPT_SRC, // Both script-src and script-src-attr
|
|
Directive.STYLE_SRC, // Both style-src and style-src-attr
|
|
Directive.SCRIPT_SRC_ELEM,
|
|
Directive.STYLE_SRC_ELEM,
|
|
]);
|
|
});
|
|
|
|
it('PolicyHasScriptNoncesScriptSrcWithNonce', () => {
|
|
const testCsp = 'default-src https:; script-src \'nonce-test123\'';
|
|
const parsed = (new CspParser(testCsp)).csp;
|
|
|
|
expect(parsed.policyHasScriptNonces()).toBeTrue();
|
|
});
|
|
|
|
|
|
it('PolicyHasScriptNoncesNoNonce', () => {
|
|
const testCsp =
|
|
'default-src https: \'nonce-ignored\'; script-src nonce-invalid';
|
|
const parsed = (new CspParser(testCsp)).csp;
|
|
|
|
expect(parsed.policyHasScriptNonces()).toBeFalse();
|
|
});
|
|
|
|
|
|
it('PolicyHasScriptHashesScriptSrcWithHash', () => {
|
|
const testCsp = 'default-src https:; script-src \'sha256-asdfASDF\'';
|
|
const parsed = (new CspParser(testCsp)).csp;
|
|
|
|
expect(parsed.policyHasScriptHashes()).toBeTrue();
|
|
});
|
|
|
|
|
|
it('PolicyHasScriptHashesNoHash', () => {
|
|
const testCsp =
|
|
'default-src https: \'nonce-ignored\'; script-src sha256-invalid';
|
|
const parsed = (new CspParser(testCsp)).csp;
|
|
|
|
expect(parsed.policyHasScriptHashes()).toBeFalse();
|
|
});
|
|
|
|
|
|
it('PolicyHasStrictDynamicScriptSrcWithStrictDynamic', () => {
|
|
const testCsp = 'default-src https:; script-src \'strict-dynamic\'';
|
|
const parsed = (new CspParser(testCsp)).csp;
|
|
|
|
expect(parsed.policyHasStrictDynamic()).toBeTrue();
|
|
});
|
|
|
|
|
|
it('PolicyHasStrictDynamicDefaultSrcWithStrictDynamic', () => {
|
|
const testCsp = 'default-src https \'strict-dynamic\'';
|
|
const parsed = (new CspParser(testCsp)).csp;
|
|
|
|
expect(parsed.policyHasStrictDynamic()).toBeTrue();
|
|
});
|
|
|
|
|
|
it('PolicyHasStrictDynamicNoStrictDynamic', () => {
|
|
const testCsp = 'default-src \'strict-dynamic\'; script-src foo.bar';
|
|
const parsed = (new CspParser(testCsp)).csp;
|
|
|
|
expect(parsed.policyHasStrictDynamic()).toBeFalse();
|
|
});
|
|
|
|
|
|
it('IsDirective', () => {
|
|
const directives = Object.keys(Directive).map(
|
|
(name) => Directive[name as keyof typeof Directive]);
|
|
|
|
expect(directives.every(isDirective)).toBeTrue();
|
|
expect(isDirective('invalid-src')).toBeFalse();
|
|
});
|
|
|
|
|
|
it('IsKeyword', () => {
|
|
const keywords = Object.keys(Keyword).map(
|
|
(name) => (Keyword[name as keyof typeof Keyword]));
|
|
|
|
expect(keywords.every(isKeyword)).toBeTrue();
|
|
expect(isKeyword('invalid')).toBeFalse();
|
|
});
|
|
|
|
|
|
it('IsUrlScheme', () => {
|
|
expect(isUrlScheme('http:')).toBeTrue();
|
|
expect(isUrlScheme('https:')).toBeTrue();
|
|
expect(isUrlScheme('data:')).toBeTrue();
|
|
expect(isUrlScheme('blob:')).toBeTrue();
|
|
expect(isUrlScheme('b+l.o-b:')).toBeTrue();
|
|
expect(isUrlScheme('filesystem:')).toBeTrue();
|
|
expect(isUrlScheme('invalid')).toBeFalse();
|
|
expect(isUrlScheme('ht_tp:')).toBeFalse();
|
|
});
|
|
|
|
|
|
it('IsNonce', () => {
|
|
expect(isNonce('\'nonce-asdfASDF=\'')).toBeTrue();
|
|
expect(isNonce('\'sha256-asdfASDF=\'')).toBeFalse();
|
|
expect(isNonce('\'asdfASDF=\'')).toBeFalse();
|
|
expect(isNonce('example.com')).toBeFalse();
|
|
});
|
|
|
|
|
|
it('IsStrictNonce', () => {
|
|
expect(isNonce('\'nonce-asdfASDF=\'', true)).toBeTrue();
|
|
expect(isNonce('\'nonce-as+df/A0234SDF==\'', true)).toBeTrue();
|
|
expect(isNonce('\'nonce-as_dfASDF=\'', true)).toBeTrue();
|
|
expect(isNonce('\'nonce-asdfASDF===\'', true)).toBeFalse();
|
|
expect(isNonce('\'sha256-asdfASDF=\'', true)).toBeFalse();
|
|
});
|
|
|
|
|
|
it('IsHash', () => {
|
|
expect(isHash('\'sha256-asdfASDF=\'')).toBeTrue();
|
|
expect(isHash('\'sha777-asdfASDF=\'')).toBeFalse();
|
|
expect(isHash('\'asdfASDF=\'')).toBeFalse();
|
|
expect(isHash('example.com')).toBeFalse();
|
|
});
|
|
|
|
it('IsStrictHash', () => {
|
|
expect(isHash('\'sha256-asdfASDF=\'', true)).toBeTrue();
|
|
expect(isHash('\'sha256-as+d/f/ASD0+4F==\'', true)).toBeTrue();
|
|
expect(isHash('\'sha256-asdfASDF===\'', true)).toBeFalse();
|
|
expect(isHash('\'sha256-asd_fASDF=\'', true)).toBeFalse();
|
|
expect(isHash('\'sha777-asdfASDF=\'', true)).toBeFalse();
|
|
expect(isHash('\'asdfASDF=\'', true)).toBeFalse();
|
|
expect(isHash('example.com', true)).toBeFalse();
|
|
});
|
|
|
|
it('ParseNavigateTo', () => {
|
|
const testCsp = 'navigate-to \'self\'; script-src \'nonce-foo\'';
|
|
const parsed = (new CspParser(testCsp)).csp;
|
|
|
|
expect(parsed.policyHasStrictDynamic()).toBeFalse();
|
|
expect(parsed.policyHasScriptNonces()).toBeTrue();
|
|
});
|
|
|
|
it('ParseWebRtc', () => {
|
|
const testCsp = 'web-rtc \'allow\'; script-src \'nonce-foo\'';
|
|
const parsed = (new CspParser(testCsp)).csp;
|
|
|
|
expect(parsed.policyHasStrictDynamic()).toBeFalse();
|
|
expect(parsed.policyHasScriptNonces()).toBeTrue();
|
|
});
|
|
});
|