Skip to content

fix(site): restore hydration and reject invalid signup bodies - #66

Merged
scttbnsn merged 1 commit into
devfrom
scttbnsn/fix-site-boundaries
Sep 13, 2026
Merged

scttbnsn merged 1 commit into
devfrom
scttbnsn/fix-site-boundaries

Conversation

@scttbnsn

@scttbnsn scttbnsn commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator

The live page rendered HTML but its CSP blocked Next’s inline Flight scripts, leaving the theme control and 3D scene uninitialized. Generate a fresh request nonce, forward the same policy to Next, and apply it to the theme script. HTML now renders per request; static asset caching stays in place.

Reject null, arrays, and primitive newsletter bodies with 400 before reading email. Update the setup/deployment docs to reflect the working Git integration and add production-browser checks to the existing CI.

Verified red first, then green: build, lint, typecheck, 28 existing tests, and 4 production-browser/API tests covering actual theme interaction, mounted canvas, fresh caller-independent nonces, blocked unauthorized inline scripts, and malformed bodies. Independent scoped review found no material issues.

Summary by CodeRabbit

  • Security

    • Added per-request Content Security Policy nonces to protect inline scripts while supporting application functionality.
    • Continued restricting analytics requests to the approved PostHog proxy.
  • Bug Fixes

    • Improved subscription endpoint handling for malformed or unexpected request bodies.
  • Tests

    • Added production browser checks for page hydration, CSP behavior, and subscription validation.
  • Documentation

    • Updated development, deployment, configuration, and release workflow guidance, including Node.js 24 and Git-integrated deployments.

@vercel

vercel Bot commented Sep 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
codeswhat-website Ready Ready Preview Sep 13, 2026 4:57pm UTC

@coderabbitai

coderabbitai Bot commented Sep 13, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 43d0d8cd-8fef-4828-ae57-53d66851abad

📥 Commits

Reviewing files that changed from the base of the PR and between 16c153c and 15ddcee.

📒 Files selected for processing (11)
  • .github/workflows/website.yml
  • README.md
  • frontend/README-VERCEL.md
  • frontend/app/api/subscribe/route.ts
  • frontend/app/layout.tsx
  • frontend/next.config.ts
  • frontend/package.json
  • frontend/proxy.ts
  • frontend/test/posthog-browser.test.mjs
  • frontend/test/posthog-source.test.mjs
  • frontend/test/site-browser.test.mjs
💤 Files with no reviewable changes (1)
  • frontend/next.config.ts

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


Walkthrough

The pull request replaces static CSP hashing with per-request nonces, validates subscription payload shapes, adds production browser coverage, updates CI, and revises repository and deployment documentation.

Changes

Website contracts

Layer / File(s) Summary
CSP nonce flow
frontend/proxy.ts, frontend/app/layout.tsx, frontend/next.config.ts, frontend/test/*
The proxy generates per-request nonces and CSP headers. RootLayout applies the nonce to the theme script. Static CSP hashing is removed, and source tests follow the new configuration.
Subscription payload validation
frontend/app/api/subscribe/route.ts, frontend/test/site-browser.test.mjs
The subscribe route rejects null, arrays, non-objects, malformed JSON, and payloads without a valid email shape with a 400 response.
Production browser verification
frontend/package.json, frontend/test/site-browser.test.mjs, .github/workflows/website.yml, frontend/test/posthog-browser.test.mjs
A production browser test checks hydration, theme behavior, canvas mounting, CSP nonce freshness, inline-script protection, and API boundaries. CI installs Chromium, runs the test, and applies a 15-minute timeout.
Deployment and repository documentation
README.md, frontend/README-VERCEL.md
The documentation describes the public repository, Node.js 24 development workflow, Vercel Git deployment, nonce-based CSP, configuration variables, and browser verification.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant NextProxy
  participant RootLayout
  participant API
  Browser->>NextProxy: Request production page
  NextProxy->>NextProxy: Generate nonce and CSP
  NextProxy->>RootLayout: Forward x-nonce
  RootLayout-->>Browser: Return HTML with nonce-bearing scripts
  Browser->>API: Submit subscription payload
  API-->>Browser: Return validation response
Loading

Merge Risk: ⚪ Minimal · up to 15ddc

The updated CSP and subscription validation paths are covered by production-oriented checks, with no actionable merge risk identified.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 6 files. (4 skipped: 4 … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the primary changes: restoring hydration and rejecting invalid signup request bodies.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 6 files. (4 skipped: 4 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch scttbnsn/fix-site-boundaries

Warning

Some tools did not complete. Review the errors below.

🔧 Biome (2.5.10)
frontend/app/api/subscribe/route.ts

Biome could not lint this file: nested root configuration. Check the repository's Biome configuration and plugins.

frontend/app/layout.tsx

Biome could not lint this file: nested root configuration. Check the repository's Biome configuration and plugins.

frontend/package.json

Biome could not lint this file: nested root configuration. Check the repository's Biome configuration and plugins.

  • 4 others

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@scttbnsn

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@scttbnsn

Copy link
Copy Markdown
Collaborator Author

@greptileai Review exact head 15ddcee656e3ab4c5f4ae723eee0f52013319a08. Review the CSP nonce request/response boundary and newsletter JSON validation. Threat model is a public marketing site with ordinary untrusted visitors. Confirm hydration works without broadening production script origins, caller nonce headers are replaced, and malformed bodies return 400. Avoid unrelated new subsystems.

@coderabbitai

coderabbitai Bot commented Sep 13, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@biggest-littlest biggest-littlest left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recorded approval after the scoped review and passing Website contracts run 34770128524. Production verification follows promotion.

@ALARGECOMPANY ALARGECOMPANY left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recorded approval for the tested candidate 15ddcee. No protection changes or bypasses.

@scttbnsn

Copy link
Copy Markdown
Collaborator Author

Reviewed the completed CodeRabbit output for 15ddcee and checked inline comments; none were raised. The docstring warning reports all four functions as unsupported, so it does not establish missing documentation. The nested-root Biome warning is from the review runner: the repo-owned lint command runs from frontend and passed locally, in the commit hook, and in Website contracts run 34770128524. Greptile was requested once but has not returned review evidence.

@scttbnsn
scttbnsn merged commit 4493004 into dev Sep 13, 2026
4 checks passed
scttbnsn added a commit that referenced this pull request Sep 13, 2026
* fix(analytics): forward $raw_user_agent and $host for cookieless ingestion (#52)

PostHog's cookieless server-hash step reads $raw_user_agent and $host
straight off event.properties and drops the event with a
cookieless_missing_user_agent/cookieless_missing_host ingestion warning
if either is absent. createCommonProperties rebuilt an allowlisted
properties object that dropped both, so every event was silently
discarded at ingestion. Forward them through; never add $ip, which
PostHog's capture service fills in server-side from the connection.

Co-authored-by: scttbnsn <80784472+scttbnsn@users.noreply.github.com>

* chore(config): remove the stale Cursor rules (#54)

* fix(analytics): promote cookieless ingestion fix to production (#53)

PostHog's cookieless server-hash step reads $raw_user_agent and $host
straight off event.properties and drops the event with a
cookieless_missing_user_agent/cookieless_missing_host ingestion warning
if either is absent. createCommonProperties rebuilt an allowlisted
properties object that dropped both, so every event was silently
discarded at ingestion. Forward them through; never add $ip, which
PostHog's capture service fills in server-side from the connection.

Co-authored-by: biggest-littlest <zap_inane.2p@icloud.com>

* chore(config): drop the stale Cursor rules folder

* docs(config): drop dangling .cursorrules references

---------

Co-authored-by: biggest-littlest <zap_inane.2p@icloud.com>

* docs(readme): describe what this repo is and how it deploys (#55)

* chore(gitignore): ignore the root .vercel link and history-backup bundles

* feat(analytics): capture $pageleave and send $pathname (#60)

Measured over the shared PostHog project, 208 of 432 sessions across
the five instrumented sites record zero duration, and PostHog's
built-in Web analytics Page/Entry page/Exit page tables return zero
rows. capture_pageleave was false, so a session's last recorded
timestamp is its last pageview, and a five-minute read of one page
scores as zero seconds.

Flipping the option alone fixes nothing: sanitizeEvent allowlisted
only $pageview, cta activated, and $web_vitals, so every $pageleave
posthog-js emitted would have been dropped silently with no error and
no ingestion warning. This adds a $pageleave branch that rebuilds the
event the same way $pageview does. capture_pageview is false here
(pageviews are captured by hand), so posthog-js's
_shouldCapturePageleave gate needs an explicit true rather than the
default.

$pathname is the property PostHog's page tables actually key off, and
it was never sent. It's bound to the already-sanitized `path` value,
never the raw pathname, so it can't carry a route outside
ALLOWED_ROUTES and adds no information the event wasn't already
sending. A regression test asserts the two never diverge.

No privacy option changes: cookieless_mode, person_profiles,
persistence, disable_persistence, respect_dnt, save_referrer, and
save_campaign_params are untouched.

Part of X16 in the ops execution plan.

* fix(seo): repair JSON-LD logo 404 and double-slash base URLs

- fix(seo): point Organization.logo at /icon-512x512.png; the referenced
  /logos/codeswhat-logo-green.png never existed, so crawlers got a 404
- fix(seo): strip trailing slashes from BASE_URL and reuse it in robots.ts
  and sitemap.ts, so a NEXT_PUBLIC_SITE_URL set with a trailing slash can't
  emit //sitemap.xml-style URLs
- chore(seo): disallow /studio/ in robots.txt; the capture pages already
  404 in production but the exclusion shouldn't depend on that guard
- chore(seo): 308 the stable *.vercel.app production aliases to
  codeswhat.com instead of serving duplicate content
- fix(api): stop forwarding EmailOctopus error detail to subscribe clients;
  log it server-side and return a fixed message

* build(deps): bump next to ^16.2.11 to clear all 35 Dependabot alerts

One-line range bump; npm resolves next 16.3.3, which also pulls the
patched transitive versions: postcss 8.5.23, nanoid 3.3.18, sharp 0.35.4.
npm audit now reports zero vulnerabilities. No code changes needed: the
app has no middleware, rewrites, server actions, CSP nonces, or
next/image usage, so none of the fixed CVEs required app-side work.

* build(deps): regenerate next-env.d.ts for next 16.3

- build(deps): pick up the root-params.d.ts reference next 16.3 adds
- ci(hooks): pass --no-errors-on-unmatched to the biome pre-commit job so
  committing only biome-ignored files (like next-env.d.ts) doesn't fail

* docs(roadmap): track web-analytics table coverage follow-ups (ops X37)

* chore: ignore .claude/ with a tracked line (#59)

It was covered only by .git/info/exclude, which protects one clone and nobody else's. Without a tracked line, `git add -A` in the parent stages a nested worktree as an embedded gitlink and `git clean -ffd` deletes it.

* docs(roadmap): point acquisition-data item at the ops analytics standard

* docs(roadmap): pageleave ratio is structural; note the bot-detection canary caveat

* chore(config): add .planning/ to .gitignore (#62)

* fix(analytics): preserve validated referring hostnames (#64)

* fix(site): restore hydration and reject invalid signup bodies (#66)

---------

Co-authored-by: biggest-littlest <zap_inane.2p@icloud.com>
@scttbnsn
scttbnsn deleted the scttbnsn/fix-site-boundaries branch September 13, 2026 17:10
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.

3 participants