secure_headers 6.3.2 → 6.4.0
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.
- checksums.yaml +4 -4
- data/.github/workflows/build.yml +2 -2
- data/.github/workflows/github-release.yml +28 -0
- data/CHANGELOG.md +12 -0
- data/Gemfile +1 -1
- data/README.md +3 -3
- data/docs/per_action_configuration.md +2 -4
- data/lib/secure_headers/configuration.rb +6 -5
- data/lib/secure_headers/headers/content_security_policy.rb +21 -16
- data/lib/secure_headers/headers/content_security_policy_config.rb +2 -0
- data/lib/secure_headers/headers/policy_management.rb +40 -7
- data/lib/secure_headers/version.rb +1 -1
- data/lib/secure_headers/view_helper.rb +7 -6
- data/lib/tasks/tasks.rake +6 -7
- data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +25 -0
- data/spec/lib/secure_headers/headers/policy_management_spec.rb +8 -0
- data/spec/lib/secure_headers/view_helpers_spec.rb +5 -4
- metadata +4 -4
- data/.github/workflows/sync.yml +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e5372953d5180d45526a777c1b66e7e86a8ccfca08f6b7b613549d8ba1509a6e
|
4
|
+
data.tar.gz: 0d1cc3b012df7b1cfd549bcf09063a390945b05c03009acda31017f859ff7d2d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 35844f24ecb678a83548492545403bc174a6d0ce74896f7817d93cb30f62d97b415ea876dccd3efbed7cad3e5dd5c9cda4f676f7dc2c858b2f2010f2c33a0f73
|
7
|
+
data.tar.gz: 196a212de355c06a2a3fee6c22302fb1bfd7471dba402124adc7072f90dbd0c4a5b3ee8fa7604d0b164479717bfb65050d3b4802948410b61aba3382d5c8eb39
|
data/.github/workflows/build.yml
CHANGED
@@ -7,12 +7,12 @@ jobs:
|
|
7
7
|
runs-on: ubuntu-latest
|
8
8
|
strategy:
|
9
9
|
matrix:
|
10
|
-
ruby: [ '2.
|
10
|
+
ruby: [ '2.5', '2.6', '2.7', '3.0', '3.1' ]
|
11
11
|
|
12
12
|
steps:
|
13
13
|
- uses: actions/checkout@v2
|
14
14
|
- name: Set up Ruby ${{ matrix.ruby }}
|
15
|
-
uses:
|
15
|
+
uses: ruby/setup-ruby@v1
|
16
16
|
with:
|
17
17
|
ruby-version: ${{ matrix.ruby }}
|
18
18
|
- name: Build and test with Rake
|
@@ -0,0 +1,28 @@
|
|
1
|
+
name: GitHub Release
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
tags:
|
6
|
+
- v*
|
7
|
+
|
8
|
+
jobs:
|
9
|
+
Publish:
|
10
|
+
permissions:
|
11
|
+
contents: write
|
12
|
+
runs-on: ubuntu-latest
|
13
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
14
|
+
steps:
|
15
|
+
- name: Calculate release name
|
16
|
+
run: |
|
17
|
+
GITHUB_REF=${{ github.ref }}
|
18
|
+
RELEASE_NAME=${GITHUB_REF#"refs/tags/"}
|
19
|
+
echo "RELEASE_NAME=${RELEASE_NAME}" >> $GITHUB_ENV
|
20
|
+
- name: Publish release
|
21
|
+
uses: actions/create-release@v1
|
22
|
+
env:
|
23
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
24
|
+
with:
|
25
|
+
tag_name: ${{ github.ref }}
|
26
|
+
release_name: ${{ env.RELEASE_NAME }}
|
27
|
+
draft: false
|
28
|
+
prerelease: false
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
## 6.4.0
|
2
|
+
|
3
|
+
- CSP: Add support for trusted-types, require-trusted-types-for directive (@JackMc): https://github.com/github/secure_headers/pull/486
|
4
|
+
|
5
|
+
## 6.3.4
|
6
|
+
|
7
|
+
- CSP: Do not deduplicate alternate schema source expressions (@keithamus): https://github.com/github/secure_headers/pull/478
|
8
|
+
|
9
|
+
## 6.3.3
|
10
|
+
|
11
|
+
Fix hash generation for indented helper methods (@rahearn)
|
12
|
+
|
1
13
|
## 6.3.2
|
2
14
|
|
3
15
|
Add support for style-src-attr, style-src-elem, script-src-attr, and script-src-elem directives (@ggalmazor)
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
**main branch represents 6.x line**. See the [upgrading to 4.x doc](docs/upgrading-to-4-0.md), [upgrading to 5.x doc](docs/upgrading-to-5-0.md), or [upgrading to 6.x doc](docs/upgrading-to-6-0.md) for instructions on how to upgrade. Bug fixes should go in the 5.x branch for now.
|
4
4
|
|
5
5
|
The gem will automatically apply several headers that are related to security. This includes:
|
6
|
-
- Content Security Policy (CSP) - Helps detect/prevent XSS, mixed-content, and other classes of attack. [CSP 2 Specification](
|
6
|
+
- Content Security Policy (CSP) - Helps detect/prevent XSS, mixed-content, and other classes of attack. [CSP 2 Specification](https://www.w3.org/TR/CSP2/)
|
7
7
|
- https://csp.withgoogle.com
|
8
8
|
- https://csp.withgoogle.com/docs/strict-csp.html
|
9
9
|
- https://csp-evaluator.withgoogle.com
|
@@ -62,7 +62,7 @@ SecureHeaders::Configuration.default do |config|
|
|
62
62
|
# directive values: these values will directly translate into source directives
|
63
63
|
default_src: %w('none'),
|
64
64
|
base_uri: %w('self'),
|
65
|
-
block_all_mixed_content: true, # see
|
65
|
+
block_all_mixed_content: true, # see https://www.w3.org/TR/mixed-content/
|
66
66
|
child_src: %w('self'), # if child-src isn't supported, the value for frame-src will be set.
|
67
67
|
connect_src: %w(wss:),
|
68
68
|
font_src: %w('self' data:),
|
@@ -170,7 +170,7 @@ If you've made a contribution and see your name missing from the list, make a PR
|
|
170
170
|
* Node.js (express) [helmet](https://github.com/helmetjs/helmet) and [hood](https://github.com/seanmonstar/hood)
|
171
171
|
* Node.js (hapi) [blankie](https://github.com/nlf/blankie)
|
172
172
|
* ASP.NET - [NWebsec](https://github.com/NWebsec/NWebsec/wiki)
|
173
|
-
* Python - [django-csp](https://github.com/mozilla/django-csp) + [commonware](https://github.com/jsocol/commonware/); [django-security](https://github.com/sdelements/django-security)
|
173
|
+
* Python - [django-csp](https://github.com/mozilla/django-csp) + [commonware](https://github.com/jsocol/commonware/); [django-security](https://github.com/sdelements/django-security), [secure](https://github.com/TypeError/secure)
|
174
174
|
* Go - [secureheader](https://github.com/kr/secureheader)
|
175
175
|
* Elixir [secure_headers](https://github.com/anotherhale/secure_headers)
|
176
176
|
* Dropwizard [dropwizard-web-security](https://github.com/palantir/dropwizard-web-security)
|
@@ -54,7 +54,7 @@ Code | Result
|
|
54
54
|
|
55
55
|
#### Nonce
|
56
56
|
|
57
|
-
You can use a view helper to automatically add nonces to script tags
|
57
|
+
You can use a view helper to automatically add nonces to script tags. Currently, using a nonce helper or calling `content_security_policy_nonce` will populate all configured CSP headers, including report-only and enforced policies.
|
58
58
|
|
59
59
|
```erb
|
60
60
|
<%= nonced_javascript_tag do %>
|
@@ -120,9 +120,7 @@ You can clear the browser cache after the logout request by using the following.
|
|
120
120
|
class ApplicationController < ActionController::Base
|
121
121
|
# Configuration override to send the Clear-Site-Data header.
|
122
122
|
SecureHeaders::Configuration.override(:clear_browser_cache) do |config|
|
123
|
-
config.clear_site_data =
|
124
|
-
SecureHeaders::ClearSiteData::ALL_TYPES
|
125
|
-
]
|
123
|
+
config.clear_site_data = SecureHeaders::ClearSiteData::ALL_TYPES
|
126
124
|
end
|
127
125
|
|
128
126
|
|
@@ -84,11 +84,12 @@ module SecureHeaders
|
|
84
84
|
def deep_copy(config)
|
85
85
|
return unless config
|
86
86
|
config.each_with_object({}) do |(key, value), hash|
|
87
|
-
hash[key] =
|
88
|
-
value.
|
89
|
-
|
90
|
-
|
91
|
-
|
87
|
+
hash[key] =
|
88
|
+
if value.is_a?(Array)
|
89
|
+
value.dup
|
90
|
+
else
|
91
|
+
value
|
92
|
+
end
|
92
93
|
end
|
93
94
|
end
|
94
95
|
|
@@ -7,17 +7,18 @@ module SecureHeaders
|
|
7
7
|
include PolicyManagement
|
8
8
|
|
9
9
|
def initialize(config = nil)
|
10
|
-
@config =
|
11
|
-
if config
|
12
|
-
|
10
|
+
@config =
|
11
|
+
if config.is_a?(Hash)
|
12
|
+
if config[:report_only]
|
13
|
+
ContentSecurityPolicyReportOnlyConfig.new(config || DEFAULT_CONFIG)
|
14
|
+
else
|
15
|
+
ContentSecurityPolicyConfig.new(config || DEFAULT_CONFIG)
|
16
|
+
end
|
17
|
+
elsif config.nil?
|
18
|
+
ContentSecurityPolicyConfig.new(DEFAULT_CONFIG)
|
13
19
|
else
|
14
|
-
|
20
|
+
config
|
15
21
|
end
|
16
|
-
elsif config.nil?
|
17
|
-
ContentSecurityPolicyConfig.new(DEFAULT_CONFIG)
|
18
|
-
else
|
19
|
-
config
|
20
|
-
end
|
21
22
|
|
22
23
|
@preserve_schemes = @config.preserve_schemes
|
23
24
|
@script_nonce = @config.script_nonce
|
@@ -34,11 +35,12 @@ module SecureHeaders
|
|
34
35
|
##
|
35
36
|
# Return the value of the CSP header
|
36
37
|
def value
|
37
|
-
@value ||=
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
38
|
+
@value ||=
|
39
|
+
if @config
|
40
|
+
build_value
|
41
|
+
else
|
42
|
+
DEFAULT_VALUE
|
43
|
+
end
|
42
44
|
end
|
43
45
|
|
44
46
|
private
|
@@ -51,7 +53,9 @@ module SecureHeaders
|
|
51
53
|
def build_value
|
52
54
|
directives.map do |directive_name|
|
53
55
|
case DIRECTIVE_VALUE_TYPES[directive_name]
|
54
|
-
when :source_list,
|
56
|
+
when :source_list,
|
57
|
+
:require_sri_for_list, # require_sri is a simple set of strings that don't need to deal with symbol casing
|
58
|
+
:require_trusted_types_for_list
|
55
59
|
build_source_list_directive(directive_name)
|
56
60
|
when :boolean
|
57
61
|
symbol_to_hyphen_case(directive_name) if @config.directive_value(directive_name)
|
@@ -155,9 +159,10 @@ module SecureHeaders
|
|
155
159
|
wild_sources = sources.select { |source| source =~ STAR_REGEXP }
|
156
160
|
|
157
161
|
if wild_sources.any?
|
162
|
+
schemes = sources.map { |source| [source, URI(source).scheme] }.to_h
|
158
163
|
sources.reject do |source|
|
159
164
|
!wild_sources.include?(source) &&
|
160
|
-
wild_sources.any? { |pattern| File.fnmatch(pattern, source) }
|
165
|
+
wild_sources.any? { |pattern| schemes[pattern] == schemes[source] && File.fnmatch(pattern, source) }
|
161
166
|
end
|
162
167
|
else
|
163
168
|
sources
|
@@ -35,6 +35,7 @@ module SecureHeaders
|
|
35
35
|
@report_only = nil
|
36
36
|
@report_uri = nil
|
37
37
|
@require_sri_for = nil
|
38
|
+
@require_trusted_types_for = nil
|
38
39
|
@sandbox = nil
|
39
40
|
@script_nonce = nil
|
40
41
|
@script_src = nil
|
@@ -44,6 +45,7 @@ module SecureHeaders
|
|
44
45
|
@style_src = nil
|
45
46
|
@style_src_elem = nil
|
46
47
|
@style_src_attr = nil
|
48
|
+
@trusted_types = nil
|
47
49
|
@worker_src = nil
|
48
50
|
@upgrade_insecure_requests = nil
|
49
51
|
@disable_nonce_backwards_compatibility = nil
|
@@ -98,7 +98,19 @@ module SecureHeaders
|
|
98
98
|
STYLE_SRC_ATTR
|
99
99
|
].flatten.freeze
|
100
100
|
|
101
|
-
|
101
|
+
# Experimental directives - these vary greatly in support
|
102
|
+
# See MDN for details.
|
103
|
+
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/trusted-types
|
104
|
+
TRUSTED_TYPES = :trusted_types
|
105
|
+
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/require-trusted-types-for
|
106
|
+
REQUIRE_TRUSTED_TYPES_FOR = :require_trusted_types_for
|
107
|
+
|
108
|
+
DIRECTIVES_EXPERIMENTAL = [
|
109
|
+
TRUSTED_TYPES,
|
110
|
+
REQUIRE_TRUSTED_TYPES_FOR,
|
111
|
+
].flatten.freeze
|
112
|
+
|
113
|
+
ALL_DIRECTIVES = (DIRECTIVES_1_0 + DIRECTIVES_2_0 + DIRECTIVES_3_0 + DIRECTIVES_EXPERIMENTAL).uniq.sort
|
102
114
|
|
103
115
|
# Think of default-src and report-uri as the beginning and end respectively,
|
104
116
|
# everything else is in between.
|
@@ -121,6 +133,7 @@ module SecureHeaders
|
|
121
133
|
OBJECT_SRC => :source_list,
|
122
134
|
PLUGIN_TYPES => :media_type_list,
|
123
135
|
REQUIRE_SRI_FOR => :require_sri_for_list,
|
136
|
+
REQUIRE_TRUSTED_TYPES_FOR => :require_trusted_types_for_list,
|
124
137
|
REPORT_URI => :source_list,
|
125
138
|
PREFETCH_SRC => :source_list,
|
126
139
|
SANDBOX => :sandbox_list,
|
@@ -130,6 +143,7 @@ module SecureHeaders
|
|
130
143
|
STYLE_SRC => :source_list,
|
131
144
|
STYLE_SRC_ELEM => :source_list,
|
132
145
|
STYLE_SRC_ATTR => :source_list,
|
146
|
+
TRUSTED_TYPES => :source_list,
|
133
147
|
WORKER_SRC => :source_list,
|
134
148
|
UPGRADE_INSECURE_REQUESTS => :boolean,
|
135
149
|
}.freeze
|
@@ -175,6 +189,7 @@ module SecureHeaders
|
|
175
189
|
].freeze
|
176
190
|
|
177
191
|
REQUIRE_SRI_FOR_VALUES = Set.new(%w(script style))
|
192
|
+
REQUIRE_TRUSTED_TYPES_FOR_VALUES = Set.new(%w(script))
|
178
193
|
|
179
194
|
module ClassMethods
|
180
195
|
# Public: generate a header name, value array that is user-agent-aware.
|
@@ -270,7 +285,8 @@ module SecureHeaders
|
|
270
285
|
source_list?(directive) ||
|
271
286
|
sandbox_list?(directive) ||
|
272
287
|
media_type_list?(directive) ||
|
273
|
-
require_sri_for_list?(directive)
|
288
|
+
require_sri_for_list?(directive) ||
|
289
|
+
require_trusted_types_for_list?(directive)
|
274
290
|
end
|
275
291
|
|
276
292
|
# For each directive in additions that does not exist in the original config,
|
@@ -278,11 +294,12 @@ module SecureHeaders
|
|
278
294
|
def populate_fetch_source_with_default!(original, additions)
|
279
295
|
# in case we would be appending to an empty directive, fill it with the default-src value
|
280
296
|
additions.each_key do |directive|
|
281
|
-
directive =
|
282
|
-
directive.to_s.
|
283
|
-
|
284
|
-
|
285
|
-
|
297
|
+
directive =
|
298
|
+
if directive.to_s.end_with?("_nonce")
|
299
|
+
directive.to_s.gsub(/_nonce/, "_src").to_sym
|
300
|
+
else
|
301
|
+
directive
|
302
|
+
end
|
286
303
|
# Don't set a default if directive has an existing value
|
287
304
|
next if original[directive]
|
288
305
|
if FETCH_SOURCES.include?(directive)
|
@@ -307,6 +324,10 @@ module SecureHeaders
|
|
307
324
|
DIRECTIVE_VALUE_TYPES[directive] == :require_sri_for_list
|
308
325
|
end
|
309
326
|
|
327
|
+
def require_trusted_types_for_list?(directive)
|
328
|
+
DIRECTIVE_VALUE_TYPES[directive] == :require_trusted_types_for_list
|
329
|
+
end
|
330
|
+
|
310
331
|
# Private: Validates that the configuration has a valid type, or that it is a valid
|
311
332
|
# source expression.
|
312
333
|
def validate_directive!(directive, value)
|
@@ -324,6 +345,8 @@ module SecureHeaders
|
|
324
345
|
validate_media_type_expression!(directive, value)
|
325
346
|
when :require_sri_for_list
|
326
347
|
validate_require_sri_source_expression!(directive, value)
|
348
|
+
when :require_trusted_types_for_list
|
349
|
+
validate_require_trusted_types_for_source_expression!(directive, value)
|
327
350
|
else
|
328
351
|
raise ContentSecurityPolicyConfigError.new("Unknown directive #{directive}")
|
329
352
|
end
|
@@ -368,6 +391,16 @@ module SecureHeaders
|
|
368
391
|
end
|
369
392
|
end
|
370
393
|
|
394
|
+
# Private: validates that a require trusted types for expression:
|
395
|
+
# 1. is an array of strings
|
396
|
+
# 2. is a subset of ["script"]
|
397
|
+
def validate_require_trusted_types_for_source_expression!(directive, require_trusted_types_for_expression)
|
398
|
+
ensure_array_of_strings!(directive, require_trusted_types_for_expression)
|
399
|
+
unless require_trusted_types_for_expression.to_set.subset?(REQUIRE_TRUSTED_TYPES_FOR_VALUES)
|
400
|
+
raise ContentSecurityPolicyConfigError.new(%(require-trusted-types-for for must be a subset of #{REQUIRE_TRUSTED_TYPES_FOR_VALUES.to_a} but was #{require_trusted_types_for_expression}))
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
371
404
|
# Private: validates that a source expression:
|
372
405
|
# 1. is an array of strings
|
373
406
|
# 2. does not contain any deprecated, now invalid values (inline, eval, self, none)
|
@@ -147,12 +147,13 @@ module SecureHeaders
|
|
147
147
|
|
148
148
|
def nonced_tag(type, content_or_options, block)
|
149
149
|
options = {}
|
150
|
-
content =
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
150
|
+
content =
|
151
|
+
if block
|
152
|
+
options = content_or_options
|
153
|
+
capture(&block)
|
154
|
+
else
|
155
|
+
content_or_options.html_safe # :'(
|
156
|
+
end
|
156
157
|
content_tag type, content, options.merge(nonce: _content_security_policy_nonce(type))
|
157
158
|
end
|
158
159
|
|
data/lib/tasks/tasks.rake
CHANGED
@@ -20,10 +20,11 @@ namespace :secure_headers do
|
|
20
20
|
(is_erb?(filename) && inline_script =~ /<%.*%>/)
|
21
21
|
end
|
22
22
|
|
23
|
-
def find_inline_content(filename, regex, hashes)
|
23
|
+
def find_inline_content(filename, regex, hashes, strip_trailing_whitespace)
|
24
24
|
file = File.read(filename)
|
25
25
|
file.scan(regex) do # TODO don't use gsub
|
26
26
|
inline_script = Regexp.last_match.captures.last
|
27
|
+
inline_script.gsub!(/(\r?\n)[\t ]+\z/, '\1') if strip_trailing_whitespace
|
27
28
|
if dynamic_content?(filename, inline_script)
|
28
29
|
puts "Looks like there's some dynamic content inside of a tag :-/"
|
29
30
|
puts "That pretty much means the hash value will never match."
|
@@ -38,9 +39,8 @@ namespace :secure_headers do
|
|
38
39
|
def generate_inline_script_hashes(filename)
|
39
40
|
hashes = []
|
40
41
|
|
41
|
-
|
42
|
-
|
43
|
-
end
|
42
|
+
find_inline_content(filename, INLINE_SCRIPT_REGEX, hashes, false)
|
43
|
+
find_inline_content(filename, INLINE_HASH_SCRIPT_HELPER_REGEX, hashes, true)
|
44
44
|
|
45
45
|
hashes
|
46
46
|
end
|
@@ -48,9 +48,8 @@ namespace :secure_headers do
|
|
48
48
|
def generate_inline_style_hashes(filename)
|
49
49
|
hashes = []
|
50
50
|
|
51
|
-
|
52
|
-
|
53
|
-
end
|
51
|
+
find_inline_content(filename, INLINE_STYLE_REGEX, hashes, false)
|
52
|
+
find_inline_content(filename, INLINE_HASH_STYLE_HELPER_REGEX, hashes, true)
|
54
53
|
|
55
54
|
hashes
|
56
55
|
end
|
@@ -106,6 +106,11 @@ module SecureHeaders
|
|
106
106
|
expect(csp.value).to eq("default-src example.org")
|
107
107
|
end
|
108
108
|
|
109
|
+
it "does not deduplicate non-matching schema source expressions" do
|
110
|
+
csp = ContentSecurityPolicy.new(default_src: %w(*.example.org wss://example.example.org))
|
111
|
+
expect(csp.value).to eq("default-src *.example.org wss://example.example.org")
|
112
|
+
end
|
113
|
+
|
109
114
|
it "creates maximally strict sandbox policy when passed no sandbox token values" do
|
110
115
|
csp = ContentSecurityPolicy.new(default_src: %w(example.org), sandbox: [])
|
111
116
|
expect(csp.value).to eq("default-src example.org; sandbox")
|
@@ -141,6 +146,11 @@ module SecureHeaders
|
|
141
146
|
expect(csp.value).to eq("default-src 'self'; require-sri-for script style")
|
142
147
|
end
|
143
148
|
|
149
|
+
it "allows style as a require-trusted-types-for source" do
|
150
|
+
csp = ContentSecurityPolicy.new(default_src: %w('self'), require_trusted_types_for: %w(script))
|
151
|
+
expect(csp.value).to eq("default-src 'self'; require-trusted-types-for script")
|
152
|
+
end
|
153
|
+
|
144
154
|
it "includes prefetch-src" do
|
145
155
|
csp = ContentSecurityPolicy.new(default_src: %w('self'), prefetch_src: %w(foo.com))
|
146
156
|
expect(csp.value).to eq("default-src 'self'; prefetch-src foo.com")
|
@@ -180,6 +190,21 @@ module SecureHeaders
|
|
180
190
|
csp = ContentSecurityPolicy.new({style_src: %w('self'), style_src_attr: %w('self')})
|
181
191
|
expect(csp.value).to eq("style-src 'self'; style-src-attr 'self'")
|
182
192
|
end
|
193
|
+
|
194
|
+
it "supports trusted-types directive" do
|
195
|
+
csp = ContentSecurityPolicy.new({trusted_types: %w(blahblahpolicy)})
|
196
|
+
expect(csp.value).to eq("trusted-types blahblahpolicy")
|
197
|
+
end
|
198
|
+
|
199
|
+
it "supports trusted-types directive with 'none'" do
|
200
|
+
csp = ContentSecurityPolicy.new({trusted_types: %w(none)})
|
201
|
+
expect(csp.value).to eq("trusted-types none")
|
202
|
+
end
|
203
|
+
|
204
|
+
it "allows duplicate policy names in trusted-types directive" do
|
205
|
+
csp = ContentSecurityPolicy.new({trusted_types: %w(blahblahpolicy 'allow-duplicates')})
|
206
|
+
expect(csp.value).to eq("trusted-types blahblahpolicy 'allow-duplicates'")
|
207
|
+
end
|
183
208
|
end
|
184
209
|
end
|
185
210
|
end
|
@@ -45,6 +45,7 @@ module SecureHeaders
|
|
45
45
|
plugin_types: %w(application/x-shockwave-flash),
|
46
46
|
prefetch_src: %w(fetch.com),
|
47
47
|
require_sri_for: %w(script style),
|
48
|
+
require_trusted_types_for: %w(script),
|
48
49
|
script_src: %w('self'),
|
49
50
|
style_src: %w('unsafe-inline'),
|
50
51
|
upgrade_insecure_requests: true, # see https://www.w3.org/TR/upgrade-insecure-requests/
|
@@ -53,6 +54,7 @@ module SecureHeaders
|
|
53
54
|
script_src_attr: %w(example.com),
|
54
55
|
style_src_elem: %w(example.com),
|
55
56
|
style_src_attr: %w(example.com),
|
57
|
+
trusted_types: %w(abcpolicy),
|
56
58
|
|
57
59
|
report_uri: %w(https://example.com/uri-directive),
|
58
60
|
}
|
@@ -120,6 +122,12 @@ module SecureHeaders
|
|
120
122
|
end.to raise_error(ContentSecurityPolicyConfigError)
|
121
123
|
end
|
122
124
|
|
125
|
+
it "rejects style for trusted types" do
|
126
|
+
expect do
|
127
|
+
ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_opts.merge(style_src: %w('self'), require_trusted_types_for: %w(script style), trusted_types: %w(abcpolicy))))
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
123
131
|
# this is mostly to ensure people don't use the antiquated shorthands common in other configs
|
124
132
|
it "performs light validation on source lists" do
|
125
133
|
expect do
|
@@ -6,7 +6,7 @@ class Message < ERB
|
|
6
6
|
include SecureHeaders::ViewHelpers
|
7
7
|
|
8
8
|
def self.template
|
9
|
-
<<-TEMPLATE
|
9
|
+
<<-TEMPLATE
|
10
10
|
<% hashed_javascript_tag(raise_error_on_unrecognized_hash = true) do %>
|
11
11
|
console.log(1)
|
12
12
|
<% end %>
|
@@ -62,9 +62,10 @@ TEMPLATE
|
|
62
62
|
end
|
63
63
|
|
64
64
|
def content_tag(type, content = nil, options = nil, &block)
|
65
|
-
content =
|
66
|
-
|
67
|
-
|
65
|
+
content =
|
66
|
+
if block_given?
|
67
|
+
capture(block)
|
68
|
+
end
|
68
69
|
|
69
70
|
if options.is_a?(Hash)
|
70
71
|
options = options.map { |k, v| " #{k}=#{v}" }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: secure_headers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 6.
|
4
|
+
version: 6.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Neil Matatall
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-08-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -34,7 +34,7 @@ files:
|
|
34
34
|
- ".github/ISSUE_TEMPLATE.md"
|
35
35
|
- ".github/PULL_REQUEST_TEMPLATE.md"
|
36
36
|
- ".github/workflows/build.yml"
|
37
|
-
- ".github/workflows/
|
37
|
+
- ".github/workflows/github-release.yml"
|
38
38
|
- ".gitignore"
|
39
39
|
- ".rspec"
|
40
40
|
- ".rubocop.yml"
|
@@ -116,7 +116,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
116
116
|
- !ruby/object:Gem::Version
|
117
117
|
version: '0'
|
118
118
|
requirements: []
|
119
|
-
rubygems_version: 3.
|
119
|
+
rubygems_version: 3.2.9
|
120
120
|
signing_key:
|
121
121
|
specification_version: 4
|
122
122
|
summary: Add easily configured security headers to responses including content-security-policy,
|
data/.github/workflows/sync.yml
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
# This workflow ensures the "master" branch is always up-to-date with the
|
2
|
-
# "main" branch (our default one)
|
3
|
-
name: sync_main_branch
|
4
|
-
on:
|
5
|
-
push:
|
6
|
-
branches: [ main ]
|
7
|
-
jobs:
|
8
|
-
catch_up:
|
9
|
-
runs-on: ubuntu-latest
|
10
|
-
steps:
|
11
|
-
- name: Check out the repository
|
12
|
-
uses: actions/checkout@v2
|
13
|
-
with:
|
14
|
-
fetch-depth: 0
|
15
|
-
- name: Merge development into master, then push it
|
16
|
-
run: |
|
17
|
-
git pull
|
18
|
-
git checkout master
|
19
|
-
git merge main
|
20
|
-
git push
|