secure_headers 6.0.0 → 6.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +5 -5
  2. data/.github/dependabot.yml +6 -0
  3. data/.github/workflows/build.yml +24 -0
  4. data/.github/workflows/github-release.yml +28 -0
  5. data/.rubocop.yml +1 -0
  6. data/.ruby-version +1 -1
  7. data/CHANGELOG.md +47 -1
  8. data/Gemfile +4 -1
  9. data/LICENSE +1 -1
  10. data/README.md +70 -30
  11. data/docs/cookies.md +5 -4
  12. data/docs/named_overrides_and_appends.md +3 -6
  13. data/docs/per_action_configuration.md +2 -4
  14. data/docs/upgrading-to-6-0.md +4 -4
  15. data/lib/secure_headers/configuration.rb +24 -16
  16. data/lib/secure_headers/headers/content_security_policy.rb +34 -41
  17. data/lib/secure_headers/headers/content_security_policy_config.rb +15 -48
  18. data/lib/secure_headers/headers/cookie.rb +9 -3
  19. data/lib/secure_headers/headers/policy_management.rb +92 -17
  20. data/lib/secure_headers/middleware.rb +0 -6
  21. data/lib/secure_headers/utils/cookies_config.rb +7 -5
  22. data/lib/secure_headers/version.rb +5 -0
  23. data/lib/secure_headers/view_helper.rb +11 -10
  24. data/lib/secure_headers.rb +3 -11
  25. data/lib/tasks/tasks.rake +6 -7
  26. data/secure_headers.gemspec +9 -5
  27. data/spec/lib/secure_headers/configuration_spec.rb +54 -0
  28. data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +98 -8
  29. data/spec/lib/secure_headers/headers/cookie_spec.rb +22 -25
  30. data/spec/lib/secure_headers/headers/policy_management_spec.rb +29 -19
  31. data/spec/lib/secure_headers/middleware_spec.rb +0 -19
  32. data/spec/lib/secure_headers/view_helpers_spec.rb +5 -4
  33. data/spec/lib/secure_headers_spec.rb +0 -35
  34. data/spec/spec_helper.rb +10 -1
  35. metadata +13 -12
  36. data/.travis.yml +0 -29
  37. data/lib/secure_headers/headers/public_key_pins.rb +0 -81
  38. data/spec/lib/secure_headers/headers/public_key_pins_spec.rb +0 -38
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 6d7392744d486b32bda2b91432d39ddfeee87dfd
4
- data.tar.gz: 38328982bf71376b9412ed8c7d0652163741bf16
2
+ SHA256:
3
+ metadata.gz: 6919954e57f87c70a4fa42baa5285649bd7484ec6785894a2b461b6f52558f29
4
+ data.tar.gz: 710492a0e64a47f41f2b079e6d4799922aa05e51d017cacafcc20d77383815ee
5
5
  SHA512:
6
- metadata.gz: e08d9df89db6908d0c8419d0086bf8fb6c7e374c53e0bf774bd74d534094c66ef3cdcbac23e97c03b05d4045ba6878c3f6d8a17877146bdb6f2f0c15b57d1784
7
- data.tar.gz: 304824374c236d11edc52049732a2ea133bbbdf611f35b2c5309a950125e879fb4016f0f15d7ad6c09310ab4b6080e966fac912147f43ed91989aa15898595a7
6
+ metadata.gz: efd8e608dfeafc5d7e7fcd06274b0b8ed0c640744ab9d8113597bef916f31666cbf518b1dcd6869d7af42fbcbaf15a6a4cf8100b97fcbf52f5ba790e495e26c0
7
+ data.tar.gz: dcc504641e1c22b24a05c76534e2f8ba7a7fd5ff1b5f891eb467d23876c60900ef235d2dd4ba49af4352b23ffd1cd246c720aff79668244b6a10cec3aab8ed6f
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "github-actions"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
@@ -0,0 +1,24 @@
1
+ name: Build + Test
2
+ on: [pull_request, push]
3
+
4
+ jobs:
5
+ build:
6
+ name: Build + Test
7
+ runs-on: ubuntu-latest
8
+ strategy:
9
+ matrix:
10
+ ruby: [ '2.6', '2.7', '3.0', '3.1', '3.2' ]
11
+
12
+ steps:
13
+ - uses: actions/checkout@v3
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
@@ -1,3 +1,4 @@
1
1
  inherit_gem:
2
2
  rubocop-github:
3
3
  - config/default.yml
4
+ require: rubocop-performance
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.4.2
1
+ 3.1.1
data/CHANGELOG.md CHANGED
@@ -1,3 +1,49 @@
1
+ ## 6.5.0
2
+
3
+ - CSP: Remove source expression deduplication. (@lgarron) https://github.com/github/secure_headers/pull/499
4
+
5
+ ## 6.4.0
6
+
7
+ - CSP: Add support for trusted-types, require-trusted-types-for directive (@JackMc): https://github.com/github/secure_headers/pull/486
8
+
9
+ ## 6.3.4
10
+
11
+ - CSP: Do not deduplicate alternate schema source expressions (@keithamus): https://github.com/github/secure_headers/pull/478
12
+
13
+ ## 6.3.3
14
+
15
+ Fix hash generation for indented helper methods (@rahearn)
16
+
17
+ ## 6.3.2
18
+
19
+ Add support for style-src-attr, style-src-elem, script-src-attr, and script-src-elem directives (@ggalmazor)
20
+
21
+ ## 6.3.1
22
+
23
+ Fixes deprecation warnings when running under ruby 2.7
24
+
25
+ ## 6.3.0
26
+
27
+ Fixes newline injection issue
28
+
29
+ ## 6.2.0
30
+
31
+ Fixes semicolon injection issue reported by @mvgijssel see https://github.com/twitter/secure_headers/issues/418
32
+
33
+ ## 6.1.2
34
+
35
+ Adds the ability to specify `SameSite=none` with the same configurability as `Strict`/`Lax` in order to disable Chrome's soon-to-be-lax-by-default state.
36
+
37
+ ## 6.1.1
38
+
39
+ Adds the ability to disable the automatically-appended `'unsafe-inline'` value when nonces are used #404 (@will)
40
+
41
+ ## 6.1
42
+
43
+ Adds support for navigate-to, prefetch-src, and require-sri-for #395
44
+
45
+ NOTE: this version is a breaking change due to the removal of HPKP. Remove the HPKP config, the standard is dead. Apologies for not doing a proper deprecate/major rev cycle :pray:
46
+
1
47
  ## 6.0
2
48
 
3
49
  - See the [upgrading to 6.0](docs/upgrading-to-6-0.md) guide for the breaking changes.
@@ -350,7 +396,7 @@ Adds `upgrade-insecure-requests` support for requests from Firefox and Chrome (a
350
396
 
351
397
  ## 3.0.0
352
398
 
353
- 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/master/docs/upgrading-to-3-0.md) for an in-depth explanation of the changes and the suggested upgrade path.
399
+ 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.
354
400
 
355
401
  ## 2.5.1 - 2016-02-16 18:11:11 UTC - Remove noisy deprecation warning
356
402
 
data/Gemfile CHANGED
@@ -3,6 +3,8 @@ source "https://rubygems.org"
3
3
 
4
4
  gemspec
5
5
 
6
+ gem "benchmark-ips"
7
+
6
8
  group :test do
7
9
  gem "coveralls"
8
10
  gem "json"
@@ -11,13 +13,14 @@ group :test do
11
13
  gem "rspec"
12
14
  gem "rubocop"
13
15
  gem "rubocop-github"
16
+ gem "rubocop-performance"
14
17
  gem "term-ansicolor"
15
18
  gem "tins"
16
19
  end
17
20
 
18
21
  group :guard do
19
22
  gem "growl"
20
- gem "guard-rspec", platforms: [:ruby_19, :ruby_20, :ruby_21, :ruby_22, :ruby_23, :ruby_24]
23
+ gem "guard-rspec", platforms: [:ruby]
21
24
  gem "rb-fsevent"
22
25
  gem "terminal-notifier-guard"
23
26
  end
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2013, 2014, 2015, 2016, 2017 Twitter, Inc.
1
+ Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Twitter, Inc.
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
4
 
data/README.md CHANGED
@@ -1,13 +1,9 @@
1
- # Secure Headers [![Build Status](https://travis-ci.org/twitter/secureheaders.svg?branch=master)](http://travis-ci.org/twitter/secureheaders) [![Code Climate](https://codeclimate.com/github/twitter/secureheaders.svg)](https://codeclimate.com/github/twitter/secureheaders) [![Coverage Status](https://coveralls.io/repos/twitter/secureheaders/badge.svg)](https://coveralls.io/r/twitter/secureheaders)
1
+ # Secure Headers ![Build + Test](https://github.com/github/secure_headers/workflows/Build%20+%20Test/badge.svg?branch=main)
2
2
 
3
- **master 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
-
5
- **The [3.x](https://github.com/twitter/secureheaders/tree/2.x) branch is moving into maintenance mode**. See the [upgrading to 3.x doc](docs/upgrading-to-3-0.md) for instructions on how to upgrade including the differences and benefits of using the 3.x branch.
6
-
7
- **The [2.x branch](https://github.com/twitter/secureheaders/tree/2.x) will be not be maintained once 4.x is released**. The documentation below only applies to the 3.x branch. See the 2.x [README](https://github.com/twitter/secureheaders/blob/2.x/README.md) for the old way of doing things.
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.
8
4
 
9
5
  The gem will automatically apply several headers that are related to security. This includes:
10
- - 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/)
11
7
  - https://csp.withgoogle.com
12
8
  - https://csp.withgoogle.com/docs/strict-csp.html
13
9
  - https://csp-evaluator.withgoogle.com
@@ -33,20 +29,6 @@ It can also mark all http cookies with the Secure, HttpOnly and SameSite attribu
33
29
  - [Hashes](docs/hashes.md)
34
30
  - [Sinatra Config](docs/sinatra.md)
35
31
 
36
- ## Getting Started
37
-
38
- ### Rails 3+
39
-
40
- For Rails 3+ applications, `secure_headers` has a `railtie` that should automatically include the middleware. If for some reason the middleware is not being included follow the instructions for Rails 2.
41
-
42
- ### Rails 2
43
-
44
- For Rails 2 or non-rails applications, an explicit statement is required to use the middleware component.
45
-
46
- ```ruby
47
- use SecureHeaders::Middleware
48
- ```
49
-
50
32
  ## Configuration
51
33
 
52
34
  If you do not supply a `default` configuration, exceptions will be raised. If you would like to use a default configuration (which is fairly locked down), just call `SecureHeaders::Configuration.default` without any arguments or block.
@@ -75,11 +57,11 @@ SecureHeaders::Configuration.default do |config|
75
57
  config.csp = {
76
58
  # "meta" values. these will shape the header, but the values are not included in the header.
77
59
  preserve_schemes: true, # default: false. Schemes are removed from host sources to save bytes and discourage mixed content.
60
+ disable_nonce_backwards_compatibility: true, # default: false. If false, `unsafe-inline` will be added automatically when using nonces. If true, it won't. See #403 for why you'd want this.
78
61
 
79
62
  # directive values: these values will directly translate into source directives
80
63
  default_src: %w('none'),
81
64
  base_uri: %w('self'),
82
- block_all_mixed_content: true, # see http://www.w3.org/TR/mixed-content/
83
65
  child_src: %w('self'), # if child-src isn't supported, the value for frame-src will be set.
84
66
  connect_src: %w(wss:),
85
67
  font_src: %w('self' data:),
@@ -92,7 +74,11 @@ SecureHeaders::Configuration.default do |config|
92
74
  sandbox: true, # true and [] will set a maximally restrictive setting
93
75
  plugin_types: %w(application/x-shockwave-flash),
94
76
  script_src: %w('self'),
77
+ script_src_elem: %w('self'),
78
+ script_src_attr: %w('self'),
95
79
  style_src: %w('unsafe-inline'),
80
+ style_src_elem: %w('unsafe-inline'),
81
+ style_src_attr: %w('unsafe-inline'),
96
82
  worker_src: %w('self'),
97
83
  upgrade_insecure_requests: true, # see https://www.w3.org/TR/upgrade-insecure-requests/
98
84
  report_uri: %w(https://report-uri.io/example-csp)
@@ -105,6 +91,9 @@ SecureHeaders::Configuration.default do |config|
105
91
  end
106
92
  ```
107
93
 
94
+ ### Deprecated Configuration Values
95
+ * `block_all_mixed_content` - this value is deprecated in favor of `upgrade_insecure_requests`. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/block-all-mixed-content for more information.
96
+
108
97
  ## Default values
109
98
 
110
99
  All headers except for PublicKeyPins and ClearSiteData have a default value. The default set of headers is:
@@ -119,22 +108,73 @@ X-Permitted-Cross-Domain-Policies: none
119
108
  X-Xss-Protection: 1; mode=block
120
109
  ```
121
110
 
111
+ ## API configurations
112
+
113
+ Which headers you decide to use for API responses is entirely a personal choice. Things like X-Frame-Options seem to have no place in an API response and would be wasting bytes. While this is true, browsers can do funky things with non-html responses. At the minimum, we suggest CSP:
114
+
115
+ ```ruby
116
+ SecureHeaders::Configuration.override(:api) do |config|
117
+ config.csp = { default_src: 'none' }
118
+ config.hsts = SecureHeaders::OPT_OUT
119
+ config.x_frame_options = SecureHeaders::OPT_OUT
120
+ config.x_content_type_options = SecureHeaders::OPT_OUT
121
+ config.x_xss_protection = SecureHeaders::OPT_OUT
122
+ config.x_permitted_cross_domain_policies = SecureHeaders::OPT_OUT
123
+ end
124
+ ```
125
+
126
+ However, I would consider these headers anyways depending on your load and bandwidth requirements.
127
+
128
+ ## Acknowledgements
129
+
130
+ 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.
131
+
132
+ Contributors include:
133
+ * Neil Matatall @oreoshake
134
+ * Chris Aniszczyk
135
+ * Artur Dryomov
136
+ * Bjørn Mæland
137
+ * Arthur Chiu
138
+ * Jonathan Viney
139
+ * Jeffrey Horn
140
+ * David Collazo
141
+ * Brendon Murphy
142
+ * William Makley
143
+ * Reed Loden
144
+ * Noah Kantrowitz
145
+ * Wyatt Anderson
146
+ * Salimane Adjao Moustapha
147
+ * Francois Chagnon
148
+ * Jeff Hodges
149
+ * Ian Melven
150
+ * Darío Javier Cravero
151
+ * Logan Hasson
152
+ * Raul E Rangel
153
+ * Steve Agalloco
154
+ * Nate Collings
155
+ * Josh Kalderimis
156
+ * Alex Kwiatkowski
157
+ * Julich Mera
158
+ * Jesse Storimer
159
+ * Tom Daniels
160
+ * Kolja Dummann
161
+ * Jean-Philippe Doyle
162
+ * Blake Hitchcock
163
+ * vanderhoorn
164
+ * orthographic-pedant
165
+ * Narsimham Chelluri
166
+
167
+ If you've made a contribution and see your name missing from the list, make a PR and add it!
168
+
122
169
  ## Similar libraries
123
170
 
124
171
  * Rack [rack-secure_headers](https://github.com/frodsan/rack-secure_headers)
125
172
  * Node.js (express) [helmet](https://github.com/helmetjs/helmet) and [hood](https://github.com/seanmonstar/hood)
126
173
  * Node.js (hapi) [blankie](https://github.com/nlf/blankie)
127
- * J2EE Servlet >= 3.0 [headlines](https://github.com/sourceclear/headlines)
128
174
  * 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)
175
+ * 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
176
  * Go - [secureheader](https://github.com/kr/secureheader)
131
177
  * Elixir [secure_headers](https://github.com/anotherhale/secure_headers)
132
178
  * Dropwizard [dropwizard-web-security](https://github.com/palantir/dropwizard-web-security)
133
179
  * Ember.js [ember-cli-content-security-policy](https://github.com/rwjblue/ember-cli-content-security-policy/)
134
180
  * PHP [secure-headers](https://github.com/BePsvPT/secure-headers)
135
-
136
- ## License
137
-
138
- Copyright 2013-2014 Twitter, Inc and other contributors.
139
-
140
- Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
data/docs/cookies.md CHANGED
@@ -25,7 +25,7 @@ Boolean-based configuration is intended to globally enable or disable a specific
25
25
  ```ruby
26
26
  config.cookies = {
27
27
  secure: true, # mark all cookies as Secure
28
- httponly: OPT_OUT, # do not mark any cookies as HttpOnly
28
+ httponly: SecureHeaders::OPT_OUT, # do not mark any cookies as HttpOnly
29
29
  }
30
30
  ```
31
31
 
@@ -52,13 +52,14 @@ config.cookies = {
52
52
  }
53
53
  ```
54
54
 
55
- `Strict` and `Lax` enforcement modes can also be specified using a Hash.
55
+ `Strict`, `Lax`, and `None` enforcement modes can also be specified using a Hash.
56
56
 
57
57
  ```ruby
58
58
  config.cookies = {
59
59
  samesite: {
60
- strict: { only: ['_rails_session'] },
61
- lax: { only: ['_guest'] }
60
+ strict: { only: ['session_id_duplicate'] },
61
+ lax: { only: ['_guest', '_rails_session', 'device_id'] },
62
+ none: { only: ['_tracking', 'saml_cookie', 'session_id'] },
62
63
  }
63
64
  }
64
65
  ```
@@ -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
@@ -76,11 +76,6 @@ class ApplicationController < ActionController::Base
76
76
  SecureHeaders::Configuration.override(:script_from_otherdomain_com) do |config|
77
77
  config.csp[:script_src] << "otherdomain.com"
78
78
  end
79
-
80
- # overrides the :script_from_otherdomain_com configuration
81
- SecureHeaders::Configuration.override(:another_config, :script_from_otherdomain_com) do |config|
82
- config.csp[:script_src] << "evenanotherdomain.com"
83
- end
84
79
  end
85
80
 
86
81
  class MyController < ApplicationController
@@ -96,6 +91,8 @@ class MyController < ApplicationController
96
91
  end
97
92
  ```
98
93
 
94
+ Reusing a configuration name is not allowed and will throw an exception.
95
+
99
96
  By default, a no-op configuration is provided. No headers will be set when this default override is used.
100
97
 
101
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
 
@@ -5,7 +5,7 @@ The original implementation of name overrides worked by making a copy of the def
5
5
  ```ruby
6
6
  class ApplicationController < ActionController::Base
7
7
  Configuration.default do |config|
8
- config.x_frame_options = OPT_OUT
8
+ config.x_frame_options = SecureHeaders::OPT_OUT
9
9
  end
10
10
 
11
11
  SecureHeaders::Configuration.override(:dynamic_override) do |config|
@@ -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).
@@ -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,17 +74,26 @@ 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
- config.each_with_object({}) do |(key, value), hash|
76
- hash[key] = if value.is_a?(Array)
77
- value.dup
78
- else
79
- value
80
- end
86
+ result = {}
87
+ config.each_pair do |key, value|
88
+ result[key] =
89
+ case value
90
+ when Array
91
+ value.dup
92
+ else
93
+ value
94
+ end
81
95
  end
96
+ result
82
97
  end
83
98
 
84
99
  # Private: Returns the internal default configuration. This should only
@@ -115,7 +130,6 @@ module SecureHeaders
115
130
  expect_certificate_transparency: ExpectCertificateTransparency,
116
131
  csp: ContentSecurityPolicy,
117
132
  csp_report_only: ContentSecurityPolicy,
118
- hpkp: PublicKeyPins,
119
133
  cookies: Cookie,
120
134
  }.freeze
121
135
 
@@ -127,7 +141,9 @@ module SecureHeaders
127
141
  # The list of attributes that must respond to a `make_header` method
128
142
  HEADERABLE_ATTRIBUTES = (CONFIG_ATTRIBUTES - [:cookies]).freeze
129
143
 
130
- attr_accessor(*CONFIG_ATTRIBUTES_TO_HEADER_CLASSES.keys)
144
+ attr_writer(*(CONFIG_ATTRIBUTES_TO_HEADER_CLASSES.reject { |key| [:csp, :csp_report_only].include?(key) }.keys))
145
+
146
+ attr_reader(*(CONFIG_ATTRIBUTES_TO_HEADER_CLASSES.keys))
131
147
 
132
148
  @script_hashes = nil
133
149
  @style_hashes = nil
@@ -144,7 +160,6 @@ module SecureHeaders
144
160
  @clear_site_data = nil
145
161
  @csp = nil
146
162
  @csp_report_only = nil
147
- @hpkp = nil
148
163
  @hsts = nil
149
164
  @x_content_type_options = nil
150
165
  @x_download_options = nil
@@ -153,7 +168,6 @@ module SecureHeaders
153
168
  @x_xss_protection = nil
154
169
  @expect_certificate_transparency = nil
155
170
 
156
- self.hpkp = OPT_OUT
157
171
  self.referrer_policy = OPT_OUT
158
172
  self.csp = ContentSecurityPolicyConfig.new(ContentSecurityPolicyConfig::DEFAULT)
159
173
  self.csp_report_only = OPT_OUT
@@ -178,7 +192,6 @@ module SecureHeaders
178
192
  copy.clear_site_data = @clear_site_data
179
193
  copy.expect_certificate_transparency = @expect_certificate_transparency
180
194
  copy.referrer_policy = @referrer_policy
181
- copy.hpkp = @hpkp
182
195
  copy
183
196
  end
184
197
 
@@ -263,10 +276,5 @@ module SecureHeaders
263
276
  raise ArgumentError, "Must provide either an existing CSP config or a CSP config hash"
264
277
  end
265
278
  end
266
-
267
- def hpkp_report_host
268
- return nil unless @hpkp && hpkp != OPT_OUT && @hpkp[:report_uri]
269
- URI.parse(@hpkp[:report_uri]).host
270
- end
271
279
  end
272
280
  end