secure_headers 3.9.0 → 4.0.0.alpha01
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of secure_headers might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/.rspec +1 -0
- data/.rubocop.yml +3 -0
- data/.ruby-version +1 -1
- data/.travis.yml +8 -6
- data/CHANGELOG.md +2 -34
- data/CONTRIBUTING.md +1 -1
- data/Gemfile +7 -4
- data/Guardfile +1 -0
- data/README.md +4 -25
- data/Rakefile +22 -18
- data/docs/cookies.md +18 -5
- data/lib/secure_headers.rb +1 -2
- data/lib/secure_headers/configuration.rb +6 -16
- data/lib/secure_headers/hash_helper.rb +2 -1
- data/lib/secure_headers/headers/clear_site_data.rb +2 -1
- data/lib/secure_headers/headers/content_security_policy.rb +14 -60
- data/lib/secure_headers/headers/content_security_policy_config.rb +1 -1
- data/lib/secure_headers/headers/cookie.rb +22 -10
- data/lib/secure_headers/headers/policy_management.rb +57 -98
- data/lib/secure_headers/headers/public_key_pins.rb +4 -3
- data/lib/secure_headers/headers/referrer_policy.rb +1 -0
- data/lib/secure_headers/headers/strict_transport_security.rb +2 -1
- data/lib/secure_headers/headers/x_content_type_options.rb +1 -0
- data/lib/secure_headers/headers/x_download_options.rb +2 -1
- data/lib/secure_headers/headers/x_frame_options.rb +1 -0
- data/lib/secure_headers/headers/x_permitted_cross_domain_policies.rb +2 -1
- data/lib/secure_headers/headers/x_xss_protection.rb +2 -1
- data/lib/secure_headers/middleware.rb +10 -9
- data/lib/secure_headers/railtie.rb +7 -6
- data/lib/secure_headers/utils/cookies_config.rb +17 -18
- data/lib/secure_headers/view_helper.rb +2 -1
- data/lib/tasks/tasks.rake +2 -1
- data/secure_headers.gemspec +13 -3
- data/spec/lib/secure_headers/configuration_spec.rb +9 -8
- data/spec/lib/secure_headers/headers/clear_site_data_spec.rb +2 -1
- data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +17 -53
- data/spec/lib/secure_headers/headers/cookie_spec.rb +58 -37
- data/spec/lib/secure_headers/headers/policy_management_spec.rb +20 -41
- data/spec/lib/secure_headers/headers/public_key_pins_spec.rb +7 -6
- data/spec/lib/secure_headers/headers/referrer_policy_spec.rb +4 -3
- data/spec/lib/secure_headers/headers/strict_transport_security_spec.rb +5 -4
- data/spec/lib/secure_headers/headers/x_content_type_options_spec.rb +2 -1
- data/spec/lib/secure_headers/headers/x_download_options_spec.rb +3 -2
- data/spec/lib/secure_headers/headers/x_frame_options_spec.rb +2 -1
- data/spec/lib/secure_headers/headers/x_permitted_cross_domain_policies_spec.rb +4 -3
- data/spec/lib/secure_headers/headers/x_xss_protection_spec.rb +4 -3
- data/spec/lib/secure_headers/middleware_spec.rb +18 -21
- data/spec/lib/secure_headers/view_helpers_spec.rb +5 -4
- data/spec/lib/secure_headers_spec.rb +92 -120
- data/spec/spec_helper.rb +9 -23
- data/upgrading-to-4-0.md +49 -0
- metadata +16 -11
- data/lib/secure_headers/headers/expect_certificate_transparency.rb +0 -70
- data/spec/lib/secure_headers/headers/expect_certificate_transparency_spec.rb +0 -42
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module SecureHeaders
|
2
3
|
module DynamicConfig
|
3
4
|
def self.included(base)
|
@@ -37,7 +38,6 @@ module SecureHeaders
|
|
37
38
|
@script_src = nil
|
38
39
|
@style_nonce = nil
|
39
40
|
@style_src = nil
|
40
|
-
@worker_src = nil
|
41
41
|
@upgrade_insecure_requests = nil
|
42
42
|
|
43
43
|
from_hash(hash)
|
@@ -1,5 +1,7 @@
|
|
1
|
-
|
2
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "cgi"
|
3
|
+
require "secure_headers/utils/cookies_config"
|
4
|
+
|
3
5
|
|
4
6
|
module SecureHeaders
|
5
7
|
class CookiesConfigError < StandardError; end
|
@@ -13,8 +15,18 @@ module SecureHeaders
|
|
13
15
|
|
14
16
|
attr_reader :raw_cookie, :config
|
15
17
|
|
18
|
+
COOKIE_DEFAULTS = {
|
19
|
+
httponly: true,
|
20
|
+
secure: true,
|
21
|
+
samesite: { lax: true },
|
22
|
+
}.freeze
|
23
|
+
|
16
24
|
def initialize(cookie, config)
|
17
25
|
@raw_cookie = cookie
|
26
|
+
unless config == OPT_OUT
|
27
|
+
config ||= {}
|
28
|
+
config = COOKIE_DEFAULTS.merge(config)
|
29
|
+
end
|
18
30
|
@config = config
|
19
31
|
@attributes = {
|
20
32
|
httponly: nil,
|
@@ -56,6 +68,7 @@ module SecureHeaders
|
|
56
68
|
end
|
57
69
|
|
58
70
|
def flag_cookie?(attribute)
|
71
|
+
return false if config == OPT_OUT
|
59
72
|
case config[attribute]
|
60
73
|
when TrueClass
|
61
74
|
true
|
@@ -81,13 +94,12 @@ module SecureHeaders
|
|
81
94
|
"SameSite=Lax"
|
82
95
|
elsif flag_samesite_strict?
|
83
96
|
"SameSite=Strict"
|
84
|
-
elsif flag_samesite_none?
|
85
|
-
"SameSite=None"
|
86
97
|
end
|
87
98
|
end
|
88
99
|
|
89
100
|
def flag_samesite?
|
90
|
-
|
101
|
+
return false if config == OPT_OUT || config[:samesite] == OPT_OUT
|
102
|
+
flag_samesite_lax? || flag_samesite_strict?
|
91
103
|
end
|
92
104
|
|
93
105
|
def flag_samesite_lax?
|
@@ -98,13 +110,13 @@ module SecureHeaders
|
|
98
110
|
flag_samesite_enforcement?(:strict)
|
99
111
|
end
|
100
112
|
|
101
|
-
def flag_samesite_none?
|
102
|
-
flag_samesite_enforcement?(:none)
|
103
|
-
end
|
104
|
-
|
105
113
|
def flag_samesite_enforcement?(mode)
|
106
114
|
return unless config[:samesite]
|
107
115
|
|
116
|
+
if config[:samesite].is_a?(TrueClass) && mode == :lax
|
117
|
+
return true
|
118
|
+
end
|
119
|
+
|
108
120
|
case config[:samesite][mode]
|
109
121
|
when Hash
|
110
122
|
conditionally_flag?(config[:samesite][mode])
|
@@ -119,7 +131,7 @@ module SecureHeaders
|
|
119
131
|
return unless cookie
|
120
132
|
|
121
133
|
cookie.split(/[;,]\s?/).each do |pairs|
|
122
|
-
name, values = pairs.split(
|
134
|
+
name, values = pairs.split("=", 2)
|
123
135
|
name = CGI.unescape(name)
|
124
136
|
|
125
137
|
attribute = name.downcase.to_sym
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module SecureHeaders
|
2
3
|
module PolicyManagement
|
3
4
|
def self.included(base)
|
@@ -5,8 +6,14 @@ module SecureHeaders
|
|
5
6
|
end
|
6
7
|
|
7
8
|
MODERN_BROWSERS = %w(Chrome Opera Firefox)
|
8
|
-
|
9
|
-
|
9
|
+
DEFAULT_CONFIG = {
|
10
|
+
default_src: %w(https:),
|
11
|
+
img_src: %w(https: data: 'self'),
|
12
|
+
object_src: %w('none'),
|
13
|
+
script_src: %w(https:),
|
14
|
+
style_src: %w('self' 'unsafe-inline' https:),
|
15
|
+
form_action: %w('self')
|
16
|
+
}.freeze
|
10
17
|
DATA_PROTOCOL = "data:".freeze
|
11
18
|
BLOB_PROTOCOL = "blob:".freeze
|
12
19
|
SELF = "'self'".freeze
|
@@ -65,13 +72,10 @@ module SecureHeaders
|
|
65
72
|
BLOCK_ALL_MIXED_CONTENT = :block_all_mixed_content
|
66
73
|
MANIFEST_SRC = :manifest_src
|
67
74
|
UPGRADE_INSECURE_REQUESTS = :upgrade_insecure_requests
|
68
|
-
WORKER_SRC = :worker_src
|
69
|
-
|
70
75
|
DIRECTIVES_3_0 = [
|
71
76
|
DIRECTIVES_2_0,
|
72
77
|
BLOCK_ALL_MIXED_CONTENT,
|
73
78
|
MANIFEST_SRC,
|
74
|
-
WORKER_SRC,
|
75
79
|
UPGRADE_INSECURE_REQUESTS
|
76
80
|
].flatten.freeze
|
77
81
|
|
@@ -82,7 +86,6 @@ module SecureHeaders
|
|
82
86
|
FIREFOX_UNSUPPORTED_DIRECTIVES = [
|
83
87
|
BLOCK_ALL_MIXED_CONTENT,
|
84
88
|
CHILD_SRC,
|
85
|
-
WORKER_SRC,
|
86
89
|
PLUGIN_TYPES
|
87
90
|
].freeze
|
88
91
|
|
@@ -92,7 +95,6 @@ module SecureHeaders
|
|
92
95
|
|
93
96
|
FIREFOX_46_UNSUPPORTED_DIRECTIVES = [
|
94
97
|
BLOCK_ALL_MIXED_CONTENT,
|
95
|
-
WORKER_SRC,
|
96
98
|
PLUGIN_TYPES
|
97
99
|
].freeze
|
98
100
|
|
@@ -114,6 +116,18 @@ module SecureHeaders
|
|
114
116
|
# everything else is in between.
|
115
117
|
BODY_DIRECTIVES = ALL_DIRECTIVES - [DEFAULT_SRC, REPORT_URI]
|
116
118
|
|
119
|
+
# These are directives that do not inherit the default-src value. This is
|
120
|
+
# useful when calling #combine_policies.
|
121
|
+
NON_FETCH_SOURCES = [
|
122
|
+
BASE_URI,
|
123
|
+
FORM_ACTION,
|
124
|
+
FRAME_ANCESTORS,
|
125
|
+
PLUGIN_TYPES,
|
126
|
+
REPORT_URI
|
127
|
+
]
|
128
|
+
|
129
|
+
FETCH_SOURCES = ALL_DIRECTIVES - NON_FETCH_SOURCES
|
130
|
+
|
117
131
|
VARIATIONS = {
|
118
132
|
"Chrome" => CHROME_DIRECTIVES,
|
119
133
|
"Opera" => CHROME_DIRECTIVES,
|
@@ -141,31 +155,14 @@ module SecureHeaders
|
|
141
155
|
MANIFEST_SRC => :source_list,
|
142
156
|
MEDIA_SRC => :source_list,
|
143
157
|
OBJECT_SRC => :source_list,
|
144
|
-
PLUGIN_TYPES => :
|
158
|
+
PLUGIN_TYPES => :source_list,
|
145
159
|
REPORT_URI => :source_list,
|
146
|
-
SANDBOX => :
|
160
|
+
SANDBOX => :source_list,
|
147
161
|
SCRIPT_SRC => :source_list,
|
148
162
|
STYLE_SRC => :source_list,
|
149
|
-
WORKER_SRC => :source_list,
|
150
163
|
UPGRADE_INSECURE_REQUESTS => :boolean
|
151
164
|
}.freeze
|
152
165
|
|
153
|
-
# These are directives that don't have use a source list, and hence do not
|
154
|
-
# inherit the default-src value.
|
155
|
-
NON_SOURCE_LIST_SOURCES = DIRECTIVE_VALUE_TYPES.select do |_, type|
|
156
|
-
type != :source_list
|
157
|
-
end.keys.freeze
|
158
|
-
|
159
|
-
# These are directives that take a source list, but that do not inherit
|
160
|
-
# the default-src value.
|
161
|
-
NON_FETCH_SOURCES = [
|
162
|
-
BASE_URI,
|
163
|
-
FORM_ACTION,
|
164
|
-
FRAME_ANCESTORS,
|
165
|
-
REPORT_URI
|
166
|
-
]
|
167
|
-
|
168
|
-
FETCH_SOURCES = ALL_DIRECTIVES - NON_FETCH_SOURCES - NON_SOURCE_LIST_SOURCES
|
169
166
|
|
170
167
|
STAR_REGEXP = Regexp.new(Regexp.escape(STAR))
|
171
168
|
HTTP_SCHEME_REGEX = %r{\Ahttps?://}
|
@@ -205,6 +202,7 @@ module SecureHeaders
|
|
205
202
|
def validate_config!(config)
|
206
203
|
return if config.nil? || config.opt_out?
|
207
204
|
raise ContentSecurityPolicyConfigError.new(":default_src is required") unless config.directive_value(:default_src)
|
205
|
+
raise ContentSecurityPolicyConfigError.new(":script_src is required, falling back to default-src is too dangerous") unless config.directive_value(:script_src)
|
208
206
|
ContentSecurityPolicyConfig.attrs.each do |key|
|
209
207
|
value = config.directive_value(key)
|
210
208
|
next unless value
|
@@ -263,7 +261,7 @@ module SecureHeaders
|
|
263
261
|
# when each hash contains a value for a given key.
|
264
262
|
def merge_policy_additions(original, additions)
|
265
263
|
original.merge(additions) do |directive, lhs, rhs|
|
266
|
-
if
|
264
|
+
if source_list?(directive)
|
267
265
|
(lhs.to_a + rhs.to_a).compact.uniq
|
268
266
|
else
|
269
267
|
rhs
|
@@ -271,27 +269,20 @@ module SecureHeaders
|
|
271
269
|
end.reject { |_, value| value.nil? || value == [] } # this mess prevents us from adding empty directives.
|
272
270
|
end
|
273
271
|
|
274
|
-
# Returns True if a directive expects a list of values and False otherwise.
|
275
|
-
def list_directive?(directive)
|
276
|
-
source_list?(directive) ||
|
277
|
-
sandbox_list?(directive) ||
|
278
|
-
media_type_list?(directive)
|
279
|
-
end
|
280
|
-
|
281
272
|
# For each directive in additions that does not exist in the original config,
|
282
273
|
# copy the default-src value to the original config. This modifies the original hash.
|
283
274
|
def populate_fetch_source_with_default!(original, additions)
|
284
275
|
# in case we would be appending to an empty directive, fill it with the default-src value
|
285
276
|
additions.each_key do |directive|
|
286
|
-
directive
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
277
|
+
if !original[directive] && ((source_list?(directive) && FETCH_SOURCES.include?(directive)) || nonce_added?(original, additions))
|
278
|
+
if nonce_added?(original, additions)
|
279
|
+
inferred_directive = directive.to_s.gsub(/_nonce/, "_src").to_sym
|
280
|
+
unless original[inferred_directive] || NON_FETCH_SOURCES.include?(inferred_directive)
|
281
|
+
original[inferred_directive] = default_for(directive, original)
|
282
|
+
end
|
283
|
+
else
|
284
|
+
original[directive] = default_for(directive, original)
|
285
|
+
end
|
295
286
|
end
|
296
287
|
end
|
297
288
|
end
|
@@ -302,77 +293,45 @@ module SecureHeaders
|
|
302
293
|
original[DEFAULT_SRC]
|
303
294
|
end
|
304
295
|
|
305
|
-
def
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
296
|
+
def nonce_added?(original, additions)
|
297
|
+
[:script_nonce, :style_nonce].each do |nonce|
|
298
|
+
if additions[nonce] && !original[nonce]
|
299
|
+
return true
|
300
|
+
end
|
301
|
+
end
|
311
302
|
end
|
312
303
|
|
313
|
-
def
|
314
|
-
DIRECTIVE_VALUE_TYPES[directive] == :
|
304
|
+
def source_list?(directive)
|
305
|
+
DIRECTIVE_VALUE_TYPES[directive] == :source_list
|
315
306
|
end
|
316
307
|
|
317
308
|
# Private: Validates that the configuration has a valid type, or that it is a valid
|
318
309
|
# source expression.
|
319
|
-
def validate_directive!(directive,
|
320
|
-
ensure_valid_directive!(directive)
|
310
|
+
def validate_directive!(directive, source_expression)
|
321
311
|
case ContentSecurityPolicy::DIRECTIVE_VALUE_TYPES[directive]
|
322
312
|
when :boolean
|
323
|
-
unless boolean?(
|
324
|
-
raise ContentSecurityPolicyConfigError.new("#{directive} must be a boolean
|
313
|
+
unless boolean?(source_expression)
|
314
|
+
raise ContentSecurityPolicyConfigError.new("#{directive} must be a boolean value")
|
315
|
+
end
|
316
|
+
when :string
|
317
|
+
unless source_expression.is_a?(String)
|
318
|
+
raise ContentSecurityPolicyConfigError.new("#{directive} Must be a string. Found #{config.class}: #{config} value")
|
325
319
|
end
|
326
|
-
when :sandbox_list
|
327
|
-
validate_sandbox_expression!(directive, value)
|
328
|
-
when :media_type_list
|
329
|
-
validate_media_type_expression!(directive, value)
|
330
|
-
when :source_list
|
331
|
-
validate_source_expression!(directive, value)
|
332
320
|
else
|
333
|
-
|
334
|
-
end
|
335
|
-
end
|
336
|
-
|
337
|
-
# Private: validates that a sandbox token expression:
|
338
|
-
# 1. is an array of strings or optionally `true` (to enable maximal sandboxing)
|
339
|
-
# 2. For arrays, each element is of the form allow-*
|
340
|
-
def validate_sandbox_expression!(directive, sandbox_token_expression)
|
341
|
-
# We support sandbox: true to indicate a maximally secure sandbox.
|
342
|
-
return if boolean?(sandbox_token_expression) && sandbox_token_expression == true
|
343
|
-
ensure_array_of_strings!(directive, sandbox_token_expression)
|
344
|
-
valid = sandbox_token_expression.compact.all? do |v|
|
345
|
-
v.is_a?(String) && v.start_with?("allow-")
|
346
|
-
end
|
347
|
-
if !valid
|
348
|
-
raise ContentSecurityPolicyConfigError.new("#{directive} must be True or an array of zero or more sandbox token strings (ex. allow-forms)")
|
349
|
-
end
|
350
|
-
end
|
351
|
-
|
352
|
-
# Private: validates that a media type expression:
|
353
|
-
# 1. is an array of strings
|
354
|
-
# 2. each element is of the form type/subtype
|
355
|
-
def validate_media_type_expression!(directive, media_type_expression)
|
356
|
-
ensure_array_of_strings!(directive, media_type_expression)
|
357
|
-
valid = media_type_expression.compact.all? do |v|
|
358
|
-
# All media types are of the form: <type from RFC 2045> "/" <subtype from RFC 2045>.
|
359
|
-
v =~ /\A.+\/.+\z/
|
360
|
-
end
|
361
|
-
if !valid
|
362
|
-
raise ContentSecurityPolicyConfigError.new("#{directive} must be an array of valid media types (ex. application/pdf)")
|
321
|
+
validate_source_expression!(directive, source_expression)
|
363
322
|
end
|
364
323
|
end
|
365
324
|
|
366
325
|
# Private: validates that a source expression:
|
367
|
-
# 1.
|
368
|
-
# 2.
|
326
|
+
# 1. has a valid name
|
327
|
+
# 2. is an array of strings
|
328
|
+
# 3. does not contain any depreated, now invalid values (inline, eval, self, none)
|
369
329
|
#
|
370
330
|
# Does not validate the invididual values of the source expression (e.g.
|
371
331
|
# script_src => h*t*t*p: will not raise an exception)
|
372
332
|
def validate_source_expression!(directive, source_expression)
|
373
|
-
|
374
|
-
|
375
|
-
end
|
333
|
+
ensure_valid_directive!(directive)
|
334
|
+
ensure_array_of_strings!(directive, source_expression)
|
376
335
|
ensure_valid_sources!(directive, source_expression)
|
377
336
|
end
|
378
337
|
|
@@ -382,8 +341,8 @@ module SecureHeaders
|
|
382
341
|
end
|
383
342
|
end
|
384
343
|
|
385
|
-
def ensure_array_of_strings!(directive,
|
386
|
-
|
344
|
+
def ensure_array_of_strings!(directive, source_expression)
|
345
|
+
unless source_expression.is_a?(Array) && source_expression.compact.all? { |v| v.is_a?(String) }
|
387
346
|
raise ContentSecurityPolicyConfigError.new("#{directive} must be an array of strings")
|
388
347
|
end
|
389
348
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module SecureHeaders
|
2
3
|
class PublicKeyPinsConfigError < StandardError; end
|
3
4
|
class PublicKeyPins
|
@@ -54,7 +55,7 @@ module SecureHeaders
|
|
54
55
|
pin_directives,
|
55
56
|
report_uri_directive,
|
56
57
|
subdomain_directive
|
57
|
-
].compact.join(
|
58
|
+
].compact.join("; ").strip
|
58
59
|
end
|
59
60
|
|
60
61
|
def pin_directives
|
@@ -63,7 +64,7 @@ module SecureHeaders
|
|
63
64
|
pin.map do |token, hash|
|
64
65
|
"pin-#{token}=\"#{hash}\"" if HASH_ALGORITHMS.include?(token)
|
65
66
|
end
|
66
|
-
end.join(
|
67
|
+
end.join("; ")
|
67
68
|
end
|
68
69
|
|
69
70
|
def max_age_directive
|
@@ -75,7 +76,7 @@ module SecureHeaders
|
|
75
76
|
end
|
76
77
|
|
77
78
|
def subdomain_directive
|
78
|
-
@include_subdomains ?
|
79
|
+
@include_subdomains ? "includeSubDomains" : nil
|
79
80
|
end
|
80
81
|
end
|
81
82
|
end
|
@@ -1,8 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module SecureHeaders
|
2
3
|
class STSConfigError < StandardError; end
|
3
4
|
|
4
5
|
class StrictTransportSecurity
|
5
|
-
HEADER_NAME =
|
6
|
+
HEADER_NAME = "Strict-Transport-Security".freeze
|
6
7
|
HSTS_MAX_AGE = "631138519"
|
7
8
|
DEFAULT_VALUE = "max-age=" + HSTS_MAX_AGE
|
8
9
|
VALID_STS_HEADER = /\Amax-age=\d+(; includeSubdomains)?(; preload)?\z/i
|
@@ -1,8 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module SecureHeaders
|
2
3
|
class XDOConfigError < StandardError; end
|
3
4
|
class XDownloadOptions
|
4
5
|
HEADER_NAME = "X-Download-Options".freeze
|
5
|
-
DEFAULT_VALUE =
|
6
|
+
DEFAULT_VALUE = "noopen"
|
6
7
|
CONFIG_KEY = :x_download_options
|
7
8
|
|
8
9
|
class << self
|
@@ -1,8 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module SecureHeaders
|
2
3
|
class XPCDPConfigError < StandardError; end
|
3
4
|
class XPermittedCrossDomainPolicies
|
4
5
|
HEADER_NAME = "X-Permitted-Cross-Domain-Policies".freeze
|
5
|
-
DEFAULT_VALUE =
|
6
|
+
DEFAULT_VALUE = "none"
|
6
7
|
VALID_POLICIES = %w(all none master-only by-content-type by-ftp-filename)
|
7
8
|
CONFIG_KEY = :x_permitted_cross_domain_policies
|
8
9
|
|
@@ -1,7 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module SecureHeaders
|
2
3
|
class XXssProtectionConfigError < StandardError; end
|
3
4
|
class XXssProtection
|
4
|
-
HEADER_NAME =
|
5
|
+
HEADER_NAME = "X-XSS-Protection".freeze
|
5
6
|
DEFAULT_VALUE = "1; mode=block"
|
6
7
|
VALID_X_XSS_HEADER = /\A[01](; mode=block)?(; report=.*)?\z/i
|
7
8
|
CONFIG_KEY = :x_xss_protection
|