Repository navigation
feat(security): strict Content Security Policy, no inline script or style - #133
Merged
Merged
Conversation
Every style="..." in the dashboard's markup and JS strings is gone, as a strict Content Security Policy (style-src 'self') requires. Fixed values became classes at the end of dashboard.css; bar widths and heights and per-row colours travel as data-w, data-h, data-bg and data-color and are applied through the CSSOM by a MutationObserver, which the policy allows. Elements that start hidden use the hidden attribute ([hidden] with !important, since a display rule on the element would show it anyway) and are shown with el.hidden = false. No change on screen. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The 83 onclick, oninput, onchange, onsubmit, onkeydown, onpointerdown and onblur attributes are gone, as a strict Content Security Policy (script-src 'self') requires: nonces do not cover inline handlers. An element names its action in data-action (click) or data-on-<event> and carries its arguments in data attributes; one listener per event type walks from the target up to the document and runs every action on the way, as the inline handlers bubbled, until one stops propagation. Blur does not bubble, so data-on-blur listens to focusout. jsArg() is gone with the handlers: arguments are escaped as attributes with esc(). The smoke test's synthetic change events now bubble, as a browser's do; the delegated listener never saw them before. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…tyle script-src 'self'; style-src 'self' on the dashboard, the shared dashboard and the login page, as the design kit asks. One missed esc() can no longer run script (as it did in v1.26.1). The branding and the share reach the page as a JSON data block (<script type="application/json" id="puls-config">), which the policy does not govern; theme.js and dashboard.js read it. The theme script in <head> is public/theme.js, shared with the login page, and applies the install's accent through the CSSOM. The login page's styles are public/login.css and its CSRF refresh public/login.js. The smoke test fails on any CSP violation (securitypolicyviolation -> console.error). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.
Epic 3, the rest of the way:
script-src 'self'; style-src 'self'on the dashboard, the shared dashboard and the login page, as the design kit asks. One missedesc()can no longer run script (as it did in v1.26.1); it shows as text.Four commits, each green on its own:
style="..."in the JS strings and the markup became classes at the end ofdashboard.cssfor fixed values, anddata-w(percent),data-h(pixels),data-bganddata-colorfor bar sizes and per-row colours, applied through the CSSOM by a MutationObserver, which the policy allows. Elements that start hidden use thehiddenattribute ([hidden]with!important, since a display rule on the element would show it anyway) andel.hidden = false.data-action(click) anddata-on-<event>attributes with their arguments indata-*. One listener per event type walks from the target up to the document and runs every action on the way, as the inline handlers bubbled, until one stops propagation.data-on-blurlistens tofocusout.jsArg()is gone with the handlers. The smoke test's synthetic change events now bubble, as a browser's do.<script type="application/json" id="puls-config">,<>&escaped), whichscript-srcdoes not govern. The theme script in<head>istheme.js, shared with the login page, and applies the install's accent through the CSSOM, so no nonce and no stylesheet endpoint were needed. The login page's styles and CSRF refresh arelogin.cssandlogin.js. The smoke test registers asecuritypolicyviolationlistener before every page loads and fails on any violation, so no inline bit can come back.Checked: 360 tests, the smoke test under the strict header, and every README image regenerated and compared by eye (bars, dots, badges, flags, goal meters, legend, tooltips all as before; the images are not committed, they change with the date).
Changelog has a Security entry with the note that anyone upgrading by hand copies all of
public/.🤖 Generated with Claude Code