Cross-Site Scripting (XSS) bugs are common, but sometimes they appear in places you wouldn’t expect. CVE-2022-22577 is one such vulnerability that hit Ruby on Rails’s Action Pack library. This bug is unique—it lets attackers bypass your Content Security Policy (CSP) protections, even on responses which aren't typical HTML!

In this post, we’ll dive deep into what went wrong, how to trigger this XSS, and give you practical advice to protect your Rails apps. Let’s go!

What is CVE-2022-22577?

CVE-2022-22577 is a vulnerability in the Action Pack module of Ruby on Rails. It affects versions >= 5.2. and < 5.2. (though technically, 5.2. is both inclusive and exclusive in the NIST notice—see details below).

Action Pack is the controller and view layer of Rails. The bug happens when responses are rendered with types that kinda look like HTML (for example, "application/xhtml+xml" or "text/plain"), but Rails doesn’t apply CSP the same way it does for pure "text/html" responses.

If an attacker can trick the app into serving a malicious payload with such a Content-Type, they might be able to run code in a browser that CSP wouldn’t normally allow. That’s bad news.

Where Did Rails Slip Up?

Rails uses Content Security Policy (CSP) headers to help protect pages from XSS. Normally, if you set up a strong CSP, attackers can't easily insert script tags or inline JavaScript.

But here’s the problem – Rails set CSP headers mainly for text/html responses. For other content-types that *look* like HTML (for example, XML that a browser parses as markup), the headers might be missing or wrong.

Imagine you have a controller that does this

class PagesController < ApplicationController
  def show
    respond_to do |format|
      format.html # normal HTML
      format.xhtml { render xml: params[:body], content_type: 'application/xhtml+xml' }
    end
  end
end

Now, if an attacker can control params[:body], and the request uses the .xhtml extension or Accept: application/xhtml+xml, they could inject script-like content in this response. Without a proper CSP header, modern browsers might execute that code.

Why?
Because CSP is only set for HTML, and browsers might treat other similar content types as "HTML-like" and execute JavaScript regardless.

To show this in practice, here’s a simple script

# Example: Vulnerable Rails Route
Rails.application.routes.draw do
  get '/vuln', to: 'pages#show'
end

# Controller Example
class PagesController < ApplicationController
  def show
    # INTENTIONALLY VULNERABLE: Don't do this in real apps!
    render plain: params[:body], content_type: 'application/xhtml+xml'
  end
end

URL:

http://your-rails-app/vuln?body=%3Cscript%3Ealert%281%29%3C%2Fscript%3E

Accept header:

application/xhtml+xml

The response will be

HTTP/1.1 200 OK
Content-Type: application/xhtml+xml

<script>alert(1)</script>

If you open that response in a browser, it will execute the JavaScript. Since Rails didn’t include CSP for this response, none of your CSP protections apply!

How Does Bypassing CSP Work?

Content Security Policy is a browser mechanism, enforced only if the browser sees the correct HTTP headers on the response.

Rails only added CSP headers to text/html responses. So, if the attacker gets a different mime type, like application/xhtml+xml or text/plain, CSP won't kick in—even if the browser executes the code!

Supply HTML or XML-like content as a parameter or file upload.

2. Get the response served with a HTML-like Content-Type *but not* exactly text/html.

Official Fix

The Rails team fixed this in newer versions after this PR:

> Rails now applies CSP headers to any "HTML-like" content types, not just text/html.

You can see the advisory at the official Rails Security page.

Solution:

7...rc2 or later

See details:  
- NIST NVD entry
- GitHub security advisory

If you can’t upgrade right away

1. Never render user-supplied content with HTML-like Content-Types. Stick to pure XML or plain-text, and sanitize carefully.

Example in your controller

response.set_header('Content-Security-Policy', "default-src 'none'")

Apply this to any responses where you dynamically set the content type.

References

- NVD CVE-2022-22577
- Rails Official Security Advisory
- Rails GitHub security advisory
- Rails Fix Pull Request #44617
- Understanding CSP

Final Thoughts

This bug is a good reminder: XSS isn’t always about HTML! If your web app handles _any_ user-supplied content, always be careful about the content type and remember that browsers (and attackers) are creative. Patch your Rails, set your CSP, and review your render logic—especially for "HTML-like" content.

Stay safe, and happy coding! 🚀

*Copying and pasting the code or details from above? Make sure you’re not using a vulnerable Rails version in production!*

Timeline

Published on: 05/26/2022 17:15:00 UTC
Last modified on: 06/07/2022 14:37:00 UTC