Class: SecureHeaders::ContentSecurityPolicy
- Inherits:
-
Object
- Object
- SecureHeaders::ContentSecurityPolicy
- Defined in:
- lib/secure_headers/headers/content_security_policy.rb
Constant Summary collapse
- MODERN_BROWSERS =
%w(Chrome Opera Firefox)
- DEFAULT_VALUE =
"default-src https:".freeze
- DEFAULT_CONFIG =
{ default_src: %w(https:) }.freeze
- HEADER_NAME =
"Content-Security-Policy".freeze
- REPORT_ONLY =
"Content-Security-Policy-Report-Only".freeze
- HEADER_NAMES =
[HEADER_NAME, REPORT_ONLY]
- DATA_PROTOCOL =
"data:".freeze
- BLOB_PROTOCOL =
"blob:".freeze
- SELF =
"'self'".freeze
- NONE =
"'none'".freeze
- STAR =
"*".freeze
- UNSAFE_INLINE =
"'unsafe-inline'".freeze
- UNSAFE_EVAL =
"'unsafe-eval'".freeze
- DEPRECATED_SOURCE_VALUES =
leftover deprecated values that will be in common use upon upgrading.
[SELF, NONE, UNSAFE_EVAL, UNSAFE_INLINE, "inline", "eval"].map { |value| value.delete("'") }.freeze
- DEFAULT_SRC =
:default_src- CONNECT_SRC =
:connect_src- FONT_SRC =
:font_src- FRAME_SRC =
:frame_src- IMG_SRC =
:img_src- MEDIA_SRC =
:media_src- OBJECT_SRC =
:object_src- SANDBOX =
:sandbox- SCRIPT_SRC =
:script_src- STYLE_SRC =
:style_src- REPORT_URI =
:report_uri- DIRECTIVES_1_0 =
[ DEFAULT_SRC, CONNECT_SRC, FONT_SRC, FRAME_SRC, IMG_SRC, MEDIA_SRC, OBJECT_SRC, SANDBOX, SCRIPT_SRC, STYLE_SRC, REPORT_URI ].freeze
- BASE_URI =
:base_uri- CHILD_SRC =
:child_src- FORM_ACTION =
:form_action- FRAME_ANCESTORS =
:frame_ancestors- PLUGIN_TYPES =
:plugin_types- DIRECTIVES_2_0 =
[ DIRECTIVES_1_0, BASE_URI, CHILD_SRC, FORM_ACTION, FRAME_ANCESTORS, PLUGIN_TYPES ].flatten.freeze
- MANIFEST_SRC =
All the directives currently under consideration for CSP level 3. https://w3c.github.io/webappsec/specs/CSP2/
:manifest_src- REFLECTED_XSS =
:reflected_xss- DIRECTIVES_3_0 =
[ DIRECTIVES_2_0, MANIFEST_SRC, REFLECTED_XSS ].flatten.freeze
- BLOCK_ALL_MIXED_CONTENT =
All the directives that are not currently in a formal spec, but have been implemented somewhere.
:block_all_mixed_content- UPGRADE_INSECURE_REQUESTS =
:upgrade_insecure_requests- DIRECTIVES_DRAFT =
[ BLOCK_ALL_MIXED_CONTENT, UPGRADE_INSECURE_REQUESTS ].freeze
- SAFARI_DIRECTIVES =
DIRECTIVES_1_0- FIREFOX_UNSUPPORTED_DIRECTIVES =
[ BLOCK_ALL_MIXED_CONTENT, CHILD_SRC, PLUGIN_TYPES ].freeze
- FIREFOX_DIRECTIVES =
( DIRECTIVES_2_0 + DIRECTIVES_DRAFT - FIREFOX_UNSUPPORTED_DIRECTIVES ).freeze
- CHROME_DIRECTIVES =
( DIRECTIVES_2_0 + DIRECTIVES_DRAFT ).freeze
- ALL_DIRECTIVES =
[DIRECTIVES_1_0 + DIRECTIVES_2_0 + DIRECTIVES_3_0 + DIRECTIVES_DRAFT].flatten.uniq.sort
- BODY_DIRECTIVES =
Think of default-src and report-uri as the beginning and end respectively, everything else is in between.
ALL_DIRECTIVES - [DEFAULT_SRC, REPORT_URI]
- VARIATIONS =
{ "Chrome" => CHROME_DIRECTIVES, "Opera" => CHROME_DIRECTIVES, "Firefox" => FIREFOX_DIRECTIVES, "Safari" => SAFARI_DIRECTIVES, "Other" => CHROME_DIRECTIVES }.freeze
- OTHER =
"Other".freeze
- DIRECTIVE_VALUE_TYPES =
{ BASE_URI => :source_list, BLOCK_ALL_MIXED_CONTENT => :boolean, CHILD_SRC => :source_list, CONNECT_SRC => :source_list, DEFAULT_SRC => :source_list, FONT_SRC => :source_list, FORM_ACTION => :source_list, FRAME_ANCESTORS => :source_list, FRAME_SRC => :source_list, IMG_SRC => :source_list, MANIFEST_SRC => :source_list, MEDIA_SRC => :source_list, OBJECT_SRC => :source_list, PLUGIN_TYPES => :source_list, REFLECTED_XSS => :string, REPORT_URI => :source_list, SANDBOX => :string, SCRIPT_SRC => :source_list, STYLE_SRC => :source_list, UPGRADE_INSECURE_REQUESTS => :boolean }.freeze
- CONFIG_KEY =
:csp- STAR_REGEXP =
Regexp.new(Regexp.escape(STAR))
- HTTP_SCHEME_REGEX =
%r{\Ahttps?://}- WILDCARD_SOURCES =
[ UNSAFE_EVAL, UNSAFE_INLINE, STAR, DATA_PROTOCOL, BLOB_PROTOCOL ].freeze
- META_CONFIGS =
[ :report_only, :preserve_schemes ].freeze
Class Method Summary collapse
-
.combine_policies(original, additions) ⇒ Object
Public: combine the values from two different configs.
-
.idempotent_additions?(config, additions) ⇒ Boolean
Public: determine if merging
additionswill cause a change to the actual value of the config. -
.make_header(config, user_agent) ⇒ Object
Public: generate a header name, value array that is user-agent-aware.
-
.validate_config!(config) ⇒ Object
Public: Validates each source expression.
Instance Method Summary collapse
-
#initialize(config = nil, user_agent = OTHER) ⇒ ContentSecurityPolicy
constructor
A new instance of ContentSecurityPolicy.
-
#name ⇒ Object
Returns the name to use for the header.
-
#value ⇒ Object
Return the value of the CSP header.
Constructor Details
#initialize(config = nil, user_agent = OTHER) ⇒ ContentSecurityPolicy
Returns a new instance of ContentSecurityPolicy.
285 286 287 288 289 290 291 292 293 294 295 296 297 |
# File 'lib/secure_headers/headers/content_security_policy.rb', line 285 def initialize(config = nil, user_agent = OTHER) config = Configuration.deep_copy(DEFAULT_CONFIG) unless config @config = config @parsed_ua = if user_agent.is_a?(UserAgent::Browsers::Base) user_agent else UserAgent.parse(user_agent) end @report_only = @config[:report_only] @preserve_schemes = @config[:preserve_schemes] @script_nonce = @config[:script_nonce] @style_nonce = @config[:style_nonce] end |
Class Method Details
.combine_policies(original, additions) ⇒ Object
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.
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
# File 'lib/secure_headers/headers/content_security_policy.rb', line 208 def combine_policies(original, additions) if original == OPT_OUT raise ContentSecurityPolicyConfigError.new("Attempted to override an opt-out CSP config.") end original = original.dup if original.frozen? # in case we would be appending to an empty directive, fill it with the default-src value additions.keys.each do |directive| unless original[directive] || !source_list?(directive) original[directive] = original[:default_src] end end # merge the two hashes. combine (instead of overwrite) the array values # when each hash contains a value for a given key. 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 |
.idempotent_additions?(config, additions) ⇒ Boolean
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.
190 191 192 193 |
# File 'lib/secure_headers/headers/content_security_policy.rb', line 190 def idempotent_additions?(config, additions) return false if config == OPT_OUT config.to_s == combine_policies(config, additions).to_s end |
.make_header(config, user_agent) ⇒ Object
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.
163 164 165 166 |
# File 'lib/secure_headers/headers/content_security_policy.rb', line 163 def make_header(config, user_agent) header = new(config, user_agent) [header.name, header.value] end |
.validate_config!(config) ⇒ Object
Public: Validates each source expression.
Does not validate the invididual values of the source expression (e.g. script_src => htt*p: will not raise an exception)
172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/secure_headers/headers/content_security_policy.rb', line 172 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 |
Instance Method Details
#name ⇒ Object
Returns the name to use for the header. Either "Content-Security-Policy" or "Content-Security-Policy-Report-Only"
302 303 304 305 306 307 308 |
# File 'lib/secure_headers/headers/content_security_policy.rb', line 302 def name if @report_only REPORT_ONLY else HEADER_NAME end end |
#value ⇒ Object
Return the value of the CSP header
312 313 314 315 316 317 318 |
# File 'lib/secure_headers/headers/content_security_policy.rb', line 312 def value @value ||= if @config build_value else DEFAULT_VALUE end end |