secure_headers 6.3.0 → 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 +24 -0
- data/.github/workflows/github-release.yml +28 -0
- data/.rubocop.yml +1 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +21 -1
- data/Gemfile +2 -1
- data/README.md +51 -7
- data/docs/named_overrides_and_appends.md +3 -1
- data/docs/per_action_configuration.md +2 -4
- data/docs/upgrading-to-6-0.md +3 -3
- data/lib/secure_headers/configuration.rb +17 -5
- data/lib/secure_headers/headers/content_security_policy.rb +21 -16
- data/lib/secure_headers/headers/content_security_policy_config.rb +6 -0
- data/lib/secure_headers/headers/policy_management.rb +53 -8
- data/lib/secure_headers/version.rb +1 -1
- data/lib/secure_headers/view_helper.rb +11 -10
- data/lib/tasks/tasks.rake +6 -7
- data/spec/lib/secure_headers/configuration_spec.rb +54 -0
- data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +45 -0
- data/spec/lib/secure_headers/headers/policy_management_spec.rb +12 -0
- data/spec/lib/secure_headers/view_helpers_spec.rb +5 -4
- data/spec/spec_helper.rb +10 -0
- metadata +5 -4
- data/.travis.yml +0 -28
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
|
@@ -0,0 +1,24 @@
|
|
1
|
+
name: Build + Test
|
2
|
+
on: [pull_request]
|
3
|
+
|
4
|
+
jobs:
|
5
|
+
build:
|
6
|
+
name: Build + Test
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
strategy:
|
9
|
+
matrix:
|
10
|
+
ruby: [ '2.5', '2.6', '2.7', '3.0', '3.1' ]
|
11
|
+
|
12
|
+
steps:
|
13
|
+
- uses: actions/checkout@v2
|
14
|
+
- name: Set up Ruby ${{ matrix.ruby }}
|
15
|
+
uses: ruby/setup-ruby@v1
|
16
|
+
with:
|
17
|
+
ruby-version: ${{ matrix.ruby }}
|
18
|
+
- name: Build and test with Rake
|
19
|
+
run: |
|
20
|
+
gem install bundler
|
21
|
+
bundle install --jobs 4 --retry 3 --without guard
|
22
|
+
bundle exec rspec spec
|
23
|
+
bundle exec rubocop
|
24
|
+
|
@@ -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/.rubocop.yml
CHANGED
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.6.
|
1
|
+
2.6.6
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,23 @@
|
|
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
|
+
|
13
|
+
## 6.3.2
|
14
|
+
|
15
|
+
Add support for style-src-attr, style-src-elem, script-src-attr, and script-src-elem directives (@ggalmazor)
|
16
|
+
|
17
|
+
## 6.3.1
|
18
|
+
|
19
|
+
Fixes deprecation warnings when running under ruby 2.7
|
20
|
+
|
1
21
|
## 6.3.0
|
2
22
|
|
3
23
|
Fixes newline injection issue
|
@@ -372,7 +392,7 @@ Adds `upgrade-insecure-requests` support for requests from Firefox and Chrome (a
|
|
372
392
|
|
373
393
|
## 3.0.0
|
374
394
|
|
375
|
-
secure_headers 3.0.0 is a near-complete, not-entirely-backward-compatible rewrite. Please see the [upgrade guide](https://github.com/twitter/secureheaders/blob/
|
395
|
+
secure_headers 3.0.0 is a near-complete, not-entirely-backward-compatible rewrite. Please see the [upgrade guide](https://github.com/twitter/secureheaders/blob/main/docs/upgrading-to-3-0.md) for an in-depth explanation of the changes and the suggested upgrade path.
|
376
396
|
|
377
397
|
## 2.5.1 - 2016-02-16 18:11:11 UTC - Remove noisy deprecation warning
|
378
398
|
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
# Secure Headers
|
1
|
+
# Secure Headers 
|
2
2
|
|
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:),
|
@@ -75,7 +75,11 @@ SecureHeaders::Configuration.default do |config|
|
|
75
75
|
sandbox: true, # true and [] will set a maximally restrictive setting
|
76
76
|
plugin_types: %w(application/x-shockwave-flash),
|
77
77
|
script_src: %w('self'),
|
78
|
+
script_src_elem: %w('self'),
|
79
|
+
script_src_attr: %w('self'),
|
78
80
|
style_src: %w('unsafe-inline'),
|
81
|
+
style_src_elem: %w('unsafe-inline'),
|
82
|
+
style_src_attr: %w('unsafe-inline'),
|
79
83
|
worker_src: %w('self'),
|
80
84
|
upgrade_insecure_requests: true, # see https://www.w3.org/TR/upgrade-insecure-requests/
|
81
85
|
report_uri: %w(https://report-uri.io/example-csp)
|
@@ -117,16 +121,56 @@ SecureHeaders::Configuration.override(:api) do |config|
|
|
117
121
|
end
|
118
122
|
```
|
119
123
|
|
120
|
-
However, I would consider these headers anyways depending on your load and bandwidth requirements.
|
124
|
+
However, I would consider these headers anyways depending on your load and bandwidth requirements.
|
125
|
+
|
126
|
+
## Acknowledgements
|
127
|
+
|
128
|
+
This project originated within the Security team at Twitter. An archived fork from the point of transition is here: https://github.com/twitter-archive/secure_headers.
|
129
|
+
|
130
|
+
Contributors include:
|
131
|
+
* Neil Matatall @oreoshake
|
132
|
+
* Chris Aniszczyk
|
133
|
+
* Artur Dryomov
|
134
|
+
* Bjørn Mæland
|
135
|
+
* Arthur Chiu
|
136
|
+
* Jonathan Viney
|
137
|
+
* Jeffrey Horn
|
138
|
+
* David Collazo
|
139
|
+
* Brendon Murphy
|
140
|
+
* William Makley
|
141
|
+
* Reed Loden
|
142
|
+
* Noah Kantrowitz
|
143
|
+
* Wyatt Anderson
|
144
|
+
* Salimane Adjao Moustapha
|
145
|
+
* Francois Chagnon
|
146
|
+
* Jeff Hodges
|
147
|
+
* Ian Melven
|
148
|
+
* Darío Javier Cravero
|
149
|
+
* Logan Hasson
|
150
|
+
* Raul E Rangel
|
151
|
+
* Steve Agalloco
|
152
|
+
* Nate Collings
|
153
|
+
* Josh Kalderimis
|
154
|
+
* Alex Kwiatkowski
|
155
|
+
* Julich Mera
|
156
|
+
* Jesse Storimer
|
157
|
+
* Tom Daniels
|
158
|
+
* Kolja Dummann
|
159
|
+
* Jean-Philippe Doyle
|
160
|
+
* Blake Hitchcock
|
161
|
+
* vanderhoorn
|
162
|
+
* orthographic-pedant
|
163
|
+
* Narsimham Chelluri
|
164
|
+
|
165
|
+
If you've made a contribution and see your name missing from the list, make a PR and add it!
|
121
166
|
|
122
167
|
## Similar libraries
|
123
168
|
|
124
169
|
* Rack [rack-secure_headers](https://github.com/frodsan/rack-secure_headers)
|
125
170
|
* Node.js (express) [helmet](https://github.com/helmetjs/helmet) and [hood](https://github.com/seanmonstar/hood)
|
126
171
|
* Node.js (hapi) [blankie](https://github.com/nlf/blankie)
|
127
|
-
* J2EE Servlet >= 3.0 [headlines](https://github.com/sourceclear/headlines)
|
128
172
|
* ASP.NET - [NWebsec](https://github.com/NWebsec/NWebsec/wiki)
|
129
|
-
* 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)
|
130
174
|
* Go - [secureheader](https://github.com/kr/secureheader)
|
131
175
|
* Elixir [secure_headers](https://github.com/anotherhale/secure_headers)
|
132
176
|
* Dropwizard [dropwizard-web-security](https://github.com/palantir/dropwizard-web-security)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
## Named Appends
|
2
2
|
|
3
|
-
Named Appends are blocks of code that can be reused and composed during requests. e.g. If a certain partial is rendered conditionally, and the csp needs to be adjusted for that partial, you can create a named append for that situation. The value returned by the block will be passed into `append_content_security_policy_directives`. The current request object is passed as an argument to the block for even more flexibility.
|
3
|
+
Named Appends are blocks of code that can be reused and composed during requests. e.g. If a certain partial is rendered conditionally, and the csp needs to be adjusted for that partial, you can create a named append for that situation. The value returned by the block will be passed into `append_content_security_policy_directives`. The current request object is passed as an argument to the block for even more flexibility. Reusing a configuration name is not allowed and will throw an exception.
|
4
4
|
|
5
5
|
```ruby
|
6
6
|
def show
|
@@ -91,6 +91,8 @@ class MyController < ApplicationController
|
|
91
91
|
end
|
92
92
|
```
|
93
93
|
|
94
|
+
Reusing a configuration name is not allowed and will throw an exception.
|
95
|
+
|
94
96
|
By default, a no-op configuration is provided. No headers will be set when this default override is used.
|
95
97
|
|
96
98
|
```ruby
|
@@ -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
|
|
data/docs/upgrading-to-6-0.md
CHANGED
@@ -29,7 +29,7 @@ Prior to 6.0.0, the response would NOT include a `X-Frame-Options` header since
|
|
29
29
|
|
30
30
|
## `ContentSecurityPolicyConfig#merge` and `ContentSecurityPolicyReportOnlyConfig#merge` work more like `Hash#merge`
|
31
31
|
|
32
|
-
These classes are typically not directly instantiated by users of SecureHeaders. But, if you access `config.csp` you end up accessing one of these objects. Prior to 6.0.0, `#merge` worked more like `#append` in that it would combine policies (i.e. if both policies contained the same key the values would be combined rather than overwritten). This was not consistent with `#merge!`, which worked more like
|
32
|
+
These classes are typically not directly instantiated by users of SecureHeaders. But, if you access `config.csp` you end up accessing one of these objects. Prior to 6.0.0, `#merge` worked more like `#append` in that it would combine policies (i.e. if both policies contained the same key the values would be combined rather than overwritten). This was not consistent with `#merge!`, which worked more like Ruby's `Hash#merge!` (overwriting duplicate keys). As of 6.0.0, `#merge` works the same as `#merge!`, but returns a new object instead of mutating `self`.
|
33
33
|
|
34
34
|
## `Configuration#get` has been removed
|
35
35
|
|
@@ -39,7 +39,7 @@ This method is not typically directly called by users of SecureHeaders. Given th
|
|
39
39
|
|
40
40
|
Prior to 6.0.0 SecureHeaders pre-built and cached the headers that corresponded to the default configuration. The same was also done for named overrides. However, now that named overrides are applied dynamically, those can no longer be cached. As a result, caching has been removed in the name of simplicity. Some micro-benchmarks indicate this shouldn't be a performance problem and will help to eliminate a class of bugs entirely.
|
41
41
|
|
42
|
-
##
|
42
|
+
## Calling the default configuration more than once will result in an Exception
|
43
43
|
|
44
44
|
Prior to 6.0.0 you could conceivably, though unlikely, have `Configure#default` called more than once. Because configurations are dynamic, configuring more than once could result in unexpected behavior. So, as of 6.0.0 we raise `AlreadyConfiguredError` if the default configuration is setup more than once.
|
45
45
|
|
@@ -47,4 +47,4 @@ Prior to 6.0.0 you could conceivably, though unlikely, have `Configure#default`
|
|
47
47
|
|
48
48
|
The policy configured is the policy that is delivered in terms of which directives are sent. We still dedup, strip schemes, and look for other optimizations but we will not e.g. conditionally send `frame-src` / `child-src` or apply `nonce`s / `unsafe-inline`.
|
49
49
|
|
50
|
-
The primary reason for these per-browser customization was to reduce console warnings. This has lead to many bugs and results
|
50
|
+
The primary reason for these per-browser customization was to reduce console warnings. This has lead to many bugs and results in confusing behavior. Also, console logs are incredibly noisy today and increasingly warn you about perfectly valid things (like sending `X-Frame-Options` and `frame-ancestors` together).
|
@@ -43,6 +43,9 @@ module SecureHeaders
|
|
43
43
|
def override(name, &block)
|
44
44
|
@overrides ||= {}
|
45
45
|
raise "Provide a configuration block" unless block_given?
|
46
|
+
if named_append_or_override_exists?(name)
|
47
|
+
raise AlreadyConfiguredError, "Configuration already exists"
|
48
|
+
end
|
46
49
|
@overrides[name] = block
|
47
50
|
end
|
48
51
|
|
@@ -59,6 +62,9 @@ module SecureHeaders
|
|
59
62
|
def named_append(name, &block)
|
60
63
|
@appends ||= {}
|
61
64
|
raise "Provide a configuration block" unless block_given?
|
65
|
+
if named_append_or_override_exists?(name)
|
66
|
+
raise AlreadyConfiguredError, "Configuration already exists"
|
67
|
+
end
|
62
68
|
@appends[name] = block
|
63
69
|
end
|
64
70
|
|
@@ -68,16 +74,22 @@ module SecureHeaders
|
|
68
74
|
|
69
75
|
private
|
70
76
|
|
77
|
+
def named_append_or_override_exists?(name)
|
78
|
+
(defined?(@appends) && @appends.key?(name)) ||
|
79
|
+
(defined?(@overrides) && @overrides.key?(name))
|
80
|
+
end
|
81
|
+
|
71
82
|
# Public: perform a basic deep dup. The shallow copy provided by dup/clone
|
72
83
|
# can lead to modifying parent objects.
|
73
84
|
def deep_copy(config)
|
74
85
|
return unless config
|
75
86
|
config.each_with_object({}) do |(key, value), hash|
|
76
|
-
hash[key] =
|
77
|
-
value.
|
78
|
-
|
79
|
-
|
80
|
-
|
87
|
+
hash[key] =
|
88
|
+
if value.is_a?(Array)
|
89
|
+
value.dup
|
90
|
+
else
|
91
|
+
value
|
92
|
+
end
|
81
93
|
end
|
82
94
|
end
|
83
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,11 +35,17 @@ 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
|
42
|
+
@script_src_elem = nil
|
43
|
+
@script_src_attr = nil
|
41
44
|
@style_nonce = nil
|
42
45
|
@style_src = nil
|
46
|
+
@style_src_elem = nil
|
47
|
+
@style_src_attr = nil
|
48
|
+
@trusted_types = nil
|
43
49
|
@worker_src = nil
|
44
50
|
@upgrade_insecure_requests = nil
|
45
51
|
@disable_nonce_backwards_compatibility = nil
|
@@ -78,6 +78,10 @@ module SecureHeaders
|
|
78
78
|
REQUIRE_SRI_FOR = :require_sri_for
|
79
79
|
UPGRADE_INSECURE_REQUESTS = :upgrade_insecure_requests
|
80
80
|
WORKER_SRC = :worker_src
|
81
|
+
SCRIPT_SRC_ELEM = :script_src_elem
|
82
|
+
SCRIPT_SRC_ATTR = :script_src_attr
|
83
|
+
STYLE_SRC_ELEM = :style_src_elem
|
84
|
+
STYLE_SRC_ATTR = :style_src_attr
|
81
85
|
|
82
86
|
DIRECTIVES_3_0 = [
|
83
87
|
DIRECTIVES_2_0,
|
@@ -87,10 +91,26 @@ module SecureHeaders
|
|
87
91
|
PREFETCH_SRC,
|
88
92
|
REQUIRE_SRI_FOR,
|
89
93
|
WORKER_SRC,
|
90
|
-
UPGRADE_INSECURE_REQUESTS
|
94
|
+
UPGRADE_INSECURE_REQUESTS,
|
95
|
+
SCRIPT_SRC_ELEM,
|
96
|
+
SCRIPT_SRC_ATTR,
|
97
|
+
STYLE_SRC_ELEM,
|
98
|
+
STYLE_SRC_ATTR
|
91
99
|
].flatten.freeze
|
92
100
|
|
93
|
-
|
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
|
94
114
|
|
95
115
|
# Think of default-src and report-uri as the beginning and end respectively,
|
96
116
|
# everything else is in between.
|
@@ -113,11 +133,17 @@ module SecureHeaders
|
|
113
133
|
OBJECT_SRC => :source_list,
|
114
134
|
PLUGIN_TYPES => :media_type_list,
|
115
135
|
REQUIRE_SRI_FOR => :require_sri_for_list,
|
136
|
+
REQUIRE_TRUSTED_TYPES_FOR => :require_trusted_types_for_list,
|
116
137
|
REPORT_URI => :source_list,
|
117
138
|
PREFETCH_SRC => :source_list,
|
118
139
|
SANDBOX => :sandbox_list,
|
119
140
|
SCRIPT_SRC => :source_list,
|
141
|
+
SCRIPT_SRC_ELEM => :source_list,
|
142
|
+
SCRIPT_SRC_ATTR => :source_list,
|
120
143
|
STYLE_SRC => :source_list,
|
144
|
+
STYLE_SRC_ELEM => :source_list,
|
145
|
+
STYLE_SRC_ATTR => :source_list,
|
146
|
+
TRUSTED_TYPES => :source_list,
|
121
147
|
WORKER_SRC => :source_list,
|
122
148
|
UPGRADE_INSECURE_REQUESTS => :boolean,
|
123
149
|
}.freeze
|
@@ -163,6 +189,7 @@ module SecureHeaders
|
|
163
189
|
].freeze
|
164
190
|
|
165
191
|
REQUIRE_SRI_FOR_VALUES = Set.new(%w(script style))
|
192
|
+
REQUIRE_TRUSTED_TYPES_FOR_VALUES = Set.new(%w(script))
|
166
193
|
|
167
194
|
module ClassMethods
|
168
195
|
# Public: generate a header name, value array that is user-agent-aware.
|
@@ -258,7 +285,8 @@ module SecureHeaders
|
|
258
285
|
source_list?(directive) ||
|
259
286
|
sandbox_list?(directive) ||
|
260
287
|
media_type_list?(directive) ||
|
261
|
-
require_sri_for_list?(directive)
|
288
|
+
require_sri_for_list?(directive) ||
|
289
|
+
require_trusted_types_for_list?(directive)
|
262
290
|
end
|
263
291
|
|
264
292
|
# For each directive in additions that does not exist in the original config,
|
@@ -266,11 +294,12 @@ module SecureHeaders
|
|
266
294
|
def populate_fetch_source_with_default!(original, additions)
|
267
295
|
# in case we would be appending to an empty directive, fill it with the default-src value
|
268
296
|
additions.each_key do |directive|
|
269
|
-
directive =
|
270
|
-
directive.to_s.
|
271
|
-
|
272
|
-
|
273
|
-
|
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
|
274
303
|
# Don't set a default if directive has an existing value
|
275
304
|
next if original[directive]
|
276
305
|
if FETCH_SOURCES.include?(directive)
|
@@ -295,6 +324,10 @@ module SecureHeaders
|
|
295
324
|
DIRECTIVE_VALUE_TYPES[directive] == :require_sri_for_list
|
296
325
|
end
|
297
326
|
|
327
|
+
def require_trusted_types_for_list?(directive)
|
328
|
+
DIRECTIVE_VALUE_TYPES[directive] == :require_trusted_types_for_list
|
329
|
+
end
|
330
|
+
|
298
331
|
# Private: Validates that the configuration has a valid type, or that it is a valid
|
299
332
|
# source expression.
|
300
333
|
def validate_directive!(directive, value)
|
@@ -312,6 +345,8 @@ module SecureHeaders
|
|
312
345
|
validate_media_type_expression!(directive, value)
|
313
346
|
when :require_sri_for_list
|
314
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)
|
315
350
|
else
|
316
351
|
raise ContentSecurityPolicyConfigError.new("Unknown directive #{directive}")
|
317
352
|
end
|
@@ -356,6 +391,16 @@ module SecureHeaders
|
|
356
391
|
end
|
357
392
|
end
|
358
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
|
+
|
359
404
|
# Private: validates that a source expression:
|
360
405
|
# 1. is an array of strings
|
361
406
|
# 2. does not contain any deprecated, now invalid values (inline, eval, self, none)
|
@@ -21,7 +21,7 @@ module SecureHeaders
|
|
21
21
|
def nonced_stylesheet_link_tag(*args, &block)
|
22
22
|
opts = extract_options(args).merge(nonce: _content_security_policy_nonce(:style))
|
23
23
|
|
24
|
-
stylesheet_link_tag(*args, opts, &block)
|
24
|
+
stylesheet_link_tag(*args, **opts, &block)
|
25
25
|
end
|
26
26
|
|
27
27
|
# Public: create a script tag using the content security policy nonce.
|
@@ -39,7 +39,7 @@ module SecureHeaders
|
|
39
39
|
def nonced_javascript_include_tag(*args, &block)
|
40
40
|
opts = extract_options(args).merge(nonce: _content_security_policy_nonce(:script))
|
41
41
|
|
42
|
-
javascript_include_tag(*args, opts, &block)
|
42
|
+
javascript_include_tag(*args, **opts, &block)
|
43
43
|
end
|
44
44
|
|
45
45
|
# Public: create a script Webpacker pack tag using the content security policy nonce.
|
@@ -49,7 +49,7 @@ module SecureHeaders
|
|
49
49
|
def nonced_javascript_pack_tag(*args, &block)
|
50
50
|
opts = extract_options(args).merge(nonce: _content_security_policy_nonce(:script))
|
51
51
|
|
52
|
-
javascript_pack_tag(*args, opts, &block)
|
52
|
+
javascript_pack_tag(*args, **opts, &block)
|
53
53
|
end
|
54
54
|
|
55
55
|
# Public: create a stylesheet Webpacker link tag using the content security policy nonce.
|
@@ -59,7 +59,7 @@ module SecureHeaders
|
|
59
59
|
def nonced_stylesheet_pack_tag(*args, &block)
|
60
60
|
opts = extract_options(args).merge(nonce: _content_security_policy_nonce(:style))
|
61
61
|
|
62
|
-
stylesheet_pack_tag(*args, opts, &block)
|
62
|
+
stylesheet_pack_tag(*args, **opts, &block)
|
63
63
|
end
|
64
64
|
|
65
65
|
# Public: use the content security policy nonce for this request directly.
|
@@ -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
|
@@ -34,6 +34,60 @@ module SecureHeaders
|
|
34
34
|
expect(Configuration.overrides(:test_override)).to_not be_nil
|
35
35
|
end
|
36
36
|
|
37
|
+
describe "#override" do
|
38
|
+
it "raises on configuring an existing override" do
|
39
|
+
set_override = Proc.new {
|
40
|
+
Configuration.override(:test_override) do |config|
|
41
|
+
config.x_frame_options = "DENY"
|
42
|
+
end
|
43
|
+
}
|
44
|
+
|
45
|
+
set_override.call
|
46
|
+
|
47
|
+
expect { set_override.call }
|
48
|
+
.to raise_error(Configuration::AlreadyConfiguredError, "Configuration already exists")
|
49
|
+
end
|
50
|
+
|
51
|
+
it "raises when a named append with the given name exists" do
|
52
|
+
Configuration.named_append(:test_override) do |config|
|
53
|
+
config.x_frame_options = "DENY"
|
54
|
+
end
|
55
|
+
|
56
|
+
expect do
|
57
|
+
Configuration.override(:test_override) do |config|
|
58
|
+
config.x_frame_options = "SAMEORIGIN"
|
59
|
+
end
|
60
|
+
end.to raise_error(Configuration::AlreadyConfiguredError, "Configuration already exists")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "#named_append" do
|
65
|
+
it "raises on configuring an existing append" do
|
66
|
+
set_override = Proc.new {
|
67
|
+
Configuration.named_append(:test_override) do |config|
|
68
|
+
config.x_frame_options = "DENY"
|
69
|
+
end
|
70
|
+
}
|
71
|
+
|
72
|
+
set_override.call
|
73
|
+
|
74
|
+
expect { set_override.call }
|
75
|
+
.to raise_error(Configuration::AlreadyConfiguredError, "Configuration already exists")
|
76
|
+
end
|
77
|
+
|
78
|
+
it "raises when an override with the given name exists" do
|
79
|
+
Configuration.override(:test_override) do |config|
|
80
|
+
config.x_frame_options = "DENY"
|
81
|
+
end
|
82
|
+
|
83
|
+
expect do
|
84
|
+
Configuration.named_append(:test_override) do |config|
|
85
|
+
config.x_frame_options = "SAMEORIGIN"
|
86
|
+
end
|
87
|
+
end.to raise_error(Configuration::AlreadyConfiguredError, "Configuration already exists")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
37
91
|
it "deprecates the secure_cookies configuration" do
|
38
92
|
expect {
|
39
93
|
Configuration.default do |config|
|
@@ -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")
|
@@ -160,6 +170,41 @@ module SecureHeaders
|
|
160
170
|
csp = ContentSecurityPolicy.new({default_src: %w('self'), script_src: [ContentSecurityPolicy::STRICT_DYNAMIC], script_nonce: 123456, disable_nonce_backwards_compatibility: true })
|
161
171
|
expect(csp.value).to eq("default-src 'self'; script-src 'strict-dynamic' 'nonce-123456'")
|
162
172
|
end
|
173
|
+
|
174
|
+
it "supports script-src-elem directive" do
|
175
|
+
csp = ContentSecurityPolicy.new({script_src: %w('self'), script_src_elem: %w('self')})
|
176
|
+
expect(csp.value).to eq("script-src 'self'; script-src-elem 'self'")
|
177
|
+
end
|
178
|
+
|
179
|
+
it "supports script-src-attr directive" do
|
180
|
+
csp = ContentSecurityPolicy.new({script_src: %w('self'), script_src_attr: %w('self')})
|
181
|
+
expect(csp.value).to eq("script-src 'self'; script-src-attr 'self'")
|
182
|
+
end
|
183
|
+
|
184
|
+
it "supports style-src-elem directive" do
|
185
|
+
csp = ContentSecurityPolicy.new({style_src: %w('self'), style_src_elem: %w('self')})
|
186
|
+
expect(csp.value).to eq("style-src 'self'; style-src-elem 'self'")
|
187
|
+
end
|
188
|
+
|
189
|
+
it "supports style-src-attr directive" do
|
190
|
+
csp = ContentSecurityPolicy.new({style_src: %w('self'), style_src_attr: %w('self')})
|
191
|
+
expect(csp.value).to eq("style-src 'self'; style-src-attr 'self'")
|
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
|
163
208
|
end
|
164
209
|
end
|
165
210
|
end
|
@@ -45,10 +45,16 @@ 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/
|
51
52
|
worker_src: %w(worker.com),
|
53
|
+
script_src_elem: %w(example.com),
|
54
|
+
script_src_attr: %w(example.com),
|
55
|
+
style_src_elem: %w(example.com),
|
56
|
+
style_src_attr: %w(example.com),
|
57
|
+
trusted_types: %w(abcpolicy),
|
52
58
|
|
53
59
|
report_uri: %w(https://example.com/uri-directive),
|
54
60
|
}
|
@@ -116,6 +122,12 @@ module SecureHeaders
|
|
116
122
|
end.to raise_error(ContentSecurityPolicyConfigError)
|
117
123
|
end
|
118
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
|
+
|
119
131
|
# this is mostly to ensure people don't use the antiquated shorthands common in other configs
|
120
132
|
it "performs light validation on source lists" do
|
121
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}" }
|
data/spec/spec_helper.rb
CHANGED
@@ -45,10 +45,20 @@ module SecureHeaders
|
|
45
45
|
def clear_default_config
|
46
46
|
remove_instance_variable(:@default_config) if defined?(@default_config)
|
47
47
|
end
|
48
|
+
|
49
|
+
def clear_overrides
|
50
|
+
remove_instance_variable(:@overrides) if defined?(@overrides)
|
51
|
+
end
|
52
|
+
|
53
|
+
def clear_appends
|
54
|
+
remove_instance_variable(:@appends) if defined?(@appends)
|
55
|
+
end
|
48
56
|
end
|
49
57
|
end
|
50
58
|
end
|
51
59
|
|
52
60
|
def reset_config
|
53
61
|
SecureHeaders::Configuration.clear_default_config
|
62
|
+
SecureHeaders::Configuration.clear_overrides
|
63
|
+
SecureHeaders::Configuration.clear_appends
|
54
64
|
end
|
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
|
@@ -33,12 +33,13 @@ extra_rdoc_files: []
|
|
33
33
|
files:
|
34
34
|
- ".github/ISSUE_TEMPLATE.md"
|
35
35
|
- ".github/PULL_REQUEST_TEMPLATE.md"
|
36
|
+
- ".github/workflows/build.yml"
|
37
|
+
- ".github/workflows/github-release.yml"
|
36
38
|
- ".gitignore"
|
37
39
|
- ".rspec"
|
38
40
|
- ".rubocop.yml"
|
39
41
|
- ".ruby-gemset"
|
40
42
|
- ".ruby-version"
|
41
|
-
- ".travis.yml"
|
42
43
|
- CHANGELOG.md
|
43
44
|
- CODE_OF_CONDUCT.md
|
44
45
|
- CONTRIBUTING.md
|
@@ -115,7 +116,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
115
116
|
- !ruby/object:Gem::Version
|
116
117
|
version: '0'
|
117
118
|
requirements: []
|
118
|
-
rubygems_version: 3.
|
119
|
+
rubygems_version: 3.2.9
|
119
120
|
signing_key:
|
120
121
|
specification_version: 4
|
121
122
|
summary: Add easily configured security headers to responses including content-security-policy,
|
data/.travis.yml
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
language: ruby
|
2
|
-
|
3
|
-
rvm:
|
4
|
-
- ruby-head
|
5
|
-
- 2.5
|
6
|
-
- 2.6
|
7
|
-
- 2.7
|
8
|
-
- jruby-head
|
9
|
-
|
10
|
-
env:
|
11
|
-
- SUITE=rspec spec
|
12
|
-
- SUITE=rubocop
|
13
|
-
|
14
|
-
script: bundle exec $SUITE
|
15
|
-
|
16
|
-
matrix:
|
17
|
-
allow_failures:
|
18
|
-
- rvm: jruby-head
|
19
|
-
- rvm: ruby-head
|
20
|
-
|
21
|
-
before_install:
|
22
|
-
- gem update --system
|
23
|
-
- gem --version
|
24
|
-
- gem update bundler
|
25
|
-
bundler_args: --without guard -j 3
|
26
|
-
|
27
|
-
sudo: false
|
28
|
-
cache: bundler
|