Module: Otto::Security::CSP

Defined in:
lib/otto/security/csp/nonce.rb,
lib/otto/security/csp/parser.rb,
lib/otto/security/csp/policy.rb,
lib/otto/security/csp/report.rb,
lib/otto/security/csp/writer.rb,
lib/otto/security/csp/emit_middleware.rb,
lib/otto/security/csp/report_middleware.rb

Overview

Content-Security-Policy support. The framework-owned lazy nonce accessor lives directly on this module (CSP.nonce / CSP.nonce?), beside the Policy builder, the Writer apply core, the Parser, and the report/emit middlewares.

Defined Under Namespace

Modules: Parser, Policy Classes: EmitMiddleware, Report, ReportMiddleware, Writer

Constant Summary collapse

DEFAULT_NONCE_KEY =

Default Rack env key the per-request nonce is memoized under. Registered as documentation in EnvKeys::NONCE; per that module’s convention the string literal (not the constant) is what the codebase passes around, so this DEFAULT_NONCE_KEY exists for the CSP code’s own use and the two are kept identical.

'otto.nonce'

Class Method Summary collapse

Class Method Details

.nonce(env, key: nil) ⇒ String

Framework-owned, request-scoped, LAZY CSP nonce.

Generates a fresh base64 nonce on first access and memoizes it into the request env under the resolved key, so every later reader observes ONE value: the views that stamp nonce="…" onto <script>/<link> tags and the EmitMiddleware that writes the script-src 'nonce-…' header both read it here. The header’s nonce matching the views’ nonce is therefore a STRUCTURAL property, not a convention each app re-implements (Rails’ request.content_security_policy_nonce model).

An untouched request never generates a nonce and pays nothing — which is also why the emit-if-consumed middleware is safe: it only emits a nonce-only policy for a request whose views actually consumed the nonce.

A value already present under the key (e.g. an app that still mints its own under the same convention) is honored, not overwritten.

Parameters:

  • env (Hash)

    the Rack request env (mutated: the nonce is memoized in)

  • key (String, nil) (defaults to: nil)

    override the env key; nil resolves it from the security config’s Otto::Security::Config#csp_nonce_key (or the default)

Returns:

  • (String)

    the request’s nonce



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

def nonce(env, key: nil)
  resolved = key || nonce_key(env)
  existing = env[resolved]
  return existing if existing && !existing.empty?

  env[resolved] = SecureRandom.base64(16)
end

.nonce?(env, key: nil) ⇒ Boolean

Whether a nonce was already minted for this request, WITHOUT minting one. This is the emit-if-consumed predicate.

Parameters:

  • env (Hash)
  • key (String, nil) (defaults to: nil)

    see nonce

Returns:

  • (Boolean)


58
59
60
61
# File 'lib/otto/security/csp/nonce.rb', line 58

def nonce?(env, key: nil)
  value = env[key || nonce_key(env)]
  !value.nil? && !value.empty?
end

.nonce_key(env) ⇒ String

The env key the nonce lives under: the app’s configured convention (Otto::Security::Config#csp_nonce_key) when a security config is present on the env, else the framework default. Lets an app with an existing convention (e.g. onetime.nonce) adopt the accessor without renaming its env key.

Parameters:

  • env (Hash)

Returns:

  • (String)


71
72
73
74
75
# File 'lib/otto/security/csp/nonce.rb', line 71

def nonce_key(env)
  config = env['otto.security_config']
  configured = config.csp_nonce_key if config.respond_to?(:csp_nonce_key)
  configured && !configured.empty? ? configured : DEFAULT_NONCE_KEY
end