feat(admin/security): strict CSP with Trusted Types + DOMPurify in editor - #469
Merged
Conversation
Adds AdminStrictPolicy() which tightens AdminPolicy() in three ways required by the strict admin CSP shape: - script-src always includes 'strict-dynamic' so the per-request nonce transitively authorizes the Next.js runtime's dynamic chunks without a per-host allowlist. - require-trusted-types-for 'script' forced on. - trusted-types defaults to "gn-admin gn-editor 'allow-duplicates'" — the policy names the admin and block editor mint at runtime. Caller overrides (TrustedTypePolicies, IncludeStrictDynamic) still win so a hardened deployment can drop 'allow-duplicates' or constrain the list further. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Signed-off-by: Tayeb Mokni <tayeb.mokni@gmail.com>
Adds the `gn-admin` and `gn-editor` Trusted Types policies plus the `setHTML(el, str)` / `setURL(el, attr, str)` helpers in apps/admin/src/lib/trusted-types.ts. Both policies route their input through DOMPurify (admin: strict profile; editor: strict + inline-SVG profile) before minting the TrustedHTML value, so admin code that previously assigned to `innerHTML` directly now satisfies the strict admin CSP `require-trusted-types-for 'script'` directive. A companion `<SafeHTML>` component wraps the imperative setter for React render-side use. Every prior `dangerouslySetInnerHTML` callsite in the admin (`/search` page, `<GlobalSearch>`) now uses `<SafeHTML>`; the block inserter's icon path routes through the new `sanitizeBlockIcon` helper in `@gonext/blocks-editor`, which uses the SSR-safe `isomorphic-dompurify` build so SSR + Trusted Types are both honoured. Tests: 18 specs for `apps/admin/src/lib/trusted-types.ts` covering policy registration, idempotency, SSR fallback, script-vector sanitization, URL pseudo-scheme rejection, and the React escape hatch. 8 specs for `packages/ts/blocks-editor/src/trusted-types.ts` covering icon sanitization (SVG preservation, foreignObject removal, onerror stripping). All 68 existing admin suites (468 tests) and 24 blocks-editor suites (236 tests) remain green. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Signed-off-by: Tayeb Mokni <tayeb.mokni@gmail.com>
Switches apps/admin/middleware.ts to emit the AdminStrictPolicy shape:
- script-src 'self' 'nonce-…' 'strict-dynamic' (transitive nonce trust)
- trusted-types gn-admin gn-editor 'allow-duplicates'
The TRUSTED_TYPES_POLICIES list is now exactly { gn-admin, gn-editor,
'allow-duplicates' } — dropping the legacy gn-plugin / nextjs#bundler /
default entries that defeated the point of 'strict-dynamic' (any
listed bundler policy can mint scripts without nonce review). Plugin-
contributed HTML must round-trip through gn-editor (DOMPurify) instead.
Adds react/no-danger as an ESLint error in apps/admin/.eslintrc.json so
any new dangerouslySetInnerHTML usage fails CI; the existing call sites
were converted to <SafeHTML> / sanitizeBlockIcon in commit 051ea51.
Middleware tests updated: new assertions for 'strict-dynamic', the
allow-duplicates keyword, and explicit denials for the dropped policy
names. Tiny TS fix in packages/ts/blocks-editor/src/trusted-types.ts
to keep the DOMPurify Config object mutable (Config has string[] field
types).
Closes #59.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Tayeb Mokni <tayeb.mokni@gmail.com>
tayebmokni
enabled auto-merge (squash)
May 25, 2026 21:27
|
Heads up — this PR touches strings that often signal a security disclosure ( If this PR fixes or describes a real vulnerability that has not yet been publicly disclosed, please stop and use the private path:
See If this is a false positive (test fixture, doc update, release notes, etc.) please ignore this comment — the check is advisory only and does not block the PR. Matched files:
|
6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #59, #90.
Delivers the strict admin CSP and the DOMPurify-backed Trusted Types layer:
packages/go/middleware/csp/preset.go) emitsscript-src 'self' 'nonce-…' 'strict-dynamic',require-trusted-types-for 'script', andtrusted-types gn-admin gn-editor 'allow-duplicates'. Mirrors CSP3 §6.1's strict pattern;strict-dynamiccloses the host-allowlist trap by transitively trusting nonced bundle loads.apps/admin/middleware.ts) emits the exact same shape so the dev path matches prod byte-for-byte. New negative assertions inmiddleware.test.tslock the absence ofgn-plugin/nextjs#bundler/defaultfrom the trusted-types directive.apps/admin/src/lib/trusted-types.ts,packages/ts/blocks-editor/src/trusted-types.ts) registergn-admin(strict admin chrome) andgn-editor(block editor; permits inline SVG). Both route input through DOMPurify before mintingTrustedHTML. SSR / non-TT environments fall through to a shim that still sanitises so unit tests observe identical behaviour.setHTML(el, html),setURL(el, attr, url),<SafeHTML>(declarative React wrapper), andsanitizeBlockIcon(svg)for the editor's icon path.apps/admin/package.json;isomorphic-dompurifyadded topackages/ts/blocks-editor/package.json(SSR-safe path for blocks-editor consumers).dangerouslySetInnerHTMLcallsite underapps/admin/srcandpackages/ts/blocks-editor/converted:/searchpage and<GlobalSearch>(admin) now use<SafeHTML>;<BlockTile>icon (blocks-editor) wraps throughsanitizeBlockIcon.apps/admin/.eslintrc.jsongainsreact/no-danger: errorso any future regression fails CI.Test plan
pnpm vitest runinapps/admin— 68 files, 470 tests pass (+20 new specs).pnpm vitest runinpackages/ts/blocks-editor— 24 files, 236 tests pass (+8 new specs).pnpm typecheck+pnpm lintclean in both packages.pnpm build(Next.js prod build) succeeds.go test ./packages/go/middleware/csp/... ./apps/api/...green./search?q=…in dev — confirm no Trusted Types violations and<mark>highlights still render./posts/new(block editor) — confirm registered SVG block icons render under the strict CSP.Content-Security-Policyresponse header in dev — confirmstrict-dynamic, the gn-admin/gn-editor policy names, and absence ofunsafe-inline/unsafe-eval.