Skip to content

Interactivity API: CSP-Safe Inline JS Expressions - #82155

Open
nickchomey wants to merge 19 commits into
WordPress:trunkfrom
nickchomey:add/interactivity-full-expressions
Open

nickchomey wants to merge 19 commits into
WordPress:trunkfrom
nickchomey:add/interactivity-full-expressions

Conversation

@nickchomey

@nickchomey nickchomey commented Aug 27, 2026 •

Copy link
Copy Markdown
Contributor

What?

Closes #79765

Why?

So, here's an interesting development. Datastar just released v1.0.3, which includes a CSP mechanism. They use a data-nonce="abc123" attribute on the document root plus a script-src 'nonce-abc123' header so inline expressions evaluate without unsafe-eval.

As they note in their docs, you still have to be careful putting user input in HTML templates, but that's always been the case.

This PR refreshes #79780 with that same mechanism layered on top. There are unit and e2e tests covering several CSP permutations (no CSP, unsafe-eval, nonce, and strict policies with neither). It works as expected.

How it behaves:

  • No CSP: expressions compile via Function() (current behaviour, fully cacheable).
  • CSP allowing unsafe-eval: still Function().
  • data-nonce present on <html>: a new mechanism compiles each expression by injecting it into a <script nonce="..."> element, so script-src 'nonce-...' works without unsafe-eval. The attribute is read and removed at runtime.
  • Strict CSP with neither a nonce nor unsafe-eval: expressions aren't compiled, same as today - the iAPI degrades like any other script under a policy that disallows it.

One detail worth calling out: the JS can't read the CSP header, so the server communicates the nonce via the data-nonce attribute. The same nonce must appear in both the Content-Security-Policy header and the <html data-nonce="..."> attribute, and the server is responsible for noncing the iAPI module script too.

Given this addresses the primary concern from #79780 - that sites would otherwise need unsafe-eval - is this something you'd be open to considering?

Note: the branch isn't rebased on trunk (local dev reasons). Not important for an initial review

Testing Instructions

The behaviour is covered by unit tests for the JS compilation path (packages/interactivity/src/test/csp.ts) and e2e tests that drive a real browser under different CSP policies (test/e2e/specs/interactivity/csp-nonce.spec.ts). The e2e tests intercept the document response and inject the Content-Security-Policy header + data-nonce attribute, simulating what a server/CDN would send — no PHP required to exercise the JS path.

The e2e matrix:

CSP policy data-nonce Expressions
none — ✅ work (Function())
'self' 'unsafe-eval' 'unsafe-inline' — ✅ work (Function())
'self' 'nonce-...' present, scripts nonced ✅ work (nonced <script> compilation; data-nonce removed)
'self' 'unsafe-inline' — ❌ blocked (no unsafe-eval, no nonce)
'nonce-...' 'strict-dynamic' present, scripts not nonced ❌ blocked (iAPI module un-nonced → data-nonce stays)

The last two confirm the failure modes: without unsafe-eval or a nonce, expressions don't evaluate; and a nonce on <html> alone isn't enough — the iAPI module script must also carry the nonce, which is the server's responsibility (mirrored in the mu-plugin via the wp_inline_script_attributes / wp_script_attributes filters).

  • e2e - npm run test:e2e -- test/e2e/specs/interactivity/csp-nonce.spec.ts
  • unit - npm run test:unit packages/interactivity/src/test/csp.ts
  • real site - you can use the mu-plugin in this zip to test and tinker (the configurable things are at the very bottom. I assume there was a way to add this to the PR somehow, but I dont know how. Manual should be fine).

iapi-nonces.zip

Use of AI Tools

HY3, Muse Spark 1.2 via VS Code Copilot Chat as a pair programmer.

- Tests for splitStatements: null cases, basic split, string/regex/template
  literal handling, mixed content, non-string input
- Tests for getEvaluate full-expression path: context comparisons (!==, ===),
  ternary, negation, logical operators, array filter, multi-statement,
  invalid expressions, backward-compatible fast path
…ake sure that hasNegativeOperator is working
…ass now as they go through full expression path
@github-actions

Copy link
Copy Markdown

Warning: Type of PR label mismatch

To merge this PR, it requires exactly 1 label indicating the type of PR. Other labels are optional and not being checked here.

  • Required label: Any label starting with [Type].
  • Labels found: .

Read more about Type labels in Gutenberg. Don't worry if you don't have the required permissions to add labels; the PR reviewer should be able to help with the task.

1 similar comment
@github-actions

Copy link
Copy Markdown

Warning: Type of PR label mismatch

To merge this PR, it requires exactly 1 label indicating the type of PR. Other labels are optional and not being checked here.

  • Required label: Any label starting with [Type].
  • Labels found: .

Read more about Type labels in Gutenberg. Don't worry if you don't have the required permissions to add labels; the PR reviewer should be able to help with the task.

@nickchomey

Copy link
Copy Markdown
Contributor Author

this should probably also change to es-module-lexer/minimal/js in

to account for it blocking CSP as reported in #82492

@github-actions github-actions Bot added [Package] Block editor /packages/block-editor [Package] Base styles /packages/base-styles [Package] Commands /packages/commands [Package] Boot /packages/boot [Package] Admin UI /packages/admin-ui [Package] Abilities /packages/abilities labels Oct 17, 2026
@nickchomey
nickchomey force-pushed the add/interactivity-full-expressions branch from 0d7c912 to 37cb4a2 Compare October 17, 2026 21:03
@github-actions github-actions Bot added [Package] E2E Tests /packages/e2e-tests [Package] Interactivity /packages/interactivity and removed [Package] A11y /packages/a11y [Package] Autop /packages/autop [Tool] Babel preset /packages/babel-preset-default [Tool] Browserslist config /packages/browserslist-config [Tool] Babel plugin makepot /packages/babel-plugin-makepot [Tool] Babel plugin import JSX pragma /packages/babel-plugin-import-jsx-pragma [Package] Blob /packages/blob [Package] API fetch /packages/api-fetch [Package] Components /packages/components [Package] Blocks /packages/blocks [Package] Block library /packages/block-library [Package] Block editor /packages/block-editor [Package] Base styles /packages/base-styles [Package] Commands /packages/commands [Package] Boot /packages/boot [Package] Admin UI /packages/admin-ui [Package] Abilities /packages/abilities labels Oct 17, 2026
@nickchomey nickchomey mentioned this pull request Oct 17, 2026
5 of 10 tasks

This branch has not been deployed

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

Labels

[Package] E2E Tests /packages/e2e-tests [Package] Interactivity /packages/interactivity

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: Support evaluation of inline JS expressions in Interactivity API

1 participant