fix(tauri): enable a Content Security Policy for the webview

Security audit finding SEC-IPC-02. `tauri.conf.json` set `"csp": null`,
which disables Tauri's CSP injection entirely. The audit found no
script-injection route in the frontend, so this is defense in depth:
should an XSS ever land through peer-supplied text, a CSP stops it from
loading remote scripts, exfiltrating over fetch/WebSocket or framing the
app, and confines IPC to Tauri's own channel.

Production policy (`csp`):
- default/script-src 'self': only the bundled Vite output runs. Tauri
  adds hashes for the init scripts it injects.
- style-src 'self' 'unsafe-inline' plus fonts.googleapis.com: React
  inline `style` props and the Bebas Neue stylesheet that index.html
  already links.
- font-src 'self' data: fonts.gstatic.com: the font files behind that
  stylesheet.
- img-src 'self' data: asset: http://asset.localhost: thumbnails arrive
  as base64 data URLs from get_game_thumbnail.
- connect-src ipc: http://ipc.localhost: Tauri does not append these
  itself; without them every `invoke` would be blocked.
- object-src/frame-src/form-action 'none', base-uri 'none'.

Development policy (`devCsp`): Vite's dev server injects the React
refresh preamble as an inline script and needs eval and a WebSocket to
localhost:1420 for HMR, so `just run` uses a permissive policy that
still forbids frames, plugins and form submission.

Verification here was limited to a static check: the production bundle
built by `deno task build` contains only external module scripts and
stylesheets, and `cargo tauri` parses the new config. The policy has
not been exercised in a running webview on this machine; if the app
shows a blank window or missing fonts/thumbnails after this change,
the WebView console will name the blocked directive.

Test plan: `just run` (dev) and `just build` then launch the binary;
confirm the library renders, thumbnails and the display font load,
IPC-backed actions (settings, log windows, Call to Play) work, and the
webview console shows no CSP violations.

Claude-Session: https://claude.ai/code/session_017C3Nbgwpdm3YNwZhhFLHwg
This commit is contained in:
2026-09-02 22:37:40 +02:00
parent 20e3d6aec4
commit f9c64d7c18
@@ -18,7 +18,8 @@
}
],
"security": {
"csp": null
"csp": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' data: https://fonts.gstatic.com; img-src 'self' data: asset: http://asset.localhost; connect-src ipc: http://ipc.localhost; object-src 'none'; base-uri 'none'; frame-src 'none'; form-action 'none'",
"devCsp": "default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob: ws://localhost:1420 http://localhost:1420 https://fonts.googleapis.com https://fonts.gstatic.com ipc: http://ipc.localhost asset: http://asset.localhost; object-src 'none'; base-uri 'none'; frame-src 'none'; form-action 'none'"
}
},
"bundle": {