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(
+ ``
+ );
}
});
});
@@ -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 =
+ ``;
} else if (ext === ".css") {
- inlineSubAppJs = ``;
+ inlineSubAppJs =
+ ``;
} 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 = `
-
+
`;
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}"`);
+ });
+});