From 62488cd81f158209d60e1d508be0726593f9bc4d Mon Sep 17 00:00:00 2001 From: mgreminger Date: Sat, 21 Jan 2023 23:18:05 -0600 Subject: [PATCH 1/5] Add content security policy Prevents inline javascript and cross site scripting. This breaks live reload in local development. Will move csp to _wrangler.ts to allow different csp for dev and production. Current exceptions: font-src * for carbon components that use IBM CDN for fonts (will look into bundling fonts with site instead) style-src 'unsafe-inline' required for plotlyjs image-src * currently users can paste image links into quill editor, will look into disabling this capability image-src data: required if user pastes image into quill editor image-src blob: needed when plotlyjs downloads plot as png --- public/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/public/index.html b/public/index.html index 2f57ca97..c46408e4 100644 --- a/public/index.html +++ b/public/index.html @@ -1,6 +1,7 @@ + From 918f14ee338b201484af4bf2f67557233b6564e0 Mon Sep 17 00:00:00 2001 From: mgreminger Date: Thu, 26 Jan 2023 14:13:34 -0600 Subject: [PATCH 2/5] Remove font exception for CSP Fonts are now bundled after merging #139 --- public/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/index.html b/public/index.html index c46408e4..08cbdaee 100644 --- a/public/index.html +++ b/public/index.html @@ -1,7 +1,7 @@ - + From f3b0f3a50531a0ebd1f47acb496c976d15bc1292 Mon Sep 17 00:00:00 2001 From: mgreminger Date: Thu, 26 Jan 2023 21:32:18 -0600 Subject: [PATCH 3/5] Set CSP at worker level This makes it easy to have a different CSP for dev mode to enable live reload. --- .dev.vars | 1 + public/index.html | 1 - src/database/_worker.ts | 37 +++++++++++++++++++++++++++++-------- 3 files changed, 30 insertions(+), 9 deletions(-) create mode 100644 .dev.vars diff --git a/.dev.vars b/.dev.vars new file mode 100644 index 00000000..a7b73d43 --- /dev/null +++ b/.dev.vars @@ -0,0 +1 @@ +DEV=1 diff --git a/public/index.html b/public/index.html index 08cbdaee..2f57ca97 100644 --- a/public/index.html +++ b/public/index.html @@ -1,7 +1,6 @@ - diff --git a/src/database/_worker.ts b/src/database/_worker.ts index 37f1573c..598e3c00 100644 --- a/src/database/_worker.ts +++ b/src/database/_worker.ts @@ -5,6 +5,11 @@ const maxSize = 2000000; // max length of byte string that represents sheet export const API_MANUAL_SAVE_PATH = "/documents/manual-save"; +const cspHeaderValue = "default-src 'self'; style-src 'self' 'unsafe-inline'; img-src * data: blob:;"; + +// local dev mode requires some extra exceptions for live reload +const devCspHeaderValue = cspHeaderValue + " script-src 'self' http://localhost:35729; connect-src 'self' ws://localhost:35729;"; + type Flag = "0" | "1" | 0 | 1 | undefined; interface Env { @@ -14,6 +19,7 @@ interface Env { ENABLE_MANUAL_SAVE: Flag; MANUAL_SAVE_KEY: string | undefined; ENABLE_D1: Flag; + DEV: Flag; } interface SheetPostBody { @@ -76,14 +82,29 @@ export default { kv: env.SHEETS, d1: env.TABLES, useD1: checkFlag(env.ENABLE_D1) }); - } else if (!path.includes('.') - && !path.slice(1).includes('/') - && path.length === 23 - && request.method === "GET" ) { - const mainPage = await fetch(`${url.origin}/index.html`) - return new HTMLRewriter() - .on('meta[name="googlebot"]', new IndexIfEmbedded()) - .transform(mainPage); + } else if (( path === "/" && request.method === "GET") || + (!path.includes('.') + && !path.slice(1).includes('/') + && path.length === 23 + && request.method === "GET") ) { + let mainPage = await env.ASSETS.fetch(request); + + const updatedHeaders = new Headers(mainPage.headers); + updatedHeaders.set('Content-Security-Policy', checkFlag(env.DEV) ? devCspHeaderValue : cspHeaderValue); + + mainPage = new Response(mainPage.body, { + status: mainPage.status, + statusText: mainPage.statusText, + headers: updatedHeaders + }); + + if (path === "/") { + return mainPage; + } else { + return new HTMLRewriter() + .on('meta[name="googlebot"]', new IndexIfEmbedded()) + .transform(mainPage); + } } else { return await env.ASSETS.fetch(request); } From 5bf4a8952350f69222575819106cc162485b3d0c Mon Sep 17 00:00:00 2001 From: mgreminger Date: Fri, 27 Jan 2023 10:52:29 -0600 Subject: [PATCH 4/5] fix: update path to svelte tsconfig.json This silences some TS errors --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 04a39c1f..8424af1d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,7 @@ "dom" ] }, - "extends": "@tsconfig/svelte/tsconfig.json", + "extends": "./node_modules/@tsconfig/svelte/tsconfig.json", "include": ["src/**/*"], "exclude": ["node_modules/*", "src/database/_worker.ts"] } From 6e8b4a983df17196cf678be2cdef8897b63c4ea3 Mon Sep 17 00:00:00 2001 From: mgreminger Date: Fri, 27 Jan 2023 11:01:05 -0600 Subject: [PATCH 5/5] refactor: no longer hard code spaUrl for sheet URLS Use origin instead. Doesn't really impact anything except for the URL's stored in the history since front end was using getHash for everything anyway --- src/database/_worker.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/database/_worker.ts b/src/database/_worker.ts index 598e3c00..270bbb02 100644 --- a/src/database/_worker.ts +++ b/src/database/_worker.ts @@ -1,15 +1,14 @@ import { getHash, API_GET_PATH, API_SAVE_PATH } from "./utility"; -const spaUrl = "https://engineeringpaper.xyz"; const maxSize = 2000000; // max length of byte string that represents sheet -export const API_MANUAL_SAVE_PATH = "/documents/manual-save"; - const cspHeaderValue = "default-src 'self'; style-src 'self' 'unsafe-inline'; img-src * data: blob:;"; - // local dev mode requires some extra exceptions for live reload const devCspHeaderValue = cspHeaderValue + " script-src 'self' http://localhost:35729; connect-src 'self' ws://localhost:35729;"; +export const API_MANUAL_SAVE_PATH = "/documents/manual-save"; + + type Flag = "0" | "1" | 0 | 1 | undefined; interface Env { @@ -57,6 +56,7 @@ export default { if (path.startsWith(API_SAVE_PATH) && request.method === "POST") { // Store sheet return await postSheet({ + origin: url.origin, requestHash: path.replace(API_SAVE_PATH, ''), requestBody: await request.json(), requestIp: request.headers.get("CF-Connecting-IP") || "", @@ -160,8 +160,9 @@ function getNewId(): string { return crypto.randomUUID().replaceAll('-', '').slice(0, 22); } -async function postSheet({ requestHash, requestBody, requestIp, kv, d1, useD1 }: +async function postSheet({ origin, requestHash, requestBody, requestIp, kv, d1, useD1 }: { + origin: string, requestHash: string, requestBody: SheetPostBody, requestIp: string, @@ -204,7 +205,7 @@ async function postSheet({ requestHash, requestBody, requestIp, kv, d1, useD1 }: if (createNewDocument) { // update document history to include latest version dbEntry.history.unshift({ - url: `${spaUrl}/#${id}`, + url: `${origin}/${id}`, hash: id, creation: dbEntry.creation }); @@ -223,7 +224,7 @@ async function postSheet({ requestHash, requestBody, requestIp, kv, d1, useD1 }: } return new Response(JSON.stringify({ - url: `${spaUrl}/#${id}`, + url: `${origin}/${id}`, hash: id, history: dbEntry.history }));