Add csp nonce to all inline script tags (#5566)
This commit is contained in:
@@ -67,24 +67,24 @@ export const renderApp = async (
|
|||||||
const { shareId } = ctx.params;
|
const { shareId } = ctx.params;
|
||||||
const page = await readIndexFile();
|
const page = await readIndexFile();
|
||||||
const environment = `
|
const environment = `
|
||||||
<script>
|
<script nonce="${ctx.state.cspNonce}">
|
||||||
window.env = ${JSON.stringify(presentEnv(env, options.analytics))};
|
window.env = ${JSON.stringify(presentEnv(env, options.analytics))};
|
||||||
</script>
|
</script>
|
||||||
`;
|
`;
|
||||||
const entry = "app/index.tsx";
|
const entry = "app/index.tsx";
|
||||||
const scriptTags = isProduction
|
const scriptTags = isProduction
|
||||||
? `<script type="module" src="${env.CDN_URL || ""}/static/${
|
? `<script type="module" nonce="${ctx.state.cspNonce}" src="${
|
||||||
readManifestFile()[entry]["file"]
|
env.CDN_URL || ""
|
||||||
}"></script>`
|
}/static/${readManifestFile()[entry]["file"]}"></script>`
|
||||||
: `<script type="module">
|
: `<script type="module" nonce="${ctx.state.cspNonce}">
|
||||||
import RefreshRuntime from 'http://localhost:3001/static/@react-refresh'
|
import RefreshRuntime from 'http://localhost:3001/static/@react-refresh'
|
||||||
RefreshRuntime.injectIntoGlobalHook(window)
|
RefreshRuntime.injectIntoGlobalHook(window)
|
||||||
window.$RefreshReg$ = () => { }
|
window.$RefreshReg$ = () => { }
|
||||||
window.$RefreshSig$ = () => (type) => type
|
window.$RefreshSig$ = () => (type) => type
|
||||||
window.__vite_plugin_react_preamble_installed__ = true
|
window.__vite_plugin_react_preamble_installed__ = true
|
||||||
</script>
|
</script>
|
||||||
<script type="module" src="http://localhost:3001/static/@vite/client"></script>
|
<script type="module" nonce="${ctx.state.cspNonce}" src="http://localhost:3001/static/@vite/client"></script>
|
||||||
<script type="module" src="http://localhost:3001/static/${entry}"></script>
|
<script type="module" nonce="${ctx.state.cspNonce}" src="http://localhost:3001/static/${entry}"></script>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
ctx.body = page
|
ctx.body = page
|
||||||
@@ -97,7 +97,8 @@ export const renderApp = async (
|
|||||||
.replace(/\{prefetch\}/g, shareId ? "" : prefetchTags)
|
.replace(/\{prefetch\}/g, shareId ? "" : prefetchTags)
|
||||||
.replace(/\{slack-app-id\}/g, env.SLACK_APP_ID || "")
|
.replace(/\{slack-app-id\}/g, env.SLACK_APP_ID || "")
|
||||||
.replace(/\{cdn-url\}/g, env.CDN_URL || "")
|
.replace(/\{cdn-url\}/g, env.CDN_URL || "")
|
||||||
.replace(/\{script-tags\}/g, scriptTags);
|
.replace(/\{script-tags\}/g, scriptTags)
|
||||||
|
.replace(/\{csp-nonce\}/g, ctx.state.cspNonce);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const renderShare = async (ctx: Context, next: Next) => {
|
export const renderShare = async (ctx: Context, next: Next) => {
|
||||||
|
|||||||
@@ -156,10 +156,12 @@ koa.use(async (ctx, next) => {
|
|||||||
ctx.set("Timing-Allow-Origin", timingOrigins.join(", "));
|
ctx.set("Timing-Allow-Origin", timingOrigins.join(", "));
|
||||||
await next();
|
await next();
|
||||||
});
|
});
|
||||||
|
|
||||||
koa.use(apexRedirect());
|
koa.use(apexRedirect());
|
||||||
if (env.ENVIRONMENT === "test") {
|
if (env.ENVIRONMENT === "test") {
|
||||||
koa.use(errors.routes());
|
koa.use(errors.routes());
|
||||||
}
|
}
|
||||||
|
|
||||||
koa.use(router.routes());
|
koa.use(router.routes());
|
||||||
|
|
||||||
export default koa;
|
export default koa;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
|
import crypto from "crypto";
|
||||||
import { Server } from "https";
|
import { Server } from "https";
|
||||||
import Koa from "koa";
|
import Koa from "koa";
|
||||||
import {
|
import {
|
||||||
@@ -27,8 +28,6 @@ const isProduction = env.ENVIRONMENT === "production";
|
|||||||
const defaultSrc = ["'self'"];
|
const defaultSrc = ["'self'"];
|
||||||
const scriptSrc = [
|
const scriptSrc = [
|
||||||
"'self'",
|
"'self'",
|
||||||
"'unsafe-inline'",
|
|
||||||
"'unsafe-eval'",
|
|
||||||
"gist.github.com",
|
"gist.github.com",
|
||||||
"www.googletagmanager.com",
|
"www.googletagmanager.com",
|
||||||
"cdn.zapier.com",
|
"cdn.zapier.com",
|
||||||
@@ -103,19 +102,22 @@ export default function init(app: Koa = new Koa(), server?: Server) {
|
|||||||
// Sets common security headers by default, such as no-sniff, hsts, hide powered
|
// Sets common security headers by default, such as no-sniff, hsts, hide powered
|
||||||
// by etc, these are applied after auth and api so they are only returned on
|
// by etc, these are applied after auth and api so they are only returned on
|
||||||
// standard non-XHR accessed routes
|
// standard non-XHR accessed routes
|
||||||
app.use(
|
app.use((ctx, next) => {
|
||||||
contentSecurityPolicy({
|
ctx.state.cspNonce = crypto.randomBytes(16).toString("hex");
|
||||||
|
|
||||||
|
return contentSecurityPolicy({
|
||||||
directives: {
|
directives: {
|
||||||
defaultSrc,
|
defaultSrc,
|
||||||
scriptSrc,
|
|
||||||
styleSrc,
|
styleSrc,
|
||||||
|
scriptSrc: [...scriptSrc, `'nonce-${ctx.state.cspNonce}'`],
|
||||||
imgSrc: ["*", "data:", "blob:"],
|
imgSrc: ["*", "data:", "blob:"],
|
||||||
frameSrc: ["*", "data:"],
|
frameSrc: ["*", "data:"],
|
||||||
connectSrc: ["*"], // Do not use connect-src: because self + websockets does not work in
|
// Do not use connect-src: because self + websockets does not work in
|
||||||
// Safari, ref: https://bugs.webkit.org/show_bug.cgi?id=201591
|
// Safari, ref: https://bugs.webkit.org/show_bug.cgi?id=201591
|
||||||
|
connectSrc: ["*"],
|
||||||
},
|
},
|
||||||
})
|
})(ctx, next);
|
||||||
);
|
});
|
||||||
|
|
||||||
// Allow DNS prefetching for performance, we do not care about leaking requests
|
// Allow DNS prefetching for performance, we do not care about leaking requests
|
||||||
// to our own CDN's
|
// to our own CDN's
|
||||||
@@ -129,6 +131,8 @@ export default function init(app: Koa = new Koa(), server?: Server) {
|
|||||||
policy: "no-referrer",
|
policy: "no-referrer",
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
app.use(mount(routes));
|
app.use(mount(routes));
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
{env}
|
{env}
|
||||||
<script>
|
<script nonce="{csp-nonce}">
|
||||||
if (
|
if (
|
||||||
window.localStorage &&
|
window.localStorage &&
|
||||||
window.localStorage.getItem("theme") === "dark"
|
window.localStorage.getItem("theme") === "dark"
|
||||||
|
|||||||
Reference in New Issue
Block a user