Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make SecureSecurityPolicyConfig significantly faster #506

Merged
merged 6 commits into from
Aug 11, 2023
Merged
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
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ source "https://rubygems.org"

gemspec

gem "benchmark-ips"

group :test do
gem "coveralls"
gem "json"
Expand Down
9 changes: 6 additions & 3 deletions lib/secure_headers/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions lib/secure_headers/headers/content_security_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

##
Expand Down
72 changes: 15 additions & 57 deletions lib/secure_headers/headers/content_security_policy_config.rb
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -113,16 +68,19 @@ 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

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
Expand Down