1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
|
# 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
@block_all_mixed_content = 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
@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
@worker_src = nil
@upgrade_insecure_requests = nil
@disable_nonce_backwards_compatibility = nil
from_hash(hash)
end
def update_directive(directive, value)
self.send("#{directive}=", value)
end
def directive_value(directive)
if self.class.attrs.include?(directive)
self.send(directive)
end
end
def merge(new_hash)
new_config = self.dup
new_config.send(:from_hash, new_hash)
new_config
end
def merge!(new_hash)
from_hash(new_hash)
end
def append(new_hash)
from_hash(ContentSecurityPolicy.combine_policies(self.to_h, 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
end
def dup
self.class.new(self.to_h)
end
def opt_out?
false
end
def ==(o)
self.class == o.class && self.to_h == o.to_h
end
alias_method :[], :directive_value
alias_method :[]=, :update_directive
private
def from_hash(hash)
hash.each_pair do |k, v|
next if v.nil?
if self.class.attrs.include?(k)
write_attribute(k, v)
else
raise ContentSecurityPolicyConfigError, "Unknown config directive: #{k}=#{v}"
end
end
end
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)
end
end
class ContentSecurityPolicyConfigError < StandardError; end
class ContentSecurityPolicyConfig
HEADER_NAME = "Content-Security-Policy".freeze
ATTRS = PolicyManagement::ALL_DIRECTIVES + PolicyManagement::META_CONFIGS + PolicyManagement::NONCES
def self.attrs
ATTRS
end
include DynamicConfig
# based on what was suggested in https://github.com/rails/rails/pull/24961/files
DEFAULT = {
default_src: %w('self' https:),
font_src: %w('self' https: data:),
img_src: %w('self' https: data:),
object_src: %w('none'),
script_src: %w(https:),
style_src: %w('self' https: 'unsafe-inline')
}
def report_only?
false
end
def make_report_only
ContentSecurityPolicyReportOnlyConfig.new(self.to_h)
end
end
class ContentSecurityPolicyReportOnlyConfig < ContentSecurityPolicyConfig
HEADER_NAME = "Content-Security-Policy-Report-Only".freeze
def report_only?
true
end
def make_report_only
self
end
end
end
|