secure_headers 1.1.1 → 2.0.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.
- data/.gitignore +4 -8
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +8 -3
- data/Gemfile +6 -11
- data/README.md +184 -101
- data/Rakefile +11 -116
- data/fixtures/rails_3_2_12/Gemfile +0 -8
- data/fixtures/rails_3_2_12/app/controllers/things_controller.rb +0 -1
- data/fixtures/rails_3_2_12/app/views/layouts/application.html.erb +1 -4
- data/fixtures/rails_3_2_12/app/views/other_things/index.html.erb +2 -1
- data/fixtures/rails_3_2_12/app/views/things/index.html.erb +1 -21
- data/fixtures/rails_3_2_12/config/application.rb +0 -54
- data/fixtures/rails_3_2_12/config/environments/test.rb +2 -2
- data/fixtures/rails_3_2_12/config/initializers/secure_headers.rb +3 -1
- data/fixtures/rails_3_2_12/config/routes.rb +0 -57
- data/fixtures/rails_3_2_12/config/script_hashes.yml +5 -0
- data/fixtures/rails_3_2_12/config.ru +3 -0
- data/fixtures/rails_3_2_12/spec/controllers/other_things_controller_spec.rb +57 -19
- data/fixtures/rails_3_2_12/spec/controllers/things_controller_spec.rb +18 -13
- data/fixtures/rails_3_2_12/spec/spec_helper.rb +1 -5
- data/fixtures/rails_3_2_12_no_init/Gemfile +0 -9
- data/fixtures/rails_3_2_12_no_init/app/controllers/other_things_controller.rb +1 -2
- data/fixtures/rails_3_2_12_no_init/app/controllers/things_controller.rb +1 -1
- data/fixtures/rails_3_2_12_no_init/app/views/layouts/application.html.erb +0 -2
- data/fixtures/rails_3_2_12_no_init/app/views/things/index.html.erb +0 -21
- data/fixtures/rails_3_2_12_no_init/config/application.rb +0 -51
- data/fixtures/rails_3_2_12_no_init/config/environments/test.rb +2 -2
- data/fixtures/rails_3_2_12_no_init/config/routes.rb +0 -57
- data/fixtures/rails_3_2_12_no_init/spec/controllers/other_things_controller_spec.rb +17 -12
- data/fixtures/rails_3_2_12_no_init/spec/controllers/things_controller_spec.rb +17 -12
- data/fixtures/rails_3_2_12_no_init/spec/spec_helper.rb +3 -18
- data/fixtures/rails_4_1_8/Gemfile +5 -0
- data/fixtures/rails_4_1_8/README.rdoc +28 -0
- data/fixtures/rails_4_1_8/Rakefile +6 -0
- data/fixtures/rails_4_1_8/app/controllers/application_controller.rb +4 -0
- data/fixtures/rails_4_1_8/app/controllers/concerns/.keep +0 -0
- data/fixtures/rails_4_1_8/app/controllers/other_things_controller.rb +5 -0
- data/fixtures/rails_4_1_8/app/controllers/things_controller.rb +5 -0
- data/fixtures/rails_4_1_8/app/models/.keep +0 -0
- data/fixtures/rails_4_1_8/app/models/concerns/.keep +0 -0
- data/fixtures/rails_4_1_8/app/views/layouts/application.html.erb +11 -0
- data/fixtures/rails_4_1_8/app/views/other_things/index.html.erb +2 -0
- data/fixtures/rails_4_1_8/app/views/things/index.html.erb +1 -0
- data/fixtures/rails_4_1_8/config/application.rb +15 -0
- data/fixtures/rails_4_1_8/config/boot.rb +4 -0
- data/fixtures/rails_4_1_8/config/environment.rb +5 -0
- data/fixtures/rails_4_1_8/config/environments/test.rb +10 -0
- data/fixtures/rails_4_1_8/config/initializers/secure_headers.rb +17 -0
- data/fixtures/rails_4_1_8/config/routes.rb +4 -0
- data/fixtures/rails_4_1_8/config/script_hashes.yml +5 -0
- data/fixtures/rails_4_1_8/config/secrets.yml +22 -0
- data/fixtures/rails_4_1_8/config.ru +4 -0
- data/fixtures/rails_4_1_8/lib/assets/.keep +0 -0
- data/fixtures/rails_4_1_8/lib/tasks/.keep +0 -0
- data/fixtures/rails_4_1_8/log/.keep +0 -0
- data/fixtures/rails_4_1_8/spec/controllers/other_things_controller_spec.rb +83 -0
- data/fixtures/rails_4_1_8/spec/controllers/things_controller_spec.rb +59 -0
- data/fixtures/rails_4_1_8/spec/spec_helper.rb +15 -0
- data/fixtures/rails_4_1_8/vendor/assets/javascripts/.keep +0 -0
- data/fixtures/rails_4_1_8/vendor/assets/stylesheets/.keep +0 -0
- data/lib/secure_headers/hash_helper.rb +7 -0
- data/lib/secure_headers/headers/content_security_policy/script_hash_middleware.rb +22 -0
- data/lib/secure_headers/headers/content_security_policy.rb +154 -129
- data/lib/secure_headers/headers/strict_transport_security.rb +2 -1
- data/lib/secure_headers/headers/x_download_options.rb +39 -0
- data/lib/secure_headers/headers/x_permitted_cross_domain_policies.rb +40 -0
- data/lib/secure_headers/headers/x_xss_protection.rb +2 -1
- data/lib/secure_headers/padrino.rb +14 -0
- data/lib/secure_headers/railtie.rb +19 -24
- data/lib/secure_headers/version.rb +1 -1
- data/lib/secure_headers/view_helper.rb +68 -0
- data/lib/secure_headers.rb +65 -16
- data/lib/tasks/tasks.rake +48 -0
- data/secure_headers.gemspec +4 -4
- data/spec/lib/secure_headers/headers/content_security_policy/script_hash_middleware_spec.rb +47 -0
- data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +127 -220
- data/spec/lib/secure_headers/headers/strict_transport_security_spec.rb +21 -20
- data/spec/lib/secure_headers/headers/x_content_type_options_spec.rb +12 -12
- data/spec/lib/secure_headers/headers/x_download_options_spec.rb +32 -0
- data/spec/lib/secure_headers/headers/x_frame_options_spec.rb +12 -12
- data/spec/lib/secure_headers/headers/x_permitted_cross_domain_policies_spec.rb +64 -0
- data/spec/lib/secure_headers/headers/x_xss_protection_spec.rb +20 -19
- data/spec/lib/secure_headers_spec.rb +69 -68
- data/spec/spec_helper.rb +29 -18
- metadata +51 -46
- data/.rvmrc +0 -1
- data/Guardfile +0 -10
- data/HISTORY.md +0 -115
- data/app/controllers/content_security_policy_controller.rb +0 -75
- data/config/curl-ca-bundle.crt +0 -5420
- data/config/routes.rb +0 -3
- data/fixtures/rails_3_2_12/Guardfile +0 -14
- data/fixtures/rails_3_2_12/app/models/thing.rb +0 -3
- data/fixtures/rails_3_2_12/config/database.yml +0 -25
- data/fixtures/rails_3_2_12/config/environments/development.rb +0 -37
- data/fixtures/rails_3_2_12/config/environments/production.rb +0 -67
- data/fixtures/rails_3_2_12/config/initializers/backtrace_silencers.rb +0 -7
- data/fixtures/rails_3_2_12/config/initializers/inflections.rb +0 -15
- data/fixtures/rails_3_2_12/config/initializers/mime_types.rb +0 -5
- data/fixtures/rails_3_2_12/config/initializers/secret_token.rb +0 -7
- data/fixtures/rails_3_2_12/config/initializers/session_store.rb +0 -8
- data/fixtures/rails_3_2_12/config/initializers/wrap_parameters.rb +0 -14
- data/fixtures/rails_3_2_12/config/locales/en.yml +0 -5
- data/fixtures/rails_3_2_12/db/schema.rb +0 -16
- data/fixtures/rails_3_2_12/db/seeds.rb +0 -7
- data/fixtures/rails_3_2_12_no_init/Guardfile +0 -14
- data/fixtures/rails_3_2_12_no_init/app/models/thing.rb +0 -3
- data/fixtures/rails_3_2_12_no_init/app/views/things/_form.html.erb +0 -17
- data/fixtures/rails_3_2_12_no_init/app/views/things/edit.html.erb +0 -6
- data/fixtures/rails_3_2_12_no_init/app/views/things/new.html.erb +0 -5
- data/fixtures/rails_3_2_12_no_init/app/views/things/show.html.erb +0 -5
- data/fixtures/rails_3_2_12_no_init/config/database.yml +0 -25
- data/fixtures/rails_3_2_12_no_init/config/environments/development.rb +0 -37
- data/fixtures/rails_3_2_12_no_init/config/environments/production.rb +0 -67
- data/fixtures/rails_3_2_12_no_init/config/initializers/backtrace_silencers.rb +0 -7
- data/fixtures/rails_3_2_12_no_init/config/initializers/inflections.rb +0 -15
- data/fixtures/rails_3_2_12_no_init/config/initializers/mime_types.rb +0 -5
- data/fixtures/rails_3_2_12_no_init/config/initializers/secret_token.rb +0 -7
- data/fixtures/rails_3_2_12_no_init/config/initializers/session_store.rb +0 -8
- data/fixtures/rails_3_2_12_no_init/config/initializers/wrap_parameters.rb +0 -14
- data/fixtures/rails_3_2_12_no_init/config/locales/en.yml +0 -5
- data/fixtures/rails_3_2_12_no_init/db/schema.rb +0 -16
- data/fixtures/rails_3_2_12_no_init/db/seeds.rb +0 -7
- data/spec/controllers/content_security_policy_controller_spec.rb +0 -90
|
@@ -2,34 +2,34 @@ require 'spec_helper'
|
|
|
2
2
|
|
|
3
3
|
module SecureHeaders
|
|
4
4
|
describe XFrameOptions do
|
|
5
|
-
specify{ XFrameOptions.new.name.
|
|
5
|
+
specify{ expect(XFrameOptions.new.name).to eq("X-Frame-Options") }
|
|
6
6
|
|
|
7
7
|
describe "#value" do
|
|
8
|
-
specify { XFrameOptions.new.value.
|
|
9
|
-
specify { XFrameOptions.new("SAMEORIGIN").value.
|
|
10
|
-
specify { XFrameOptions.new(:value => 'DENY').value.
|
|
8
|
+
specify { expect(XFrameOptions.new.value).to eq(XFrameOptions::Constants::DEFAULT_VALUE)}
|
|
9
|
+
specify { expect(XFrameOptions.new("SAMEORIGIN").value).to eq("SAMEORIGIN")}
|
|
10
|
+
specify { expect(XFrameOptions.new(:value => 'DENY').value).to eq("DENY")}
|
|
11
11
|
|
|
12
12
|
context "with invalid configuration" do
|
|
13
13
|
it "allows SAMEORIGIN" do
|
|
14
|
-
|
|
14
|
+
expect {
|
|
15
15
|
XFrameOptions.new("SAMEORIGIN").value
|
|
16
|
-
}.
|
|
16
|
+
}.not_to raise_error
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
it "allows DENY" do
|
|
20
|
-
|
|
20
|
+
expect {
|
|
21
21
|
XFrameOptions.new("DENY").value
|
|
22
|
-
}.
|
|
22
|
+
}.not_to raise_error end
|
|
23
23
|
|
|
24
24
|
it "allows ALLOW-FROM*" do
|
|
25
|
-
|
|
25
|
+
expect {
|
|
26
26
|
XFrameOptions.new("ALLOW-FROM: example.com").value
|
|
27
|
-
}.
|
|
27
|
+
}.not_to raise_error
|
|
28
28
|
end
|
|
29
29
|
it "does not allow garbage" do
|
|
30
|
-
|
|
30
|
+
expect {
|
|
31
31
|
XFrameOptions.new("I like turtles").value
|
|
32
|
-
}.
|
|
32
|
+
}.to raise_error(XFOBuildError)
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
35
|
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
module SecureHeaders
|
|
2
|
+
describe XPermittedCrossDomainPolicies do
|
|
3
|
+
specify { expect(XPermittedCrossDomainPolicies.new.name).to eq(XPermittedCrossDomainPolicies::Constants::XPCDP_HEADER_NAME)}
|
|
4
|
+
specify { expect(XPermittedCrossDomainPolicies.new.value).to eq("none")}
|
|
5
|
+
specify { expect(XPermittedCrossDomainPolicies.new('master-only').value).to eq('master-only')}
|
|
6
|
+
specify { expect(XPermittedCrossDomainPolicies.new(:value => 'master-only').value).to eq('master-only') }
|
|
7
|
+
|
|
8
|
+
context "valid configuration values" do
|
|
9
|
+
it "accepts 'all'" do
|
|
10
|
+
expect {
|
|
11
|
+
XPermittedCrossDomainPolicies.new("all")
|
|
12
|
+
}.not_to raise_error
|
|
13
|
+
|
|
14
|
+
expect {
|
|
15
|
+
XPermittedCrossDomainPolicies.new(:value => "all")
|
|
16
|
+
}.not_to raise_error
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "accepts 'by-ftp-filename'" do
|
|
20
|
+
expect {
|
|
21
|
+
XPermittedCrossDomainPolicies.new("by-ftp-filename")
|
|
22
|
+
}.not_to raise_error
|
|
23
|
+
|
|
24
|
+
expect {
|
|
25
|
+
XPermittedCrossDomainPolicies.new(:value => "by-ftp-filename")
|
|
26
|
+
}.not_to raise_error
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "accepts 'by-content-type'" do
|
|
30
|
+
expect {
|
|
31
|
+
XPermittedCrossDomainPolicies.new("by-content-type")
|
|
32
|
+
}.not_to raise_error
|
|
33
|
+
|
|
34
|
+
expect {
|
|
35
|
+
XPermittedCrossDomainPolicies.new(:value => "by-content-type")
|
|
36
|
+
}.not_to raise_error
|
|
37
|
+
end
|
|
38
|
+
it "accepts 'master-only'" do
|
|
39
|
+
expect {
|
|
40
|
+
XPermittedCrossDomainPolicies.new("master-only")
|
|
41
|
+
}.not_to raise_error
|
|
42
|
+
|
|
43
|
+
expect {
|
|
44
|
+
XPermittedCrossDomainPolicies.new(:value => "master-only")
|
|
45
|
+
}.not_to raise_error
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "accepts nil" do
|
|
49
|
+
expect {
|
|
50
|
+
XPermittedCrossDomainPolicies.new
|
|
51
|
+
}.not_to raise_error
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
context 'invlaid configuration values' do
|
|
56
|
+
|
|
57
|
+
it "doesn't accept invalid values" do
|
|
58
|
+
expect {
|
|
59
|
+
XPermittedCrossDomainPolicies.new("open")
|
|
60
|
+
}.to raise_error
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -1,53 +1,54 @@
|
|
|
1
1
|
module SecureHeaders
|
|
2
2
|
describe XXssProtection do
|
|
3
|
-
specify { XXssProtection.new.name.
|
|
4
|
-
specify { XXssProtection.new.value.
|
|
5
|
-
specify { XXssProtection.new("0").value.
|
|
6
|
-
specify { XXssProtection.new(:value => 1, :mode => 'block').value.
|
|
3
|
+
specify { expect(XXssProtection.new.name).to eq(X_XSS_PROTECTION_HEADER_NAME)}
|
|
4
|
+
specify { expect(XXssProtection.new.value).to eq("1")}
|
|
5
|
+
specify { expect(XXssProtection.new("0").value).to eq("0")}
|
|
6
|
+
specify { expect(XXssProtection.new(:value => 1, :mode => 'block').value).to eq('1; mode=block') }
|
|
7
|
+
specify { expect(XXssProtection.new(:value => 1, :mode => 'block', :report_uri => 'https://www.secure.com/reports').value).to eq('1; mode=block; report=https://www.secure.com/reports') }
|
|
7
8
|
|
|
8
9
|
context "with invalid configuration" do
|
|
9
10
|
it "should raise an error when providing a string that is not valid" do
|
|
10
|
-
|
|
11
|
+
expect {
|
|
11
12
|
XXssProtection.new("asdf")
|
|
12
|
-
}.
|
|
13
|
+
}.to raise_error(XXssProtectionBuildError)
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
expect {
|
|
15
16
|
XXssProtection.new("asdf; mode=donkey")
|
|
16
|
-
}.
|
|
17
|
+
}.to raise_error(XXssProtectionBuildError)
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
context "when using a hash value" do
|
|
20
21
|
it "should allow string values ('1' or '0' are the only valid strings)" do
|
|
21
|
-
|
|
22
|
+
expect {
|
|
22
23
|
XXssProtection.new(:value => '1')
|
|
23
|
-
}.
|
|
24
|
+
}.not_to raise_error
|
|
24
25
|
end
|
|
25
26
|
|
|
26
27
|
it "should allow integer values (1 or 0 are the only valid integers)" do
|
|
27
|
-
|
|
28
|
+
expect {
|
|
28
29
|
XXssProtection.new(:value => 1)
|
|
29
|
-
}.
|
|
30
|
+
}.not_to raise_error
|
|
30
31
|
end
|
|
31
32
|
|
|
32
33
|
it "should raise an error if no value key is supplied" do
|
|
33
|
-
|
|
34
|
+
expect {
|
|
34
35
|
XXssProtection.new(:mode => 'block')
|
|
35
|
-
}.
|
|
36
|
+
}.to raise_error(XXssProtectionBuildError)
|
|
36
37
|
end
|
|
37
38
|
|
|
38
39
|
it "should raise an error if an invalid key is supplied" do
|
|
39
|
-
|
|
40
|
+
expect {
|
|
40
41
|
XXssProtection.new(:value => 123)
|
|
41
|
-
}.
|
|
42
|
+
}.to raise_error(XXssProtectionBuildError)
|
|
42
43
|
end
|
|
43
44
|
|
|
44
45
|
it "should raise an error if mode != block" do
|
|
45
|
-
|
|
46
|
+
expect {
|
|
46
47
|
XXssProtection.new(:value => 1, :mode => "donkey")
|
|
47
|
-
}.
|
|
48
|
+
}.to raise_error(XXssProtectionBuildError)
|
|
48
49
|
end
|
|
49
50
|
end
|
|
50
51
|
|
|
51
52
|
end
|
|
52
53
|
end
|
|
53
|
-
end
|
|
54
|
+
end
|
|
@@ -13,34 +13,15 @@ describe SecureHeaders do
|
|
|
13
13
|
|
|
14
14
|
before(:each) do
|
|
15
15
|
stub_user_agent(nil)
|
|
16
|
-
headers.
|
|
17
|
-
subject.
|
|
18
|
-
subject.
|
|
16
|
+
allow(headers).to receive(:[])
|
|
17
|
+
allow(subject).to receive(:response).and_return(response)
|
|
18
|
+
allow(subject).to receive(:request).and_return(request)
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
ALL_HEADERS = Hash[[:hsts, :csp, :x_frame_options, :x_content_type_options, :x_xss_protection].map{|header| [header, false]}]
|
|
22
|
-
USER_AGENTS = {
|
|
23
|
-
:firefox => 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:14.0) Gecko/20100101 Firefox/14.0.1',
|
|
24
|
-
: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',
|
|
25
|
-
:ie => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/5.0)',
|
|
26
|
-
:opera => 'Opera/9.80 (Windows NT 6.1; U; es-ES) Presto/2.9.181 Version/12.00',
|
|
27
|
-
:ios5 => "Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3",
|
|
28
|
-
:ios6 => "Mozilla/5.0 (iPhone; CPU iPhone OS 614 like Mac OS X) AppleWebKit/536.26 (KHTML like Gecko) Version/6.0 Mobile/10B350 Safari/8536.25",
|
|
29
|
-
:safari5 => "Mozilla/5.0 (iPad; CPU OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko ) Version/5.1 Mobile/9B176 Safari/7534.48.3",
|
|
30
|
-
:safari5_1 => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/534.55.3 (KHTML, like Gecko) Version/5.1.3 Safari/534.53.10",
|
|
31
|
-
:safari6 => "Mozilla/5.0 (Macintosh; Intel Mac OS X 1084) AppleWebKit/536.30.1 (KHTML like Gecko) Version/6.0.5 Safari/536.30.1"
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
def should_assign_header name, value
|
|
35
|
-
response.headers.should_receive(:[]=).with(name, value)
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def should_not_assign_header name
|
|
39
|
-
response.headers.should_not_receive(:[]=).with(name, anything)
|
|
40
|
-
end
|
|
21
|
+
ALL_HEADERS = Hash[[:hsts, :csp, :x_frame_options, :x_content_type_options, :x_xss_protection, :x_permitted_cross_domain_policies].map{|header| [header, false]}]
|
|
41
22
|
|
|
42
23
|
def stub_user_agent val
|
|
43
|
-
request.
|
|
24
|
+
allow(request).to receive_message_chain(:env, :[]).and_return(val)
|
|
44
25
|
end
|
|
45
26
|
|
|
46
27
|
def options_for header
|
|
@@ -54,6 +35,8 @@ describe SecureHeaders do
|
|
|
54
35
|
config.x_content_type_options = nil
|
|
55
36
|
config.x_xss_protection = nil
|
|
56
37
|
config.csp = nil
|
|
38
|
+
config.x_download_options = nil
|
|
39
|
+
config.x_permitted_cross_domain_policies = nil
|
|
57
40
|
end
|
|
58
41
|
end
|
|
59
42
|
|
|
@@ -63,14 +46,8 @@ describe SecureHeaders do
|
|
|
63
46
|
subject.set_x_frame_options_header
|
|
64
47
|
subject.set_x_content_type_options_header
|
|
65
48
|
subject.set_x_xss_protection_header
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
describe "#ensure_security_headers" do
|
|
69
|
-
it "sets a before filter" do
|
|
70
|
-
options = {}
|
|
71
|
-
DummyClass.should_receive(:before_filter).exactly(5).times
|
|
72
|
-
DummyClass.ensure_security_headers(options)
|
|
73
|
-
end
|
|
49
|
+
subject.set_x_download_options_header
|
|
50
|
+
subject.set_x_permitted_cross_domain_policies_header
|
|
74
51
|
end
|
|
75
52
|
|
|
76
53
|
describe "#set_header" do
|
|
@@ -86,19 +63,18 @@ describe SecureHeaders do
|
|
|
86
63
|
end
|
|
87
64
|
|
|
88
65
|
describe "#set_security_headers" do
|
|
89
|
-
before(:each) do
|
|
90
|
-
SecureHeaders::ContentSecurityPolicy.stub(:new).and_return(double.as_null_object)
|
|
91
|
-
end
|
|
92
66
|
USER_AGENTS.each do |name, useragent|
|
|
93
67
|
it "sets all default headers for #{name} (smoke test)" do
|
|
94
68
|
stub_user_agent(useragent)
|
|
95
|
-
number_of_headers =
|
|
96
|
-
subject.
|
|
69
|
+
number_of_headers = 7
|
|
70
|
+
expect(subject).to receive(:set_header).exactly(number_of_headers).times # a request for a given header
|
|
97
71
|
subject.set_csp_header
|
|
98
72
|
subject.set_x_frame_options_header
|
|
99
73
|
subject.set_hsts_header
|
|
100
74
|
subject.set_x_xss_protection_header
|
|
101
75
|
subject.set_x_content_type_options_header
|
|
76
|
+
subject.set_x_download_options_header
|
|
77
|
+
subject.set_x_permitted_cross_domain_policies_header
|
|
102
78
|
end
|
|
103
79
|
end
|
|
104
80
|
|
|
@@ -113,26 +89,47 @@ describe SecureHeaders do
|
|
|
113
89
|
subject.set_x_xss_protection_header(false)
|
|
114
90
|
end
|
|
115
91
|
|
|
92
|
+
it "does not set the X-Download-Options header if disabled" do
|
|
93
|
+
should_not_assign_header(XDO_HEADER_NAME)
|
|
94
|
+
subject.set_x_download_options_header(false)
|
|
95
|
+
end
|
|
96
|
+
|
|
116
97
|
it "does not set the X-Frame-Options header if disabled" do
|
|
117
98
|
should_not_assign_header(XFO_HEADER_NAME)
|
|
118
99
|
subject.set_x_frame_options_header(false)
|
|
119
100
|
end
|
|
120
101
|
|
|
102
|
+
it "does not set the X-Permitted-Cross-Domain-Policies header if disabled" do
|
|
103
|
+
should_not_assign_header(XPCDP_HEADER_NAME)
|
|
104
|
+
subject.set_x_permitted_cross_domain_policies_header(false)
|
|
105
|
+
end
|
|
106
|
+
|
|
121
107
|
it "does not set the HSTS header if disabled" do
|
|
122
108
|
should_not_assign_header(HSTS_HEADER_NAME)
|
|
123
109
|
subject.set_hsts_header(false)
|
|
124
110
|
end
|
|
125
111
|
|
|
126
112
|
it "does not set the HSTS header if request is over HTTP" do
|
|
127
|
-
subject.
|
|
113
|
+
allow(subject).to receive_message_chain(:request, :ssl?).and_return(false)
|
|
128
114
|
should_not_assign_header(HSTS_HEADER_NAME)
|
|
129
115
|
subject.set_hsts_header({:include_subdomains => true})
|
|
130
116
|
end
|
|
131
117
|
|
|
132
118
|
it "does not set the CSP header if disabled" do
|
|
133
119
|
stub_user_agent(USER_AGENTS[:chrome])
|
|
134
|
-
should_not_assign_header(
|
|
135
|
-
subject.set_csp_header(
|
|
120
|
+
should_not_assign_header(HEADER_NAME)
|
|
121
|
+
subject.set_csp_header(false)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
it "saves the options to the env when using script hashes" do
|
|
125
|
+
opts = {
|
|
126
|
+
:default_src => 'self',
|
|
127
|
+
:script_hash_middleware => true
|
|
128
|
+
}
|
|
129
|
+
stub_user_agent(USER_AGENTS[:chrome])
|
|
130
|
+
|
|
131
|
+
expect(SecureHeaders::ContentSecurityPolicy).to receive(:add_to_env)
|
|
132
|
+
subject.set_csp_header(opts)
|
|
136
133
|
end
|
|
137
134
|
|
|
138
135
|
context "when disabled by configuration settings" do
|
|
@@ -143,8 +140,10 @@ describe SecureHeaders do
|
|
|
143
140
|
config.x_content_type_options = false
|
|
144
141
|
config.x_xss_protection = false
|
|
145
142
|
config.csp = false
|
|
143
|
+
config.x_download_options = false
|
|
144
|
+
config.x_permitted_cross_domain_policies = false
|
|
146
145
|
end
|
|
147
|
-
subject.
|
|
146
|
+
expect(subject).not_to receive(:set_header)
|
|
148
147
|
set_security_headers(subject)
|
|
149
148
|
reset_config
|
|
150
149
|
end
|
|
@@ -163,6 +162,18 @@ describe SecureHeaders do
|
|
|
163
162
|
end
|
|
164
163
|
end
|
|
165
164
|
|
|
165
|
+
describe "#set_x_download_options_header" do
|
|
166
|
+
it "sets the X-Download-Options header" do
|
|
167
|
+
should_assign_header(XDO_HEADER_NAME, SecureHeaders::XDownloadOptions::Constants::DEFAULT_VALUE)
|
|
168
|
+
subject.set_x_download_options_header
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
it "allows a custom X-Download-Options header" do
|
|
172
|
+
should_assign_header(XDO_HEADER_NAME, "noopen")
|
|
173
|
+
subject.set_x_download_options_header(:value => 'noopen')
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
166
177
|
describe "#set_strict_transport_security" do
|
|
167
178
|
it "sets the Strict-Transport-Security header" do
|
|
168
179
|
should_assign_header(HSTS_HEADER_NAME, SecureHeaders::StrictTransportSecurity::Constants::DEFAULT_VALUE)
|
|
@@ -178,6 +189,11 @@ describe SecureHeaders do
|
|
|
178
189
|
should_assign_header(HSTS_HEADER_NAME, "max-age=#{HSTS_MAX_AGE}; includeSubdomains")
|
|
179
190
|
subject.set_hsts_header(:max_age => HSTS_MAX_AGE, :include_subdomains => true)
|
|
180
191
|
end
|
|
192
|
+
|
|
193
|
+
it "allows you to specify preload" do
|
|
194
|
+
should_assign_header(HSTS_HEADER_NAME, "max-age=#{HSTS_MAX_AGE}; includeSubdomains; preload")
|
|
195
|
+
subject.set_hsts_header(:max_age => HSTS_MAX_AGE, :include_subdomains => true, :preload => true)
|
|
196
|
+
end
|
|
181
197
|
end
|
|
182
198
|
|
|
183
199
|
describe "#set_x_xss_protection" do
|
|
@@ -221,7 +237,7 @@ describe SecureHeaders do
|
|
|
221
237
|
context "when using Firefox" do
|
|
222
238
|
it "sets CSP headers" do
|
|
223
239
|
stub_user_agent(USER_AGENTS[:firefox])
|
|
224
|
-
should_assign_header(
|
|
240
|
+
should_assign_header(HEADER_NAME + "-Report-Only", DEFAULT_CSP_HEADER)
|
|
225
241
|
subject.set_csp_header
|
|
226
242
|
end
|
|
227
243
|
end
|
|
@@ -229,7 +245,7 @@ describe SecureHeaders do
|
|
|
229
245
|
context "when using Chrome" do
|
|
230
246
|
it "sets default CSP header" do
|
|
231
247
|
stub_user_agent(USER_AGENTS[:chrome])
|
|
232
|
-
should_assign_header(
|
|
248
|
+
should_assign_header(HEADER_NAME + "-Report-Only", DEFAULT_CSP_HEADER)
|
|
233
249
|
subject.set_csp_header
|
|
234
250
|
end
|
|
235
251
|
end
|
|
@@ -237,36 +253,21 @@ describe SecureHeaders do
|
|
|
237
253
|
context "when using a browser besides chrome/firefox" do
|
|
238
254
|
it "sets the CSP header" do
|
|
239
255
|
stub_user_agent(USER_AGENTS[:opera])
|
|
240
|
-
should_assign_header(
|
|
256
|
+
should_assign_header(HEADER_NAME + "-Report-Only", DEFAULT_CSP_HEADER)
|
|
241
257
|
subject.set_csp_header
|
|
242
258
|
end
|
|
243
259
|
end
|
|
260
|
+
end
|
|
244
261
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
:default_src => 'self',
|
|
251
|
-
:script_src => 'https://mycdn.example.com',
|
|
252
|
-
:experimental => {
|
|
253
|
-
:script_src => 'self',
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
end
|
|
257
|
-
|
|
258
|
-
it "does not set the header in enforce mode if experimental is supplied, but enforce is disabled" do
|
|
259
|
-
opts = @opts.merge(:enforce => false)
|
|
260
|
-
should_assign_header(STANDARD_HEADER_NAME + "-Report-Only", anything)
|
|
261
|
-
should_not_assign_header(STANDARD_HEADER_NAME)
|
|
262
|
-
subject.set_csp_header(opts)
|
|
263
|
-
end
|
|
262
|
+
describe "#set_x_permitted_cross_domain_policies_header" do
|
|
263
|
+
it "sets the X-Permitted-Cross-Domain-Policies header" do
|
|
264
|
+
should_assign_header(XPCDP_HEADER_NAME, SecureHeaders::XPermittedCrossDomainPolicies::Constants::DEFAULT_VALUE)
|
|
265
|
+
subject.set_x_permitted_cross_domain_policies_header
|
|
266
|
+
end
|
|
264
267
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
subject.set_csp_header(@opts)
|
|
269
|
-
end
|
|
268
|
+
it "allows a custom X-Permitted-Cross-Domain-Policies header" do
|
|
269
|
+
should_assign_header(XPCDP_HEADER_NAME, "master-only")
|
|
270
|
+
subject.set_x_permitted_cross_domain_policies_header(:value => 'master-only')
|
|
270
271
|
end
|
|
271
272
|
end
|
|
272
273
|
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -1,25 +1,36 @@
|
|
|
1
1
|
require 'rubygems'
|
|
2
|
-
require '
|
|
2
|
+
require 'rspec'
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
require 'simplecov'
|
|
6
|
-
SimpleCov.start do
|
|
7
|
-
add_filter "spec"
|
|
8
|
-
end
|
|
9
|
-
end
|
|
4
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'secure_headers')
|
|
10
5
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
require 'rspec'
|
|
6
|
+
if defined?(Coveralls)
|
|
7
|
+
Coveralls.wear!
|
|
14
8
|
end
|
|
15
9
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
10
|
+
include ::SecureHeaders::StrictTransportSecurity::Constants
|
|
11
|
+
include ::SecureHeaders::ContentSecurityPolicy::Constants
|
|
12
|
+
include ::SecureHeaders::XFrameOptions::Constants
|
|
13
|
+
include ::SecureHeaders::XXssProtection::Constants
|
|
14
|
+
include ::SecureHeaders::XContentTypeOptions::Constants
|
|
15
|
+
include ::SecureHeaders::XDownloadOptions::Constants
|
|
16
|
+
include ::SecureHeaders::XPermittedCrossDomainPolicies::Constants
|
|
17
|
+
|
|
18
|
+
USER_AGENTS = {
|
|
19
|
+
:firefox => 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:14.0) Gecko/20100101 Firefox/14.0.1',
|
|
20
|
+
: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',
|
|
21
|
+
:ie => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/5.0)',
|
|
22
|
+
:opera => 'Opera/9.80 (Windows NT 6.1; U; es-ES) Presto/2.9.181 Version/12.00',
|
|
23
|
+
:ios5 => "Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3",
|
|
24
|
+
:ios6 => "Mozilla/5.0 (iPhone; CPU iPhone OS 614 like Mac OS X) AppleWebKit/536.26 (KHTML like Gecko) Version/6.0 Mobile/10B350 Safari/8536.25",
|
|
25
|
+
:safari5 => "Mozilla/5.0 (iPad; CPU OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko ) Version/5.1 Mobile/9B176 Safari/7534.48.3",
|
|
26
|
+
:safari5_1 => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/534.55.3 (KHTML, like Gecko) Version/5.1.3 Safari/534.53.10",
|
|
27
|
+
:safari6 => "Mozilla/5.0 (Macintosh; Intel Mac OS X 1084) AppleWebKit/536.30.1 (KHTML like Gecko) Version/6.0.5 Safari/536.30.1"
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
def should_assign_header name, value
|
|
31
|
+
expect(response.headers).to receive(:[]=).with(name, value)
|
|
24
32
|
end
|
|
25
33
|
|
|
34
|
+
def should_not_assign_header name
|
|
35
|
+
expect(response.headers).not_to receive(:[]=).with(name, anything)
|
|
36
|
+
end
|