class SecureHeaders::Configuration

Constants

CONFIG_ATTRIBUTES
CONFIG_ATTRIBUTES_TO_HEADER_CLASSES
DEFAULT_CONFIG
HASH_CONFIG_FILE
HEADERABLE_ATTRIBUTES

The list of attributes that must respond to a `make_header` method

NOOP_OVERRIDE
VALIDATABLE_ATTRIBUTES

The list of attributes that must respond to a `validate_config!` method

Public Class Methods

configure(&block)
Alias for: default
default(&block) click to toggle source

Public: Set the global default configuration.

Optionally supply a block to override the defaults set by this library.

Returns the newly created config. Raises AlreadyConfiguredError if Configuration.disable! has already been called

# File lib/secure_headers/configuration.rb, line 47
def default(&block)
  if disabled?
    raise AlreadyConfiguredError, "Configuration has been disabled, cannot set default"
  end

  if defined?(@default_config)
    raise AlreadyConfiguredError, "Policy already configured"
  end

  # Define a built-in override that clears all configuration options and
  # results in no security headers being set.
  override(NOOP_OVERRIDE, &method(:create_noop_config_block))

  new_config = new(&block).freeze
  new_config.validate_config!
  @default_config = new_config
end
Also aliased as: configure
disable!() click to toggle source

Public: Disable secure_headers entirely. When disabled, no headers will be set.

Note: This must be called before Configuration.default. Calling it after Configuration.default has been set will raise an AlreadyConfiguredError.

Returns nothing Raises AlreadyConfiguredError if Configuration.default has already been called

# File lib/secure_headers/configuration.rb, line 19
def disable!
  if defined?(@default_config)
    raise AlreadyConfiguredError, "Configuration already set, cannot disable"
  end

  @disabled = true
  @noop_config = create_noop_config.freeze

  # Ensure the built-in NOOP override is available even if `default` has never been called
  @overrides ||= {}
  unless @overrides.key?(NOOP_OVERRIDE)
    @overrides[NOOP_OVERRIDE] = method(:create_noop_config_block)
  end
end
disabled?() click to toggle source

Public: Check if secure_headers is disabled

Returns boolean

# File lib/secure_headers/configuration.rb, line 37
def disabled?
  defined?(@disabled) && @disabled
end
dup() click to toggle source
# File lib/secure_headers/configuration.rb, line 101
def dup
  default_config.dup
end
named_append(name, &block) click to toggle source
# File lib/secure_headers/configuration.rb, line 92
def named_append(name, &block)
  @appends ||= {}
  raise "Provide a configuration block" unless block_given?
  if named_append_or_override_exists?(name)
    raise AlreadyConfiguredError, "Configuration already exists"
  end
  @appends[name] = block
end
named_appends(name) click to toggle source
# File lib/secure_headers/configuration.rb, line 87
def named_appends(name)
  @appends ||= {}
  @appends[name]
end
new(&block) click to toggle source
# File lib/secure_headers/configuration.rb, line 203
def initialize(&block)
  @cookies = self.class.send(:deep_copy_if_hash, Cookie::COOKIE_DEFAULTS)
  @clear_site_data = nil
  @csp = nil
  @csp_report_only = nil
  @hsts = nil
  @x_content_type_options = nil
  @x_download_options = nil
  @x_frame_options = nil
  @x_permitted_cross_domain_policies = nil
  @x_xss_protection = nil
  @expect_certificate_transparency = nil
  @reporting_endpoints = nil

  self.referrer_policy = OPT_OUT
  self.csp = ContentSecurityPolicyConfig.new(ContentSecurityPolicyConfig::DEFAULT)
  self.csp_report_only = OPT_OUT

  instance_eval(&block) if block_given?
end
override(name, &block) click to toggle source

Public: create a named configuration that overrides the default config.

name - use an identifier for the override config. base - override another existing config, or override the default config if no value is supplied.

Returns: the newly created config

# File lib/secure_headers/configuration.rb, line 73
def override(name, &block)
  @overrides ||= {}
  raise "Provide a configuration block" unless block_given?
  if named_append_or_override_exists?(name)
    raise AlreadyConfiguredError, "Configuration already exists"
  end
  @overrides[name] = block
end
overrides(name) click to toggle source
# File lib/secure_headers/configuration.rb, line 82
def overrides(name)
  @overrides ||= {}
  @overrides[name]
end

Private Class Methods

create_noop_config() click to toggle source

Private: Creates a NOOP configuration that opts out of all headers

# File lib/secure_headers/configuration.rb, line 152
def create_noop_config
  new(&method(:create_noop_config_block))
end
create_noop_config_block(config) click to toggle source

Private: Block for creating NOOP configuration Used by both create_noop_config and the NOOP_OVERRIDE mechanism

# File lib/secure_headers/configuration.rb, line 158
def create_noop_config_block(config)
  CONFIG_ATTRIBUTES.each do |attr|
    config.instance_variable_set("@#{attr}", OPT_OUT)
  end
end
deep_copy(config) click to toggle source

Public: perform a basic deep dup. The shallow copy provided by dup/clone can lead to modifying parent objects.

# File lib/secure_headers/configuration.rb, line 114
def deep_copy(config)
  return unless config
  result = {}
  config.each_pair do |key, value|
    result[key] =
      case value
      when Array
        value.dup
      else
        value
      end
  end
  result
end
deep_copy_if_hash(value) click to toggle source

Private: convenience method purely DRY things up. The value may not be a hash (e.g. OPT_OUT, nil)

# File lib/secure_headers/configuration.rb, line 143
def deep_copy_if_hash(value)
  if value.is_a?(Hash)
    deep_copy(value)
  else
    value
  end
end
default_config() click to toggle source

Private: Returns the internal default configuration. This should only ever be called by internal callers (or tests) that know the semantics of ensuring that the default config is never mutated and is dup(ed) before it is used in a request.

# File lib/secure_headers/configuration.rb, line 133
def default_config
  return @noop_config if disabled?
  unless defined?(@default_config)
    raise NotYetConfiguredError, "Default policy not yet configured"
  end
  @default_config
end
named_append_or_override_exists?(name) click to toggle source
# File lib/secure_headers/configuration.rb, line 107
def named_append_or_override_exists?(name)
  (defined?(@appends) && @appends.key?(name)) ||
    (defined?(@overrides) && @overrides.key?(name))
end

Public Instance Methods

csp=(new_csp) click to toggle source
# File lib/secure_headers/configuration.rb, line 293
def csp=(new_csp)
  case new_csp
  when OPT_OUT
    @csp = new_csp
  when ContentSecurityPolicyConfig
    @csp = new_csp
  when Hash
    @csp = ContentSecurityPolicyConfig.new(new_csp)
  else
    raise ArgumentError, "Must provide either an existing CSP config or a CSP config hash"
  end
end
csp_report_only=(new_csp) click to toggle source

Configures the content-security-policy-report-only header. `new_csp` cannot contain `report_only: false` or an error will be raised.

NOTE: if csp has not been configured/has the default value when configuring csp_report_only, the code will assume you mean to only use report-only mode and you will be opted-out of enforce mode.

# File lib/secure_headers/configuration.rb, line 312
def csp_report_only=(new_csp)
  case new_csp
  when OPT_OUT
    @csp_report_only = new_csp
  when ContentSecurityPolicyReportOnlyConfig
    @csp_report_only = new_csp.dup
  when ContentSecurityPolicyConfig
    @csp_report_only = new_csp.make_report_only
  when Hash
    @csp_report_only = ContentSecurityPolicyReportOnlyConfig.new(new_csp)
  else
    raise ArgumentError, "Must provide either an existing CSP config or a CSP config hash"
  end
end
dup() click to toggle source

Public: copy everything

Returns a deep-dup'd copy of this configuration.

# File lib/secure_headers/configuration.rb, line 227
def dup
  copy = self.class.new
  copy.cookies = self.class.send(:deep_copy_if_hash, @cookies)
  copy.csp = @csp.dup if @csp
  copy.csp_report_only = @csp_report_only.dup if @csp_report_only
  copy.x_content_type_options = @x_content_type_options
  copy.hsts = @hsts
  copy.x_frame_options = @x_frame_options
  copy.x_xss_protection = @x_xss_protection
  copy.x_download_options = @x_download_options
  copy.x_permitted_cross_domain_policies = @x_permitted_cross_domain_policies
  copy.clear_site_data = @clear_site_data
  copy.expect_certificate_transparency = @expect_certificate_transparency
  copy.referrer_policy = @referrer_policy
  copy.reporting_endpoints = self.class.send(:deep_copy_if_hash, @reporting_endpoints)
  copy
end
generate_headers() click to toggle source
# File lib/secure_headers/configuration.rb, line 257
def generate_headers
  headers = {}
  HEADERABLE_ATTRIBUTES.each do |attr|
    klass = CONFIG_ATTRIBUTES_TO_HEADER_CLASSES[attr]
    header_name, value = klass.make_header(instance_variable_get("@#{attr}"))
    if header_name && value
      headers[header_name] = value
    end
  end
  headers
end
opt_out(header) click to toggle source
# File lib/secure_headers/configuration.rb, line 269
def opt_out(header)
  send("#{header}=", OPT_OUT)
end
override(name = nil, &block) click to toggle source

Public: Apply a named override to the current config

Returns self

# File lib/secure_headers/configuration.rb, line 248
def override(name = nil, &block)
  if override = self.class.overrides(name)
    instance_eval(&override)
  else
    raise ArgumentError.new("no override by the name of #{name} has been configured")
  end
  self
end
secure_cookies=(secure_cookies) click to toggle source
# File lib/secure_headers/configuration.rb, line 289
def secure_cookies=(secure_cookies)
  raise ArgumentError, "#{Kernel.caller.first}: `#secure_cookies=` is no longer supported. Please use `#cookies=` to configure secure cookies instead."
end
update_x_frame_options(value) click to toggle source
# File lib/secure_headers/configuration.rb, line 273
def update_x_frame_options(value)
  @x_frame_options = value
end
validate_config!() click to toggle source

Public: validates all configurations values.

Raises various configuration errors if any invalid config is detected.

Returns nothing

# File lib/secure_headers/configuration.rb, line 282
def validate_config!
  VALIDATABLE_ATTRIBUTES.each do |attr|
    klass = CONFIG_ATTRIBUTES_TO_HEADER_CLASSES[attr]
    klass.validate_config!(instance_variable_get("@#{attr}"))
  end
end