secure_headers 3.1.2 → 3.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +126 -0
- data/README.md +147 -24
- data/lib/secure_headers/configuration.rb +46 -4
- data/lib/secure_headers/hash_helper.rb +10 -0
- data/lib/secure_headers/headers/cookie.rb +126 -0
- data/lib/secure_headers/headers/policy_management.rb +2 -0
- data/lib/secure_headers/headers/referrer_policy.rb +33 -0
- data/lib/secure_headers/middleware.rb +28 -8
- data/lib/secure_headers/railtie.rb +5 -1
- data/lib/secure_headers/utils/cookies_config.rb +94 -0
- data/lib/secure_headers/view_helper.rb +64 -0
- data/lib/secure_headers.rb +4 -0
- data/lib/tasks/tasks.rake +81 -0
- data/secure_headers.gemspec +1 -1
- data/spec/lib/secure_headers/configuration_spec.rb +9 -1
- data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +5 -0
- data/spec/lib/secure_headers/headers/cookie_spec.rb +164 -0
- data/spec/lib/secure_headers/headers/referrer_policy_spec.rb +54 -0
- data/spec/lib/secure_headers/middleware_spec.rb +64 -13
- data/spec/lib/secure_headers/view_helpers_spec.rb +125 -0
- data/spec/lib/secure_headers_spec.rb +23 -0
- data/spec/spec_helper.rb +14 -0
- data/upgrading-to-3-0.md +1 -0
- metadata +13 -2
|
@@ -10,9 +10,25 @@ module SecureHeaders
|
|
|
10
10
|
|
|
11
11
|
before(:each) do
|
|
12
12
|
reset_config
|
|
13
|
+
Configuration.default
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "warns if the hpkp report-uri host is the same as the current host" do
|
|
17
|
+
report_host = "report-uri.io"
|
|
13
18
|
Configuration.default do |config|
|
|
14
|
-
|
|
19
|
+
config.hpkp = {
|
|
20
|
+
max_age: 10000000,
|
|
21
|
+
pins: [
|
|
22
|
+
{sha256: 'b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c'},
|
|
23
|
+
{sha256: '73a2c64f9545172c1195efb6616ca5f7afd1df6f245407cafb90de3998a1c97f'}
|
|
24
|
+
],
|
|
25
|
+
report_uri: "https://#{report_host}/example-hpkp"
|
|
26
|
+
}
|
|
15
27
|
end
|
|
28
|
+
|
|
29
|
+
expect(Kernel).to receive(:warn).with(Middleware::HPKP_SAME_HOST_WARNING)
|
|
30
|
+
|
|
31
|
+
middleware.call(Rack::MockRequest.env_for("https://#{report_host}", {}))
|
|
16
32
|
end
|
|
17
33
|
|
|
18
34
|
it "sets the headers" do
|
|
@@ -38,21 +54,56 @@ module SecureHeaders
|
|
|
38
54
|
expect(env[CSP::HEADER_NAME]).to match("example.org")
|
|
39
55
|
end
|
|
40
56
|
|
|
41
|
-
context "
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
57
|
+
context "secure_cookies" do
|
|
58
|
+
context "cookies should be flagged" do
|
|
59
|
+
it "flags cookies as secure" do
|
|
60
|
+
capture_warning do
|
|
61
|
+
Configuration.default { |config| config.secure_cookies = true }
|
|
62
|
+
end
|
|
63
|
+
request = Rack::Request.new("HTTPS" => "on")
|
|
64
|
+
_, env = cookie_middleware.call request.env
|
|
65
|
+
expect(env['Set-Cookie']).to eq("foo=bar; secure")
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
context "cookies should not be flagged" do
|
|
70
|
+
it "does not flags cookies as secure" do
|
|
71
|
+
capture_warning do
|
|
72
|
+
Configuration.default { |config| config.secure_cookies = false }
|
|
73
|
+
end
|
|
74
|
+
request = Rack::Request.new("HTTPS" => "on")
|
|
75
|
+
_, env = cookie_middleware.call request.env
|
|
76
|
+
expect(env['Set-Cookie']).to eq("foo=bar")
|
|
77
|
+
end
|
|
47
78
|
end
|
|
48
79
|
end
|
|
49
80
|
|
|
50
|
-
context "cookies
|
|
51
|
-
it "
|
|
52
|
-
Configuration.default { |config| config.
|
|
53
|
-
request = Rack::
|
|
54
|
-
|
|
55
|
-
|
|
81
|
+
context "cookies" do
|
|
82
|
+
it "flags cookies from configuration" do
|
|
83
|
+
Configuration.default { |config| config.cookies = { secure: true, httponly: true } }
|
|
84
|
+
request = Rack::Request.new("HTTPS" => "on")
|
|
85
|
+
_, env = cookie_middleware.call request.env
|
|
86
|
+
|
|
87
|
+
expect(env['Set-Cookie']).to eq("foo=bar; secure; HttpOnly")
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
it "flags cookies with a combination of SameSite configurations" do
|
|
91
|
+
cookie_middleware = Middleware.new(lambda { |env| [200, env.merge("Set-Cookie" => ["_session=foobar", "_guest=true"]), "app"] })
|
|
92
|
+
|
|
93
|
+
Configuration.default { |config| config.cookies = { samesite: { lax: { except: ["_session"] }, strict: { only: ["_session"] } } } }
|
|
94
|
+
request = Rack::Request.new("HTTPS" => "on")
|
|
95
|
+
_, env = cookie_middleware.call request.env
|
|
96
|
+
|
|
97
|
+
expect(env['Set-Cookie']).to match("_session=foobar; SameSite=Strict")
|
|
98
|
+
expect(env['Set-Cookie']).to match("_guest=true; SameSite=Lax")
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it "disables secure cookies for non-https requests" do
|
|
102
|
+
Configuration.default { |config| config.cookies = { secure: true } }
|
|
103
|
+
|
|
104
|
+
request = Rack::Request.new("HTTPS" => "off")
|
|
105
|
+
_, env = cookie_middleware.call request.env
|
|
106
|
+
expect(env['Set-Cookie']).to eq("foo=bar")
|
|
56
107
|
end
|
|
57
108
|
end
|
|
58
109
|
end
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "erb"
|
|
3
|
+
|
|
4
|
+
class Message < ERB
|
|
5
|
+
include SecureHeaders::ViewHelpers
|
|
6
|
+
|
|
7
|
+
def self.template
|
|
8
|
+
<<-TEMPLATE
|
|
9
|
+
<% hashed_javascript_tag(raise_error_on_unrecognized_hash = true) do %>
|
|
10
|
+
console.log(1)
|
|
11
|
+
<% end %>
|
|
12
|
+
|
|
13
|
+
<% hashed_style_tag do %>
|
|
14
|
+
body {
|
|
15
|
+
background-color: black;
|
|
16
|
+
}
|
|
17
|
+
<% end %>
|
|
18
|
+
|
|
19
|
+
<% nonced_javascript_tag do %>
|
|
20
|
+
body {
|
|
21
|
+
console.log(1)
|
|
22
|
+
}
|
|
23
|
+
<% end %>
|
|
24
|
+
|
|
25
|
+
<% nonced_style_tag do %>
|
|
26
|
+
body {
|
|
27
|
+
background-color: black;
|
|
28
|
+
}
|
|
29
|
+
<% end %>
|
|
30
|
+
<%= @name %>
|
|
31
|
+
|
|
32
|
+
TEMPLATE
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def initialize(request, options = {})
|
|
36
|
+
@virtual_path = "/asdfs/index"
|
|
37
|
+
@_request = request
|
|
38
|
+
@template = self.class.template
|
|
39
|
+
super(@template)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def capture(*args)
|
|
43
|
+
yield(*args)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def content_tag(type, content = nil, options = nil, &block)
|
|
47
|
+
content = if block_given?
|
|
48
|
+
capture(block)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
if options.is_a?(Hash)
|
|
52
|
+
options = options.map {|k,v| " #{k}=#{v}"}
|
|
53
|
+
end
|
|
54
|
+
"<#{type}#{options}>#{content}</#{type}>"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def result
|
|
58
|
+
super(binding)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def request
|
|
62
|
+
@_request
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
module SecureHeaders
|
|
67
|
+
describe ViewHelpers do
|
|
68
|
+
let(:app) { lambda { |env| [200, env, "app"] } }
|
|
69
|
+
let(:middleware) { Middleware.new(app) }
|
|
70
|
+
let(:request) { Rack::Request.new("HTTP_USER_AGENT" => USER_AGENTS[:chrome]) }
|
|
71
|
+
let(:filename) { "app/views/asdfs/index.html.erb" }
|
|
72
|
+
|
|
73
|
+
before(:all) do
|
|
74
|
+
Configuration.default do |config|
|
|
75
|
+
config.csp[:script_src] = %w('self')
|
|
76
|
+
config.csp[:style_src] = %w('self')
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
after(:each) do
|
|
81
|
+
Configuration.instance_variable_set(:@script_hashes, nil)
|
|
82
|
+
Configuration.instance_variable_set(:@style_hashes, nil)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it "raises an error when using hashed content without precomputed hashes" do
|
|
86
|
+
expect {
|
|
87
|
+
Message.new(request).result
|
|
88
|
+
}.to raise_error(ViewHelpers::UnexpectedHashedScriptException)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "raises an error when using hashed content with precomputed hashes, but none for the given file" do
|
|
92
|
+
Configuration.instance_variable_set(:@script_hashes, filename.reverse => ["'sha256-123'"])
|
|
93
|
+
expect {
|
|
94
|
+
Message.new(request).result
|
|
95
|
+
}.to raise_error(ViewHelpers::UnexpectedHashedScriptException)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it "raises an error when using previously unknown hashed content with precomputed hashes for a given file" do
|
|
99
|
+
Configuration.instance_variable_set(:@script_hashes, filename => ["'sha256-123'"])
|
|
100
|
+
expect {
|
|
101
|
+
Message.new(request).result
|
|
102
|
+
}.to raise_error(ViewHelpers::UnexpectedHashedScriptException)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it "adds known hash values to the corresponding headers when the helper is used" do
|
|
106
|
+
begin
|
|
107
|
+
allow(SecureRandom).to receive(:base64).and_return("abc123")
|
|
108
|
+
|
|
109
|
+
expected_hash = "sha256-3/URElR9+3lvLIouavYD/vhoICSNKilh15CzI/nKqg8="
|
|
110
|
+
Configuration.instance_variable_set(:@script_hashes, filename => ["'#{expected_hash}'"])
|
|
111
|
+
expected_style_hash = "sha256-7oYK96jHg36D6BM042er4OfBnyUDTG3pH1L8Zso3aGc="
|
|
112
|
+
Configuration.instance_variable_set(:@style_hashes, filename => ["'#{expected_style_hash}'"])
|
|
113
|
+
|
|
114
|
+
# render erb that calls out to helpers.
|
|
115
|
+
Message.new(request).result
|
|
116
|
+
_, env = middleware.call request.env
|
|
117
|
+
|
|
118
|
+
expect(env[CSP::HEADER_NAME]).to match(/script-src[^;]*'#{Regexp.escape(expected_hash)}'/)
|
|
119
|
+
expect(env[CSP::HEADER_NAME]).to match(/script-src[^;]*'nonce-abc123'/)
|
|
120
|
+
expect(env[CSP::HEADER_NAME]).to match(/style-src[^;]*'nonce-abc123'/)
|
|
121
|
+
expect(env[CSP::HEADER_NAME]).to match(/style-src[^;]*'#{Regexp.escape(expected_style_hash)}'/)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
@@ -20,6 +20,13 @@ module SecureHeaders
|
|
|
20
20
|
end.to raise_error(Configuration::NotYetConfiguredError)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
it "raises and ArgumentError when referencing an override that has not been set" do
|
|
24
|
+
expect do
|
|
25
|
+
Configuration.default
|
|
26
|
+
SecureHeaders.use_secure_headers_override(request, :missing)
|
|
27
|
+
end.to raise_error(ArgumentError)
|
|
28
|
+
end
|
|
29
|
+
|
|
23
30
|
describe "#header_hash_for" do
|
|
24
31
|
it "allows you to opt out of individual headers via API" do
|
|
25
32
|
Configuration.default
|
|
@@ -305,6 +312,14 @@ module SecureHeaders
|
|
|
305
312
|
end.to raise_error(XPCDPConfigError)
|
|
306
313
|
end
|
|
307
314
|
|
|
315
|
+
it "validates your referrer_policy config upon configuration" do
|
|
316
|
+
expect do
|
|
317
|
+
Configuration.default do |config|
|
|
318
|
+
config.referrer_policy = "lol"
|
|
319
|
+
end
|
|
320
|
+
end.to raise_error(ReferrerPolicyConfigError)
|
|
321
|
+
end
|
|
322
|
+
|
|
308
323
|
it "validates your hpkp config upon configuration" do
|
|
309
324
|
expect do
|
|
310
325
|
Configuration.default do |config|
|
|
@@ -312,6 +327,14 @@ module SecureHeaders
|
|
|
312
327
|
end
|
|
313
328
|
end.to raise_error(PublicKeyPinsConfigError)
|
|
314
329
|
end
|
|
330
|
+
|
|
331
|
+
it "validates your cookies config upon configuration" do
|
|
332
|
+
expect do
|
|
333
|
+
Configuration.default do |config|
|
|
334
|
+
config.cookies = { secure: "lol" }
|
|
335
|
+
end
|
|
336
|
+
end.to raise_error(CookiesConfigError)
|
|
337
|
+
end
|
|
315
338
|
end
|
|
316
339
|
end
|
|
317
340
|
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -11,6 +11,7 @@ require File.join(File.dirname(__FILE__), '..', 'lib', 'secure_headers')
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
USER_AGENTS = {
|
|
14
|
+
edge: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246",
|
|
14
15
|
firefox: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:14.0) Gecko/20100101 Firefox/14.0.1',
|
|
15
16
|
chrome: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.56 Safari/536.5',
|
|
16
17
|
ie: 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/5.0)',
|
|
@@ -30,6 +31,7 @@ def expect_default_values(hash)
|
|
|
30
31
|
expect(hash[SecureHeaders::XXssProtection::HEADER_NAME]).to eq(SecureHeaders::XXssProtection::DEFAULT_VALUE)
|
|
31
32
|
expect(hash[SecureHeaders::XContentTypeOptions::HEADER_NAME]).to eq(SecureHeaders::XContentTypeOptions::DEFAULT_VALUE)
|
|
32
33
|
expect(hash[SecureHeaders::XPermittedCrossDomainPolicies::HEADER_NAME]).to eq(SecureHeaders::XPermittedCrossDomainPolicies::DEFAULT_VALUE)
|
|
34
|
+
expect(hash[SecureHeaders::ReferrerPolicy::HEADER_NAME]).to be_nil
|
|
33
35
|
end
|
|
34
36
|
|
|
35
37
|
module SecureHeaders
|
|
@@ -45,3 +47,15 @@ end
|
|
|
45
47
|
def reset_config
|
|
46
48
|
SecureHeaders::Configuration.clear_configurations
|
|
47
49
|
end
|
|
50
|
+
|
|
51
|
+
def capture_warning
|
|
52
|
+
begin
|
|
53
|
+
old_stderr = $stderr
|
|
54
|
+
$stderr = StringIO.new
|
|
55
|
+
yield
|
|
56
|
+
result = $stderr.string
|
|
57
|
+
ensure
|
|
58
|
+
$stderr = old_stderr
|
|
59
|
+
end
|
|
60
|
+
result
|
|
61
|
+
end
|
data/upgrading-to-3-0.md
CHANGED
|
@@ -8,6 +8,7 @@ Changes
|
|
|
8
8
|
| Global configuration | `SecureHeaders::Configuration.configure` block | `SecureHeaders::Configuration.default` block |
|
|
9
9
|
| All headers besides HPKP and CSP | Accept hashes as config values | Must be strings (validated during configuration) |
|
|
10
10
|
| CSP directive values | Accepted space delimited strings OR arrays of strings | Must be arrays of strings |
|
|
11
|
+
| CSP Nonce values in views | `@content_security_policy_nonce` | `content_security_policy_script_nonce` or `content_security_policy_style_nonce`
|
|
11
12
|
| `self`/`none` source expressions | could be `self` / `none` / `'self'` / `'none'` | Must be `'self'` or `'none'` |
|
|
12
13
|
| `inline` / `eval` source expressions | could be `inline`, `eval`, `'unsafe-inline'`, or `'unsafe-eval'` | Must be `'unsafe-eval'` or `'unsafe-inline'` |
|
|
13
14
|
| Per-action configuration | override [`def secure_header_options_for(header, options)`](https://github.com/twitter/secureheaders/commit/bb9ebc6c12a677aad29af8e0f08ffd1def56efec#diff-04c6e90faac2675aa89e2176d2eec7d8R111) | Use [named overrides](https://github.com/twitter/secureheaders#named-overrides) or [per-action helpers](https://github.com/twitter/secureheaders#per-action-configuration) |
|
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: 3.
|
|
4
|
+
version: 3.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Neil Matatall
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2016-
|
|
11
|
+
date: 2016-04-29 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rake
|
|
@@ -58,9 +58,12 @@ files:
|
|
|
58
58
|
- Rakefile
|
|
59
59
|
- lib/secure_headers.rb
|
|
60
60
|
- lib/secure_headers/configuration.rb
|
|
61
|
+
- lib/secure_headers/hash_helper.rb
|
|
61
62
|
- lib/secure_headers/headers/content_security_policy.rb
|
|
63
|
+
- lib/secure_headers/headers/cookie.rb
|
|
62
64
|
- lib/secure_headers/headers/policy_management.rb
|
|
63
65
|
- lib/secure_headers/headers/public_key_pins.rb
|
|
66
|
+
- lib/secure_headers/headers/referrer_policy.rb
|
|
64
67
|
- lib/secure_headers/headers/strict_transport_security.rb
|
|
65
68
|
- lib/secure_headers/headers/x_content_type_options.rb
|
|
66
69
|
- lib/secure_headers/headers/x_download_options.rb
|
|
@@ -69,12 +72,16 @@ files:
|
|
|
69
72
|
- lib/secure_headers/headers/x_xss_protection.rb
|
|
70
73
|
- lib/secure_headers/middleware.rb
|
|
71
74
|
- lib/secure_headers/railtie.rb
|
|
75
|
+
- lib/secure_headers/utils/cookies_config.rb
|
|
72
76
|
- lib/secure_headers/view_helper.rb
|
|
77
|
+
- lib/tasks/tasks.rake
|
|
73
78
|
- secure_headers.gemspec
|
|
74
79
|
- spec/lib/secure_headers/configuration_spec.rb
|
|
75
80
|
- spec/lib/secure_headers/headers/content_security_policy_spec.rb
|
|
81
|
+
- spec/lib/secure_headers/headers/cookie_spec.rb
|
|
76
82
|
- spec/lib/secure_headers/headers/policy_management_spec.rb
|
|
77
83
|
- spec/lib/secure_headers/headers/public_key_pins_spec.rb
|
|
84
|
+
- spec/lib/secure_headers/headers/referrer_policy_spec.rb
|
|
78
85
|
- spec/lib/secure_headers/headers/strict_transport_security_spec.rb
|
|
79
86
|
- spec/lib/secure_headers/headers/x_content_type_options_spec.rb
|
|
80
87
|
- spec/lib/secure_headers/headers/x_download_options_spec.rb
|
|
@@ -82,6 +89,7 @@ files:
|
|
|
82
89
|
- spec/lib/secure_headers/headers/x_permitted_cross_domain_policies_spec.rb
|
|
83
90
|
- spec/lib/secure_headers/headers/x_xss_protection_spec.rb
|
|
84
91
|
- spec/lib/secure_headers/middleware_spec.rb
|
|
92
|
+
- spec/lib/secure_headers/view_helpers_spec.rb
|
|
85
93
|
- spec/lib/secure_headers_spec.rb
|
|
86
94
|
- spec/spec_helper.rb
|
|
87
95
|
- upgrading-to-3-0.md
|
|
@@ -113,8 +121,10 @@ summary: Add easily configured security headers to responses including content-s
|
|
|
113
121
|
test_files:
|
|
114
122
|
- spec/lib/secure_headers/configuration_spec.rb
|
|
115
123
|
- spec/lib/secure_headers/headers/content_security_policy_spec.rb
|
|
124
|
+
- spec/lib/secure_headers/headers/cookie_spec.rb
|
|
116
125
|
- spec/lib/secure_headers/headers/policy_management_spec.rb
|
|
117
126
|
- spec/lib/secure_headers/headers/public_key_pins_spec.rb
|
|
127
|
+
- spec/lib/secure_headers/headers/referrer_policy_spec.rb
|
|
118
128
|
- spec/lib/secure_headers/headers/strict_transport_security_spec.rb
|
|
119
129
|
- spec/lib/secure_headers/headers/x_content_type_options_spec.rb
|
|
120
130
|
- spec/lib/secure_headers/headers/x_download_options_spec.rb
|
|
@@ -122,5 +132,6 @@ test_files:
|
|
|
122
132
|
- spec/lib/secure_headers/headers/x_permitted_cross_domain_policies_spec.rb
|
|
123
133
|
- spec/lib/secure_headers/headers/x_xss_protection_spec.rb
|
|
124
134
|
- spec/lib/secure_headers/middleware_spec.rb
|
|
135
|
+
- spec/lib/secure_headers/view_helpers_spec.rb
|
|
125
136
|
- spec/lib/secure_headers_spec.rb
|
|
126
137
|
- spec/spec_helper.rb
|