secure_headers 6.3.1 → 6.3.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7b136a1c21b128826c37c798c9e20db99b1d5a5c035001ed8289692ed8f0096f
4
- data.tar.gz: d93c60ba6357a9cd8f64c16b53c9f2843753101e1669fb1bbaea56e549d89466
3
+ metadata.gz: 3953b8d0c4ce0a01012d4d6475ad94c60ce4b06b9d93994ef14cc2474ced6177
4
+ data.tar.gz: 3aa96804cacf26d4a275b19e870755313a3afd055b598014749d0338e4f0f4af
5
5
  SHA512:
6
- metadata.gz: 13083c3da3a4f68d445be6012a1fa6e37c052e6ac6bdc457d379d725383142c8698f91c2d1a6fc75f13bfad52298e291131e9828bb6f6a1fc6b8cba9bc3d5892
7
- data.tar.gz: 92214a6b589ba640e504e58f07b051351a7eea7bce87701730101a68a29045fe81c90d79600f00a36ee7bf3e4c5c7c4071ad74b01bab5e40c53751858bc198d7
6
+ metadata.gz: b04fd60ff28519273f29d335b81b44ebfe938d4e97d82d31b69d8596dc84e38c812573248e7b70bb142fecebab357c7f34f9f10934edaf354e3174915220580f
7
+ data.tar.gz: 10748a3ff12365fffe42828fe324e0bfbb3f146635b095a9e8a13b5a57b5bdfe17a5463df3b5009d8591dbc88494c45036a12dd061fc6b8e921c4c99a0d523ef
@@ -7,12 +7,12 @@ jobs:
7
7
  runs-on: ubuntu-latest
8
8
  strategy:
9
9
  matrix:
10
- ruby: [ '2.4', '2.5', '2.6', '2.7' ]
10
+ ruby: [ '2.5', '2.6', '2.7', '3.0' ]
11
11
 
12
12
  steps:
13
13
  - uses: actions/checkout@v2
14
14
  - name: Set up Ruby ${{ matrix.ruby }}
15
- uses: actions/setup-ruby@v1
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.3.4
2
+
3
+ - CSP: Do not deduplicate alternate schema source expressions (@keithamus): https://github.com/github/secure_headers/pull/478
4
+
5
+ ## 6.3.3
6
+
7
+ Fix hash generation for indented helper methods (@rahearn)
8
+
9
+ ## 6.3.2
10
+
11
+ Add support for style-src-attr, style-src-elem, script-src-attr, and script-src-elem directives (@ggalmazor)
12
+
1
13
  ## 6.3.1
2
14
 
3
15
  Fixes deprecation warnings when running under ruby 2.7
data/Gemfile CHANGED
@@ -9,7 +9,7 @@ group :test do
9
9
  gem "pry-nav"
10
10
  gem "rack"
11
11
  gem "rspec"
12
- gem "rubocop", "< 0.68"
12
+ gem "rubocop"
13
13
  gem "rubocop-github"
14
14
  gem "rubocop-performance"
15
15
  gem "term-ansicolor"
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](http://www.w3.org/TR/CSP2/)
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 http://www.w3.org/TR/mixed-content/
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)
@@ -165,9 +169,8 @@ If you've made a contribution and see your name missing from the list, make a PR
165
169
  * Rack [rack-secure_headers](https://github.com/frodsan/rack-secure_headers)
166
170
  * Node.js (express) [helmet](https://github.com/helmetjs/helmet) and [hood](https://github.com/seanmonstar/hood)
167
171
  * Node.js (hapi) [blankie](https://github.com/nlf/blankie)
168
- * J2EE Servlet >= 3.0 [headlines](https://github.com/sourceclear/headlines)
169
172
  * ASP.NET - [NWebsec](https://github.com/NWebsec/NWebsec/wiki)
170
- * 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)
171
174
  * Go - [secureheader](https://github.com/kr/secureheader)
172
175
  * Elixir [secure_headers](https://github.com/anotherhale/secure_headers)
173
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
 
@@ -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 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`.
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
- ## Configuration the default configuration more than once will result in an Exception
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 inc 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).
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).
@@ -155,9 +155,10 @@ module SecureHeaders
155
155
  wild_sources = sources.select { |source| source =~ STAR_REGEXP }
156
156
 
157
157
  if wild_sources.any?
158
+ schemes = sources.map { |source| [source, URI(source).scheme] }.to_h
158
159
  sources.reject do |source|
159
160
  !wild_sources.include?(source) &&
160
- wild_sources.any? { |pattern| File.fnmatch(pattern, source) }
161
+ wild_sources.any? { |pattern| schemes[pattern] == schemes[source] && File.fnmatch(pattern, source) }
161
162
  end
162
163
  else
163
164
  sources
@@ -38,8 +38,12 @@ module SecureHeaders
38
38
  @sandbox = nil
39
39
  @script_nonce = nil
40
40
  @script_src = nil
41
+ @script_src_elem = nil
42
+ @script_src_attr = nil
41
43
  @style_nonce = nil
42
44
  @style_src = nil
45
+ @style_src_elem = nil
46
+ @style_src_attr = nil
43
47
  @worker_src = nil
44
48
  @upgrade_insecure_requests = nil
45
49
  @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,7 +91,11 @@ 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
  ALL_DIRECTIVES = (DIRECTIVES_1_0 + DIRECTIVES_2_0 + DIRECTIVES_3_0).uniq.sort
@@ -117,7 +125,11 @@ module SecureHeaders
117
125
  PREFETCH_SRC => :source_list,
118
126
  SANDBOX => :sandbox_list,
119
127
  SCRIPT_SRC => :source_list,
128
+ SCRIPT_SRC_ELEM => :source_list,
129
+ SCRIPT_SRC_ATTR => :source_list,
120
130
  STYLE_SRC => :source_list,
131
+ STYLE_SRC_ELEM => :source_list,
132
+ STYLE_SRC_ATTR => :source_list,
121
133
  WORKER_SRC => :source_list,
122
134
  UPGRADE_INSECURE_REQUESTS => :boolean,
123
135
  }.freeze
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SecureHeaders
4
- VERSION = "6.3.1"
4
+ VERSION = "6.3.4"
5
5
  end
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
- [INLINE_SCRIPT_REGEX, INLINE_HASH_SCRIPT_HELPER_REGEX].each do |regex|
42
- find_inline_content(filename, regex, hashes)
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
- [INLINE_STYLE_REGEX, INLINE_HASH_STYLE_HELPER_REGEX].each do |regex|
52
- find_inline_content(filename, regex, hashes)
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")
@@ -160,6 +165,26 @@ module SecureHeaders
160
165
  csp = ContentSecurityPolicy.new({default_src: %w('self'), script_src: [ContentSecurityPolicy::STRICT_DYNAMIC], script_nonce: 123456, disable_nonce_backwards_compatibility: true })
161
166
  expect(csp.value).to eq("default-src 'self'; script-src 'strict-dynamic' 'nonce-123456'")
162
167
  end
168
+
169
+ it "supports script-src-elem directive" do
170
+ csp = ContentSecurityPolicy.new({script_src: %w('self'), script_src_elem: %w('self')})
171
+ expect(csp.value).to eq("script-src 'self'; script-src-elem 'self'")
172
+ end
173
+
174
+ it "supports script-src-attr directive" do
175
+ csp = ContentSecurityPolicy.new({script_src: %w('self'), script_src_attr: %w('self')})
176
+ expect(csp.value).to eq("script-src 'self'; script-src-attr 'self'")
177
+ end
178
+
179
+ it "supports style-src-elem directive" do
180
+ csp = ContentSecurityPolicy.new({style_src: %w('self'), style_src_elem: %w('self')})
181
+ expect(csp.value).to eq("style-src 'self'; style-src-elem 'self'")
182
+ end
183
+
184
+ it "supports style-src-attr directive" do
185
+ csp = ContentSecurityPolicy.new({style_src: %w('self'), style_src_attr: %w('self')})
186
+ expect(csp.value).to eq("style-src 'self'; style-src-attr 'self'")
187
+ end
163
188
  end
164
189
  end
165
190
  end
@@ -49,6 +49,10 @@ module SecureHeaders
49
49
  style_src: %w('unsafe-inline'),
50
50
  upgrade_insecure_requests: true, # see https://www.w3.org/TR/upgrade-insecure-requests/
51
51
  worker_src: %w(worker.com),
52
+ script_src_elem: %w(example.com),
53
+ script_src_attr: %w(example.com),
54
+ style_src_elem: %w(example.com),
55
+ style_src_attr: %w(example.com),
52
56
 
53
57
  report_uri: %w(https://example.com/uri-directive),
54
58
  }
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.3.1
4
+ version: 6.3.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Neil Matatall
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-26 00:00:00.000000000 Z
11
+ date: 2022-06-28 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/sync.yml"
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.0.3
119
+ rubygems_version: 3.0.3.1
120
120
  signing_key:
121
121
  specification_version: 4
122
122
  summary: Add easily configured security headers to responses including content-security-policy,
@@ -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