module SecureHeaders::PolicyManagement::ClassMethods
Public Instance Methods
Public: combine the values from two different configs.
original - the main config additions - values to be merged in
raises an error if the original config is OPT_OUT
-
for non-source-list values (report_only, block_all_mixed_content, upgrade_insecure_requests),
additions will overwrite the original value.
-
if a value in additions does not exist in the original config, the
default-src value is included to match original behavior.
-
if a value in additions does exist in the original config, the two
values are joined.
# File lib/secure_headers/headers/policy_management.rb, line 235 def combine_policies(original, additions) if original == OPT_OUT raise ContentSecurityPolicyConfigError.new("Attempted to override an opt-out CSP config.") end original = Configuration.send(:deep_copy, original) populate_fetch_source_with_default!(original, additions) merge_policy_additions(original, additions) end
Public: determine if merging additions
will cause a change to
the actual value of the config.
e.g. config = { script_src: %w(example.org google.com)} and additions = { script_src: %w(google.com)} then idempotent_additions? would return because google.com is already in the config.
# File lib/secure_headers/headers/policy_management.rb, line 216 def idempotent_additions?(config, additions) return true if config == OPT_OUT && additions == OPT_OUT return false if config == OPT_OUT config == combine_policies(config, additions) end
Public: generate a header name, value array that is user-agent-aware.
Returns a default policy if no configuration is provided, or a header name and value based on the config.
# File lib/secure_headers/headers/policy_management.rb, line 189 def make_header(config, user_agent) header = new(config, user_agent) [header.name, header.value] end
# File lib/secure_headers/headers/policy_management.rb, line 245 def ua_to_variation(user_agent) family = user_agent.browser if family && VARIATIONS.key?(family) family else OTHER end end
Public: Validates each source expression.
Does not validate the invididual values of the source expression (e.g. script_src => h*t*t*p: will not raise an exception)
# File lib/secure_headers/headers/policy_management.rb, line 198 def validate_config!(config) return if config.nil? || config == OPT_OUT raise ContentSecurityPolicyConfigError.new(":default_src is required") unless config[:default_src] config.each do |key, value| if META_CONFIGS.include?(key) raise ContentSecurityPolicyConfigError.new("#{key} must be a boolean value") unless boolean?(value) || value.nil? else validate_directive!(key, value) end end end
Private Instance Methods
# File lib/secure_headers/headers/policy_management.rb, line 348 def boolean?(source_expression) source_expression.is_a?(TrueClass) || source_expression.is_a?(FalseClass) end
# File lib/secure_headers/headers/policy_management.rb, line 334 def ensure_array_of_strings!(directive, source_expression) unless source_expression.is_a?(Array) && source_expression.compact.all? { |v| v.is_a?(String) } raise ContentSecurityPolicyConfigError.new("#{directive} must be an array of strings") end end
# File lib/secure_headers/headers/policy_management.rb, line 328 def ensure_valid_directive!(directive) unless ContentSecurityPolicy::ALL_DIRECTIVES.include?(directive) raise ContentSecurityPolicyConfigError.new("Unknown directive #{directive}") end end
# File lib/secure_headers/headers/policy_management.rb, line 340 def ensure_valid_sources!(directive, source_expression) source_expression.each do |source_expression| if ContentSecurityPolicy::DEPRECATED_SOURCE_VALUES.include?(source_expression) raise ContentSecurityPolicyConfigError.new("#{directive} contains an invalid keyword source (#{source_expression}). This value must be single quoted.") end end end
merge the two hashes. combine (instead of overwrite) the array values when each hash contains a value for a given key.
# File lib/secure_headers/headers/policy_management.rb, line 258 def merge_policy_additions(original, additions) original.merge(additions) do |directive, lhs, rhs| if source_list?(directive) (lhs.to_a + rhs.to_a).compact.uniq else rhs end end.reject { |_, value| value.nil? || value == [] } # this mess prevents us from adding empty directives. end
# File lib/secure_headers/headers/policy_management.rb, line 286 def nonce_added?(original, additions) [:script_nonce, :style_nonce].each do |nonce| if additions[nonce] && !original[nonce] return true end end end
For each directive in additions that does not exist in the original config, copy the default-src value to the original config. This modifies the original hash.
# File lib/secure_headers/headers/policy_management.rb, line 270 def populate_fetch_source_with_default!(original, additions) # in case we would be appending to an empty directive, fill it with the default-src value additions.keys.each do |directive| if !original[directive] && ((source_list?(directive) && FETCH_SOURCES.include?(directive)) || nonce_added?(original, additions)) if nonce_added?(original, additions) inferred_directive = directive.to_s.gsub(/_nonce/, "_src").to_sym unless original[inferred_directive] || NON_FETCH_SOURCES.include?(inferred_directive) original[inferred_directive] = original[:default_src] end else original[directive] = original[:default_src] end end end end
# File lib/secure_headers/headers/policy_management.rb, line 294 def source_list?(directive) DIRECTIVE_VALUE_TYPES[directive] == :source_list end
Private: Validates that the configuration has a valid type, or that it is a valid source expression.
# File lib/secure_headers/headers/policy_management.rb, line 300 def validate_directive!(directive, source_expression) case ContentSecurityPolicy::DIRECTIVE_VALUE_TYPES[directive] when :boolean unless boolean?(source_expression) raise ContentSecurityPolicyConfigError.new("#{directive} must be a boolean value") end when :string unless source_expression.is_a?(String) raise ContentSecurityPolicyConfigError.new("#{directive} Must be a string. Found #{config.class}: #{config} value") end else validate_source_expression!(directive, source_expression) end end
Private: validates that a source expression:
-
has a valid name
-
is an array of strings
-
does not contain any depreated, now invalid values (inline, eval, self, none)
Does not validate the invididual values of the source expression (e.g. script_src => h*t*t*p: will not raise an exception)
# File lib/secure_headers/headers/policy_management.rb, line 322 def validate_source_expression!(directive, source_expression) ensure_valid_directive!(directive) ensure_array_of_strings!(directive, source_expression) ensure_valid_sources!(directive, source_expression) end