Skip to content

fix(analytics): CSP-safe Clicky loader; Zod jitless under eval-less CSP - #164

Merged
WilliamAGH merged 2 commits into
mainfrom
dev
Aug 3, 2026
Merged

fix(analytics): CSP-safe Clicky loader; Zod jitless under eval-less CSP#164
WilliamAGH merged 2 commits into
mainfrom
dev

Conversation

@WilliamAGH

Copy link
Copy Markdown
Owner

Fixes the two Content-Security-Policy violations found while dogfooding the PR #163 prod deployment.

Changes

  • Clicky inline initializer blocked by CSP (analytics silently dead in prod). The backend injected var clicky_site_ids = ...push(<site-id>) as an inline script, which script-src 'self' ... blocks, so Clicky never received a site ID. The site ID now rides on the async loader tag's data-id attribute — Clicky's loader reads document.currentScript.getAttribute("data-id") itself (verified against https://static.getclicky.com/js). Legacy inline initializers are stripped from served documents unconditionally.
  • Zod eval probe reported as a CSP violation on every page load. The CSP has no 'unsafe-eval', so Zod's JIT fast path can never activate; its cached allowsEval probe still called new Function("") once per load, surfacing a securitypolicyviolation. z.config({ jitless: true }) in the canonical schemas module skips the probe.

Validation

  • make build (incl. Spotless) green
  • SeoControllerTest green, including new injects_clicky_loader_without_inline_initializer asserting the data-id loader and the absence of any inline initializer
  • Frontend npm run validate green; vitest 337/337

The deployed CSP restricts script-src to 'self' plus allowlisted origins,
so the inline clicky_site_ids initializer was blocked on every page load
and Clicky never received a site ID. Clicky's loader reads
document.currentScript.getAttribute("data-id") itself, so the ID now
rides on the async loader tag and no inline script is injected. Legacy
inline initializers are stripped unconditionally from served documents.
The deployed CSP has no 'unsafe-eval', so Zod's JIT fast path can never
activate; its cached allowsEval probe still called new Function("") once
per page load, which browsers surface as a securitypolicyviolation even
though Zod swallows the throw. Configuring jitless before any schema is
built skips the probe and keeps parsing on the interpreter path the CSP
already forces.
Copilot AI review requested due to automatic review settings August 3, 2026 05:04
@WilliamAGH
WilliamAGH merged commit 148bbcf into main Aug 3, 2026
3 of 4 checks passed
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3b3c5a24-9cce-45f6-8806-28c13ab02070

📥 Commits

Reviewing files that changed from the base of the PR and between b715f7d and 73a0f79.

📒 Files selected for processing (3)
  • frontend/src/lib/validation/schemas.ts
  • src/main/java/com/williamcallahan/javachat/web/ClickyAnalyticsInjector.java
  • src/test/java/com/williamcallahan/javachat/web/SeoControllerTest.java

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes

    • Improved compatibility with stricter Content Security Policies.
    • Updated analytics loading to avoid inline scripts while preserving asynchronous tracking.
    • Ensured analytics can be enabled, disabled, or updated reliably without duplicate loaders.
  • Tests

    • Added coverage verifying the analytics loader, site identifier, asynchronous loading, and absence of inline initialization.

Walkthrough

The change enables Zod jitless mode and updates Clicky analytics injection to use an asynchronous loader with a data-id attribute. SEO tests verify the loader output and reject legacy inline initialization.

Changes

CSP compatibility

Layer / File(s) Summary
Runtime and Clicky loader updates
frontend/src/lib/validation/schemas.ts, src/main/java/com/williamcallahan/javachat/web/ClickyAnalyticsInjector.java
Zod uses jitless: true. Clicky removes legacy initializers, updates existing loaders, removes disabled loaders, and creates asynchronous loaders with the site ID.
SEO response validation
src/test/java/com/williamcallahan/javachat/web/SeoControllerTest.java
Tests verify the Clicky loader, data-id, and async attributes. Tests reject inline initializers containing clicky_site_ids.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: copilot

Poem

Jitless checks run bright,
Clicky loads without inline light,
IDs ride the loader through,
Tests confirm the CSP view.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

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.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens the app against strict Content-Security-Policy (no inline scripts / no unsafe-eval) by making Clicky analytics initialization CSP-safe and configuring Zod v4 to avoid its eval/JIT probe path.

Changes:

  • Replace the Clicky inline initializer with a CSP-safe loader script tag carrying the site ID via data-id, and strip legacy inline initializers.
  • Add backend test coverage asserting the Clicky loader is injected with data-id and without any inline initializer.
  • Configure Zod v4 in the canonical schemas module with z.config({ jitless: true }) to avoid CSP unsafe-eval violations.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/main/java/com/williamcallahan/javachat/web/ClickyAnalyticsInjector.java Switch Clicky initialization to a CSP-safe data-id loader pattern and remove legacy inline initializers.
src/test/java/com/williamcallahan/javachat/web/SeoControllerTest.java Add regression test ensuring the Clicky loader is present with data-id and no inline initializer exists.
frontend/src/lib/validation/schemas.ts Configure Zod v4 to run in jitless mode under eval-less CSP.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

}

if (existingClickyLoader != null) {
existingClickyLoader.attr(CLICKY_SITE_ID_ATTRIBUTE, Long.toString(clickySiteId));
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