Class: Otto::Security::CSP::EmitMiddleware

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

Overview

Rack middleware that emits a nonce-based Content-Security-Policy on the way out — the EMITTING sibling of ReportMiddleware.

It is a passive BACKSTOP: it runs the CSP through Writer in :backstop mode, so it fills the gap for responses that would otherwise ship without a CSP but NEVER clobbers one a route or another layer already set. All the emission invariants (enabled / HTML-only / nonce-present / don’t-clobber / lowercase key) are the Writer’s, so this middleware carries none of that guard logic itself.

DEFAULT: emit-if-consumed. It emits only when the request actually consumed a nonce (a view called Request#csp_nonce, memoizing it into the env). This is the safe default: a nonce-only script-src on an HTML page whose templates never stamped that nonce would block EVERY script on the page. “CSP responses whose request consumed a nonce” is sound; “CSP all HTML responses” is not.

EAGER (opt-in): with eager: true it MINTS a nonce for every otherwise eligible response, even one that never touched it. Only safe when the app either uses no nonce-gated inline scripts or stamps the nonce another way; otherwise it reintroduces the blocked-script hazard above.

INERT unless Otto::Security::Config#csp_nonce_enabled?. When nonce-CSP is off it is a transparent pass-through (and never mints a nonce).

Instance Method Summary collapse

Constructor Details

#initialize(app, config = nil, eager: false, development_mode: nil) ⇒ EmitMiddleware

Returns a new instance of EmitMiddleware.

Parameters:

  • app (#call)

    the inner Rack app

  • config (Otto::Security::Config, nil) (defaults to: nil)

    security config (the middleware stack injects this); a nil config yields an inert instance

  • eager (Boolean) (defaults to: false)

    mint-and-emit for every eligible response rather than only emit-if-consumed

  • development_mode (Boolean, #call, nil) (defaults to: nil)

    whether to emit the development directive set. A callable is invoked per request with the env (e.g. ->(env) { OT.conf.dig('development', 'enabled') }); a plain value is used as-is; nil means production.



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

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

Instance Method Details

#call(env) ⇒ Object



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

def call(env)
  status, headers, body = @app.call(env)
  apply_backstop(env, headers) if @config.csp_nonce_enabled?
  [status, headers, body]
end