Class: HeaderGuard::Middleware

Inherits:
Object
  • Object
show all
Defined in:
lib/header_guard/middleware.rb

Overview

The Rack middleware class responsible for injecting security headers.

Header names are treated case-insensitively throughout. The Rack 3 SPEC requires response header keys to be lowercase, while Rack 2 applications conventionally use capitalized keys, so this middleware reads incoming headers case-insensitively and always writes lowercase keys.

Instance Method Summary collapse

Constructor Details

#initialize(app, options = {}) ⇒ Middleware

Initializes the middleware. It merges user-defined options over defaults.

Parameters:

  • app (Object)

    The next application in the Rack stack.

  • options (Hash) (defaults to: {})

    Configuration options for headers and CSP.



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/header_guard/middleware.rb', line 15

def initialize(app, options = {})
  @app = app

  # Use a copy of options for configuration extraction
  config = options.dup

  # Extract special configuration settings
  custom_csp = config.delete(:content_security_policy)
  @report_only = config.delete(:report_only) || false

  # 1. Start with DEFAULT_HEADERS (from header_guard.rb)
  # 2. Merge remaining options (which are custom headers) over the defaults,
  #    so the user's header values take precedence.
  # 3. Normalize every key to lowercase, so a custom "X-Frame-Options"
  #    overrides the default rather than being emitted alongside it.
  @headers = DEFAULT_HEADERS.merge(config).each_with_object({}) do |(key, value), normalized|
    normalized[normalize_key(key)] = value
  end.freeze

  # Set the final CSP value and the header key based on report_only setting
  @csp_value = custom_csp || DEFAULT_CSP
  @csp_header_key = @report_only ? "content-security-policy-report-only" : "content-security-policy"
end

Instance Method Details

#call(env) ⇒ Object

The Rack application call method.



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/header_guard/middleware.rb', line 40

def call(env)
  status, headers, body = @app.call(env)

  # Only inject headers on successful (2xx) responses with HTML content.
  # Exclude redirects, errors, and non-HTML assets (like JSON or images).
  if (200..299).include?(status) && html?(headers)
    # Inject the standard headers
    @headers.each do |key, value|
      # We use assignment here, not `||=`, to ensure the middleware
      # overwrites any headers set by the application before it,
      # adhering to the strong security posture.
      assign(headers, key, value)
    end

    # Inject the configured CSP header
    assign(headers, @csp_header_key, @csp_value)
  end

  [status, headers, body]
end