secure_headers 6.7.0 → 7.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) 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 +2 -2
  16. data/lib/secure_headers/railtie.rb +5 -5
  17. data/lib/secure_headers/version.rb +1 -1
  18. data/secure_headers.gemspec +14 -4
  19. metadata +15 -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 -24
  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/spec/lib/secure_headers/configuration_spec.rb +0 -121
  44. data/spec/lib/secure_headers/headers/clear_site_data_spec.rb +0 -87
  45. data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +0 -215
  46. data/spec/lib/secure_headers/headers/cookie_spec.rb +0 -179
  47. data/spec/lib/secure_headers/headers/expect_certificate_transparency_spec.rb +0 -42
  48. data/spec/lib/secure_headers/headers/policy_management_spec.rb +0 -265
  49. data/spec/lib/secure_headers/headers/referrer_policy_spec.rb +0 -91
  50. data/spec/lib/secure_headers/headers/strict_transport_security_spec.rb +0 -33
  51. data/spec/lib/secure_headers/headers/x_content_type_options_spec.rb +0 -31
  52. data/spec/lib/secure_headers/headers/x_download_options_spec.rb +0 -29
  53. data/spec/lib/secure_headers/headers/x_frame_options_spec.rb +0 -36
  54. data/spec/lib/secure_headers/headers/x_permitted_cross_domain_policies_spec.rb +0 -48
  55. data/spec/lib/secure_headers/headers/x_xss_protection_spec.rb +0 -47
  56. data/spec/lib/secure_headers/middleware_spec.rb +0 -117
  57. data/spec/lib/secure_headers/view_helpers_spec.rb +0 -192
  58. data/spec/lib/secure_headers_spec.rb +0 -516
  59. 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