From ec91638644bc2f50e409743304a9e5cad488f0d0 Mon Sep 17 00:00:00 2001 From: Arun Vishnu Date: Sun, 12 Mar 2023 22:56:52 -0700 Subject: [PATCH] [minor] add csp functionality to subappv1 --- packages/subapp-server/lib/fastify-plugin.js | 5 ++ packages/subapp-web/lib/init.js | 6 +-- packages/subapp-web/lib/load.js | 48 ++++++++++++++----- packages/subapp-web/lib/start.js | 7 +-- packages/subapp-web/lib/util.js | 7 +++ packages/subapp-web/test/spec/load.spec.js | 8 ++-- .../xarc-index-page/src/token-handlers.ts | 15 ++++-- .../test/spec/token-handler.spec.ts | 15 ++++++ 8 files changed, 85 insertions(+), 26 deletions(-) create mode 100644 packages/xarc-index-page/test/spec/token-handler.spec.ts diff --git a/packages/subapp-server/lib/fastify-plugin.js b/packages/subapp-server/lib/fastify-plugin.js index ffb391136..c7a5d14e1 100644 --- a/packages/subapp-server/lib/fastify-plugin.js +++ b/packages/subapp-server/lib/fastify-plugin.js @@ -40,6 +40,11 @@ function makeRouteHandler({ path, routeRenderer, routeOptions }) { const data = context.result; const status = data.status; + if (routeOptions.cspNonceValue) { + reply.header( + "Content-Security-Policy", + `script-src 'nonce-${routeOptions.cspNonceValue}' 'strict-dynamic';`); + } if (data instanceof Error) { // rethrow to get default error behavior below with helpful errors in dev mode diff --git a/packages/subapp-web/lib/init.js b/packages/subapp-web/lib/init.js index 5c68d8b90..2142ee33f 100644 --- a/packages/subapp-web/lib/init.js +++ b/packages/subapp-web/lib/init.js @@ -73,11 +73,11 @@ module.exports = function setup(setupContext) { const namespaceScriptJs = namespace ? `window.__default__namespace="${namespace}";` : ""; const scriptId = namespace ? namespace : "bundle"; - - const webSubAppJs = ` -`); + resolve( + `/*${name}*/${body}` + ); } }); }); @@ -112,9 +114,11 @@ Response: ${err || body}` const src = Fs.readFileSync(Path.resolve("dist/js", bundleAsset.name)).toString(); const ext = Path.extname(bundleAsset.name); if (ext === ".js") { - inlineSubAppJs = ``; + inlineSubAppJs = + `/*${name}*/${src}`; } else if (ext === ".css") { - inlineSubAppJs = ``; + inlineSubAppJs = + `${src}`; } else { const msg = makeDevDebugMessage(`Error: UNKNOWN bundle extension ${name}`); console.error(msg); // eslint-disable-line @@ -158,14 +162,31 @@ Response: ${err || body}` const ext = Path.extname(jsBundle); if (ext === ".js") { if (context.user.headEntries) { - headSplits.push(``); + headSplits.push( + `` + ); } - a.push(``); + a.push( + `` + ); } else if (ext === ".css") { if (context.user.headEntries) { - headSplits.push(``); + headSplits.push( + `` + ); } else { - a.push(``); + a.push( + `` + ); } } else { a.push(``); @@ -269,7 +290,10 @@ Response: ${err || body}` } else { // embed large initial state as text and parse with JSON.parse instead. const dataId = `${name}-initial-state-${Date.now()}-${++INITIAL_STATE_TAG_ID}`; - dynInitialState = ` `); diff --git a/packages/subapp-web/lib/start.js b/packages/subapp-web/lib/start.js index 9701f236f..4eb9928f7 100644 --- a/packages/subapp-web/lib/start.js +++ b/packages/subapp-web/lib/start.js @@ -2,7 +2,7 @@ const DEFAULT_CONCURRENCY = 15; const xaa = require("xaa"); - +const util = require("./util"); /* * subapp start for SSR * Nothing needs to be done to start subapp for SSR @@ -10,10 +10,11 @@ const xaa = require("xaa"); module.exports = function setup() { return { process: (context, { props: { concurrency } }) => { - const { xarcSubappSSR, xarcSSREmitter } = context.user; + const { xarcSubappSSR, xarcSSREmitter, routeOptions } = context.user; + const nonce = util.getNonceValue(routeOptions); const startMsg = ` - +window.xarcV1.start(); `; if (!xarcSubappSSR) { diff --git a/packages/subapp-web/lib/util.js b/packages/subapp-web/lib/util.js index e3b8095f3..f605ff801 100644 --- a/packages/subapp-web/lib/util.js +++ b/packages/subapp-web/lib/util.js @@ -405,6 +405,13 @@ ${ignoreMsg}` }); return emitter; + }, + getNonceValue(routeOptions) { + let nonce = ""; + if (routeOptions && routeOptions.cspNonceValue) { + nonce = ` nonce="${routeOptions.cspNonceValue}"`; + } + return nonce; } }; diff --git a/packages/subapp-web/test/spec/load.spec.js b/packages/subapp-web/test/spec/load.spec.js index f60ab1db9..de2e63f3a 100644 --- a/packages/subapp-web/test/spec/load.spec.js +++ b/packages/subapp-web/test/spec/load.spec.js @@ -55,13 +55,13 @@ describe("load", function () { `` ); expect(context.user.headEntries).to.not.be.ok; - done(); }; context.output = new RenderOutput(context); loadToken.process(context, props); context.output.close(); - }); + done(); + }).timeout(5000); it("should load preload tags for scripts", done => { process.env.APP_SRC_DIR = "test/subapps"; @@ -72,12 +72,12 @@ describe("load", function () { expect(results).to.include(``); expect(results).to.include(``); expect(context.user.includedBundles).to.include({ mainbody: true }); - done(); }; context.output = new RenderOutput(context); reserveSpot({ saveId: "headEntries" }, context); loadToken.process(context, props); context.output.close(); - }); + done(); + }).timeout(5000); }); diff --git a/packages/xarc-index-page/src/token-handlers.ts b/packages/xarc-index-page/src/token-handlers.ts index 6a31c2cf7..6a71a2c0e 100644 --- a/packages/xarc-index-page/src/token-handlers.ts +++ b/packages/xarc-index-page/src/token-handlers.ts @@ -7,7 +7,6 @@ import { getDevJsBundle, getProdBundles, processRenderSsMode, - getCspNonce } from "./utils"; import prefetchBundles from "./handlers/prefetch-bundles"; @@ -23,6 +22,14 @@ export const tokens = { HTML_CLOSED: "HTML_CLOSED" }; +export const getNonceValue = (routeOptions) => { + let nonce = ""; + if (routeOptions && routeOptions.cspNonceValue) { + nonce = `nonce="${routeOptions.cspNonceValue}"`; + } + return nonce; +}; + /** * @param handlerContext */ @@ -67,7 +74,7 @@ export default function setup(handlerContext /*, asyncTemplate*/) { const devJSBundle = getDevJsBundle(chunkNames, routeData); const { jsChunk, cssChunk } = getProdBundles(chunkNames, routeData); - const { scriptNonce, styleNonce } = getCspNonce(request, routeOptions.cspNonceValue); + const nonce = getNonceValue(routeOptions); const renderJs = RENDER_JS && mode !== "nojs"; @@ -81,8 +88,8 @@ export default function setup(handlerContext /*, asyncTemplate*/) { mode, renderJs, renderSs, - scriptNonce, - styleNonce, + scriptNonce: nonce, + styleNonce: nonce, chunkNames, devCSSBundle, devJSBundle, diff --git a/packages/xarc-index-page/test/spec/token-handler.spec.ts b/packages/xarc-index-page/test/spec/token-handler.spec.ts new file mode 100644 index 000000000..566882c8e --- /dev/null +++ b/packages/xarc-index-page/test/spec/token-handler.spec.ts @@ -0,0 +1,15 @@ +"use strict"; + +import { describe } from "mocha"; +import { getNonceValue } from "../../src/token-handlers"; +import { expect } from "chai"; + +describe("subapp-server token-handler", () => { + describe("getNonceValue", () => { + const random = "random-text"; + const routeOptions = { + cspNonceValue: random + }; + expect(getNonceValue(routeOptions)).to.equal(`nonce="${random}"`); + }); +});