diff --git a/package-lock.json b/package-lock.json index 289e6c20..a7bc0f05 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,6 @@ "mdast-util-to-string": "4.0.0", "micromark-util-subtokenize": "2.0.4", "mime": "4.0.6", - "parse5-html-rewriting-stream": "7.0.0", "rehype-format": "5.0.1", "rehype-parse": "9.0.1", "remark-parse": "11.0.0", @@ -10783,20 +10782,6 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/parse5-html-rewriting-stream": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-7.0.0.tgz", - "integrity": "sha512-mazCyGWkmCRWDI15Zp+UiCqMp/0dgEmkZRvhlsqqKYr4SsVm/TvnSpD9fCvqCA2zoWJcfRym846ejWBBHRiYEg==", - "license": "MIT", - "dependencies": { - "entities": "^4.3.0", - "parse5": "^7.0.0", - "parse5-sax-parser": "^7.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, "node_modules/parse5-htmlparser2-tree-adapter": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", @@ -10818,6 +10803,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-7.0.0.tgz", "integrity": "sha512-5A+v2SNsq8T6/mG3ahcz8ZtQ0OUFTatxPbeidoMB7tkJSGDY3tdfl4MHovtLQHkEn5CGxijNWRQHhRQ6IRpXKg==", + "dev": true, "license": "MIT", "dependencies": { "parse5": "^7.0.0" diff --git a/package.json b/package.json index 4b3a455f..23210d30 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,6 @@ "mdast-util-to-string": "4.0.0", "micromark-util-subtokenize": "2.0.4", "mime": "4.0.6", - "parse5-html-rewriting-stream": "7.0.0", "rehype-format": "5.0.1", "rehype-parse": "9.0.1", "remark-parse": "11.0.0", diff --git a/src/html-pipe.js b/src/html-pipe.js index e5221e2b..4ddee834 100644 --- a/src/html-pipe.js +++ b/src/html-pipe.js @@ -149,7 +149,6 @@ export async function htmlPipe(state, req) { if (state.content.sourceBus === 'code' || state.info.originalExtension === '.md') { state.timer?.update('serialize'); - await setCustomResponseHeaders(state, req, res); await renderCode(state, req, res); } else { state.timer?.update('parse'); @@ -166,7 +165,6 @@ export async function htmlPipe(state, req) { await createPictures(state); await extractMetaData(state, req); await addHeadingIds(state); - await setCustomResponseHeaders(state, req, res); await render(state, req, res); state.timer?.update('serialize'); await tohtml(state, req, res); @@ -174,6 +172,7 @@ export async function htmlPipe(state, req) { } setLastModified(state, res); + await setCustomResponseHeaders(state, req, res); await setXSurrogateKeyHeader(state, req, res); } catch (e) { res.error = e.message; diff --git a/src/steps/csp.js b/src/steps/csp.js deleted file mode 100644 index 4c4b6b5d..00000000 --- a/src/steps/csp.js +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright 2024 Adobe. All rights reserved. - * This file is licensed to you 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 REPRESENTATIONS - * OF ANY KIND, either express or implied. See the License for the specific language - * governing permissions and limitations under the License. - */ -import crypto from 'crypto'; -import { select } from 'hast-util-select'; -import { remove } from 'unist-util-remove'; -import { RewritingStream } from 'parse5-html-rewriting-stream'; -import { visit } from 'unist-util-visit'; - -export const NONCE_AEM = '\'nonce-aem\''; - -/** - * Parse a CSP string into its directives - * @param {string | undefined | null} csp - * @returns {Object} - */ -function parseCSP(csp) { - if (!csp) { - return {}; - } - - const parts = csp.split(';'); - const result = {}; - parts.forEach((part) => { - const [directive, ...values] = part.trim().split(' '); - result[directive] = values.join(' '); - }); - return result; -} - -/** - * Computes where nonces should be applied - * @param {string | null | undefined} metaCSPText The actual CSP value from the meta tag - * @param {string | null | undefined} headersCSPText The actual CSP value from the headers - * @returns {scriptNonce: boolean, styleNonce: boolean} - */ -function shouldApplyNonce(metaCSPText, headersCSPText) { - const metaBased = parseCSP(metaCSPText); - const headersBased = parseCSP(headersCSPText); - return { - scriptNonce: metaBased['script-src']?.includes(NONCE_AEM) - || headersBased['script-src']?.includes(NONCE_AEM), - styleNonce: metaBased['style-src']?.includes(NONCE_AEM) - || headersBased['style-src']?.includes(NONCE_AEM), - }; -} - -/** - * Create a nonce for CSP - * @returns {string} - */ -function createNonce() { - return crypto.randomBytes(18).toString('base64'); -} - -/** - * Get the applied CSP header from a response - * @param {PipelineResponse} res - * @returns {string} - */ -export function getHeaderCSP(res) { - return res.headers?.get('content-security-policy'); -} - -/** - * Apply CSP with nonces on an AST - * @param {PipelineResponse} res - * @param {Object} tree - * @param {Object} metaCSP - * @param {string} headersCSP - */ -function createAndApplyNonceOnAST(res, tree, metaCSP, headersCSP) { - const nonce = createNonce(); - const { scriptNonce, styleNonce } = shouldApplyNonce(metaCSP?.properties.content, headersCSP); - - if (metaCSP) { - metaCSP.properties.content = metaCSP.properties.content.replaceAll(NONCE_AEM, `'nonce-${nonce}'`); - } - - if (headersCSP) { - res.headers.set('content-security-policy', headersCSP.replaceAll(NONCE_AEM, `'nonce-${nonce}'`)); - } - - visit(tree, (node) => { - if (scriptNonce && node.tagName === 'script' && node.properties?.nonce === 'aem') { - node.properties.nonce = nonce; - return; - } - - if (styleNonce - && (node.tagName === 'style' || (node.tagName === 'link' && node.properties?.rel?.[0] === 'stylesheet')) - && node.properties?.nonce === 'aem' - ) { - node.properties.nonce = nonce; - } - }); -} - -export function checkResponseBodyForMetaBasedCSP(res) { - return res.body?.includes('http-equiv="content-security-policy"') - || res.body?.includes('http-equiv="Content-Security-Policy"'); -} - -export function checkResponseBodyForAEMNonce(res) { - /* - we only look for 'nonce-aem' (single quote) to see if there is a meta CSP with nonce - we don't want to generate nonces if they appear just on script/style tags, - as those have no effect without the actual CSP meta (or header). - this means it is ok to not check for the "nonce-aem" (double quotes) - */ - return res.body?.includes(NONCE_AEM); -} - -export function getMetaCSP(tree) { - return select('meta[http-equiv="content-security-policy"]', tree) - || select('meta[http-equiv="Content-Security-Policy"]', tree); -} - -export function contentSecurityPolicyOnAST(res, tree) { - const metaCSP = getMetaCSP(tree); - const headersCSP = getHeaderCSP(res); - - if (!metaCSP && !headersCSP) { - // No CSP defined - return; - } - - // CSP with nonce - if (metaCSP?.properties.content.includes(NONCE_AEM) || headersCSP?.includes(NONCE_AEM)) { - createAndApplyNonceOnAST(res, tree, metaCSP, headersCSP); - } - - if (metaCSP?.properties['move-as-header'] === 'true') { - if (!headersCSP) { - // if we have a CSP in meta but no CSP in headers - // we can move the CSP from meta to headers, if requested - res.headers.set('content-security-policy', metaCSP.properties.content); - remove(tree, null, metaCSP); - } else { - delete metaCSP.properties['move-as-header']; - } - } -} - -export function contentSecurityPolicyOnCode(state, res) { - if (state.type !== 'html') { - return; - } - - const cspHeader = getHeaderCSP(res); - if (!( - cspHeader?.includes(NONCE_AEM) - || (checkResponseBodyForMetaBasedCSP(res) && checkResponseBodyForAEMNonce(res)) - )) { - return; - } - - const nonce = createNonce(); - let { scriptNonce, styleNonce } = shouldApplyNonce(null, cspHeader); - - const rewriter = new RewritingStream(); - const chunks = []; - - rewriter.on('startTag', (tag, rawHTML) => { - if (tag.tagName === 'meta' - && tag.attrs.find( - (attr) => attr.name.toLowerCase() === 'http-equiv' && attr.value.toLowerCase() === 'content-security-policy', - ) - ) { - const contentAttr = tag.attrs.find((attr) => attr.name.toLowerCase() === 'content'); - if (contentAttr) { - ({ scriptNonce, styleNonce } = shouldApplyNonce(contentAttr.value, cspHeader)); - - if (!cspHeader && tag.attrs.find((attr) => attr.name === 'move-as-header' && attr.value === 'true')) { - res.headers.set('content-security-policy', contentAttr.value.replaceAll(NONCE_AEM, `'nonce-${nonce}'`)); - return; // don't push the chunk so it gets removed from the response body - } - chunks.push(rawHTML.replaceAll(NONCE_AEM, `'nonce-${nonce}'`)); - return; - } - } - - if (scriptNonce && tag.tagName === 'script' && tag.attrs.find((attr) => attr.name === 'nonce' && attr.value === 'aem')) { - chunks.push(rawHTML.replace(/nonce="aem"/i, `nonce="${nonce}"`)); - return; - } - - if (styleNonce && (tag.tagName === 'style' || tag.tagName === 'link') && tag.attrs.find((attr) => attr.name === 'nonce' && attr.value === 'aem')) { - chunks.push(rawHTML.replace(/nonce="aem"/i, `nonce="${nonce}"`)); - return; - } - - chunks.push(rawHTML); - }); - - rewriter.on('data', (data) => { - chunks.push(data); - }); - - rewriter.write(res.body); - rewriter.end(); - res.body = chunks.join(''); - if (cspHeader) { - res.headers.set('content-security-policy', cspHeader.replaceAll(NONCE_AEM, `'nonce-${nonce}'`)); - } -} diff --git a/src/steps/fetch-404.js b/src/steps/fetch-404.js index 1c65bbf5..592406a0 100644 --- a/src/steps/fetch-404.js +++ b/src/steps/fetch-404.js @@ -10,7 +10,6 @@ * governing permissions and limitations under the License. */ import { extractLastModified, recordLastModified } from '../utils/last-modified.js'; -import { contentSecurityPolicyOnCode } from './csp.js'; import { computeContentPathKey, computeCodePathKey } from './set-x-surrogate-key-header.js'; /** @@ -35,7 +34,6 @@ export default async function fetch404(state, req, res) { // keep 404 response status res.body = ret.body; - contentSecurityPolicyOnCode(state, res); res.headers.set('last-modified', ret.headers.get('last-modified')); res.headers.set('content-type', 'text/html; charset=utf-8'); } diff --git a/src/steps/render-code.js b/src/steps/render-code.js index 46a00592..4c954568 100644 --- a/src/steps/render-code.js +++ b/src/steps/render-code.js @@ -10,9 +10,6 @@ * governing permissions and limitations under the License. */ import mime from 'mime'; -import { - contentSecurityPolicyOnCode, -} from './csp.js'; const CHARSET_RE = /charset=([^()<>@,;:"/[\]?.=\s]*)/i; @@ -35,6 +32,4 @@ export default async function renderCode(state, req, res) { } } res.headers.set('content-type', contentType); - - contentSecurityPolicyOnCode(state, res); } diff --git a/src/steps/render.js b/src/steps/render.js index 9e747187..94f5b657 100644 --- a/src/steps/render.js +++ b/src/steps/render.js @@ -15,7 +15,6 @@ import { h } from 'hastscript'; import { unified } from 'unified'; import rehypeParse from 'rehype-parse'; import { cleanupHeaderValue } from '@adobe/helix-shared-utils'; -import { contentSecurityPolicyOnAST } from './csp.js'; function appendElement($parent, $el) { if ($el) { @@ -103,7 +102,6 @@ export default async function render(state, req, res) { const $headHtml = await unified() .use(rehypeParse, { fragment: true }) .parse(headHtml); - contentSecurityPolicyOnAST(res, $headHtml); $head.children.push(...$headHtml.children); } diff --git a/test/fixtures/code/super-test/404-csp-nonce.html b/test/fixtures/code/super-test/404-csp-nonce.html deleted file mode 100644 index 0b4aa8c5..00000000 --- a/test/fixtures/code/super-test/404-csp-nonce.html +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - Page not found - - - - - - - - - - - - -
-
-
- - 404 - -

Page Not Found

-

- Go home -

-
-
- - - - \ No newline at end of file diff --git a/test/fixtures/code/super-test/404-csp-nonce.ref.html b/test/fixtures/code/super-test/404-csp-nonce.ref.html deleted file mode 100644 index a1a238e2..00000000 --- a/test/fixtures/code/super-test/404-csp-nonce.ref.html +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - Page not found - - - - - - - - - - - - -
-
-
- - 404 - -

Page Not Found

-

- Go home -

-
-
- - - - \ No newline at end of file diff --git a/test/fixtures/code/super-test/static-nonce-fragment.html b/test/fixtures/code/super-test/static-nonce-fragment.html deleted file mode 100644 index c8f32793..00000000 --- a/test/fixtures/code/super-test/static-nonce-fragment.html +++ /dev/null @@ -1,4 +0,0 @@ - - - -
Nonce Test
diff --git a/test/fixtures/code/super-test/static-nonce-fragment.ref.html b/test/fixtures/code/super-test/static-nonce-fragment.ref.html deleted file mode 100644 index 21cbd4a5..00000000 --- a/test/fixtures/code/super-test/static-nonce-fragment.ref.html +++ /dev/null @@ -1,4 +0,0 @@ - - - -
Nonce Test
diff --git a/test/fixtures/code/super-test/static-nonce-header.html b/test/fixtures/code/super-test/static-nonce-header.html deleted file mode 100644 index dabff8fa..00000000 --- a/test/fixtures/code/super-test/static-nonce-header.html +++ /dev/null @@ -1,37 +0,0 @@ - - - ACME CORP - - - - - - - - - - - - - - - - - - - - -
-
-
-

Nonce Test

- - - - - -
-
- - - diff --git a/test/fixtures/code/super-test/static-nonce-header.ref.html b/test/fixtures/code/super-test/static-nonce-header.ref.html deleted file mode 100644 index 5ee83800..00000000 --- a/test/fixtures/code/super-test/static-nonce-header.ref.html +++ /dev/null @@ -1,37 +0,0 @@ - - - ACME CORP - - - - - - - - - - - - - - - - - - - - -
-
-
-

Nonce Test

- - - - - -
-
- - - diff --git a/test/fixtures/code/super-test/static-nonce-meta-different.html b/test/fixtures/code/super-test/static-nonce-meta-different.html deleted file mode 100644 index b660b86f..00000000 --- a/test/fixtures/code/super-test/static-nonce-meta-different.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - ACME CORP - - - - - - - - - - - - - - - - - - - - -
-
-
-

Nonce Test

- - - - - -
-
- - - diff --git a/test/fixtures/code/super-test/static-nonce-meta-different.ref.html b/test/fixtures/code/super-test/static-nonce-meta-different.ref.html deleted file mode 100644 index b660b86f..00000000 --- a/test/fixtures/code/super-test/static-nonce-meta-different.ref.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - ACME CORP - - - - - - - - - - - - - - - - - - - - -
-
-
-

Nonce Test

- - - - - -
-
- - - diff --git a/test/fixtures/code/super-test/static-nonce-meta-move-as-header.html b/test/fixtures/code/super-test/static-nonce-meta-move-as-header.html deleted file mode 100644 index 70cbe1a7..00000000 --- a/test/fixtures/code/super-test/static-nonce-meta-move-as-header.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - ACME CORP - - - - - - - - - - - - - - - - - - - - -
-
-
-

Nonce Test

- - - - - -
-
- - - diff --git a/test/fixtures/code/super-test/static-nonce-meta-move-as-header.ref.html b/test/fixtures/code/super-test/static-nonce-meta-move-as-header.ref.html deleted file mode 100644 index 7a8b1c19..00000000 --- a/test/fixtures/code/super-test/static-nonce-meta-move-as-header.ref.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - ACME CORP - - - - - - - - - - - - - - - - - - - - -
-
-
-

Nonce Test

- - - - - -
-
- - - diff --git a/test/fixtures/code/super-test/static-nonce-meta.html b/test/fixtures/code/super-test/static-nonce-meta.html deleted file mode 100644 index 4a3e2a06..00000000 --- a/test/fixtures/code/super-test/static-nonce-meta.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - ACME CORP - - - - - - - - - - - - - - - - - - - - -
-
-
-

Nonce Test

- - - - - -
-
- - - diff --git a/test/fixtures/code/super-test/static-nonce-meta.ref.html b/test/fixtures/code/super-test/static-nonce-meta.ref.html deleted file mode 100644 index 9fb09c12..00000000 --- a/test/fixtures/code/super-test/static-nonce-meta.ref.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - ACME CORP - - - - - - - - - - - - - - - - - - - - -
-
-
-

Nonce Test

- - - - - -
-
- - - diff --git a/test/fixtures/content/nonce-headers-different.html b/test/fixtures/content/nonce-headers-different.html deleted file mode 100644 index ce02bb02..00000000 --- a/test/fixtures/content/nonce-headers-different.html +++ /dev/null @@ -1,30 +0,0 @@ - - - ACME CORP - - - - - - - - - - - - - - - - - - - - -
-
-

Nonce Test

-
- - - diff --git a/test/fixtures/content/nonce-headers-different.md b/test/fixtures/content/nonce-headers-different.md deleted file mode 100644 index 02445e40..00000000 --- a/test/fixtures/content/nonce-headers-different.md +++ /dev/null @@ -1 +0,0 @@ -# Nonce Test diff --git a/test/fixtures/content/nonce-headers-meta.html b/test/fixtures/content/nonce-headers-meta.html deleted file mode 100644 index 547995dc..00000000 --- a/test/fixtures/content/nonce-headers-meta.html +++ /dev/null @@ -1,31 +0,0 @@ - - - ACME CORP - - - - - - - - - - - - - - - - - - - - - -
-
-

Nonce Test

-
- - - diff --git a/test/fixtures/content/nonce-headers-meta.md b/test/fixtures/content/nonce-headers-meta.md deleted file mode 100644 index 02445e40..00000000 --- a/test/fixtures/content/nonce-headers-meta.md +++ /dev/null @@ -1 +0,0 @@ -# Nonce Test diff --git a/test/fixtures/content/nonce-headers.html b/test/fixtures/content/nonce-headers.html deleted file mode 100644 index f941a05b..00000000 --- a/test/fixtures/content/nonce-headers.html +++ /dev/null @@ -1,30 +0,0 @@ - - - ACME CORP - - - - - - - - - - - - - - - - - - - - -
-
-

Nonce Test

-
- - - diff --git a/test/fixtures/content/nonce-headers.md b/test/fixtures/content/nonce-headers.md deleted file mode 100644 index 02445e40..00000000 --- a/test/fixtures/content/nonce-headers.md +++ /dev/null @@ -1 +0,0 @@ -# Nonce Test diff --git a/test/fixtures/content/nonce-meta-different.html b/test/fixtures/content/nonce-meta-different.html deleted file mode 100644 index c5505253..00000000 --- a/test/fixtures/content/nonce-meta-different.html +++ /dev/null @@ -1,31 +0,0 @@ - - - ACME CORP - - - - - - - - - - - - - - - - - - - - - -
-
-

Nonce Test

-
- - - diff --git a/test/fixtures/content/nonce-meta-different.md b/test/fixtures/content/nonce-meta-different.md deleted file mode 100644 index 02445e40..00000000 --- a/test/fixtures/content/nonce-meta-different.md +++ /dev/null @@ -1 +0,0 @@ -# Nonce Test diff --git a/test/fixtures/content/nonce-meta-move-as-header.html b/test/fixtures/content/nonce-meta-move-as-header.html deleted file mode 100644 index 8733139a..00000000 --- a/test/fixtures/content/nonce-meta-move-as-header.html +++ /dev/null @@ -1,30 +0,0 @@ - - - ACME CORP - - - - - - - - - - - - - - - - - - - - -
-
-

Nonce Test

-
- - - diff --git a/test/fixtures/content/nonce-meta-move-as-header.md b/test/fixtures/content/nonce-meta-move-as-header.md deleted file mode 100644 index 02445e40..00000000 --- a/test/fixtures/content/nonce-meta-move-as-header.md +++ /dev/null @@ -1 +0,0 @@ -# Nonce Test diff --git a/test/fixtures/content/nonce-meta.html b/test/fixtures/content/nonce-meta.html deleted file mode 100644 index f6d3289e..00000000 --- a/test/fixtures/content/nonce-meta.html +++ /dev/null @@ -1,31 +0,0 @@ - - - ACME CORP - - - - - - - - - - - - - - - - - - - - - -
-
-

Nonce Test

-
- - - diff --git a/test/fixtures/content/nonce-meta.md b/test/fixtures/content/nonce-meta.md deleted file mode 100644 index 02445e40..00000000 --- a/test/fixtures/content/nonce-meta.md +++ /dev/null @@ -1 +0,0 @@ -# Nonce Test diff --git a/test/fixtures/content/nonce-script-only.html b/test/fixtures/content/nonce-script-only.html deleted file mode 100644 index 50b5d03c..00000000 --- a/test/fixtures/content/nonce-script-only.html +++ /dev/null @@ -1,30 +0,0 @@ - - - ACME CORP - - - - - - - - - - - - - - - - - - - - -
-
-

Nonce Test

-
- - - diff --git a/test/fixtures/content/nonce-script-only.md b/test/fixtures/content/nonce-script-only.md deleted file mode 100644 index 02445e40..00000000 --- a/test/fixtures/content/nonce-script-only.md +++ /dev/null @@ -1 +0,0 @@ -# Nonce Test diff --git a/test/fixtures/content/nonce-style-only.html b/test/fixtures/content/nonce-style-only.html deleted file mode 100644 index a21c0bd0..00000000 --- a/test/fixtures/content/nonce-style-only.html +++ /dev/null @@ -1,30 +0,0 @@ - - - ACME CORP - - - - - - - - - - - - - - - - - - - - -
-
-

Nonce Test

-
- - - diff --git a/test/fixtures/content/nonce-style-only.md b/test/fixtures/content/nonce-style-only.md deleted file mode 100644 index 02445e40..00000000 --- a/test/fixtures/content/nonce-style-only.md +++ /dev/null @@ -1 +0,0 @@ -# Nonce Test diff --git a/test/rendering.test.js b/test/rendering.test.js index 6ec6166a..0b1a5ee5 100644 --- a/test/rendering.test.js +++ b/test/rendering.test.js @@ -11,7 +11,6 @@ */ /* eslint-env mocha */ import assert from 'assert'; -import crypto from 'crypto'; import path from 'path'; import { readFile } from 'fs/promises'; import { JSDOM } from 'jsdom'; @@ -187,14 +186,13 @@ describe('Rendering', () => { } catch { // ignore } - // console.log(expHtml); if (!expStatus) { // eslint-disable-next-line no-param-reassign expStatus = expHtml === null ? 404 : 200; } const response = await render(url, '', expStatus, partition); const actHtml = response.body; - // console.log(actHtml); + console.log(actHtml); if (expStatus === 200) { const $actMain = new JSDOM(actHtml).window.document.querySelector(domSelector); const $expMain = new JSDOM(expHtml).window.document.querySelector(domSelector); @@ -222,36 +220,6 @@ describe('Rendering', () => { return response; } - async function testRenderCode(url, expStatus = 200, spec = null, forceCompare = false) { - if (!(url instanceof URL)) { - // eslint-disable-next-line no-param-reassign - url = new URL(`https://helix-pages.com/${url}`); - } - - // eslint-disable-next-line no-param-reassign - spec = spec || url.pathname.split('/').pop().split('.')[0]; - const expFile = path.resolve(__testdir, 'fixtures', 'code/super-test', `${spec}.ref.html`); - let expHtml = null; - try { - expHtml = await readFile(expFile, 'utf-8'); - } catch { - // ignore - } - - const response = await render(url, '', expStatus); - assert.strictEqual(response.status, expStatus); - const actHtml = response.body; - // console.log(actHtml); - if (expStatus === 200 || forceCompare) { - /* - we use strict equality here because we want to ensure minimal intrusion in the customer HTML - by the rendering pipeline. JSDOM will normalize the HTML, so we can't use it for comparison. - */ - assert.strictEqual(actHtml, expHtml); - } - return response; - } - describe('Section DIVS', () => { it('renders document with 1 section correctly', async () => { await testRender('one-section'); @@ -545,190 +513,6 @@ describe('Rendering', () => { await testRender('head-with-script', 'html'); }); - it('renders csp nonce meta', async () => { - const originalRandomBytes = crypto.randomBytes; - try { - crypto.randomBytes = () => Buffer.from('rA4nd0mmmrA4nd0mmm'); - config = { - ...DEFAULT_CONFIG, - head: { - // eslint-disable-next-line quotes - html: `\n` - + '\n' - + '\n' - + '\n' - + '\n' - + '', - }, - }; - const { headers } = await testRender('nonce-meta', 'html'); - assert.ok(!headers.get('content-security-policy')); - } finally { - crypto.randomBytes = originalRandomBytes; - } - }); - - it('renders csp nonce headers', async () => { - const originalRandomBytes = crypto.randomBytes; - try { - crypto.randomBytes = () => Buffer.from('rA4nd0mmmrA4nd0mmm'); - config = { - ...DEFAULT_CONFIG, - headers: { - '/**': [ - { - key: 'Content-Security-Policy', - // eslint-disable-next-line quotes - value: `script-src 'nonce-aem' 'strict-dynamic'; style-src 'nonce-aem'; base-uri 'self'; object-src 'none';`, - }, - ], - }, - head: { - html: '\n' - + '\n' - + '\n' - + '\n' - + '', - }, - }; - const { headers } = await testRender('nonce-headers', 'html'); - // eslint-disable-next-line quotes - assert.strictEqual(headers.get('content-security-policy'), `script-src 'nonce-ckE0bmQwbW1tckE0bmQwbW1t' 'strict-dynamic'; style-src 'nonce-ckE0bmQwbW1tckE0bmQwbW1t'; base-uri 'self'; object-src 'none';`); - } finally { - crypto.randomBytes = originalRandomBytes; - } - }); - - it('renders csp nonce metadata - move as header', async () => { - const originalRandomBytes = crypto.randomBytes; - try { - crypto.randomBytes = () => Buffer.from('rA4nd0mmmrA4nd0mmm'); - config = { - ...DEFAULT_CONFIG, - head: { - // eslint-disable-next-line quotes - html: `\n` - + '\n' - + '\n' - + '\n' - + '\n' - + '', - }, - }; - const { headers } = await testRender('nonce-meta-move-as-header', 'html'); - // eslint-disable-next-line quotes - assert.strictEqual(headers.get('content-security-policy'), `script-src 'nonce-ckE0bmQwbW1tckE0bmQwbW1t' 'strict-dynamic'; style-src 'nonce-ckE0bmQwbW1tckE0bmQwbW1t'; base-uri 'self'; object-src 'none';`); - } finally { - crypto.randomBytes = originalRandomBytes; - } - }); - - it('renders csp nonce headers and metadata - move as header', async () => { - const originalRandomBytes = crypto.randomBytes; - try { - crypto.randomBytes = () => Buffer.from('rA4nd0mmmrA4nd0mmm'); - config = { - ...DEFAULT_CONFIG, - headers: { - '/**': [ - { - key: 'content-security-policy', - value: 'frame-ancestors \'self\'', - }, - ], - }, - head: { - // eslint-disable-next-line quotes - html: `\n` - + '\n' - + '\n' - + '\n' - + '\n' - + '', - }, - }; - const { headers } = await testRender('nonce-headers-meta', 'html'); - assert.strictEqual(headers.get('content-security-policy'), 'frame-ancestors \'self\''); - } finally { - crypto.randomBytes = originalRandomBytes; - } - }); - - it('renders csp nonce script only', async () => { - const originalRandomBytes = crypto.randomBytes; - try { - crypto.randomBytes = () => Buffer.from('rA4nd0mmmrA4nd0mmm'); - config = { - ...DEFAULT_CONFIG, - headers: { - '/**': [ - { - key: 'content-security-policy', - // eslint-disable-next-line quotes - value: `script-src 'nonce-aem' 'strict-dynamic'; base-uri 'self'; object-src 'none';`, - }, - ], - }, - head: { - html: '\n' - + '\n' - + '\n' - + '\n' - + '', - }, - }; - const { headers } = await testRender('nonce-script-only', 'html'); - // eslint-disable-next-line quotes - assert.strictEqual(headers.get('content-security-policy'), `script-src 'nonce-ckE0bmQwbW1tckE0bmQwbW1t' 'strict-dynamic'; base-uri 'self'; object-src 'none';`); - } finally { - crypto.randomBytes = originalRandomBytes; - } - }); - - it('does not alter csp nonce if already set to a different value by meta', async () => { - crypto.randomBytes = () => Buffer.from('rA4nd0mmmrA4nd0mmm'); - config = { - ...DEFAULT_CONFIG, - head: { - // eslint-disable-next-line quotes - html: `\n` - + '\n' - + '\n' - + '\n' - + '\n' - + '', - }, - }; - const { headers } = await testRender('nonce-meta-different', 'html'); - assert.ok(!headers.get('content-security-policy')); - }); - - it('does not alter csp nonce if already set to a different value by header', async () => { - crypto.randomBytes = () => Buffer.from('rA4nd0mmmrA4nd0mmm'); - config = { - ...DEFAULT_CONFIG, - headers: { - '/**': [ - { - key: 'Content-Security-Policy', - // eslint-disable-next-line quotes - value: `script-src 'nonce-r4nD0m' 'strict-dynamic'; style-src 'nonce-r4nD0m'; base-uri 'self'; object-src 'none';`, - }, - ], - }, - head: { - html: '\n' - + '\n' - + '\n' - + '\n' - + '', - }, - }; - const { headers } = await testRender('nonce-headers-different', 'html'); - // eslint-disable-next-line quotes - assert.strictEqual(headers.get('content-security-policy'), `script-src 'nonce-r4nD0m' 'strict-dynamic'; style-src 'nonce-r4nD0m'; base-uri 'self'; object-src 'none';`); - }); - it('renders 404 if content not found', async () => { await testRender('not-found', 'html'); // preview (code coverage) @@ -1024,84 +808,5 @@ describe('Rendering', () => { link: '; rel=modulepreload; as=script; crossorigin=use-credentials', }); }); - - it('renders static html from the codebus and applies csp from header with nonce', async () => { - const originalRandomBytes = crypto.randomBytes; - try { - crypto.randomBytes = () => Buffer.from('rA4nd0mmmrA4nd0mmm'); - config = { - ...DEFAULT_CONFIG, - headers: { - '/**': [ - { - key: 'content-security-policy', - // eslint-disable-next-line quotes - value: `script-src 'nonce-aem' 'strict-dynamic'; style-src 'nonce-aem'; base-uri 'self'; object-src 'none';`, - }, - ], - }, - }; - - const { headers } = await testRenderCode(new URL('https://helix-pages.com/static-nonce-header.html')); - // eslint-disable-next-line quotes - assert.strictEqual(headers.get('content-security-policy'), `script-src 'nonce-ckE0bmQwbW1tckE0bmQwbW1t' 'strict-dynamic'; style-src 'nonce-ckE0bmQwbW1tckE0bmQwbW1t'; base-uri 'self'; object-src 'none';`); - } finally { - crypto.randomBytes = originalRandomBytes; - } - }); - - it('renders static html from the codebus and applies csp from meta with nonce', async () => { - const originalRandomBytes = crypto.randomBytes; - try { - crypto.randomBytes = () => Buffer.from('rA4nd0mmmrA4nd0mmm'); - const { headers } = await testRenderCode(new URL('https://helix-pages.com/static-nonce-meta.html')); - assert.ok(!headers.get('content-security-policy')); - } finally { - crypto.randomBytes = originalRandomBytes; - } - }); - - it('renders static html from the codebus and applies csp from meta with nonce moved as header', async () => { - const originalRandomBytes = crypto.randomBytes; - try { - crypto.randomBytes = () => Buffer.from('rA4nd0mmmrA4nd0mmm'); - const { headers } = await testRenderCode(new URL('https://helix-pages.com/static-nonce-meta-move-as-header.html')); - // eslint-disable-next-line quotes - assert.strictEqual(headers.get('content-security-policy'), `script-src 'nonce-ckE0bmQwbW1tckE0bmQwbW1t' 'strict-dynamic'; style-src 'nonce-ckE0bmQwbW1tckE0bmQwbW1t'; base-uri 'self'; object-src 'none';`); - } finally { - crypto.randomBytes = originalRandomBytes; - } - }); - - it('renders static html from the codebus and applies csp with different nonce without altering', async () => { - const { headers } = await testRenderCode(new URL('https://helix-pages.com/static-nonce-meta-different.html')); - assert.ok(!headers.get('content-security-policy')); - }); - - it('renders static html from the codebus and applies csp without altering the HTML structure', async () => { - const originalRandomBytes = crypto.randomBytes; - try { - crypto.randomBytes = () => Buffer.from('rA4nd0mmmrA4nd0mmm'); - const { headers } = await testRenderCode(new URL('https://helix-pages.com/static-nonce-fragment.html')); - assert.ok(!headers.get('content-security-policy')); - } finally { - crypto.randomBytes = originalRandomBytes; - } - }); - - it('renders 404 html from codebus and applies csp', async () => { - loader - .rewrite('404.html', 'super-test/404-csp-nonce.html') - .headers('super-test/404-test.html', 'x-amz-meta-x-source-last-modified', 'Mon, 12 Oct 2009 17:50:00 GMT'); - const originalRandomBytes = crypto.randomBytes; - try { - crypto.randomBytes = () => Buffer.from('rA4nd0mmmrA4nd0mmm'); - const { headers } = await testRenderCode('not-found', 404, '404-csp-nonce', true); - // eslint-disable-next-line quotes - assert.strictEqual(headers.get('content-security-policy'), `script-src 'nonce-ckE0bmQwbW1tckE0bmQwbW1t' 'strict-dynamic'; base-uri 'self'; object-src 'none';`); - } finally { - crypto.randomBytes = originalRandomBytes; - } - }); }); });