secure_headers 6.2.0 → 6.3.3

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: 6aed18c8a69d4ddb01e664faca9c6db1e4b4cb6fa26203b9f3c1f20ace3001bb
4
- data.tar.gz: 1a347b7ffa5efd650131b9d9bacff110ba6b08b3cb9dc5a93b6c70de6f1d3359
3
+ metadata.gz: 19914a6c5043c42b398c52760c39a22b8246815d0dd4d2e7ec2feeec703da6c0
4
+ data.tar.gz: f52cf84fe9b6a798fd167be5158c32df83aef8db0887d6c94f8f8a3b53d4427f
5
5
  SHA512:
6
- metadata.gz: a0ff8c0e7b81d8a1b543d54ff7cd4b4186521276242cff4614839f6d4abcf9313c66f0b935358d3f0f283052111000c84dbc7da40374563f9b82df8b39281d3a
7
- data.tar.gz: d22831fc277d3ecbd7d9923d8ff3ad724c0fcdab88b75389edaaa2ec8c4b0653908ef756c2fc642d8328643dca6e0417769e2b323066bb357a1230c33c17df97
6
+ metadata.gz: 043f1bc47f10e8d306debedc2081c9962e5ce5f4768fd75d4af0e092cc5d80947eb128d1f847216ed83124a495dc83df20fafb2ae28a243dd5f6ea23453bcb69
7
+ data.tar.gz: a88e51c0a14725479a9746be98495d2fac931e6cc2824fc3f5601d00ef63a292917850a57856d5dc89a654fe9de05230712e1945e1ada4ed50d8561e6f891002
@@ -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' ]
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
+
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.6.5
1
+ 2.6.6
data/CHANGELOG.md CHANGED
@@ -1,3 +1,19 @@
1
+ ## 6.3.3
2
+
3
+ Fix hash generation for indented helper methods (@rahearn)
4
+
5
+ ## 6.3.2
6
+
7
+ Add support for style-src-attr, style-src-elem, script-src-attr, and script-src-elem directives (@ggalmazor)
8
+
9
+ ## 6.3.1
10
+
11
+ Fixes deprecation warnings when running under ruby 2.7
12
+
13
+ ## 6.3.0
14
+
15
+ Fixes newline injection issue
16
+
1
17
  ## 6.2.0
2
18
 
3
19
  Fixes semicolon injection issue reported by @mvgijssel see https://github.com/twitter/secure_headers/issues/418
@@ -368,7 +384,7 @@ Adds `upgrade-insecure-requests` support for requests from Firefox and Chrome (a
368
384
 
369
385
  ## 3.0.0
370
386
 
371
- 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.
387
+ 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.
372
388
 
373
389
  ## 2.5.1 - 2016-02-16 18:11:11 UTC - Remove noisy deprecation warning
374
390
 
data/Gemfile CHANGED
@@ -11,6 +11,7 @@ group :test do
11
11
  gem "rspec"
12
12
  gem "rubocop", "< 0.68"
13
13
  gem "rubocop-github"
14
+ gem "rubocop-performance"
14
15
  gem "term-ansicolor"
15
16
  gem "tins"
16
17
  end
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # Secure Headers [![Build Status](https://travis-ci.org/twitter/secure_headers.svg?branch=master)](http://travis-ci.org/twitter/secure_headers) [![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.
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
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/)
@@ -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
 
@@ -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,6 +74,11 @@ 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)
@@ -106,11 +106,11 @@ module SecureHeaders
106
106
  if source_list != OPT_OUT && source_list && source_list.any?
107
107
  minified_source_list = minify_source_list(directive, source_list).join(" ")
108
108
 
109
- if minified_source_list.include?(";")
110
- Kernel.warn("#{directive} contains a ; in '#{minified_source_list}' which will raise an error in future versions. It has been replaced with a blank space.")
109
+ if minified_source_list =~ /(\n|;)/
110
+ Kernel.warn("#{directive} contains a #{$1} in #{minified_source_list.inspect} which will raise an error in future versions. It has been replaced with a blank space.")
111
111
  end
112
112
 
113
- escaped_source_list = minified_source_list.gsub(";", " ")
113
+ escaped_source_list = minified_source_list.gsub(/[\n;]/, " ")
114
114
  [symbol_to_hyphen_case(directive), escaped_source_list].join(" ").strip
115
115
  end
116
116
  end
@@ -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.2.0"
4
+ VERSION = "6.3.3"
5
5
  end
@@ -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.
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
@@ -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|
@@ -29,10 +29,15 @@ module SecureHeaders
29
29
  end
30
30
 
31
31
  it "deprecates and escapes semicolons in directive source lists" do
32
- expect(Kernel).to receive(:warn).with("frame_ancestors contains a ; in 'google.com;script-src *;.;' which will raise an error in future versions. It has been replaced with a blank space.")
32
+ expect(Kernel).to receive(:warn).with(%(frame_ancestors contains a ; in "google.com;script-src *;.;" which will raise an error in future versions. It has been replaced with a blank space.))
33
33
  expect(ContentSecurityPolicy.new(frame_ancestors: %w(https://google.com;script-src https://*;.;)).value).to eq("frame-ancestors google.com script-src * .")
34
34
  end
35
35
 
36
+ it "deprecates and escapes semicolons in directive source lists" do
37
+ expect(Kernel).to receive(:warn).with(%(frame_ancestors contains a \n in "\\nfoo.com\\nhacked" which will raise an error in future versions. It has been replaced with a blank space.))
38
+ expect(ContentSecurityPolicy.new(frame_ancestors: ["\nfoo.com\nhacked"]).value).to eq("frame-ancestors foo.com hacked")
39
+ end
40
+
36
41
  it "discards 'none' values if any other source expressions are present" do
37
42
  csp = ContentSecurityPolicy.new(default_opts.merge(child_src: %w('self' 'none')))
38
43
  expect(csp.value).not_to include("'none'")
@@ -155,6 +160,26 @@ module SecureHeaders
155
160
  csp = ContentSecurityPolicy.new({default_src: %w('self'), script_src: [ContentSecurityPolicy::STRICT_DYNAMIC], script_nonce: 123456, disable_nonce_backwards_compatibility: true })
156
161
  expect(csp.value).to eq("default-src 'self'; script-src 'strict-dynamic' 'nonce-123456'")
157
162
  end
163
+
164
+ it "supports script-src-elem directive" do
165
+ csp = ContentSecurityPolicy.new({script_src: %w('self'), script_src_elem: %w('self')})
166
+ expect(csp.value).to eq("script-src 'self'; script-src-elem 'self'")
167
+ end
168
+
169
+ it "supports script-src-attr directive" do
170
+ csp = ContentSecurityPolicy.new({script_src: %w('self'), script_src_attr: %w('self')})
171
+ expect(csp.value).to eq("script-src 'self'; script-src-attr 'self'")
172
+ end
173
+
174
+ it "supports style-src-elem directive" do
175
+ csp = ContentSecurityPolicy.new({style_src: %w('self'), style_src_elem: %w('self')})
176
+ expect(csp.value).to eq("style-src 'self'; style-src-elem 'self'")
177
+ end
178
+
179
+ it "supports style-src-attr directive" do
180
+ csp = ContentSecurityPolicy.new({style_src: %w('self'), style_src_attr: %w('self')})
181
+ expect(csp.value).to eq("style-src 'self'; style-src-attr 'self'")
182
+ end
158
183
  end
159
184
  end
160
185
  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
  }
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.2.0
4
+ version: 6.3.3
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-01-21 00:00:00.000000000 Z
11
+ date: 2021-09-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -33,12 +33,12 @@ extra_rdoc_files: []
33
33
  files:
34
34
  - ".github/ISSUE_TEMPLATE.md"
35
35
  - ".github/PULL_REQUEST_TEMPLATE.md"
36
+ - ".github/workflows/build.yml"
36
37
  - ".gitignore"
37
38
  - ".rspec"
38
39
  - ".rubocop.yml"
39
40
  - ".ruby-gemset"
40
41
  - ".ruby-version"
41
- - ".travis.yml"
42
42
  - CHANGELOG.md
43
43
  - CODE_OF_CONDUCT.md
44
44
  - CONTRIBUTING.md
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