diff --git a/Gemfile b/Gemfile index 78cca83b..007b8f12 100644 --- a/Gemfile +++ b/Gemfile @@ -3,6 +3,8 @@ source "https://rubygems.org" gemspec +gem "benchmark-ips" + group :test do gem "coveralls" gem "json" diff --git a/lib/secure_headers/configuration.rb b/lib/secure_headers/configuration.rb index 996ca74f..2ebbf487 100644 --- a/lib/secure_headers/configuration.rb +++ b/lib/secure_headers/configuration.rb @@ -83,14 +83,17 @@ def named_append_or_override_exists?(name) # can lead to modifying parent objects. def deep_copy(config) return unless config - config.each_with_object({}) do |(key, value), hash| - hash[key] = - if value.is_a?(Array) + result = {} + config.each_pair do |key, value| + result[key] = + case value + when Array value.dup else value end end + result end # Private: Returns the internal default configuration. This should only diff --git a/lib/secure_headers/headers/content_security_policy.rb b/lib/secure_headers/headers/content_security_policy.rb index 566004b8..4a7b0d76 100644 --- a/lib/secure_headers/headers/content_security_policy.rb +++ b/lib/secure_headers/headers/content_security_policy.rb @@ -20,9 +20,9 @@ def initialize(config = nil) config end - @preserve_schemes = @config.preserve_schemes - @script_nonce = @config.script_nonce - @style_nonce = @config.style_nonce + @preserve_schemes = @config[:preserve_schemes] + @script_nonce = @config[:script_nonce] + @style_nonce = @config[:style_nonce] end ## diff --git a/lib/secure_headers/headers/content_security_policy_config.rb b/lib/secure_headers/headers/content_security_policy_config.rb index 5f1dbb57..50ca6bd4 100644 --- a/lib/secure_headers/headers/content_security_policy_config.rb +++ b/lib/secure_headers/headers/content_security_policy_config.rb @@ -1,65 +1,23 @@ # frozen_string_literal: true module SecureHeaders module DynamicConfig - def self.included(base) - base.send(:attr_reader, *base.attrs) - base.attrs.each do |attr| - base.send(:define_method, "#{attr}=") do |value| - if self.class.attrs.include?(attr) - write_attribute(attr, value) - else - raise ContentSecurityPolicyConfigError, "Unknown config directive: #{attr}=#{value}" - end - end - end - end - def initialize(hash) - @base_uri = nil - @child_src = nil - @connect_src = nil - @default_src = nil - @font_src = nil - @form_action = nil - @frame_ancestors = nil - @frame_src = nil - @img_src = nil - @manifest_src = nil - @media_src = nil - @navigate_to = nil - @object_src = nil - @plugin_types = nil - @prefetch_src = nil - @preserve_schemes = nil - @report_only = nil - @report_uri = nil - @require_sri_for = nil - @require_trusted_types_for = nil - @sandbox = nil - @script_nonce = nil - @script_src = nil - @script_src_elem = nil - @script_src_attr = nil - @style_nonce = nil - @style_src = nil - @style_src_elem = nil - @style_src_attr = nil - @trusted_types = nil - @worker_src = nil - @upgrade_insecure_requests = nil - @disable_nonce_backwards_compatibility = nil + @config = {} from_hash(hash) end + def initialize_copy(hash) + @config = hash.to_h + end + def update_directive(directive, value) - self.send("#{directive}=", value) + @config[directive] = value end def directive_value(directive) - if self.class.attrs.include?(directive) - self.send(directive) - end + # No need to check attrs, as we only assign valid keys + @config[directive] end def merge(new_hash) @@ -77,10 +35,7 @@ def append(new_hash) end def to_h - self.class.attrs.each_with_object({}) do |key, hash| - value = self.send(key) - hash[key] = value unless value.nil? - end + @config.dup end def dup @@ -113,8 +68,11 @@ def from_hash(hash) def write_attribute(attr, value) value = value.dup if PolicyManagement::DIRECTIVE_VALUE_TYPES[attr] == :source_list - attr_variable = "@#{attr}" - self.instance_variable_set(attr_variable, value) + if value.nil? + @config.delete(attr) + else + @config[attr] = value + end end end @@ -122,7 +80,7 @@ class ContentSecurityPolicyConfigError < StandardError; end class ContentSecurityPolicyConfig HEADER_NAME = "Content-Security-Policy".freeze - ATTRS = PolicyManagement::ALL_DIRECTIVES + PolicyManagement::META_CONFIGS + PolicyManagement::NONCES + ATTRS = Set.new(PolicyManagement::ALL_DIRECTIVES + PolicyManagement::META_CONFIGS + PolicyManagement::NONCES) def self.attrs ATTRS end