Skip to content

Make admin UI CSP-compatible (nonce inline scripts, drop inline onclick)#477

Merged
dereuromark merged 1 commit intomasterfrom
csp-safe-postlinks
Apr 22, 2026
Merged

Make admin UI CSP-compatible (nonce inline scripts, drop inline onclick)#477
dereuromark merged 1 commit intomasterfrom
csp-safe-postlinks

Conversation

@dereuromark
Copy link
Copy Markdown
Owner

@dereuromark dereuromark commented Apr 22, 2026

Summary

  • Add nonce attribute to the two inline <script> blocks (layout initialization, stats chart) so apps with a strict script-src 'self' 'nonce-...' ... CSP can run them.
  • Replace every Form->postLink(..., 'confirm' => X) with Form->postButton(..., 'form' => ['data-confirm-message' => X]) across all admin templates.
    • postLink emits onclick="..." on the <a>, which CSP blocks without 'unsafe-inline' / 'unsafe-hashes'. nonce does not cover inline event handlers per the CSP spec.
    • postButton emits a <form> + <button type="submit"> — zero inline event handlers. The existing JS delegate in templates/layout/queue.php already intercepts form[data-confirm-message] submit events and calls confirm(), preserving the destructive-action confirmation UX 1:1.
  • 26 helper calls migrated across 9 template files. 2 inline scripts now carry nonces.

Before / after

- <?= $this->Form->postLink(
-     '<i class="fas fa-trash"></i> ' . __d('queue', 'Flush Failed'),
-     ['action' => 'flush'],
-     [
-         'class' => 'nav-link',
-         'escapeTitle' => false,
-         'confirm' => __d('queue', 'Sure? This will remove all failed jobs.'),
-         'block' => true,
-     ]
- ) ?>
+ <?= $this->Form->postButton(
+     '<i class="fas fa-trash"></i> ' . __d('queue', 'Flush Failed'),
+     ['action' => 'flush'],
+     [
+         'class' => 'nav-link btn btn-link text-start w-100',
+         'escapeTitle' => false,
+         'form' => [
+             'class' => 'd-inline',
+             'data-confirm-message' => __d('queue', 'Sure? This will remove all failed jobs.'),
+         ],
+     ]
+ ) ?>

Host app nonce setup

Apps that want to benefit from the new nonce support need to set a cspNonce request attribute in a middleware, for example:

$nonce = base64_encode(random_bytes(16));
$request = $request->withAttribute('cspNonce', $nonce);
$response = $response->withHeader(
    'Content-Security-Policy',
    "script-src 'self' 'nonce-{$nonce}'"
);

If no cspNonce attribute is present, the <script> tag falls back to rendering without the nonce attribute — apps on permissive CSP continue to work unchanged.

Visual side-note

Admin/Queue/index.php and Admin/QueueProcesses/index.php have a btn-group that wrapped two adjacent postLinks. Because each postButton now wraps its button in <form class="d-inline">, Bootstrap's .btn-group no longer collapses borders across them. They render as two independent adjacent buttons rather than a connected pair. Functionally identical; visual grouping lost. Mentioning for awareness.

Verified

  • Loaded /admin/queue and /admin/queue/queued-jobs in Chrome, confirmed 0 inline onclick attributes, 24 form[data-confirm-message] elements, and confirmation dialog fires as expected on button click with proper localized message.
  • Inline scripts carry nonce when cspNonce attribute is present on request.

- Add nonce attribute to inline <script> blocks (layout, stats chart)
- Replace postLink+confirm with postButton+data-confirm-message across
  all admin templates to eliminate inline onclick handlers that CSP
  blocks without unsafe-inline / unsafe-hashes
- JS delegate in the layout already intercepts form[data-confirm-message]
  submit events and replicates the confirmation UX

This lets the admin UI work under a strict script-src CSP
(e.g. "script-src 'self' 'nonce-X' 'unsafe-eval'") without needing
'unsafe-inline' or 'unsafe-hashes'.
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Apr 22, 2026

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 77.22%. Comparing base (5410512) to head (d2e1167).
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@             Coverage Diff              @@
##             master     #477      +/-   ##
============================================
- Coverage     77.22%   77.22%   -0.01%     
  Complexity      949      949              
============================================
  Files            45       45              
  Lines          3196     3209      +13     
============================================
+ Hits           2468     2478      +10     
- Misses          728      731       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@dereuromark dereuromark merged commit ef13c13 into master Apr 22, 2026
16 checks passed
@dereuromark dereuromark deleted the csp-safe-postlinks branch April 22, 2026 23: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.

2 participants