Class: Otto::Security::CSP::ReportMiddleware

Inherits:
Object
  • Object
show all
Defined in:
lib/otto/security/csp/report_middleware.rb

Overview

Rack middleware that receives browser-posted Content-Security-Policy violation reports and dispatches them to an application callback.

This is the receiving half of Otto’s CSP support; the emitting half is Otto::Security::Config#generate_nonce_csp / Response#send_csp_headers (and the static Otto::Security::Config#enable_csp!). When a report URI is configured, the emitted policy carries a report-uri directive pointing here.

Behavior (all mandatory for a public, unauthenticated receiver):

  • INERT unless Otto::Security::Config#csp_report_uri is set. When it is not configured the middleware is a transparent pass-through.
  • Only intercepts a POST whose path matches the configured report URI. Everything else (other paths, other methods) passes through untouched.
  • Short-circuits BEFORE inner middleware, so CSRF, auth, and rate limiting never see the request. This is why browsers can POST reports with no CSRF token: the report never reaches the CSRF middleware. Otto::Security::Core#enable_csp_reporting! pins this middleware OUTERMOST (via the :outermost stack position), so the guarantee holds regardless of the order security features are enabled in. The flip side is that reports also bypass rate limiting — see the DoS note on Otto::Security::Core#enable_csp_reporting!; keep callbacks cheap.
  • Enforces a hard MAX_BODY_BYTES body cap. Oversized bodies are detected with a cap + 1 read and skipped WITHOUT parsing, so a hostile client cannot force large allocations against a public endpoint.
  • Parses both wire formats via Parser and invokes the registered callback once per normalized report.
  • NEVER raises to the client and always responds 204 No Content (browsers ignore the body). A throwing callback is isolated by Otto::Security::Config#dispatch_csp_violation.

Constant Summary collapse

MAX_BODY_BYTES =

Hard cap on the request body we are willing to read/parse. Browsers send small JSON documents; anything larger is abuse and is dropped.

64 * 1024

Instance Method Summary collapse

Constructor Details

#initialize(app, config = nil) ⇒ ReportMiddleware

64 KiB



46
47
48
49
# File 'lib/otto/security/csp/report_middleware.rb', line 46

def initialize(app, config = nil)
  @app    = app
  @config = config || Otto::Security::Config.new
end

Instance Method Details

#call(env) ⇒ Object



51
52
53
54
55
# File 'lib/otto/security/csp/report_middleware.rb', line 51

def call(env)
  return @app.call(env) unless report_request?(env)

  receive_report(env)
end