Skip to content

Add nonce-based CSP for inline scripts security#1

Merged
MBombeck merged 1 commit into
mainfrom
claude/add-nonce-middleware-4hpxf
Feb 20, 2026
Merged

Add nonce-based CSP for inline scripts security#1
MBombeck merged 1 commit into
mainfrom
claude/add-nonce-middleware-4hpxf

Conversation

@MBombeck
Copy link
Copy Markdown
Owner

Summary

Implement Content Security Policy (CSP) nonce-based protection for inline scripts to improve security posture while maintaining functionality across development and production environments.

Key Changes

  • Middleware (src/middleware.ts):

    • Generate a cryptographically random nonce on each request using crypto.randomUUID()
    • Include the nonce in CSP headers for script-src directive
    • Pass the nonce to the client via x-nonce response header
    • Update CSP to use nonce instead of 'unsafe-inline' for scripts in production
  • Root Layout (src/app/layout.tsx):

    • Convert RootLayout to an async component to retrieve the nonce from request headers
    • Apply the nonce to the theme initialization inline script via the nonce attribute
    • Maintain development flexibility with 'unsafe-eval' and 'unsafe-inline' fallbacks

Implementation Details

  • The nonce is generated in middleware and passed through response headers, then retrieved in the layout component
  • Development mode retains 'unsafe-eval' and 'unsafe-inline' for better DX while still using nonce
  • Production mode enforces strict CSP with only 'self' and nonce-based inline scripts
  • This approach prevents XSS attacks while allowing the theme script to execute without blocking

https://claude.ai/code/session_016LHzTP2v77wWbFThQVnCgW

Generate a per-request nonce in middleware, inject it into the CSP
script-src directive, and expose it via the x-nonce response header.
The root layout reads the nonce and applies it to the theme-detection
inline script to satisfy the stricter CSP policy.

https://claude.ai/code/session_016LHzTP2v77wWbFThQVnCgW
@MBombeck MBombeck merged commit d5e3ec5 into main Feb 20, 2026
MBombeck added a commit that referenced this pull request Mar 8, 2026
Add nonce-based CSP for inline scripts security
MBombeck added a commit that referenced this pull request May 9, 2026
Phase A5 mobile audit / B-mobile CRITICAL #1: Recharts wraps every
chart in a `<ResponsiveContainer>` whose default `touch-action: auto`
makes the browser wait for the chart's JS touchmove handler before
starting page scroll. Recharts then dispatches a Redux action +
tooltip re-render synchronously per touchmove. On slow devices that
manifests as Marc's "Scrolling-Hänger im Dashboard auf Charts".

Add the Tailwind `touch-pan-y` utility on every chart's wrapping
`<div>` so the browser starts vertical scroll immediately without
waiting for the JS handler. Chart-internal gestures (tap for tooltip,
horizontal swipe inside the chart) still flow through to JS.

Affected charts: health-chart, mood-chart, medication-compliance-chart,
compliance-line-chart, scatter-correlation-chart. The latter two were
rendering `<ResponsiveContainer>` without an explicit wrapper div, so
both gain a `<div className="touch-pan-y">` parent.

Evidence: new `touch-action-guard.test.ts` reads each chart source and
asserts `touch-pan-y` (or the inline `touchAction: "pan-y"` style) is
present. The test was first verified to fail on every chart before the
fix, then turned green after.

Findings ID: A5/CRITICAL-#1 (top of `.planning/phase-A5-mobile-findings.md`).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MBombeck added a commit that referenced this pull request May 9, 2026
…eport

5 commits shipped this phase: chart touch-action (CRITICAL #1),
/admin/users mobile card-list (CRITICAL #2), 44px tap-target sweep
across charts + medications + mood, /settings/account passkey card
view, /auth/login button sizing.

3 HIGH + 1 MEDIUM-cluster deferred to v1.4.16 with reasoning per
finding (cross-cutting tabs primitive, BP-row grouping logic, bottom-
nav IA decision).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MBombeck added a commit that referenced this pull request May 21, 2026
…osure plus iOS PB30 enablement

Eleven touch-disjoint waves landed on develop plus a six-axis QA
reconcile round before tagging. See CHANGELOG.md for the user-facing
prose; this commit message documents the wave + report inventory for
release-management traceability.

Waves shipped (each with a phase report under .planning/):

- phase-W-POOL-v1440-report.md — p-limit(4) wrapper around the
  analytics 15-way fan-out + pg.Pool max raised 10 -> 20. Closes the
  v1.4.39 empirical-trace finding #1 (Prisma pool starvation).
- phase-W-DELETED-v1440-report.md — 11 reader-tier helpers + 3
  integration contracts close audit Critical Finding #3 (soft-delete
  invisibility) across dashboard, analytics, summaries, rollup tiers,
  and the Coach snapshot path.
- phase-W-INSIGHTS-v1440-report.md — mood-rollup swap on the three
  /api/insights/{features,targets,cards} routes plus the
  compliance-rollup hook gap closure for bulk-projection mints; the
  post-QA reconcile patch closes the six remaining measurement.findMany
  sites in the cluster that still leaked tombstoned rows.
- phase-W-INFRA-v1440-report.md — rollup helper umbrella consolidation
  under src/lib/rollups/* with zero orphan re-exports; lint, typecheck,
  knip, and 93/93 rollup tests green post-restructure.
- phase-W-RSC-v1440-report.md — per-tile Suspense boundaries plus the
  queryKey factory-bypass walker test (extended to comparison/ in the
  reconcile pass).
- phase-W-PRIVACY-v1440-report.md — bilingual /privacy page with 17
  paired-section tests pinning the 9 SB-3 disclosure requirements.
- phase-W-AASA-v1440-report.md — /.well-known/apple-app-site-association
  endpoint pinned to S8WDX4W5KX.dev.healthlog.app with app-ID parity
  assertions guarding against split rotation.
- phase-W-APNS-NOTIFY-v1440-report.md — MEDICATION_REMINDER
  time-sensitive APNs payload (priority 10 + interruption-level)
  scoped via a parameterised test that pins all 6 other event-types
  do not bypass Focus. AP-2 .p8 install gates real delivery.
- phase-W-CONSENT-v1440-report.md — AI consent receipts CRUD with
  append-only invariant pinned in test; revoke-then-regrant leaves
  both rows; idempotent DELETE returns 200 + receipt: null.
- phase-W-WMY-WIRE-v1440-report.md — three previously-dead WMY readers
  (week/month/year) wired through summaries-slice and
  health-score-fast-path with a linear-composability parity test
  pinning the cross-consumer routing contract.
- phase-W-GHOSTS-v1440-report.md — ~1 177 lines of dead code removed
  across 9 atomic commits; startOfUtcDay + wallClockInTz consolidation
  eliminates a sibling-drift risk; knip whitelist trimmed to three
  files pending follow-up.

QA reconcile patches applied per the v1.4.40 QA round (six reports
under .planning/round-v1440-QA-*-findings.md):

- senior C1 — dashboard-suspense-boundaries regex aligned with the
  post-W-INFRA Thread 2 userTimezone-local lift.
- senior H1 — soft-delete filter added to the six remaining
  insights measurement.findMany sites.
- specialist H1 — Promise.allSettled around bulk-projection
  compliance recompute on both /api/medications/intake and
  /api/dashboard/summary.
- ux H1 — compare-toggle migrated to queryKeys.dashboardWidgets
  factory; walker test extended to src/components/comparison/.
- security M1 — consent artefact 64 KB cap enforced via
  Buffer.byteLength (UTF-8 bytes, not UTF-16 code units).

Quality gates green at tag: pnpm typecheck, pnpm lint, pnpm knip
--include files,dependencies,binaries,unlisted, pnpm test --run
(4726 passing / 1 skipped). Tests 4525 -> 4726 unit + 1 skipped over
the v1.4.39 -> v1.4.40 span.

User-facing prose: see CHANGELOG.md.
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