Skip to content

DocsUI::Shell inline scripts are not nonce-aware — blocked under a nonce-based CSP #2

Description

@mhenrixon

Summary

DocsUI::Shell emits inline <script> (and inline <style> via DocsUI::Code) without a CSP nonce. On a host app that enforces a nonce-based script-src (the Rails default when content_security_policy_nonce_directives includes script-src, typically with strict-dynamic in production), the browser blocks these inline scripts. The most visible casualty is the theme-restore script — the persisted theme never applies, so the page renders the server default and there's no way to fix it without bypassing the shell.

Where

  • app/components/docs_ui/shell.rbtheme_restore_script (the inline <script> around line 83–85) and render_head.
  • app/components/docs_ui/code.rb → the inline <style> block that injects the Rouge theme CSS. (style-src commonly still allows 'unsafe_inline', so this one often survives, but it's the same class of problem and worth fixing together.)

Repro

  1. Mount docs-kit in a Rails app that sets:
    config.content_security_policy_nonce_generator = ->(_req) { SecureRandom.base64(16) }
    config.content_security_policy_nonce_directives = %w[script-src]
    config.content_security_policy do |p|
      p.script_src :self, :strict_dynamic
    end
  2. Load any docs page.
  3. Observe a CSP violation for the inline theme-restore script in the console; the persisted theme is not applied.

Expected

DocsUI::Shell should nonce its inline <script> (and ideally its inline <style>) when a nonce is available, e.g. script(nonce: request.content_security_policy_nonce) { ... }, falling back to the un-nonced form when there's no nonce (dev, or apps not using nonces). phlex-rails already exposes the CSP helpers the shell includes (CSPMetaTag), so the nonce is reachable from the render context.

Current workaround

Subclass DocsUI::Shell and override the private theme_restore_script to re-emit the identical script with nonce: request.content_security_policy_nonce. It works but relies on overriding a private method and duplicating the script body, so it silently breaks if the upstream body changes.

Possible fix

Read the request nonce inside render_head/theme_restore_script and pass it to the script/style tags when present. Happy to send a PR if the approach sounds right.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions