From 3d38e428dc8902ee6e1a34bce807dac819020016 Mon Sep 17 00:00:00 2001
From: MorganOnCode <87934408+MorganOnCode@users.noreply.github.com>
Date: Fri, 15 May 2026 12:10:43 +0000
Subject: [PATCH] feat(landing): precompile JSX so CSP can drop 'unsafe-eval'
Closes audit #13. Replaces Babel-standalone in the browser with a
precompiled bundle so the production CSP can be tightened from:
script-src: 'self' 'unsafe-inline' 'unsafe-eval'
https://static.cloudflareinsights.com https://unpkg.com
to:
script-src: 'self' https://static.cloudflareinsights.com
That removes the eval class of attack surface entirely (Babel was
compiling JSX in the browser via `new Function()`, which required
'unsafe-eval'), and drops the unpkg.com supply-chain dependency
since React + ReactDOM are now bundled.
## Approach
Zero source-file changes. The 8 .jsx files in landing/ still use the
familiar `React.useState` / `ReactDOM.createRoot` global API; nothing
was refactored to ESM imports. Instead a tiny `scripts/build-landing.mjs`
concatenates the files in their existing load order, prepends a shim
that imports React + ReactDOM, and feeds the result through esbuild.
This minimises the diff (the .jsx code is untouched), keeps the dev
workflow understandable (each component is still a self-contained .jsx
file), and gives us a clean production artifact (landing/dist/app.js,
~185 KiB minified, ~438 KiB sourcemap).
## Files
- `scripts/build-landing.mjs` -- new, ~80 lines, esbuild bundle of the
concatenated .jsx with classic JSX transform
- `package.json` -- adds react@18.3.1 + react-dom@18.3.1 as runtime
deps, @types/react + @types/react-dom + esbuild as dev deps, a
`build:landing` script, and chains it into `build`
- `Dockerfile` -- build stage now copies `landing/` and the build
script, runs `pnpm build` (both backend tsup AND landing esbuild),
production stage pulls landing/ (including dist/) from the build
stage instead of the host
- `landing/index.html` -- drops 3 unpkg.com `
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+