How to accept application/csp-report for a content security policy violation report. Or where did my params go?

I must be doing something wrong because it seems there is no one place documenting how to accept content security policy violation reports in rails. Basic question:

How do we make rails accept a POST with type “application/csp-report” and still have strong params screen it?

The csp_report gem is old and depends on a package with an unpatched CVE, so let’s set that aside. Also I know people use Sentry. I want to accept my own reports.

It seems there are a few undocumented or underdocumented hurdles:

I guess Rails needs to be told to accept “application/csp-report” somehow. I add this line:

# config/initializers/mime_types.rb
Mime::Type.register "application/csp-report", :csp_report

I make this model:

class ContentSecurityPolicyReport < ApplicationRecord


end

By migration, to keep things simple, it has one column:

class CreateContentSecurityPolicyReport < ActiveRecord::Migration[7.2]
  def change
    create_table :content_security_policy_reports do |t|
      t.string :raw_json

      t.timestamps
    end
  end
end

I make this controller:

class ContentSecurityPolicyReportController < ApplicationController

  rate_limit to: 10, within: 1.minute

  # The browser submitting the report will not have any CSRF token
  skip_forgery_protection

  def create
    # WORKS IN TEST
    param = request.request_parameters()
    # WORKS IN TEST IF WE CHANGE param BELOW TO params
    params.permit(:csp_report, :type)
    if param.has_key?('csp-report') || (param.has_key?('type') && param['type'] == 'csp-violation')
      ContentSecurityPolicyReport.create!(raw_json: param)
      head :ok
    else
      head :bad_request
    end
  end

end

I have this line in my CSP:

#config/initializers/content_security_policy.rb

policy.report_uri "/content-security-policy-report-violations"

I have this route:

post '/content-security-policy-report-violations', to: 'content_security_policy_report#create'

I make this spec:


    it 'works with old format' do
      
      expect(ContentSecurityPolicyReport.count).to eq 0
      
      post '/content-security-policy-report-violations', params: {"csp-report": {...} }, as: :csp_report
      expect(response.status).to eq 200
      
      expect(ContentSecurityPolicyReport.count).to eq 1
   end

I actually have three more specs to test the new “type”: ‘csp-violation’, and to test malicious input, and to test real world input.

Everything passes in test using either variant of my params.

I put it out into the wild, and my parameters are no longer coming through.

Has someone written somewhere about how ActionController::Parameters screens stuff out? Shouldn’t accepting CSP violation reports be a pretty basic thing that’s well documented? Any nudge in the right direction would be appreciated. Could we make this thread be the documentation?

Is that column large enough to hold the average report? 255 characters is mighty short…

@dougjq - What is the CVE number referenced above?

The “unpatched CVE” mentioned above.

@jasnow

Name: twitter-bootstrap-rails
Version: 5.3.0
CVE: CVE-2019-8331
GHSA: GHSA-9v3m-8fp8-mj99
Criticality: Medium
URL: https://blog.getbootstrap.com/2019/02/13/bootstrap-4-3-1-and-3-4-1/
Title: twitter-bootstrap-rails vulnerable to Cross-Site Scripting (XSS)
Solution: remove or disable this gem until a patch is available!

Found by the gem bundler-audit.

Yes, csp_report gem was last released in 2014. Checking twitter-bootstrap-rails gem I see that Bootstrap was probably upgraded to 5.3 on Feb.18, 2026 with this commit:

so I would suggest contacting the developer of csp_report gem to request an upgrade.