Skip to content

feat(csp): support CSP nonce on editor-injected style elements - #6811

Draft
Gxrvish wants to merge 2 commits into
GrapesJS:devfrom
Gxrvish:feat/csp-nonce
Draft

feat(csp): support CSP nonce on editor-injected style elements#6811
Gxrvish wants to merge 2 commits into
GrapesJS:devfrom
Gxrvish:feat/csp-nonce

Conversation

@Gxrvish

@Gxrvish Gxrvish commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

The editor injects stylesheets at runtime, so a page served with a strict style-src/style-src-elem policy blocks them. Unlike scripts, styles have no strict-dynamic equivalent, so a nonce is the only way to allow them.

Add a cspNonce editor config option and apply it to every <style> the editor creates:

  • CanvasView canvas style element. Moved out of the view template and created imperatively, so the nonce is set before the element enters the document.
  • FrameView frame base styles (baseCss/frameStyle/canvasCss/ protectedCss).
  • CssRuleView, which renders one <style> per CSS rule and is the main source of violations.
  • CssRulesView @keyframes grouping element.
  • BrowserParserCss, which round-trips CSS through a temporary <style> in document.head to reuse the browser parser. When blocked, el.sheet stays null and the parser silently returns no rules, so imported CSS was being dropped without any error under a strict policy.

Nonces have to be in place before insertion, hence the shared createStyleEl and setNonce helpers in utils/dom.

Out of scope: inline style attributes (governed by style-src-attr) and <script> elements appended to the canvas. The latter run today under script-src 'strict-dynamic', and nonce-ing user-authored component scripts is a separate decision.

The editor injects stylesheets at runtime, so a page served with a strict
`style-src`/`style-src-elem` policy blocks them. Unlike scripts, styles have
no `strict-dynamic` equivalent, so a nonce is the only way to allow them.

Add a `cspNonce` editor config option and apply it to every `<style>` the
editor creates:

- `CanvasView` canvas style element. Moved out of the view template and
  created imperatively, so the nonce is set before the element enters the
  document.
- `FrameView` frame base styles (`baseCss`/`frameStyle`/`canvasCss`/
  `protectedCss`).
- `CssRuleView`, which renders one `<style>` per CSS rule and is the main
  source of violations.
- `CssRulesView` `@keyframes` grouping element.
- `BrowserParserCss`, which round-trips CSS through a temporary `<style>` in
  `document.head` to reuse the browser parser. When blocked, `el.sheet` stays
  null and the parser silently returns no rules, so imported CSS was being
  dropped without any error under a strict policy.

Nonces have to be in place before insertion, hence the shared `createStyleEl`
and `setNonce` helpers in `utils/dom`.

Out of scope: inline `style` attributes (governed by `style-src-attr`) and
`<script>` elements appended to the canvas. The latter run today under
`script-src 'strict-dynamic'`, and nonce-ing user-authored component scripts
is a separate decision.
The editor builds most of its chrome by assigning HTML strings, so every
`style="..."` literal in a template is parsed as an inline style attribute and
blocked by a strict `style-src-attr` policy. `setAttribute('style', ...)` has
the same problem. CSSOM writes (`el.style.prop = value`) are not covered by
CSP, so they are the way to apply values only known at runtime.

Static styles move to utility classes, adding `gjs-pointer-events-all` and
`gjs-clear-float` next to the existing `gjs-hidden` and `gjs-no-pointer-events`:
canvas and frame tools, frame remove icon, modal collector, device add button
and the float clearers in the asset manager, file uploader, modal and style
manager.

Runtime values move to the CSSOM through a new `setStyleText` helper, which
applies a declaration string property by property. It splits only on top-level
`;`, so data URLs and quoted values survive, and it keeps `!important` and
custom properties: asset preview background, navigator indentation, style
manager layer preview, select option styles in the style and trait managers,
RTE action `style` attributes, the canvas iframe component and the color
picker swatches.

Elements whose visibility is toggled at runtime by resetting the inline
display keep using the CSSOM for their initial state, so the toggles still
work. The style manager clear button instead switches to toggling `gjs-hidden`,
since its update is debounced and a class avoids a flash on render.

Out of scope: the `style` attribute of user components (`ComponentView`), which
is the content the editor exists to author, and the SVG image placeholder,
which is serialized to a base64 data URL and governed by `img-src`. Both are
recorded in the allowlist of the new guard spec.
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.

1 participant