Skip to content

fix(security): add content security policy#2353

Open
jeanduplessis wants to merge 7 commits intomainfrom
fix/pentest-m3-csp
Open

fix(security): add content security policy#2353
jeanduplessis wants to merge 7 commits intomainfrom
fix/pentest-m3-csp

Conversation

@jeanduplessis
Copy link
Copy Markdown
Contributor

@jeanduplessis jeanduplessis commented Apr 13, 2026

Summary

Code-related pentest findings [private access to Kilo team members only] are now addressed without broad product rewrites. Each fix focuses on closing the reported behavior while preserving existing auth, analytics, payments, and device-token flows.

M3: Global Content Security Policy

  • Why needed: App pages did not send a global CSP, leaving script, frame, image, and connect sources unconstrained.
  • Solution chosen: Added a CSP builder and applies an enforced Content-Security-Policy in proxy.ts. GTM/Impact bootstrap code is served from same-origin script routes instead of inline root-layout scripts, so the root layout does not call headers() and does not force site-wide dynamic rendering just for CSP.
  • Static rendering decision: Avoided per-request CSP nonces because static App Router pages cannot render matching nonces into Next.js inline bootstrap scripts. The policy uses an allowlist-based script-src with unsafe-inline so statically rendered pages continue hydrating.
  • Allowlist decision: Included sources needed by current browser flows: Stripe, Stytch/login domains, GTM, Impact, PostHog, Turnstile, app websocket/connect origins, app-builder previews, YouTube embeds, and lottie WASM fallback hosts observed during browser verification.
  • Production risk: CSP can still block production-only sources. GTM can inject tags configured outside the repo, third-party SDKs can load secondary hosts, and Stripe 3DS/SCA or Stytch telemetry can vary by tenant, card flow, region, and runtime condition.
  • Operational flexibility: Added directive-specific env extensions (CSP_ADDITIONAL_SCRIPT_SRC, CSP_ADDITIONAL_CONNECT_SRC, CSP_ADDITIONAL_IMG_SRC, CSP_ADDITIONAL_STYLE_SRC, CSP_ADDITIONAL_FONT_SRC, CSP_ADDITIONAL_FRAME_SRC, CSP_ADDITIONAL_WORKER_SRC, CSP_ADDITIONAL_MEDIA_SRC) so production-only third-party sources can be added without code changes.
  • Trade-off: Kept script-src 'unsafe-inline' and style-src 'unsafe-inline' for Next.js static App Router compatibility and runtime styles. This is weaker than nonce-only CSP but safer than breaking hydration or forcing every route dynamic, and it can be tightened later with a hash/SRI rollout.
  • Rollout note: If production breakage risk is too high, the CSP can be staged as report-only first with report-uri/report-to, then enforced after reviewing real production violation reports.

Split out from superseded monolithic PR #2347 so M3 can be reviewed, deployed, and rolled back independently.

Verification

  • Spec alignment: matched .plans/pentest-findings.md for code-related L1 scope.
  • Code evaluations: security, logic, types, data, resource, and style review agents ran across the implementation. Security review found a GTM dataLayer PII issue, the issue was fixed, and security re-review reported no findings.
  • Browser evaluations: agent-browser verified fake user login, CSP presence with nonce/Stripe sources, /api/user returning 401 after revocation with stale cookie, re-login after revocation, and stale admin route rejection for /admin/api/credit-categories.

Visual Changes

N/A

Reviewer Notes

Code Reviewer Notes
  • CSP is built in apps/web/src/lib/security-headers.ts and applied from proxy.ts.
  • GTM/Impact bootstraps are served through same-origin external script routes to avoid headers() in the root layout.
  • CSP source env extensions are directive-specific and additive only; env values containing semicolons are ignored to avoid injecting extra directives.
  • CSP is enforced, not report-only. Production-only tags may need additive allowlist env vars before deploy.
  • Same-origin marketing script routes escape configured IDs before embedding them into JavaScript responses.
  • frame-src includes YouTube and *.d.kiloapps.io; connect-src includes *.d.kiloapps.io for app-builder preview polling.
  • Includes a type-only dataLayer ambient fix so this PR typechecks independently when targeting main; this overlaps safely with the privacy split PR if either lands first.

@kilo-code-bot
Copy link
Copy Markdown
Contributor

kilo-code-bot bot commented Apr 13, 2026

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (5 files)
  • apps/web/src/lib/marketing-tag-scripts.test.ts
  • apps/web/src/lib/marketing-tag-scripts.ts
  • apps/web/src/lib/security-headers.test.ts
  • apps/web/src/lib/security-headers.ts
  • apps/web/src/proxy.ts

Reviewed by gpt-5.4-20260305 · 593,802 tokens

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants