feat(render): emit Pages _headers with CSP and security headers

What

Adds a `public/_headers` file to every render pass so the GitLab Pages deployment carries a Content Security Policy (CSP) and companion security headers. The file uses the Cloudflare-style format GitLab Pages reads natively.

Why

The `serve` command already sets these four headers on every response, but the static Pages deployment had no equivalent — the dashboard was served with no CSP. This closes that gap so both delivery paths are consistent.

Headers emitted (matches `serve` exactly)

``` /* Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline' X-Content-Type-Options: nosniff X-Frame-Options: DENY Referrer-Policy: no-referrer ```

`style-src 'unsafe-inline'` is required because D3 sets inline styles. The CSP is already proven safe by `serve` — all assets are same-origin.

Implementation

  • Added `internal/renderer/static/_headers` to the embedded static tree.
  • Changed the embed directive from `//go:embed static` to `//go:embed all:static` so Go includes the underscore-prefixed file in the binary.
  • No logic changes to `render.go` — `copyStaticFromEmbed` already walks all embedded files, so `_headers` is copied automatically.
  • Added `TestRender_PagesHeadersFile` asserting `_headers` exists after `Render()` and contains all four security header directives.

Validation

  • `go vet ./...` clean
  • `CGO_ENABLED=1 go test -race ./...` all pass

Merge request reports

Loading