avo
avo copied to clipboard
Need to explicitly add nonce attribute to script/stylesheet tags.
Describe the bug
After upgrading to Rails 7.1, assets began failing to load, even with unsafe_inline
in my CSP for script-src and style-src.
Chasing it down a bit, it seems that unsafe_inline
is ignored when noncing is enabled.
Avo is adding the CSP tag (hooray!), but apparently not setting nonce attributes on <script>
and <style>
tags.
I was able to get things partially fixed by ejecting the layout, and adding this to all the relevant tags:
nonce: content_security_policy_nonce
(a little more digging suggests nonce: true
may be sufficient)
However, not all of the relevant tags are created from the layout, and I don't want to have to keep ejecting more and more things, and possibly monkey-patching Avo::AssetManager::JavascriptComponent
.
Some of the errors seem... spurious? Like, I can't tell what's actually being prohibited from happening by looking at the page. Others are more serious (e.g. charts completely breaking).
Previously, I worked around CSP issues by adding unsafe-inline
to the style-src
and script-src
tags, but that seems to not be Doing The Thing under Rails 7.1. I... do not know why. According to Chrome, it should never have worked, because unsafe-inline
is ignored when a nonce tag is included.
Steps to Reproduce
Steps to reproduce the behavior:
- Create a Rails 7.1 application, with Avo
- Set the content security policy to be quite strict
- Load up an Avo page
- Look at the console
Expected behavior & Actual behavior
I would expect that enabling a strict CSP Just Works
Models and resource files
Apologies for this being a bit sloppier than my usual repro efforts, but it should be sufficient to get you going:
This is our config/initializers/content_security_policy.rb
:
webpack_urls = Rails.env.development? ? ["http://localhost:3035", "ws://localhost:3035"] : [] # rubocop:disable Lint/Env
SRC_URLS = ([
:self,
Rails.application.config.frontend_base_url,
Rails.application.config.backend_base_url,
Rails.application.config.short_link_url,
] + webpack_urls).compact.uniq.freeze
backend_domain = Rails.application.config.backend_base_url.split("//").last
PROFILE_URLS = [
"https://s3.amazonaws.com/profile.#{backend_domain}/",
"https://profile.#{backend_domain}/",
].freeze
EXTRA_STYLE_URLS =
if Rails.env.local? || Rails.env.docker? || Rails.env.stage?
[:unsafe_inline]
else
[]
end
FONT_SRC_URLS = ([:data] + SRC_URLS).uniq.freeze
IMG_SRC_URLS = ([:data] + SRC_URLS + PROFILE_URLS).uniq.freeze
SCRIPT_SRC_URLS = SRC_URLS.uniq.freeze
STYLE_SRC_URLS = (SRC_URLS + EXTRA_STYLE_URLS).uniq.freeze
CONNECT_SRC_URLS = ([:data] + SRC_URLS).uniq.freeze
Rails.application.configure do
config.content_security_policy do |policy|
policy.default_src(:self)
policy.font_src(*FONT_SRC_URLS)
policy.img_src(*IMG_SRC_URLS)
policy.object_src(:none)
policy.script_src(*SCRIPT_SRC_URLS)
policy.style_src(*STYLE_SRC_URLS)
policy.connect_src(*CONNECT_SRC_URLS)
end
# Generate session nonces for permitted importmap, inline scripts, and inline styles.
config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
config.content_security_policy_nonce_directives = %w[script-src style-src]
config.middleware.insert_before(0, Rack::Cors) do
allow do
origins(*Rails.application.config.origins)
resource("*", headers: :any, credentials: true, methods: %i[get post delete put patch options head])
end
end
end
Our config/initializers/avo.rb
includes this:
Rails.application.config.to_prepare do
Avo::ApplicationController.include(LaxContentSecurityPolicy)
end
And LaxContentSecurityPolicy
, which exists to work around previous CSP issues with Avo is defined as:
module LaxContentSecurityPolicy
extend ActiveSupport::Concern
included do
class_eval do
content_security_policy do |policy|
policy.default_src(:self)
policy.font_src(*FONT_SRC_URLS)
policy.img_src(*IMG_SRC_URLS)
policy.object_src(:none)
policy.script_src(*([:unsafe_inline] + SCRIPT_SRC_URLS))
policy.style_src(*([:unsafe_inline] + SCRIPT_SRC_URLS))
policy.connect_src(*CONNECT_SRC_URLS)
end
end
end
end
System configuration
Avo version: 3.2.0 (also 3.1.7)
Rails version: 7.1.2
Ruby version: 3.2.2
License type:
- [ ] Community
- [ ] Pro
- [x] Advanced
Are you using Avo monkey patches, overriding views or view components?
- [x] Yes. If so, please post code samples.
- [ ] No
Screenshots or screen recordings
Additional context
Impact
- [ ] High impact (It makes my app un-usable.)
- [x] Medium impact (I'm annoyed, but I'll live.)
- [ ] Low impact (It's really a tiny thing that I could live with.)
Urgency
- [ ] High urgency (I can't continue development without it.)
- [x] Medium urgency (I found a workaround, but I'd love to have it fixed.)
- [ ] Low urgency (It can wait. I just wanted you to know about it.)
(My workaround, at the moment, is to disable noncing -- so, not a workaround I'm happy to keep using indefinitely!)
@MrJoy we don't have a lot of experience with CSP and I'm afraid we'll wonder blindly in the dark with this.
Would you be able to help out with a PR?
@adrianthedev Unfortunately, I'm at a loss here. I can't identify what exactly is going wrong, so I don't know precisely what to fix.
My example should be easy to simplify into something reproducible. In this case, Rails.application.config.backend_base_url
is just the link to the Rails app itself and the other URLs are pretty much irrelevant.
Like, I can make a PR that just adds nonce: content_security_policy_nonce
/ nonce: true
to all the script/style tags, but I'd just be doing so blindly without any real understanding of when that is/isn't appropriate.
It seem like nonce
attributes have been added, is this still an ongoing issue?
For unrelated reason I'm currently running an older version, and the monkey-patching presented above didn't cut it for me. Firefox essentially ignore unsafe_inline
if nonce
is set.
For posterity, if anyone end up here in the same situation as me, as a temporary fix I ended up disabling CSP completely for Avo:
module DisableContentSecurityPolicy
extend ActiveSupport::Concern
included do
class_eval do
content_security_policy false
end
end
end
Rails.application.config.to_prepare do
Avo::ApplicationController.include(DisableContentSecurityPolicy)
end
I think this is a good solution for now @oz-tal.
We don't know a lot of details about nonces and CSP so we can't make an educated decision around it. We'd welcome a PR that supports the necessary customization for other devs to apply to their apps and make this work.
Until then, I'll close the issue so we keep the queue clean.