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.
- 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
|