Skip to content

Commit fd603ad

Browse files
authored
Add dedicated CSP for the Playground worker (#530)
This will give Firefox and Safari a stricter CSP policy for the Playground worker today. After https://crbug.com/1253267 (in progress) is fixed, the same will also apply to Chrome. At that point, we will also be able to remove the 'unsafe-eval' and https://unpkg.com/ sources from our main CSP, since all browsers will have an isolated CSP for the Playground worker. Part of #517
1 parent 33d328b commit fd603ad

File tree

1 file changed

+50
-18
lines changed

1 file changed

+50
-18
lines changed

packages/lit-dev-server/src/middleware/content-security-policy-middleware.ts

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ export interface ContentSecurityPolicyMiddlewareOptions {
3333
playgroundPreviewOrigin: string;
3434
}
3535

36+
/**
37+
* A Google service which aggregates CSP violations.
38+
*/
39+
const CSP_REPORT_URI = 'https://csp.withgoogle.com/csp/lit-dev';
40+
3641
/**
3742
* Creates a Koa middleware that sets the lit.dev Content Security Policy (CSP)
3843
* headers.
@@ -46,31 +51,22 @@ export interface ContentSecurityPolicyMiddlewareOptions {
4651
export const contentSecurityPolicyMiddleware = (
4752
opts: ContentSecurityPolicyMiddlewareOptions
4853
): Koa.Middleware => {
49-
const cspHeaderValue = [
54+
const mainCsp = [
5055
// TODO(aomarks) We should also enable trusted types, but that will require
5156
// a policy in playground-elements for creating the worker, and a policy
5257
// es-module-lexer for doing an eval (see next comment for more on that).
5358

54-
// TODO(aomarks) unsafe-eval is needed for an eval that is made by
55-
// es-module-lexer to perform JavaScript string unescaping
56-
// (https://github.com/guybedford/es-module-lexer/blob/91964da6b086dc5029091eeef481180a814ce24a/src/lexer.js#L32).
57-
// There are a number of ways we could make this stricter: [1] use the
58-
// asm.js build which doesn't use eval (but it's significantly slower), [2]
59-
// use a separate CSP for the worker (except Chrome doesn't support that
60-
// yet, though we could simulate it with an iframe), [3] get trusted types
61-
// into the WASM build, [4] modify or fork the WASM build to implement
62-
// string unescaping without eval (needs benchmarking).
59+
// TODO(aomarks) Remove unsafe-eval when https://crbug.com/1253267 is fixed.
60+
// See comment below about playgroundWorkerCsp.
6361
//
6462
// In dev mode, data: scripts are required because @web/dev-server uses them
6563
// for automatic reloads.
6664
`script-src 'self' 'unsafe-eval' ${
6765
opts.inlineScriptHashes?.map((hash) => `'${hash}'`).join(' ') ?? ''
6866
} https://www.googletagmanager.com/gtag/js ${opts.devMode ? ` data:` : ''}`,
6967

70-
// unpkg.com is needed to allow the Playground worker to fetch dependencies.
71-
// TODO(aomarks) After https://crbug.com/1253267 is fixed we can serve a
72-
// separate CSP policy just for the worker script (Firefox and Safari
73-
// already support this).
68+
// TODO(aomarks) Remove unpkg.com when https://crbug.com/1253267 is fixed.
69+
// See comment below about playgroundWorkerCsp.
7470
//
7571
// In dev mode, ws: connections are required because @web/dev-server uses
7672
// them for automatic reloads.
@@ -105,17 +101,53 @@ export const contentSecurityPolicyMiddleware = (
105101
// https://github.com/w3c/webappsec-csp/issues/199.
106102
`default-src 'self'`,
107103

108-
...(opts.reportViolations
109-
? [`report-uri https://csp.withgoogle.com/csp/lit-dev`]
110-
: []),
104+
...(opts.reportViolations ? [`report-uri ${CSP_REPORT_URI}`] : []),
105+
].join('; ');
106+
107+
// TODO(aomarks) Currently this worker CSP will take effect in Firefox and
108+
// Safari, but not Chrome. Chrome does not currently follow the CSP spec for
109+
// workers; instead workers inherit the CSP policy of their parent context.
110+
// This is being actively fixed (https://crbug.com/1253267), and once it ships
111+
// we can remove unsafe-eval and unpkg.com from the main CSP above.
112+
const playgroundWorkerCsp = [
113+
// unsafe-eval is needed because we use es-module-lexer to parse import
114+
// statements in modules. es-module-lexer needs unsafe-eval because:
115+
//
116+
// 1. It uses Web Assembly, which requires unsafe-eval until
117+
// wasm-unsafe-eval ships in all browsers:
118+
//
119+
// Spec: https://github.com/w3c/webappsec-csp/pull/293
120+
// Chrome: https://bugs.chromium.org/p/chromium/issues/detail?id=948834
121+
// Safari: https://bugs.webkit.org/show_bug.cgi?id=197759
122+
// Firefox: <not yet filed>
123+
//
124+
// 2. It uses eval() to parse JavaScript string literals
125+
// (https://github.com/guybedford/es-module-lexer/blob/91964da6b086dc5029091eeef481180a814ce24a/src/lexer.js#L32).
126+
// This is theoretically a safe use of eval because it's only used to
127+
// process strings that have already been lexed as single or double quote
128+
// strings. Trusted types could be used to isolate the exact eval() call.
129+
// It may also be a good idea to replace the eval call with a fast WASM
130+
// implementation of string unescaping.
131+
`script-src 'unsafe-eval'`,
132+
133+
// Allow bare module specifiers to be fetched from unpkg. Note this does not
134+
// restrict the user from directly importing from arbitrary other URLs in
135+
// their import statements when using the Playground.
136+
`connect-src https://unpkg.com/`,
137+
138+
// Disallow everything else.
139+
`default-src 'none'`,
140+
...(opts.reportViolations ? [`report-uri ${CSP_REPORT_URI}`] : []),
111141
].join('; ');
112142

113143
return async (ctx, next) => {
114144
await next();
115145
if (ctx.response.type === 'text/html') {
116146
// TODO(aomarks) Remove -Report-Only suffix when we are confident the
117147
// policy is working.
118-
ctx.set('Content-Security-Policy-Report-Only', cspHeaderValue);
148+
ctx.set('Content-Security-Policy-Report-Only', mainCsp);
149+
} else if (ctx.path.endsWith('/playground-typescript-worker.js')) {
150+
ctx.set('Content-Security-Policy-Report-Only', playgroundWorkerCsp);
119151
}
120152
};
121153
};

0 commit comments

Comments
 (0)