Skip to content

Commit

Permalink
[minor] add csp functionality to subappv1
Browse files Browse the repository at this point in the history
  • Loading branch information
Arun Vishnu authored and arunvishnun committed Mar 17, 2023
1 parent 1482e3d commit ec91638
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 26 deletions.
5 changes: 5 additions & 0 deletions packages/subapp-server/lib/fastify-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions packages/subapp-web/lib/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,11 @@ module.exports = function setup(setupContext) {
const namespaceScriptJs = namespace ? `window.__default__namespace="${namespace}";` : "";

const scriptId = namespace ? namespace : "bundle";

const webSubAppJs = `<script id="${scriptId}Assets" type="application/json">
const nonce = util.getNonceValue(setupContext.routeOptions);
const webSubAppJs = `<script${nonce} id="${scriptId}Assets" type="application/json">
${JSON.stringify(bundleAssets)}
</script>
<script>/*LJ*/${loadJs}/*LJ*/
<script${nonce}>/*LJ*/${loadJs}/*LJ*/
${webpackJsonpJS}
${namespaceScriptJs}
${clientJs}
Expand Down
48 changes: 36 additions & 12 deletions packages/subapp-web/lib/load.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,9 @@ Response: ${err || body}`
console.error(msg); // eslint-disable-line
resolve(makeDevDebugHtml(msg));
} else {
resolve(`<script>/*${name}*/${body}</script>`);
resolve(
`<script${util.getNonceValue(setupContext.routeOptions)}>/*${name}*/${body}</script>`
);
}
});
});
Expand All @@ -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 = `<script>/*${name}*/${src}</script>`;
inlineSubAppJs =
`<script${util.getNonceValue(setupContext.routeOptions)}>/*${name}*/${src}</script>`;
} else if (ext === ".css") {
inlineSubAppJs = `<style id="${name}">${src}</style>`;
inlineSubAppJs =
`<style${util.getNonceValue(setupContext.routeOptions)} id="${name}">${src}</style>`;
} else {
const msg = makeDevDebugMessage(`Error: UNKNOWN bundle extension ${name}`);
console.error(msg); // eslint-disable-line
Expand Down Expand Up @@ -158,14 +162,31 @@ Response: ${err || body}`
const ext = Path.extname(jsBundle);
if (ext === ".js") {
if (context.user.headEntries) {
headSplits.push(`<link rel="preload" href="${jsBundle}" as="script">`);
headSplits.push(
`<link${util.getNonceValue(context.user.routeOptions)}
rel="preload"
href="${jsBundle}"
as="script">`
);
}
a.push(`<script src="${jsBundle}" async></script>`);
a.push(
`<script${util.getNonceValue(context.user.routeOptions)}
src="${jsBundle}"
async></script>`
);
} else if (ext === ".css") {
if (context.user.headEntries) {
headSplits.push(`<link rel="stylesheet" href="${jsBundle}">`);
headSplits.push(
`<link${util.getNonceValue(context.user.routeOptions)}
rel="stylesheet"
href="${jsBundle}">`
);
} else {
a.push(`<link rel="stylesheet" href="${jsBundle}">`);
a.push(
`<link${util.getNonceValue(context.user.routeOptions)}
rel="stylesheet"
href="${jsBundle}">`
);
}
} else {
a.push(`<!-- UNKNOWN bundle extension ${jsBundle} -->`);
Expand Down Expand Up @@ -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 = `<script type="application/json" id="${dataId}">
dynInitialState =
`<script${util.getNonceValue(context.user.routeOptions)}
type="application/json"
id="${dataId}">
${jsesc(JSON.parse(initialStateStr), {
json: true,
isScriptContext: true,
Expand All @@ -282,8 +306,8 @@ ${jsesc(JSON.parse(initialStateStr), {

const inlineStr = props.inline ? `inline:${props.inline},\n ` : "";
const groupStr = props.group ? `group:"${props.group}",\n ` : "";
outputSpot.add(`
${dynInitialState}<script>${xarc}.startSubAppOnLoad({
outputSpot.add(`${dynInitialState}
<script${util.getNonceValue(context.user.routeOptions)} >${xarc}.startSubAppOnLoad({
name:"${name}",
${elementId}serverSideRendering:${Boolean(props.serverSideRendering)},
${inlineStr}${groupStr}clientProps:${clientProps},
Expand Down Expand Up @@ -337,8 +361,8 @@ ${stack}`,
const { bundles, scripts, preLoads } = await prepareSubAppSplitBundles(context);
outputSpot.add(`${comment}`);
if (bundles.length > 0) {
outputSpot.add(`${scripts}
<script>${xarc}.markBundlesLoaded(${JSON.stringify(bundles)}${
outputSpot.add(`${scripts}<script${util.getNonceValue(context.user.routeOptions)} >
${xarc}.markBundlesLoaded(${JSON.stringify(bundles)}${
namespace ? ", " + JSON.stringify(namespace) : ""
});</script>
`);
Expand Down
7 changes: 4 additions & 3 deletions packages/subapp-web/lib/start.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@

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
*/
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 = `
<!-- subapp start -->
<script>window.xarcV1.start();</script>
<script${nonce}>window.xarcV1.start();</script>
`;

if (!xarcSubappSSR) {
Expand Down
7 changes: 7 additions & 0 deletions packages/subapp-web/lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,13 @@ ${ignoreMsg}`
});

return emitter;
},
getNonceValue(routeOptions) {
let nonce = "";
if (routeOptions && routeOptions.cspNonceValue) {
nonce = ` nonce="${routeOptions.cspNonceValue}"`;
}
return nonce;
}
};

Expand Down
8 changes: 4 additions & 4 deletions packages/subapp-web/test/spec/load.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,13 @@ describe("load", function () {
`<script src="ae56dc06d35e1170d047.vendors~280289005828299c685d173f73011e79.js" async></script>`
);
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";
Expand All @@ -72,12 +72,12 @@ describe("load", function () {
expect(results).to.include(`<link rel="preload" href="mainbody.bundle.dev.js" as="script">`);
expect(results).to.include(`<script src="mainbody.bundle.dev.js" async></script>`);
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);
});
15 changes: 11 additions & 4 deletions packages/xarc-index-page/src/token-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
getDevJsBundle,
getProdBundles,
processRenderSsMode,
getCspNonce
} from "./utils";

import prefetchBundles from "./handlers/prefetch-bundles";
Expand All @@ -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
*/
Expand Down Expand Up @@ -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";

Expand All @@ -81,8 +88,8 @@ export default function setup(handlerContext /*, asyncTemplate*/) {
mode,
renderJs,
renderSs,
scriptNonce,
styleNonce,
scriptNonce: nonce,
styleNonce: nonce,
chunkNames,
devCSSBundle,
devJSBundle,
Expand Down
15 changes: 15 additions & 0 deletions packages/xarc-index-page/test/spec/token-handler.spec.ts
Original file line number Diff line number Diff line change
@@ -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}"`);
});
});

0 comments on commit ec91638

Please sign in to comment.