Skip to content

CSP compatibility: inline styles/scripts and event handlers require disabling strict Content Security Policy #36

Closed

Description

@yann120

Hi,

First: thanks for the gem. I tested solid_queue_monitor as a possible replacement for mission_control-jobs in a Rails app, and the feature set looks very good.

I ran into one integration issue: the current UI is not compatible with a strict Content Security Policy.

My app uses a CSP similar to:

config.content_security_policy do |policy|
  policy.default_src :self
  policy.font_src    :self
  policy.img_src     :self, :data
  policy.object_src  :none
  policy.script_src  :self
  policy.style_src   :self
end

config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
config.content_security_policy_nonce_directives = ["script-src", "style-src"]

With that setup, solid_queue_monitor loads but the UI is broken because the browser blocks:

  • inline <style> tags
  • inline <script> tags
  • inline event handlers like onclick / onchange
  • runtime inline style mutations via element.style.*

From what I saw locally, this comes from the current self-contained rendering approach:

  • CSS generated inline
  • JS generated inline
  • some presenters using inline handlers
  • some JS toggling visibility/opacity with direct inline styles

My current workaround is to disable CSP only for the monitor controller:

Rails.application.config.to_prepare do
  SolidQueueMonitor::ApplicationController.content_security_policy false
end

This works for evaluation and internal tooling, but it weakens defense in depth for that interface, so it is not ideal as a long-term solution.

Would you be open to discussing a CSP-compatible mode or refactor?
Possible directions:

  • move CSS to an engine stylesheet asset
  • move JS to engine JS assets
  • replace inline event handlers with addEventListener
  • replace element.style.* mutations with CSS classes
  • optionally keep the current self-contained mode as a fallback for API-only / zero-setup installs

I understand why the gem is implemented this way: it makes installation extremely simple and keeps the UI self-contained. But CSP compatibility seems important for production Rails apps with stricter security defaults.

If this direction sounds reasonable, I’d be happy to help narrow down the incompatible pieces more precisely.

Thanks a lot!

Activity

  1. vishaltps commented on Apr 15, 2026

    @vishaltps
    Owner

    Hi @yann120 , thanks for the detailed write-up and for evaluating the gem! You're right that strict CSP compatibility matters for production Rails apps, and disabling CSP on the controller isn't a good long-term answer.

    Honestly, CSP wasn't on my radar when I designed the gem, my focus was on making it truly drop-in (mount the engine, get a UI, no asset pipeline, no JS/CSS files to manage). That decision is what made everything inline in the first place, and it's also what's now causing the incompatibility you're seeing. Your issue is a good nudge to fix that; I should have considered stricter CSP setups earlier, especially since nonce-based CSP is increasingly the Rails default.

    I took a pass through the codebase to map the incompatible pieces. The scope is roughly:

    • 1 inline <style> blob (~2000 lines of CSS generated by StylesheetGenerator)
    • 8 inline <script> blocks across HtmlGenerator and a few presenters (auto-refresh, theme toggle, chart tooltip, bulk retry/discard, backtrace toggle, raw-data collapse, etc.)
    • 16 inline event handlers (onclick / onchange / onsubmit)
    • ~20 runtime element.style.* mutations (display/opacity/transform/position)
    • No asset pipeline today, the engine renders full HTML documents with layout false, so there's no Sprockets/Propshaft wiring to hook into yet.

    I'm planning to tackle this in two layers so the fix lands quickly without a risky big-bang refactor:

    Phase 1: CSP-compatible without moving to assets

    • Replace all onclick / onchange / onsubmit with addEventListener using data-* attributes
    • Replace element.style.* mutations with classList toggles (add the needed utility classes to the stylesheet)
    • Support CSP nonces: read request.content_security_policy_nonce and emit <style nonce="…"> / <script nonce="…"> on the remaining inline tags
    • This unblocks your exact setup (nonce-based script-src / style-src) with minimal churn and preserves the zero-setup install story.

    Phase 2 (optional): stricter 'self'-only

    • Extract CSS/JS into engine assets served by the engine's own controller, so apps with script_src :self (no nonces) work too
    • Keep the inline/nonce mode as a fallback

    Does Phase 1 cover your use case? Your snippet uses nonce directives for script-src and style-src, so nonce support should be sufficient, but happy to hear if you need pure 'self' compliance, which would push us to Phase 2 sooner.

    Also very happy to accept help narrowing things down if you're up for it. I'll open a tracking PR shortly.

    Thanks again!

  2. yann120 commented on Apr 15, 2026

    @yann120
    Author

    Hi @vishaltps, thanks for your quick answer and the detailed breakdown, this makes sense.

    Yes, Phase 1 should cover my use case. My app already uses nonce-based script-src and style-src, so adding nonces to the remaining inline <script> / <style> tags, removing inline event handlers, and eliminating inline style mutations or attributes in favor of CSS classes should be enough on my side.

    I agree with the approach of landing that first before considering a larger asset-based refactor.

    I would be happy to help you if you need 🙂

  3. vishaltps commented on Apr 20, 2026

    @vishaltps
    Owner

    @yann120 Can you please test the latest version and let me know if you find any issue with the CSP?

  4. yann120 commented on Apr 20, 2026

    @yann120
    Author

    Hi @vishaltps, thanks for fixing this so fast!
    I checked on local with the gem on the main branch, and it works perfectly after I removed my CSP workaround.
    Thank you for fixing that! 👏🏻

  5. vishaltps commented on Apr 20, 2026

    @vishaltps
    Owner

    Thanks, closing the issue now.

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

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