From 1f39bc66729cb8a5fd80253fb91ec37efd1b6dc0 Mon Sep 17 00:00:00 2001 From: Jascha Date: Fri, 29 May 2026 23:08:11 -0700 Subject: [PATCH] security: honest E2EE messaging + CSP hardening MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lever 1 — honest messaging about the browser-E2EE threat model: - Drop "zero-knowledge" from the status bar. The web UI is end-to-end encrypted in your browser, but the server also ships the JS, so it is not zero-knowledge in the strong sense; don't imply a guarantee we can't make. - Add a FAQ entry ("Is the web UI 'zero-knowledge'?") with a per-client threat matrix lifted from docs/security.md, and point security-critical transfers at the sndr CLI. Lever 2 — cheap, verified CSP hardening (server/routes/index.js): - Remove a stray console.log that ran on every request inside the CSP connect-src callback. - Add worker-src 'self' (the download service worker is same-origin), base-uri 'self' (block cross-origin injection while still allowing the app's own same-origin base), and frame-src 'none' (the app embeds no nested browsing contexts). Also fix a broken CLI repo link: tarnover/snder -> tarnover/sndr, in both the README reference and the new FAQ content. Verified in production mode (NODE_ENV=production, CSP active): the CSP header is well-formed; the app shell, nonce'd scripts/styles, the same-origin service worker, and the How-it-works modal all load with zero CSP violations. npm test green (48 backend + 23 frontend). Deliberately not included (documented as follow-ups, not cheap here): - SRI on script/style tags. Assets are served from 'self' via server-rendered tags reading the webpack manifest; SRI would need a build-plugin + manifest/layout plumbing for marginal value on same-origin assets. - PWA service-worker bundle pinning. - A pre-existing finding surfaced while testing: with the container's own CSP active in prod mode, the app makes a data:-URL fetch that connect-src blocks. It reproduces identically on master (connect-src is unchanged here) and only appears under the ancient bundled puppeteer's uploadFile; real uploads use FileReader. Left for a separate fix, and relevant to deployments (e.g. snd.dx.pe) that front the container with nginx security headers: keep the nginx and app CSP consistent, since browsers enforce the intersection of both. --- README.md | 2 +- app/ui/statusBar.js | 8 ++++++-- docs/faq.md | 33 ++++++++++++++++++++++++++++++++- server/routes/index.js | 10 +++++++--- 4 files changed, 46 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index bbf56971..27975e2c 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ CLI as well as the browser. Everything else — protocol, file format, web UI, configuration knobs — is unchanged and remains compatible with downstream tooling. -[sndr]: https://github.com/tarnover/snder +[sndr]: https://github.com/tarnover/sndr [ffsend]: https://github.com/timvisee/ffsend [mozilla-send]: https://github.com/mozilla/send [timvisee-send]: https://github.com/timvisee/send diff --git a/app/ui/statusBar.js b/app/ui/statusBar.js index 9cf0d8eb..c98066a3 100644 --- a/app/ui/statusBar.js +++ b/app/ui/statusBar.js @@ -2,8 +2,12 @@ const html = require('choo/html'); module.exports = function() { return html` -