secure_headers 7.0.0 → 7.1.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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +13 -13
  3. data/lib/secure_headers/configuration.rb +1 -1
  4. data/lib/secure_headers/headers/clear_site_data.rb +4 -4
  5. data/lib/secure_headers/headers/content_security_policy.rb +2 -2
  6. data/lib/secure_headers/headers/content_security_policy_config.rb +2 -2
  7. data/lib/secure_headers/headers/expect_certificate_transparency.rb +2 -2
  8. data/lib/secure_headers/headers/policy_management.rb +2 -2
  9. data/lib/secure_headers/headers/referrer_policy.rb +1 -1
  10. data/lib/secure_headers/headers/strict_transport_security.rb +1 -1
  11. data/lib/secure_headers/headers/x_content_type_options.rb +1 -1
  12. data/lib/secure_headers/headers/x_download_options.rb +2 -2
  13. data/lib/secure_headers/headers/x_frame_options.rb +1 -1
  14. data/lib/secure_headers/headers/x_permitted_cross_domain_policies.rb +2 -2
  15. data/lib/secure_headers/headers/x_xss_protection.rb +1 -1
  16. data/lib/secure_headers/railtie.rb +5 -5
  17. data/lib/secure_headers/version.rb +1 -1
  18. data/secure_headers.gemspec +13 -3
  19. metadata +14 -63
  20. data/.github/ISSUE_TEMPLATE.md +0 -41
  21. data/.github/PULL_REQUEST_TEMPLATE.md +0 -20
  22. data/.github/dependabot.yml +0 -6
  23. data/.github/workflows/build.yml +0 -25
  24. data/.github/workflows/github-release.yml +0 -28
  25. data/.gitignore +0 -13
  26. data/.rspec +0 -3
  27. data/.rubocop.yml +0 -4
  28. data/.ruby-gemset +0 -1
  29. data/.ruby-version +0 -1
  30. data/CODE_OF_CONDUCT.md +0 -46
  31. data/CONTRIBUTING.md +0 -41
  32. data/Guardfile +0 -13
  33. data/Rakefile +0 -32
  34. data/docs/cookies.md +0 -65
  35. data/docs/hashes.md +0 -64
  36. data/docs/named_overrides_and_appends.md +0 -104
  37. data/docs/per_action_configuration.md +0 -139
  38. data/docs/sinatra.md +0 -25
  39. data/docs/upgrading-to-3-0.md +0 -42
  40. data/docs/upgrading-to-4-0.md +0 -35
  41. data/docs/upgrading-to-5-0.md +0 -15
  42. data/docs/upgrading-to-6-0.md +0 -50
  43. data/docs/upgrading-to-7-0.md +0 -12
  44. data/spec/lib/secure_headers/configuration_spec.rb +0 -121
  45. data/spec/lib/secure_headers/headers/clear_site_data_spec.rb +0 -87
  46. data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +0 -215
  47. data/spec/lib/secure_headers/headers/cookie_spec.rb +0 -179
  48. data/spec/lib/secure_headers/headers/expect_certificate_transparency_spec.rb +0 -42
  49. data/spec/lib/secure_headers/headers/policy_management_spec.rb +0 -265
  50. data/spec/lib/secure_headers/headers/referrer_policy_spec.rb +0 -91
  51. data/spec/lib/secure_headers/headers/strict_transport_security_spec.rb +0 -33
  52. data/spec/lib/secure_headers/headers/x_content_type_options_spec.rb +0 -31
  53. data/spec/lib/secure_headers/headers/x_download_options_spec.rb +0 -29
  54. data/spec/lib/secure_headers/headers/x_frame_options_spec.rb +0 -36
  55. data/spec/lib/secure_headers/headers/x_permitted_cross_domain_policies_spec.rb +0 -48
  56. data/spec/lib/secure_headers/headers/x_xss_protection_spec.rb +0 -47
  57. data/spec/lib/secure_headers/middleware_spec.rb +0 -117
  58. data/spec/lib/secure_headers/view_helpers_spec.rb +0 -192
  59. data/spec/lib/secure_headers_spec.rb +0 -516
  60. data/spec/spec_helper.rb +0 -64
@@ -1,117 +0,0 @@
1
- # frozen_string_literal: true
2
- require "spec_helper"
3
-
4
- module SecureHeaders
5
- describe Middleware do
6
- let(:app) { lambda { |env| [200, env, "app"] } }
7
- let(:cookie_app) { lambda { |env| [200, env.merge("Set-Cookie" => "foo=bar"), "app"] } }
8
-
9
- let(:middleware) { Middleware.new(app) }
10
- let(:cookie_middleware) { Middleware.new(cookie_app) }
11
-
12
- before(:each) do
13
- reset_config
14
- Configuration.default
15
- end
16
-
17
- it "sets the headers" do
18
- _, env = middleware.call(Rack::MockRequest.env_for("https://looocalhost", {}))
19
- expect_default_values(env)
20
- end
21
-
22
- it "respects overrides" do
23
- request = Rack::Request.new("HTTP_X_FORWARDED_SSL" => "on")
24
- SecureHeaders.override_x_frame_options(request, "DENY")
25
- _, env = middleware.call request.env
26
- expect(env[XFrameOptions::HEADER_NAME]).to eq("DENY")
27
- end
28
-
29
- it "uses named overrides" do
30
- Configuration.override("my_custom_config") do |config|
31
- config.csp[:script_src] = %w(example.org)
32
- end
33
- request = Rack::Request.new({})
34
- SecureHeaders.use_secure_headers_override(request, "my_custom_config")
35
- _, env = middleware.call request.env
36
- expect(env[ContentSecurityPolicyConfig::HEADER_NAME]).to match("example.org")
37
- end
38
-
39
- context "cookies" do
40
- before(:each) do
41
- reset_config
42
- end
43
- context "cookies should be flagged" do
44
- it "flags cookies as secure" do
45
- Configuration.default { |config| config.cookies = {secure: true, httponly: OPT_OUT, samesite: OPT_OUT} }
46
- request = Rack::Request.new("HTTPS" => "on")
47
- _, env = cookie_middleware.call request.env
48
- expect(env["Set-Cookie"]).to eq("foo=bar; secure")
49
- end
50
- end
51
-
52
- it "allows opting out of cookie protection with OPT_OUT alone" do
53
- Configuration.default { |config| config.cookies = OPT_OUT }
54
-
55
- # do NOT make this request https. non-https requests modify a config,
56
- # causing an exception when operating on OPT_OUT. This ensures we don't
57
- # try to modify the config.
58
- request = Rack::Request.new({})
59
- _, env = cookie_middleware.call request.env
60
- expect(env["Set-Cookie"]).to eq("foo=bar")
61
- end
62
-
63
- context "cookies should not be flagged" do
64
- it "does not flags cookies as secure" do
65
- Configuration.default { |config| config.cookies = {secure: OPT_OUT, httponly: OPT_OUT, samesite: OPT_OUT} }
66
- request = Rack::Request.new("HTTPS" => "on")
67
- _, env = cookie_middleware.call request.env
68
- expect(env["Set-Cookie"]).to eq("foo=bar")
69
- end
70
- end
71
- end
72
-
73
- context "cookies" do
74
- before(:each) do
75
- reset_config
76
- end
77
- it "flags cookies from configuration" do
78
- Configuration.default { |config| config.cookies = { secure: true, httponly: true, samesite: { lax: true} } }
79
- request = Rack::Request.new("HTTPS" => "on")
80
- _, env = cookie_middleware.call request.env
81
-
82
- expect(env["Set-Cookie"]).to eq("foo=bar; secure; HttpOnly; SameSite=Lax")
83
- end
84
-
85
- it "flags cookies with a combination of SameSite configurations" do
86
- cookie_middleware = Middleware.new(lambda { |env| [200, env.merge("Set-Cookie" => ["_session=foobar", "_guest=true"]), "app"] })
87
-
88
- Configuration.default { |config| config.cookies = { samesite: { lax: { except: ["_session"] }, strict: { only: ["_session"] } }, httponly: OPT_OUT, secure: OPT_OUT} }
89
- request = Rack::Request.new("HTTPS" => "on")
90
- _, env = cookie_middleware.call request.env
91
-
92
- expect(env["Set-Cookie"]).to match("_session=foobar; SameSite=Strict")
93
- expect(env["Set-Cookie"]).to match("_guest=true; SameSite=Lax")
94
- end
95
-
96
- it "disables secure cookies for non-https requests" do
97
- Configuration.default { |config| config.cookies = { secure: true, httponly: OPT_OUT, samesite: OPT_OUT } }
98
-
99
- request = Rack::Request.new("HTTPS" => "off")
100
- _, env = cookie_middleware.call request.env
101
- expect(env["Set-Cookie"]).to eq("foo=bar")
102
- end
103
-
104
- it "sets the secure cookie flag correctly on interleaved http/https requests" do
105
- Configuration.default { |config| config.cookies = { secure: true, httponly: OPT_OUT, samesite: OPT_OUT } }
106
-
107
- request = Rack::Request.new("HTTPS" => "off")
108
- _, env = cookie_middleware.call request.env
109
- expect(env["Set-Cookie"]).to eq("foo=bar")
110
-
111
- request = Rack::Request.new("HTTPS" => "on")
112
- _, env = cookie_middleware.call request.env
113
- expect(env["Set-Cookie"]).to eq("foo=bar; secure")
114
- end
115
- end
116
- end
117
- end
@@ -1,192 +0,0 @@
1
- # frozen_string_literal: true
2
- require "spec_helper"
3
- require "erb"
4
-
5
- class Message < ERB
6
- include SecureHeaders::ViewHelpers
7
-
8
- def self.template
9
- <<-TEMPLATE
10
- <% hashed_javascript_tag(raise_error_on_unrecognized_hash = true) do %>
11
- console.log(1)
12
- <% end %>
13
-
14
- <% hashed_style_tag do %>
15
- body {
16
- background-color: black;
17
- }
18
- <% end %>
19
-
20
- <% nonced_javascript_tag do %>
21
- body {
22
- console.log(1)
23
- }
24
- <% end %>
25
-
26
- <% nonced_style_tag do %>
27
- body {
28
- background-color: black;
29
- }
30
- <% end %>
31
-
32
- <script nonce="<%= content_security_policy_script_nonce %>">
33
- alert(1)
34
- </script>
35
-
36
- <style nonce="<%= content_security_policy_style_nonce %>">
37
- body {
38
- background-color: black;
39
- }
40
- </style>
41
-
42
- <%= nonced_javascript_include_tag "include.js", defer: true %>
43
-
44
- <%= nonced_javascript_pack_tag "pack.js", "otherpack.js", defer: true %>
45
-
46
- <%= nonced_stylesheet_link_tag "link.css", media: :all %>
47
-
48
- <%= nonced_stylesheet_pack_tag "pack.css", "otherpack.css", media: :all %>
49
-
50
- TEMPLATE
51
- end
52
-
53
- def initialize(request, options = {})
54
- @virtual_path = "/asdfs/index"
55
- @_request = request
56
- @template = self.class.template
57
- super(@template)
58
- end
59
-
60
- def capture(*args)
61
- yield(*args)
62
- end
63
-
64
- def content_tag(type, content = nil, options = nil, &block)
65
- content =
66
- if block_given?
67
- capture(block)
68
- end
69
-
70
- if options.is_a?(Hash)
71
- options = options.map { |k, v| " #{k}=#{v}" }
72
- end
73
- "<#{type}#{options}>#{content}</#{type}>"
74
- end
75
-
76
- def javascript_include_tag(*sources, **options)
77
- sources.map do |source|
78
- content_tag(:script, nil, options.merge(src: source))
79
- end
80
- end
81
-
82
- alias_method :javascript_pack_tag, :javascript_include_tag
83
-
84
- def stylesheet_link_tag(*sources, **options)
85
- sources.map do |source|
86
- content_tag(:link, nil, options.merge(href: source, rel: "stylesheet", media: "screen"))
87
- end
88
- end
89
-
90
- alias_method :stylesheet_pack_tag, :stylesheet_link_tag
91
-
92
- def result
93
- super(binding)
94
- end
95
-
96
- def request
97
- @_request
98
- end
99
- end
100
-
101
- class MessageWithConflictingMethod < Message
102
- def content_security_policy_nonce
103
- "rails-nonce"
104
- end
105
- end
106
-
107
- module SecureHeaders
108
- describe ViewHelpers do
109
- let(:app) { lambda { |env| [200, env, "app"] } }
110
- let(:middleware) { Middleware.new(app) }
111
- let(:request) { Rack::Request.new("HTTP_USER_AGENT" => USER_AGENTS[:chrome]) }
112
- let(:filename) { "app/views/asdfs/index.html.erb" }
113
-
114
- before(:all) do
115
- reset_config
116
- Configuration.default do |config|
117
- config.csp = {
118
- default_src: %w('self'),
119
- script_src: %w('self'),
120
- style_src: %w('self')
121
- }
122
- end
123
- end
124
-
125
- after(:each) do
126
- Configuration.instance_variable_set(:@script_hashes, nil)
127
- Configuration.instance_variable_set(:@style_hashes, nil)
128
- end
129
-
130
- it "raises an error when using hashed content without precomputed hashes" do
131
- expect {
132
- Message.new(request).result
133
- }.to raise_error(ViewHelpers::UnexpectedHashedScriptException)
134
- end
135
-
136
- it "raises an error when using hashed content with precomputed hashes, but none for the given file" do
137
- Configuration.instance_variable_set(:@script_hashes, filename.reverse => ["'sha256-123'"])
138
- expect {
139
- Message.new(request).result
140
- }.to raise_error(ViewHelpers::UnexpectedHashedScriptException)
141
- end
142
-
143
- it "raises an error when using previously unknown hashed content with precomputed hashes for a given file" do
144
- Configuration.instance_variable_set(:@script_hashes, filename => ["'sha256-123'"])
145
- expect {
146
- Message.new(request).result
147
- }.to raise_error(ViewHelpers::UnexpectedHashedScriptException)
148
- end
149
-
150
- it "adds known hash values to the corresponding headers when the helper is used" do
151
- begin
152
- allow(SecureRandom).to receive(:base64).and_return("abc123")
153
-
154
- expected_hash = "sha256-3/URElR9+3lvLIouavYD/vhoICSNKilh15CzI/nKqg8="
155
- Configuration.instance_variable_set(:@script_hashes, filename => ["'#{expected_hash}'"])
156
- expected_style_hash = "sha256-7oYK96jHg36D6BM042er4OfBnyUDTG3pH1L8Zso3aGc="
157
- Configuration.instance_variable_set(:@style_hashes, filename => ["'#{expected_style_hash}'"])
158
-
159
- # render erb that calls out to helpers.
160
- Message.new(request).result
161
- _, env = middleware.call request.env
162
-
163
- expect(env[ContentSecurityPolicyConfig::HEADER_NAME]).to match(/script-src[^;]*'#{Regexp.escape(expected_hash)}'/)
164
- expect(env[ContentSecurityPolicyConfig::HEADER_NAME]).to match(/script-src[^;]*'nonce-abc123'/)
165
- expect(env[ContentSecurityPolicyConfig::HEADER_NAME]).to match(/style-src[^;]*'nonce-abc123'/)
166
- expect(env[ContentSecurityPolicyConfig::HEADER_NAME]).to match(/style-src[^;]*'#{Regexp.escape(expected_style_hash)}'/)
167
- end
168
- end
169
-
170
- it "avoids calling content_security_policy_nonce internally" do
171
- begin
172
- allow(SecureRandom).to receive(:base64).and_return("abc123")
173
-
174
- expected_hash = "sha256-3/URElR9+3lvLIouavYD/vhoICSNKilh15CzI/nKqg8="
175
- Configuration.instance_variable_set(:@script_hashes, filename => ["'#{expected_hash}'"])
176
- expected_style_hash = "sha256-7oYK96jHg36D6BM042er4OfBnyUDTG3pH1L8Zso3aGc="
177
- Configuration.instance_variable_set(:@style_hashes, filename => ["'#{expected_style_hash}'"])
178
-
179
- # render erb that calls out to helpers.
180
- MessageWithConflictingMethod.new(request).result
181
- _, env = middleware.call request.env
182
-
183
- expect(env[ContentSecurityPolicyConfig::HEADER_NAME]).to match(/script-src[^;]*'#{Regexp.escape(expected_hash)}'/)
184
- expect(env[ContentSecurityPolicyConfig::HEADER_NAME]).to match(/script-src[^;]*'nonce-abc123'/)
185
- expect(env[ContentSecurityPolicyConfig::HEADER_NAME]).to match(/style-src[^;]*'nonce-abc123'/)
186
- expect(env[ContentSecurityPolicyConfig::HEADER_NAME]).to match(/style-src[^;]*'#{Regexp.escape(expected_style_hash)}'/)
187
-
188
- expect(env[ContentSecurityPolicyConfig::HEADER_NAME]).not_to match(/rails-nonce/)
189
- end
190
- end
191
- end
192
- end