secure_headers 7.0.0 → 7.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +13 -13
- data/lib/secure_headers/configuration.rb +1 -1
- data/lib/secure_headers/headers/clear_site_data.rb +4 -4
- data/lib/secure_headers/headers/content_security_policy.rb +2 -2
- data/lib/secure_headers/headers/content_security_policy_config.rb +2 -2
- data/lib/secure_headers/headers/expect_certificate_transparency.rb +2 -2
- data/lib/secure_headers/headers/policy_management.rb +2 -2
- data/lib/secure_headers/headers/referrer_policy.rb +1 -1
- data/lib/secure_headers/headers/strict_transport_security.rb +1 -1
- data/lib/secure_headers/headers/x_content_type_options.rb +1 -1
- data/lib/secure_headers/headers/x_download_options.rb +2 -2
- data/lib/secure_headers/headers/x_frame_options.rb +1 -1
- data/lib/secure_headers/headers/x_permitted_cross_domain_policies.rb +2 -2
- data/lib/secure_headers/headers/x_xss_protection.rb +1 -1
- data/lib/secure_headers/railtie.rb +5 -5
- data/lib/secure_headers/version.rb +1 -1
- data/secure_headers.gemspec +13 -3
- metadata +14 -63
- data/.github/ISSUE_TEMPLATE.md +0 -41
- data/.github/PULL_REQUEST_TEMPLATE.md +0 -20
- data/.github/dependabot.yml +0 -6
- data/.github/workflows/build.yml +0 -25
- data/.github/workflows/github-release.yml +0 -28
- data/.gitignore +0 -13
- data/.rspec +0 -3
- data/.rubocop.yml +0 -4
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/CODE_OF_CONDUCT.md +0 -46
- data/CONTRIBUTING.md +0 -41
- data/Guardfile +0 -13
- data/Rakefile +0 -32
- data/docs/cookies.md +0 -65
- data/docs/hashes.md +0 -64
- data/docs/named_overrides_and_appends.md +0 -104
- data/docs/per_action_configuration.md +0 -139
- data/docs/sinatra.md +0 -25
- data/docs/upgrading-to-3-0.md +0 -42
- data/docs/upgrading-to-4-0.md +0 -35
- data/docs/upgrading-to-5-0.md +0 -15
- data/docs/upgrading-to-6-0.md +0 -50
- data/docs/upgrading-to-7-0.md +0 -12
- data/spec/lib/secure_headers/configuration_spec.rb +0 -121
- data/spec/lib/secure_headers/headers/clear_site_data_spec.rb +0 -87
- data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +0 -215
- data/spec/lib/secure_headers/headers/cookie_spec.rb +0 -179
- data/spec/lib/secure_headers/headers/expect_certificate_transparency_spec.rb +0 -42
- data/spec/lib/secure_headers/headers/policy_management_spec.rb +0 -265
- data/spec/lib/secure_headers/headers/referrer_policy_spec.rb +0 -91
- data/spec/lib/secure_headers/headers/strict_transport_security_spec.rb +0 -33
- data/spec/lib/secure_headers/headers/x_content_type_options_spec.rb +0 -31
- data/spec/lib/secure_headers/headers/x_download_options_spec.rb +0 -29
- data/spec/lib/secure_headers/headers/x_frame_options_spec.rb +0 -36
- data/spec/lib/secure_headers/headers/x_permitted_cross_domain_policies_spec.rb +0 -48
- data/spec/lib/secure_headers/headers/x_xss_protection_spec.rb +0 -47
- data/spec/lib/secure_headers/middleware_spec.rb +0 -117
- data/spec/lib/secure_headers/view_helpers_spec.rb +0 -192
- data/spec/lib/secure_headers_spec.rb +0 -516
- data/spec/spec_helper.rb +0 -64
@@ -1,121 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require "spec_helper"
|
3
|
-
|
4
|
-
module SecureHeaders
|
5
|
-
describe Configuration do
|
6
|
-
before(:each) do
|
7
|
-
reset_config
|
8
|
-
end
|
9
|
-
|
10
|
-
it "has a default config" do
|
11
|
-
expect(Configuration.default).to_not be_nil
|
12
|
-
end
|
13
|
-
|
14
|
-
it "has an 'noop' override" do
|
15
|
-
Configuration.default
|
16
|
-
expect(Configuration.overrides(Configuration::NOOP_OVERRIDE)).to_not be_nil
|
17
|
-
end
|
18
|
-
|
19
|
-
it "dup results in a copy of the default config" do
|
20
|
-
Configuration.default
|
21
|
-
original_configuration = Configuration.send(:default_config)
|
22
|
-
configuration = Configuration.dup
|
23
|
-
expect(original_configuration).not_to be(configuration)
|
24
|
-
Configuration::CONFIG_ATTRIBUTES.each do |attr|
|
25
|
-
expect(original_configuration.send(attr)).to eq(configuration.send(attr))
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
it "stores an override" do
|
30
|
-
Configuration.override(:test_override) do |config|
|
31
|
-
config.x_frame_options = "DENY"
|
32
|
-
end
|
33
|
-
|
34
|
-
expect(Configuration.overrides(:test_override)).to_not be_nil
|
35
|
-
end
|
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
|
-
|
91
|
-
it "deprecates the secure_cookies configuration" do
|
92
|
-
expect {
|
93
|
-
Configuration.default do |config|
|
94
|
-
config.secure_cookies = true
|
95
|
-
end
|
96
|
-
}.to raise_error(ArgumentError)
|
97
|
-
end
|
98
|
-
|
99
|
-
it "gives cookies a default config" do
|
100
|
-
expect(Configuration.default.cookies).to eq({httponly: true, secure: true, samesite: {lax: true}})
|
101
|
-
end
|
102
|
-
|
103
|
-
it "allows OPT_OUT" do
|
104
|
-
Configuration.default do |config|
|
105
|
-
config.cookies = OPT_OUT
|
106
|
-
end
|
107
|
-
|
108
|
-
config = Configuration.dup
|
109
|
-
expect(config.cookies).to eq(OPT_OUT)
|
110
|
-
end
|
111
|
-
|
112
|
-
it "allows me to be explicit too" do
|
113
|
-
Configuration.default do |config|
|
114
|
-
config.cookies = {httponly: true, secure: true, samesite: {lax: false}}
|
115
|
-
end
|
116
|
-
|
117
|
-
config = Configuration.dup
|
118
|
-
expect(config.cookies).to eq({httponly: true, secure: true, samesite: {lax: false}})
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
@@ -1,87 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require "spec_helper"
|
3
|
-
|
4
|
-
module SecureHeaders
|
5
|
-
describe ClearSiteData do
|
6
|
-
describe "make_header" do
|
7
|
-
it "returns nil with nil config" do
|
8
|
-
expect(described_class.make_header).to be_nil
|
9
|
-
end
|
10
|
-
|
11
|
-
it "returns nil with empty config" do
|
12
|
-
expect(described_class.make_header([])).to be_nil
|
13
|
-
end
|
14
|
-
|
15
|
-
it "returns nil with opt-out config" do
|
16
|
-
expect(described_class.make_header(OPT_OUT)).to be_nil
|
17
|
-
end
|
18
|
-
|
19
|
-
it "returns all types with `true` config" do
|
20
|
-
name, value = described_class.make_header(true)
|
21
|
-
|
22
|
-
expect(name).to eq(ClearSiteData::HEADER_NAME)
|
23
|
-
expect(value).to eq(
|
24
|
-
%("cache", "cookies", "storage", "executionContexts")
|
25
|
-
)
|
26
|
-
end
|
27
|
-
|
28
|
-
it "returns specified types" do
|
29
|
-
name, value = described_class.make_header(["foo", "bar"])
|
30
|
-
|
31
|
-
expect(name).to eq(ClearSiteData::HEADER_NAME)
|
32
|
-
expect(value).to eq(%("foo", "bar"))
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
describe "validate_config!" do
|
37
|
-
it "succeeds for `true` config" do
|
38
|
-
expect do
|
39
|
-
described_class.validate_config!(true)
|
40
|
-
end.not_to raise_error
|
41
|
-
end
|
42
|
-
|
43
|
-
it "succeeds for `nil` config" do
|
44
|
-
expect do
|
45
|
-
described_class.validate_config!(nil)
|
46
|
-
end.not_to raise_error
|
47
|
-
end
|
48
|
-
|
49
|
-
it "succeeds for opt-out config" do
|
50
|
-
expect do
|
51
|
-
described_class.validate_config!(OPT_OUT)
|
52
|
-
end.not_to raise_error
|
53
|
-
end
|
54
|
-
|
55
|
-
it "succeeds for empty config" do
|
56
|
-
expect do
|
57
|
-
described_class.validate_config!([])
|
58
|
-
end.not_to raise_error
|
59
|
-
end
|
60
|
-
|
61
|
-
it "succeeds for Array of Strings config" do
|
62
|
-
expect do
|
63
|
-
described_class.validate_config!(["foo"])
|
64
|
-
end.not_to raise_error
|
65
|
-
end
|
66
|
-
|
67
|
-
it "fails for Array of non-String config" do
|
68
|
-
expect do
|
69
|
-
described_class.validate_config!([1])
|
70
|
-
end.to raise_error(ClearSiteDataConfigError)
|
71
|
-
end
|
72
|
-
|
73
|
-
it "fails for other types of config" do
|
74
|
-
expect do
|
75
|
-
described_class.validate_config!(:cookies)
|
76
|
-
end.to raise_error(ClearSiteDataConfigError)
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
describe "make_header_value" do
|
81
|
-
it "returns a string of quoted values that are comma separated" do
|
82
|
-
value = described_class.make_header_value(["foo", "bar"])
|
83
|
-
expect(value).to eq(%("foo", "bar"))
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
@@ -1,215 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require "spec_helper"
|
3
|
-
|
4
|
-
module SecureHeaders
|
5
|
-
describe ContentSecurityPolicy do
|
6
|
-
let (:default_opts) do
|
7
|
-
{
|
8
|
-
default_src: %w(https:),
|
9
|
-
img_src: %w(https: data:),
|
10
|
-
script_src: %w('unsafe-inline' 'unsafe-eval' https: data:),
|
11
|
-
style_src: %w('unsafe-inline' https: about:),
|
12
|
-
report_uri: %w(/csp_report)
|
13
|
-
}
|
14
|
-
end
|
15
|
-
|
16
|
-
describe "#name" do
|
17
|
-
context "when in report-only mode" do
|
18
|
-
specify { expect(ContentSecurityPolicy.new(default_opts.merge(report_only: true)).name).to eq(ContentSecurityPolicyReportOnlyConfig::HEADER_NAME) }
|
19
|
-
end
|
20
|
-
|
21
|
-
context "when in enforce mode" do
|
22
|
-
specify { expect(ContentSecurityPolicy.new(default_opts).name).to eq(ContentSecurityPolicyConfig::HEADER_NAME) }
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
describe "#value" do
|
27
|
-
it "uses a safe but non-breaking default value" do
|
28
|
-
expect(ContentSecurityPolicy.new.value).to eq("default-src https:; form-action 'self'; img-src https: data: 'self'; object-src 'none'; script-src https:; style-src 'self' 'unsafe-inline' https:")
|
29
|
-
end
|
30
|
-
|
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.))
|
33
|
-
expect(ContentSecurityPolicy.new(frame_ancestors: %w(https://google.com;script-src https://*;.;)).value).to eq("frame-ancestors google.com script-src * .")
|
34
|
-
end
|
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
|
-
|
41
|
-
it "discards 'none' values if any other source expressions are present" do
|
42
|
-
csp = ContentSecurityPolicy.new(default_opts.merge(child_src: %w('self' 'none')))
|
43
|
-
expect(csp.value).not_to include("'none'")
|
44
|
-
end
|
45
|
-
|
46
|
-
it "discards source expressions (besides unsafe-* and non-host source values) when * is present" do
|
47
|
-
csp = ContentSecurityPolicy.new(default_src: %w(* 'unsafe-inline' 'unsafe-eval' http: https: example.org data: blob:))
|
48
|
-
expect(csp.value).to eq("default-src * 'unsafe-inline' 'unsafe-eval' data: blob:")
|
49
|
-
end
|
50
|
-
|
51
|
-
it "does not minify source expressions based on overlapping wildcards" do
|
52
|
-
config = {
|
53
|
-
default_src: %w(a.example.org b.example.org *.example.org https://*.example.org)
|
54
|
-
}
|
55
|
-
csp = ContentSecurityPolicy.new(config)
|
56
|
-
expect(csp.value).to eq("default-src a.example.org b.example.org *.example.org")
|
57
|
-
end
|
58
|
-
|
59
|
-
it "removes http/s schemes from hosts" do
|
60
|
-
csp = ContentSecurityPolicy.new(default_src: %w(https://example.org))
|
61
|
-
expect(csp.value).to eq("default-src example.org")
|
62
|
-
end
|
63
|
-
|
64
|
-
it "does not build directives with a value of OPT_OUT (and bypasses directive requirements)" do
|
65
|
-
csp = ContentSecurityPolicy.new(default_src: %w(https://example.org), script_src: OPT_OUT)
|
66
|
-
expect(csp.value).to eq("default-src example.org")
|
67
|
-
end
|
68
|
-
|
69
|
-
it "does not remove schemes from report-uri values" do
|
70
|
-
csp = ContentSecurityPolicy.new(default_src: %w(https:), report_uri: %w(https://example.org))
|
71
|
-
expect(csp.value).to eq("default-src https:; report-uri https://example.org")
|
72
|
-
end
|
73
|
-
|
74
|
-
it "does not remove schemes when :preserve_schemes is true" do
|
75
|
-
csp = ContentSecurityPolicy.new(default_src: %w(https://example.org), preserve_schemes: true)
|
76
|
-
expect(csp.value).to eq("default-src https://example.org")
|
77
|
-
end
|
78
|
-
|
79
|
-
it "removes nil from source lists" do
|
80
|
-
csp = ContentSecurityPolicy.new(default_src: ["https://example.org", nil])
|
81
|
-
expect(csp.value).to eq("default-src example.org")
|
82
|
-
end
|
83
|
-
|
84
|
-
it "does not add a directive if the value is an empty array (or all nil)" do
|
85
|
-
csp = ContentSecurityPolicy.new(default_src: ["https://example.org"], script_src: [nil])
|
86
|
-
expect(csp.value).to eq("default-src example.org")
|
87
|
-
end
|
88
|
-
|
89
|
-
it "does not add a directive if the value is nil" do
|
90
|
-
csp = ContentSecurityPolicy.new(default_src: ["https://example.org"], script_src: nil)
|
91
|
-
expect(csp.value).to eq("default-src example.org")
|
92
|
-
end
|
93
|
-
|
94
|
-
it "does add a boolean directive if the value is true" do
|
95
|
-
csp = ContentSecurityPolicy.new(default_src: ["https://example.org"], upgrade_insecure_requests: true)
|
96
|
-
expect(csp.value).to eq("default-src example.org; upgrade-insecure-requests")
|
97
|
-
end
|
98
|
-
|
99
|
-
it "does not add a boolean directive if the value is false" do
|
100
|
-
csp = ContentSecurityPolicy.new(default_src: ["https://example.org"], upgrade_insecure_requests: false)
|
101
|
-
expect(csp.value).to eq("default-src example.org")
|
102
|
-
end
|
103
|
-
|
104
|
-
it "handles wildcard subdomain with wildcard port" do
|
105
|
-
csp = ContentSecurityPolicy.new(default_src: %w(https://*.example.org:*))
|
106
|
-
expect(csp.value).to eq("default-src *.example.org:*")
|
107
|
-
end
|
108
|
-
|
109
|
-
it "deduplicates source expressions that match exactly (after scheme stripping)" do
|
110
|
-
csp = ContentSecurityPolicy.new(default_src: %w(example.org https://example.org example.org))
|
111
|
-
expect(csp.value).to eq("default-src example.org")
|
112
|
-
end
|
113
|
-
|
114
|
-
it "does not deduplicate non-matching schema source expressions" do
|
115
|
-
csp = ContentSecurityPolicy.new(default_src: %w(*.example.org wss://example.example.org))
|
116
|
-
expect(csp.value).to eq("default-src *.example.org wss://example.example.org")
|
117
|
-
end
|
118
|
-
|
119
|
-
it "creates maximally strict sandbox policy when passed no sandbox token values" do
|
120
|
-
csp = ContentSecurityPolicy.new(default_src: %w(example.org), sandbox: [])
|
121
|
-
expect(csp.value).to eq("default-src example.org; sandbox")
|
122
|
-
end
|
123
|
-
|
124
|
-
it "creates maximally strict sandbox policy when passed true" do
|
125
|
-
csp = ContentSecurityPolicy.new(default_src: %w(example.org), sandbox: true)
|
126
|
-
expect(csp.value).to eq("default-src example.org; sandbox")
|
127
|
-
end
|
128
|
-
|
129
|
-
it "creates sandbox policy when passed valid sandbox token values" do
|
130
|
-
csp = ContentSecurityPolicy.new(default_src: %w(example.org), sandbox: %w(allow-forms allow-scripts))
|
131
|
-
expect(csp.value).to eq("default-src example.org; sandbox allow-forms allow-scripts")
|
132
|
-
end
|
133
|
-
|
134
|
-
it "does not emit a warning when using frame-src" do
|
135
|
-
expect(Kernel).to_not receive(:warn)
|
136
|
-
ContentSecurityPolicy.new(default_src: %w('self'), frame_src: %w('self')).value
|
137
|
-
end
|
138
|
-
|
139
|
-
it "allows script as a require-sri-src" do
|
140
|
-
csp = ContentSecurityPolicy.new(default_src: %w('self'), require_sri_for: %w(script))
|
141
|
-
expect(csp.value).to eq("default-src 'self'; require-sri-for script")
|
142
|
-
end
|
143
|
-
|
144
|
-
it "allows style as a require-sri-src" do
|
145
|
-
csp = ContentSecurityPolicy.new(default_src: %w('self'), require_sri_for: %w(style))
|
146
|
-
expect(csp.value).to eq("default-src 'self'; require-sri-for style")
|
147
|
-
end
|
148
|
-
|
149
|
-
it "allows script and style as a require-sri-src" do
|
150
|
-
csp = ContentSecurityPolicy.new(default_src: %w('self'), require_sri_for: %w(script style))
|
151
|
-
expect(csp.value).to eq("default-src 'self'; require-sri-for script style")
|
152
|
-
end
|
153
|
-
|
154
|
-
it "allows style as a require-trusted-types-for source" do
|
155
|
-
csp = ContentSecurityPolicy.new(default_src: %w('self'), require_trusted_types_for: %w(script))
|
156
|
-
expect(csp.value).to eq("default-src 'self'; require-trusted-types-for script")
|
157
|
-
end
|
158
|
-
|
159
|
-
it "includes prefetch-src" do
|
160
|
-
csp = ContentSecurityPolicy.new(default_src: %w('self'), prefetch_src: %w(foo.com))
|
161
|
-
expect(csp.value).to eq("default-src 'self'; prefetch-src foo.com")
|
162
|
-
end
|
163
|
-
|
164
|
-
it "includes navigate-to" do
|
165
|
-
csp = ContentSecurityPolicy.new(default_src: %w('self'), navigate_to: %w(foo.com))
|
166
|
-
expect(csp.value).to eq("default-src 'self'; navigate-to foo.com")
|
167
|
-
end
|
168
|
-
|
169
|
-
it "supports strict-dynamic" do
|
170
|
-
csp = ContentSecurityPolicy.new({default_src: %w('self'), script_src: [ContentSecurityPolicy::STRICT_DYNAMIC], script_nonce: 123456})
|
171
|
-
expect(csp.value).to eq("default-src 'self'; script-src 'strict-dynamic' 'nonce-123456' 'unsafe-inline'")
|
172
|
-
end
|
173
|
-
|
174
|
-
it "supports strict-dynamic and opting out of the appended 'unsafe-inline'" do
|
175
|
-
csp = ContentSecurityPolicy.new({default_src: %w('self'), script_src: [ContentSecurityPolicy::STRICT_DYNAMIC], script_nonce: 123456, disable_nonce_backwards_compatibility: true })
|
176
|
-
expect(csp.value).to eq("default-src 'self'; script-src 'strict-dynamic' 'nonce-123456'")
|
177
|
-
end
|
178
|
-
|
179
|
-
it "supports script-src-elem directive" do
|
180
|
-
csp = ContentSecurityPolicy.new({script_src: %w('self'), script_src_elem: %w('self')})
|
181
|
-
expect(csp.value).to eq("script-src 'self'; script-src-elem 'self'")
|
182
|
-
end
|
183
|
-
|
184
|
-
it "supports script-src-attr directive" do
|
185
|
-
csp = ContentSecurityPolicy.new({script_src: %w('self'), script_src_attr: %w('self')})
|
186
|
-
expect(csp.value).to eq("script-src 'self'; script-src-attr 'self'")
|
187
|
-
end
|
188
|
-
|
189
|
-
it "supports style-src-elem directive" do
|
190
|
-
csp = ContentSecurityPolicy.new({style_src: %w('self'), style_src_elem: %w('self')})
|
191
|
-
expect(csp.value).to eq("style-src 'self'; style-src-elem 'self'")
|
192
|
-
end
|
193
|
-
|
194
|
-
it "supports style-src-attr directive" do
|
195
|
-
csp = ContentSecurityPolicy.new({style_src: %w('self'), style_src_attr: %w('self')})
|
196
|
-
expect(csp.value).to eq("style-src 'self'; style-src-attr 'self'")
|
197
|
-
end
|
198
|
-
|
199
|
-
it "supports trusted-types directive" do
|
200
|
-
csp = ContentSecurityPolicy.new({trusted_types: %w(blahblahpolicy)})
|
201
|
-
expect(csp.value).to eq("trusted-types blahblahpolicy")
|
202
|
-
end
|
203
|
-
|
204
|
-
it "supports trusted-types directive with 'none'" do
|
205
|
-
csp = ContentSecurityPolicy.new({trusted_types: %w('none')})
|
206
|
-
expect(csp.value).to eq("trusted-types 'none'")
|
207
|
-
end
|
208
|
-
|
209
|
-
it "allows duplicate policy names in trusted-types directive" do
|
210
|
-
csp = ContentSecurityPolicy.new({trusted_types: %w(blahblahpolicy 'allow-duplicates')})
|
211
|
-
expect(csp.value).to eq("trusted-types blahblahpolicy 'allow-duplicates'")
|
212
|
-
end
|
213
|
-
end
|
214
|
-
end
|
215
|
-
end
|
@@ -1,179 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require "spec_helper"
|
3
|
-
|
4
|
-
module SecureHeaders
|
5
|
-
describe Cookie do
|
6
|
-
let(:raw_cookie) { "_session=thisisatest" }
|
7
|
-
|
8
|
-
it "does not tamper with cookies when using OPT_OUT is used" do
|
9
|
-
cookie = Cookie.new(raw_cookie, OPT_OUT)
|
10
|
-
expect(cookie.to_s).to eq(raw_cookie)
|
11
|
-
end
|
12
|
-
|
13
|
-
it "applies httponly, secure, and samesite by default" do
|
14
|
-
cookie = Cookie.new(raw_cookie, nil)
|
15
|
-
expect(cookie.to_s).to eq("_session=thisisatest; secure; HttpOnly; SameSite=Lax")
|
16
|
-
end
|
17
|
-
|
18
|
-
it "preserves existing attributes" do
|
19
|
-
cookie = Cookie.new("_session=thisisatest; secure", secure: true, httponly: OPT_OUT, samesite: OPT_OUT)
|
20
|
-
expect(cookie.to_s).to eq("_session=thisisatest; secure")
|
21
|
-
end
|
22
|
-
|
23
|
-
it "prevents duplicate flagging of attributes" do
|
24
|
-
cookie = Cookie.new("_session=thisisatest; secure", secure: true, httponly: OPT_OUT)
|
25
|
-
expect(cookie.to_s.scan(/secure/i).count).to eq(1)
|
26
|
-
end
|
27
|
-
|
28
|
-
context "Secure cookies" do
|
29
|
-
context "when configured with a boolean" do
|
30
|
-
it "flags cookies as Secure" do
|
31
|
-
cookie = Cookie.new(raw_cookie, secure: true, httponly: OPT_OUT, samesite: OPT_OUT)
|
32
|
-
expect(cookie.to_s).to eq("_session=thisisatest; secure")
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
context "when configured with a Hash" do
|
37
|
-
it "flags cookies as Secure when whitelisted" do
|
38
|
-
cookie = Cookie.new(raw_cookie, secure: { only: ["_session"]}, httponly: OPT_OUT, samesite: OPT_OUT)
|
39
|
-
expect(cookie.to_s).to eq("_session=thisisatest; secure")
|
40
|
-
end
|
41
|
-
|
42
|
-
it "does not flag cookies as Secure when excluded" do
|
43
|
-
cookie = Cookie.new(raw_cookie, secure: { except: ["_session"] }, httponly: OPT_OUT, samesite: OPT_OUT)
|
44
|
-
expect(cookie.to_s).to eq("_session=thisisatest")
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
context "HttpOnly cookies" do
|
50
|
-
context "when configured with a boolean" do
|
51
|
-
it "flags cookies as HttpOnly" do
|
52
|
-
cookie = Cookie.new(raw_cookie, httponly: true, secure: OPT_OUT, samesite: OPT_OUT)
|
53
|
-
expect(cookie.to_s).to eq("_session=thisisatest; HttpOnly")
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
context "when configured with a Hash" do
|
58
|
-
it "flags cookies as HttpOnly when whitelisted" do
|
59
|
-
cookie = Cookie.new(raw_cookie, httponly: { only: ["_session"]}, secure: OPT_OUT, samesite: OPT_OUT)
|
60
|
-
expect(cookie.to_s).to eq("_session=thisisatest; HttpOnly")
|
61
|
-
end
|
62
|
-
|
63
|
-
it "does not flag cookies as HttpOnly when excluded" do
|
64
|
-
cookie = Cookie.new(raw_cookie, httponly: { except: ["_session"] }, secure: OPT_OUT, samesite: OPT_OUT)
|
65
|
-
expect(cookie.to_s).to eq("_session=thisisatest")
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
context "SameSite cookies" do
|
71
|
-
%w(None Lax Strict).each do |flag|
|
72
|
-
it "flags SameSite=#{flag}" do
|
73
|
-
cookie = Cookie.new(raw_cookie, samesite: { flag.downcase.to_sym => { only: ["_session"] } }, secure: OPT_OUT, httponly: OPT_OUT)
|
74
|
-
expect(cookie.to_s).to eq("_session=thisisatest; SameSite=#{flag}")
|
75
|
-
end
|
76
|
-
|
77
|
-
it "flags SameSite=#{flag} when configured with a boolean" do
|
78
|
-
cookie = Cookie.new(raw_cookie, samesite: { flag.downcase.to_sym => true}, secure: OPT_OUT, httponly: OPT_OUT)
|
79
|
-
expect(cookie.to_s).to eq("_session=thisisatest; SameSite=#{flag}")
|
80
|
-
end
|
81
|
-
|
82
|
-
it "does not flag cookies as SameSite=#{flag} when excluded" do
|
83
|
-
cookie = Cookie.new(raw_cookie, samesite: { flag.downcase.to_sym => { except: ["_session"] } }, secure: OPT_OUT, httponly: OPT_OUT)
|
84
|
-
expect(cookie.to_s).to eq("_session=thisisatest")
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
it "flags SameSite=Strict when configured with a boolean" do
|
89
|
-
cookie = Cookie.new(raw_cookie, {samesite: { strict: true}, secure: OPT_OUT, httponly: OPT_OUT})
|
90
|
-
expect(cookie.to_s).to eq("_session=thisisatest; SameSite=Strict")
|
91
|
-
end
|
92
|
-
|
93
|
-
it "flags properly when both lax and strict are configured" do
|
94
|
-
raw_cookie = "_session=thisisatest"
|
95
|
-
cookie = Cookie.new(raw_cookie, samesite: { strict: { only: ["_session"] }, lax: { only: ["_additional_session"] } }, secure: OPT_OUT, httponly: OPT_OUT)
|
96
|
-
expect(cookie.to_s).to eq("_session=thisisatest; SameSite=Strict")
|
97
|
-
end
|
98
|
-
|
99
|
-
it "ignores configuration if the cookie is already flagged" do
|
100
|
-
raw_cookie = "_session=thisisatest; SameSite=Strict"
|
101
|
-
cookie = Cookie.new(raw_cookie, samesite: { lax: true }, secure: OPT_OUT, httponly: OPT_OUT)
|
102
|
-
expect(cookie.to_s).to eq(raw_cookie)
|
103
|
-
end
|
104
|
-
|
105
|
-
it "samesite: true sets all cookies to samesite=lax" do
|
106
|
-
raw_cookie = "_session=thisisatest"
|
107
|
-
cookie = Cookie.new(raw_cookie, samesite: true, secure: OPT_OUT, httponly: OPT_OUT)
|
108
|
-
expect(cookie.to_s).to eq("_session=thisisatest; SameSite=Lax")
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
context "with an invalid configuration" do
|
114
|
-
it "raises an exception when not configured with a Hash" do
|
115
|
-
expect do
|
116
|
-
Cookie.validate_config!("configuration")
|
117
|
-
end.to raise_error(CookiesConfigError)
|
118
|
-
end
|
119
|
-
|
120
|
-
it "raises an exception when configured without a boolean(true or OPT_OUT)/Hash" do
|
121
|
-
expect do
|
122
|
-
Cookie.validate_config!(secure: "true")
|
123
|
-
end.to raise_error(CookiesConfigError)
|
124
|
-
end
|
125
|
-
|
126
|
-
it "raises an exception when configured with false" do
|
127
|
-
expect do
|
128
|
-
Cookie.validate_config!(secure: false)
|
129
|
-
end.to raise_error(CookiesConfigError)
|
130
|
-
end
|
131
|
-
|
132
|
-
it "raises an exception when both only and except filters are provided" do
|
133
|
-
expect do
|
134
|
-
Cookie.validate_config!(secure: { only: [], except: [] })
|
135
|
-
end.to raise_error(CookiesConfigError)
|
136
|
-
end
|
137
|
-
|
138
|
-
it "raises an exception when SameSite is not configured with a Hash" do
|
139
|
-
expect do
|
140
|
-
Cookie.validate_config!(samesite: true)
|
141
|
-
end.to raise_error(CookiesConfigError)
|
142
|
-
end
|
143
|
-
|
144
|
-
cookie_options = %i(none lax strict)
|
145
|
-
cookie_options.each do |flag|
|
146
|
-
(cookie_options - [flag]).each do |other_flag|
|
147
|
-
it "raises an exception when SameSite #{flag} and #{other_flag} enforcement modes are configured with booleans" do
|
148
|
-
expect do
|
149
|
-
Cookie.validate_config!(samesite: { flag => true, other_flag => true})
|
150
|
-
end.to raise_error(CookiesConfigError)
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
it "raises an exception when SameSite lax and strict enforcement modes are configured with booleans" do
|
156
|
-
expect do
|
157
|
-
Cookie.validate_config!(samesite: { lax: true, strict: { only: ["_anything"] } })
|
158
|
-
end.to raise_error(CookiesConfigError)
|
159
|
-
end
|
160
|
-
|
161
|
-
it "raises an exception when both only and except filters are provided to SameSite configurations" do
|
162
|
-
expect do
|
163
|
-
Cookie.validate_config!(samesite: { lax: { only: ["_anything"], except: ["_anythingelse"] } })
|
164
|
-
end.to raise_error(CookiesConfigError)
|
165
|
-
end
|
166
|
-
|
167
|
-
it "raises an exception when both lax and strict only filters are provided to SameSite configurations" do
|
168
|
-
expect do
|
169
|
-
Cookie.validate_config!(samesite: { lax: { only: ["_anything"] }, strict: { only: ["_anything"] } })
|
170
|
-
end.to raise_error(CookiesConfigError)
|
171
|
-
end
|
172
|
-
|
173
|
-
it "raises an exception when both lax and strict only filters are provided to SameSite configurations" do
|
174
|
-
expect do
|
175
|
-
Cookie.validate_config!(samesite: { lax: { except: ["_anything"] }, strict: { except: ["_anything"] } })
|
176
|
-
end.to raise_error(CookiesConfigError)
|
177
|
-
end
|
178
|
-
end
|
179
|
-
end
|
@@ -1,42 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require "spec_helper"
|
3
|
-
|
4
|
-
module SecureHeaders
|
5
|
-
describe ExpectCertificateTransparency do
|
6
|
-
specify { expect(ExpectCertificateTransparency.new(max_age: 1234, enforce: true).value).to eq("enforce, max-age=1234") }
|
7
|
-
specify { expect(ExpectCertificateTransparency.new(max_age: 1234, enforce: false).value).to eq("max-age=1234") }
|
8
|
-
specify { expect(ExpectCertificateTransparency.new(max_age: 1234, enforce: "yolocopter").value).to eq("max-age=1234") }
|
9
|
-
specify { expect(ExpectCertificateTransparency.new(max_age: 1234, report_uri: "https://report-uri.io/expect-ct").value).to eq("max-age=1234, report-uri=\"https://report-uri.io/expect-ct\"") }
|
10
|
-
specify do
|
11
|
-
config = { enforce: true, max_age: 1234, report_uri: "https://report-uri.io/expect-ct" }
|
12
|
-
header_value = "enforce, max-age=1234, report-uri=\"https://report-uri.io/expect-ct\""
|
13
|
-
expect(ExpectCertificateTransparency.new(config).value).to eq(header_value)
|
14
|
-
end
|
15
|
-
|
16
|
-
context "with an invalid configuration" do
|
17
|
-
it "raises an exception when configuration isn't a hash" do
|
18
|
-
expect do
|
19
|
-
ExpectCertificateTransparency.validate_config!(%w(a))
|
20
|
-
end.to raise_error(ExpectCertificateTransparencyConfigError)
|
21
|
-
end
|
22
|
-
|
23
|
-
it "raises an exception when max-age is not provided" do
|
24
|
-
expect do
|
25
|
-
ExpectCertificateTransparency.validate_config!(foo: "bar")
|
26
|
-
end.to raise_error(ExpectCertificateTransparencyConfigError)
|
27
|
-
end
|
28
|
-
|
29
|
-
it "raises an exception with an invalid max-age" do
|
30
|
-
expect do
|
31
|
-
ExpectCertificateTransparency.validate_config!(max_age: "abc123")
|
32
|
-
end.to raise_error(ExpectCertificateTransparencyConfigError)
|
33
|
-
end
|
34
|
-
|
35
|
-
it "raises an exception with an invalid enforce value" do
|
36
|
-
expect do
|
37
|
-
ExpectCertificateTransparency.validate_config!(enforce: "brokenstring")
|
38
|
-
end.to raise_error(ExpectCertificateTransparencyConfigError)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|