Skip to content

Conversation

SebouChu
Copy link

@SebouChu SebouChu commented Aug 23, 2024

Problem

Rack 3 now sends headers with lowercase names. This breaks the override CSP used by rswag, as the middleware writes the CSP with the name Content-Security-Policy, but Rails (with Rack 3) searches for content-security-policy to know if it should end its own policy.

Solution

We configure the CSP header name based on the Rack version in the middleware

This concerns this parts of the OpenAPI Specification:

It does not concern any part of the OpenAPI spec.

The changes I made are compatible with:

  • OAS2
  • OAS3
  • OAS3.1

Related Issues

Related to #744

Checklist

  • Added tests
  • Changelog updated
  • Added documentation to README.md Does not need documentation
  • Added example of using the enhancement into test-app

Steps to Test or Reproduce

  • Set up a Rails application using Rack 3.
  • Set a CSP policy in Rails initializer
  • Set up rswag in the application
  • Try to display the webpage on rswag endpoint, page should be blank with CSP errors in the browser console.

@SebouChu
Copy link
Author

Don't know where to test this tho, if anyone has an idea :)

@woodhull
Copy link

We also experienced this!

@tagliala
Copy link

Hello,

the following change in Rack will not play well with this fix:

https://github.com/rack/rack/pull/2199/files

@SebouChu
Copy link
Author

@tagliala I don't see how it can be a problem as Rack::RELEASE is kept the same as Rack::VERSION

@tagliala
Copy link

tagliala commented Feb 13, 2025

@tagliala I don't see how it can be a problem as Rack::RELEASE is kept the same as Rack::VERSION

I did read the comments at rack/rack#2199, and apparently Rack::RELEASE will not be deprecated at all (or it is not planned to be deprecated), so yes, it is not a concern anymore

I was concerned about a future deprecation, and a subsequent removal of that constant

However, Rack.release is available and did not change

@SebouChu
Copy link
Author

@tagliala I'm going to use this to make sure it does not need a change for Rack 4

@SebouChu SebouChu force-pushed the fix-csp-header-name branch from 1388600 to e54fe0b Compare February 13, 2025 09:09
Rack::RELEASE might be removed or deprecated from Rack 4 (details: rack/rack#2199)
@@ -14,7 +19,7 @@ def call(env)
end

if index_path?(env)
return [ 200, { 'Content-Type' => 'text/html', 'Content-Security-Policy' => csp }, [ render_template ] ]
return [ 200, { 'Content-Type' => 'text/html', CONTENT_SECURITY_POLICY_HEADER => csp }, [ render_template ] ]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As actionpack is defined as dependency of this gem, we can rely on the constants defined there

Suggested change
return [ 200, { 'Content-Type' => 'text/html', CONTENT_SECURITY_POLICY_HEADER => csp }, [ render_template ] ]
return [ 200, { 'Content-Type' => 'text/html', ActionDispatch::Constants::CONTENT_SECURITY_POLICY => csp }, [ render_template ] ]

to get rid of the definition of CONTENT_SECURITY_POLICY_HEADER above

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @Lorel, made the adjustment!

Copy link

@tagliala tagliala May 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello, this is a good suggestion

However, as per gemspec, rswag-ui supports actionpack >= 5.2

s.add_dependency 'actionpack', '>= 5.2', '< 8.1'
s.add_dependency 'railties', '>= 5.2', '< 8.1'

ActionDispatch::Constants has been introduced 2 years ago, probably in 7.1.0

rails/rails@1fd79ab

I would reconsider this change, and maybe add a note to migrate to ActionDispatch::Constants when all Rails < 7.1 support will be dropped

Copilot analysys

This change updates the HTTP header key for Content Security Policy (CSP) from the hardcoded string 'Content-Security-Policy' to ActionDispatch::Constants::CONTENT_SECURITY_POLICY. Specifically:

Before:

return [200, { 'Content-Type' => 'text/html', 'Content-Security-Policy' => csp }, [render_template]]

After:

return [200, { 'Content-Type' => 'text/html', ActionDispatch::Constants::CONTENT_SECURITY_POLICY => csp }, [render_template]]

What does this do?

  • Instead of using the string 'Content-Security-Policy' as the header key, it uses the constant ActionDispatch::Constants::CONTENT_SECURITY_POLICY.
  • This constant is essentially an alias for 'Content-Security-Policy' in modern Rails versions.

Is this change good given the supported Rails versions?

1. Support for ActionDispatch::Constants::CONTENT_SECURITY_POLICY

  • This constant was introduced in Rails 7.1.
  • If rswag-ui supports Rails < 7.1 (e.g., Rails 6.1, 7.0), this change will break compatibility, as the constant will not exist in those versions.
  • If rswag-ui has dropped support for Rails < 7.1, this change is safe and slightly preferred for consistency and avoiding typos.

2. Current rswag Supported Rails Versions

  • As of early 2024, rswag supports Rails 6.1, 7.0, and 7.1 (see gemspec and documentation).
  • Therefore, this change is NOT safe for all supported rswag users—it will cause a NameError on Rails < 7.1.

Recommendation

  • If rswag-ui needs to support Rails < 7.1:
    Do not make this change or use a fallback:
    csp_header = defined?(ActionDispatch::Constants::CONTENT_SECURITY_POLICY) ?
      ActionDispatch::Constants::CONTENT_SECURITY_POLICY : 'Content-Security-Policy'
    return [200, { 'Content-Type' => 'text/html', csp_header => csp }, [render_template]]
  • If rswag-ui only supports Rails >= 7.1:
    The change is good and more robust.

Summary

This change is not safe if rswag-ui supports any Rails version lower than 7.1.
If you want to support older versions, use a conditional fallback. If you only support Rails 7.1 and above, the change is fine.

Notes on Copilot analysis:

  • The link to the PR is completely wrong
  • The versions supported by rswag ui are wrong

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yeah, now I remember, I think that was why I didn't use ActionPack constant, due to this minimal version. @Lorel Are you okay with me reverting the last change?

@franzliedke
Copy link

This is the correct fix from my perspective, except for the small difficulty with older Rails versions, where the constant is not present. (Should be fixable by using a custom constant here that falls back the upper-case variant when the Rails constant is not defined.)

In the meantime, here's our generic patch for anybody with similar problems on a Rails app with Rack 3:

class RswagCSPDisableMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    @req = Rack::Request.new(env)
    _, headers, = response = @app.call(env)

    if headers.key?("Content-Security-Policy")
      headers["content-security-policy"] = headers.delete("Content-Security-Policy")
    end

    response
  end
end

Rails.application.config.middleware.insert_after(
  ActionDispatch::ContentSecurityPolicy::Middleware,
  RswagCSPDisableMiddleware
)

@franzliedke
Copy link

Interestingly, the Rack 3 upgrade guide claims that using all-lower-case header names should give you full compatibility with Rack 2 and 3. Is this not correct in the Rails context?

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.

5 participants