Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
inherit_gem:
rubocop-github:
- config/default.yml
require: rubocop-performance
plugins:
- rubocop-performance
2 changes: 1 addition & 1 deletion lib/secure_headers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ def raise_on_unknown_target(target)

def config_and_target(request, target)
config = config_for(request)
target = guess_target(config) unless target
target ||= guess_target(config)
raise_on_unknown_target(target)
[config, target]
end
Expand Down
62 changes: 30 additions & 32 deletions lib/secure_headers/headers/clear_site_data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,43 +11,41 @@ class ClearSiteData
EXECUTION_CONTEXTS = "executionContexts".freeze
ALL_TYPES = [CACHE, COOKIES, STORAGE, EXECUTION_CONTEXTS]

class << self
# Public: make an clear-site-data header name, value pair
#
# Returns nil if not configured, returns header name and value if configured.
def make_header(config = nil, user_agent = nil)
case config
when nil, OPT_OUT, []
# noop
when Array
[HEADER_NAME, make_header_value(config)]
when true
[HEADER_NAME, make_header_value(ALL_TYPES)]
end
# Public: make an clear-site-data header name, value pair
#
# Returns nil if not configured, returns header name and value if configured.
def self.make_header(config = nil, user_agent = nil)
case config
when nil, OPT_OUT, []
# noop
when Array
[HEADER_NAME, make_header_value(config)]
when true
[HEADER_NAME, make_header_value(ALL_TYPES)]
end
end

def validate_config!(config)
case config
when nil, OPT_OUT, true
# valid
when Array
unless config.all? { |t| t.is_a?(String) }
raise ClearSiteDataConfigError.new("types must be Strings")
end
else
raise ClearSiteDataConfigError.new("config must be an Array of Strings or `true`")
def self.validate_config!(config)
case config
when nil, OPT_OUT, true
# valid
when Array
unless config.all? { |t| t.is_a?(String) }
raise ClearSiteDataConfigError.new("types must be Strings")
end
else
raise ClearSiteDataConfigError.new("config must be an Array of Strings or `true`")
end
end

# Public: Transform a clear-site-data config (an Array of Strings) into a
# String that can be used as the value for the clear-site-data header.
#
# types - An Array of String of types of data to clear.
#
# Returns a String of quoted values that are comma separated.
def make_header_value(types)
types.map { |t| %("#{t}") }.join(", ")
end
# Public: Transform a clear-site-data config (an Array of Strings) into a
# String that can be used as the value for the clear-site-data header.
#
# types - An Array of String of types of data to clear.
#
# Returns a String of quoted values that are comma separated.
def self.make_header_value(types)
types.map { |t| %("#{t}") }.join(", ")
end
end
end
6 changes: 2 additions & 4 deletions lib/secure_headers/headers/cookie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@ module SecureHeaders
class CookiesConfigError < StandardError; end
class Cookie

class << self
def validate_config!(config)
CookiesConfig.new(config).validate!
end
def self.validate_config!(config)
CookiesConfig.new(config).validate!
end

attr_reader :raw_cookie, :config
Expand Down
40 changes: 19 additions & 21 deletions lib/secure_headers/headers/expect_certificate_transparency.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,29 @@ class ExpectCertificateTransparency
REQUIRED_MAX_AGE_ERROR = "max-age is a required directive.".freeze
INVALID_MAX_AGE_ERROR = "max-age must be a number.".freeze

class << self
# Public: Generate a expect-ct header.
#
# Returns nil if not configured, returns header name and value if
# configured.
def make_header(config, use_agent = nil)
return if config.nil? || config == OPT_OUT
# Public: Generate a expect-ct header.
#
# Returns nil if not configured, returns header name and value if
# configured.
def self.make_header(config, use_agent = nil)
return if config.nil? || config == OPT_OUT

header = new(config)
[HEADER_NAME, header.value]
end
header = new(config)
[HEADER_NAME, header.value]
end

def validate_config!(config)
return if config.nil? || config == OPT_OUT
raise ExpectCertificateTransparencyConfigError.new(INVALID_CONFIGURATION_ERROR) unless config.is_a? Hash
def self.validate_config!(config)
return if config.nil? || config == OPT_OUT
raise ExpectCertificateTransparencyConfigError.new(INVALID_CONFIGURATION_ERROR) unless config.is_a? Hash

unless [true, false, nil].include?(config[:enforce])
raise ExpectCertificateTransparencyConfigError.new(INVALID_ENFORCE_VALUE_ERROR)
end
unless [true, false, nil].include?(config[:enforce])
raise ExpectCertificateTransparencyConfigError.new(INVALID_ENFORCE_VALUE_ERROR)
end

if !config[:max_age]
raise ExpectCertificateTransparencyConfigError.new(REQUIRED_MAX_AGE_ERROR)
elsif config[:max_age].to_s !~ /\A\d+\z/
raise ExpectCertificateTransparencyConfigError.new(INVALID_MAX_AGE_ERROR)
end
if !config[:max_age]
raise ExpectCertificateTransparencyConfigError.new(REQUIRED_MAX_AGE_ERROR)
elsif config[:max_age].to_s !~ /\A\d+\z/
raise ExpectCertificateTransparencyConfigError.new(INVALID_MAX_AGE_ERROR)
end
end

Expand Down
40 changes: 19 additions & 21 deletions lib/secure_headers/headers/referrer_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,27 @@ class ReferrerPolicy
unsafe-url
)

class << self
# Public: generate an Referrer Policy header.
#
# Returns a default header if no configuration is provided, or a
# header name and value based on the config.
def make_header(config = nil, user_agent = nil)
return if config == OPT_OUT
config ||= DEFAULT_VALUE
[HEADER_NAME, Array(config).join(", ")]
end
# Public: generate an Referrer Policy header.
#
# Returns a default header if no configuration is provided, or a
# header name and value based on the config.
def self.make_header(config = nil, user_agent = nil)
return if config == OPT_OUT
config ||= DEFAULT_VALUE
[HEADER_NAME, Array(config).join(", ")]
end

def validate_config!(config)
case config
when nil, OPT_OUT
# valid
when String, Array
config = Array(config)
unless config.all? { |t| t.is_a?(String) && VALID_POLICIES.include?(t.downcase) }
raise ReferrerPolicyConfigError.new("Value can only be one or more of #{VALID_POLICIES.join(", ")}")
end
else
raise TypeError.new("Must be a string or array of strings. Found #{config.class}: #{config}")
def self.validate_config!(config)
case config
when nil, OPT_OUT
# valid
when String, Array
config = Array(config)
unless config.all? { |t| t.is_a?(String) && VALID_POLICIES.include?(t.downcase) }
raise ReferrerPolicyConfigError.new("Value can only be one or more of #{VALID_POLICIES.join(", ")}")
end
else
raise TypeError.new("Must be a string or array of strings. Found #{config.class}: #{config}")
end
end
end
Expand Down
26 changes: 12 additions & 14 deletions lib/secure_headers/headers/strict_transport_security.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,19 @@ class StrictTransportSecurity
VALID_STS_HEADER = /\Amax-age=\d+(; includeSubdomains)?(; preload)?\z/i
MESSAGE = "The config value supplied for the HSTS header was invalid. Must match #{VALID_STS_HEADER}"

class << self
# Public: generate an hsts header name, value pair.
#
# Returns a default header if no configuration is provided, or a
# header name and value based on the config.
def make_header(config = nil, user_agent = nil)
return if config == OPT_OUT
[HEADER_NAME, config || DEFAULT_VALUE]
end
# Public: generate an hsts header name, value pair.
#
# Returns a default header if no configuration is provided, or a
# header name and value based on the config.
def self.make_header(config = nil, user_agent = nil)
return if config == OPT_OUT
[HEADER_NAME, config || DEFAULT_VALUE]
end

def validate_config!(config)
return if config.nil? || config == OPT_OUT
raise TypeError.new("Must be a string. Found #{config.class}: #{config} #{config.class}") unless config.is_a?(String)
raise STSConfigError.new(MESSAGE) unless config =~ VALID_STS_HEADER
end
def self.validate_config!(config)
return if config.nil? || config == OPT_OUT
raise TypeError.new("Must be a string. Found #{config.class}: #{config} #{config.class}") unless config.is_a?(String)
raise STSConfigError.new(MESSAGE) unless config =~ VALID_STS_HEADER
end
end
end
28 changes: 13 additions & 15 deletions lib/secure_headers/headers/x_content_type_options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,20 @@ class XContentTypeOptions
HEADER_NAME = "x-content-type-options".freeze
DEFAULT_VALUE = "nosniff"

class << self
# Public: generate an X-Content-Type-Options header.
#
# Returns a default header if no configuration is provided, or a
# header name and value based on the config.
def make_header(config = nil, user_agent = nil)
return if config == OPT_OUT
[HEADER_NAME, config || DEFAULT_VALUE]
end
# Public: generate an X-Content-Type-Options header.
#
# Returns a default header if no configuration is provided, or a
# header name and value based on the config.
def self.make_header(config = nil, user_agent = nil)
return if config == OPT_OUT
[HEADER_NAME, config || DEFAULT_VALUE]
end

def validate_config!(config)
return if config.nil? || config == OPT_OUT
raise TypeError.new("Must be a string. Found #{config.class}: #{config}") unless config.is_a?(String)
unless config.casecmp(DEFAULT_VALUE) == 0
raise XContentTypeOptionsConfigError.new("Value can only be nil or 'nosniff'")
end
def self.validate_config!(config)
return if config.nil? || config == OPT_OUT
raise TypeError.new("Must be a string. Found #{config.class}: #{config}") unless config.is_a?(String)
unless config.casecmp(DEFAULT_VALUE) == 0
raise XContentTypeOptionsConfigError.new("Value can only be nil or 'nosniff'")
end
end
end
Expand Down
28 changes: 13 additions & 15 deletions lib/secure_headers/headers/x_download_options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,20 @@ class XDownloadOptions
HEADER_NAME = "x-download-options".freeze
DEFAULT_VALUE = "noopen"

class << self
# Public: generate an x-download-options header.
#
# Returns a default header if no configuration is provided, or a
# header name and value based on the config.
def make_header(config = nil, user_agent = nil)
return if config == OPT_OUT
[HEADER_NAME, config || DEFAULT_VALUE]
end
# Public: generate an x-download-options header.
#
# Returns a default header if no configuration is provided, or a
# header name and value based on the config.
def self.make_header(config = nil, user_agent = nil)
return if config == OPT_OUT
[HEADER_NAME, config || DEFAULT_VALUE]
end

def validate_config!(config)
return if config.nil? || config == OPT_OUT
raise TypeError.new("Must be a string. Found #{config.class}: #{config}") unless config.is_a?(String)
unless config.casecmp(DEFAULT_VALUE) == 0
raise XDOConfigError.new("Value can only be nil or 'noopen'")
end
def self.validate_config!(config)
return if config.nil? || config == OPT_OUT
raise TypeError.new("Must be a string. Found #{config.class}: #{config}") unless config.is_a?(String)
unless config.casecmp(DEFAULT_VALUE) == 0
raise XDOConfigError.new("Value can only be nil or 'noopen'")
end
end
end
Expand Down
28 changes: 13 additions & 15 deletions lib/secure_headers/headers/x_frame_options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,20 @@ class XFrameOptions
DEFAULT_VALUE = SAMEORIGIN
VALID_XFO_HEADER = /\A(#{SAMEORIGIN}\z|#{DENY}\z|#{ALLOW_ALL}\z|#{ALLOW_FROM}[:\s])/i

class << self
# Public: generate an X-Frame-Options header.
#
# Returns a default header if no configuration is provided, or a
# header name and value based on the config.
def make_header(config = nil, user_agent = nil)
return if config == OPT_OUT
[HEADER_NAME, config || DEFAULT_VALUE]
end
# Public: generate an X-Frame-Options header.
#
# Returns a default header if no configuration is provided, or a
# header name and value based on the config.
def self.make_header(config = nil, user_agent = nil)
return if config == OPT_OUT
[HEADER_NAME, config || DEFAULT_VALUE]
end

def validate_config!(config)
return if config.nil? || config == OPT_OUT
raise TypeError.new("Must be a string. Found #{config.class}: #{config}") unless config.is_a?(String)
unless config =~ VALID_XFO_HEADER
raise XFOConfigError.new("Value must be SAMEORIGIN|DENY|ALLOW-FROM:|ALLOWALL")
end
def self.validate_config!(config)
return if config.nil? || config == OPT_OUT
raise TypeError.new("Must be a string. Found #{config.class}: #{config}") unless config.is_a?(String)
unless config =~ VALID_XFO_HEADER
raise XFOConfigError.new("Value must be SAMEORIGIN|DENY|ALLOW-FROM:|ALLOWALL")
end
end
end
Expand Down
28 changes: 13 additions & 15 deletions lib/secure_headers/headers/x_permitted_cross_domain_policies.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,20 @@ class XPermittedCrossDomainPolicies
DEFAULT_VALUE = "none"
VALID_POLICIES = %w(all none master-only by-content-type by-ftp-filename)

class << self
# Public: generate an x-permitted-cross-domain-policies header.
#
# Returns a default header if no configuration is provided, or a
# header name and value based on the config.
def make_header(config = nil, user_agent = nil)
return if config == OPT_OUT
[HEADER_NAME, config || DEFAULT_VALUE]
end
# Public: generate an x-permitted-cross-domain-policies header.
#
# Returns a default header if no configuration is provided, or a
# header name and value based on the config.
def self.make_header(config = nil, user_agent = nil)
return if config == OPT_OUT
[HEADER_NAME, config || DEFAULT_VALUE]
end

def validate_config!(config)
return if config.nil? || config == OPT_OUT
raise TypeError.new("Must be a string. Found #{config.class}: #{config}") unless config.is_a?(String)
unless VALID_POLICIES.include?(config.downcase)
raise XPCDPConfigError.new("Value can only be one of #{VALID_POLICIES.join(', ')}")
end
def self.validate_config!(config)
return if config.nil? || config == OPT_OUT
raise TypeError.new("Must be a string. Found #{config.class}: #{config}") unless config.is_a?(String)
unless VALID_POLICIES.include?(config.downcase)
raise XPCDPConfigError.new("Value can only be one of #{VALID_POLICIES.join(', ')}")
end
end
end
Expand Down
Loading
Loading