secure_headers 1.0.0 → 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.
Files changed (129) hide show
  1. data/.gitignore +4 -8
  2. data/.ruby-gemset +1 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +8 -3
  5. data/Gemfile +6 -8
  6. data/README.md +208 -134
  7. data/Rakefile +11 -116
  8. data/fixtures/rails_3_2_12/Gemfile +0 -8
  9. data/fixtures/rails_3_2_12/app/controllers/things_controller.rb +0 -1
  10. data/fixtures/rails_3_2_12/app/views/layouts/application.html.erb +1 -4
  11. data/fixtures/rails_3_2_12/app/views/other_things/index.html.erb +2 -1
  12. data/fixtures/rails_3_2_12/app/views/things/index.html.erb +1 -21
  13. data/fixtures/rails_3_2_12/config/application.rb +0 -54
  14. data/fixtures/rails_3_2_12/config/environments/test.rb +2 -2
  15. data/fixtures/rails_3_2_12/config/initializers/secure_headers.rb +3 -1
  16. data/fixtures/rails_3_2_12/config/routes.rb +0 -57
  17. data/fixtures/rails_3_2_12/config/script_hashes.yml +5 -0
  18. data/fixtures/rails_3_2_12/config.ru +3 -0
  19. data/fixtures/rails_3_2_12/spec/controllers/other_things_controller_spec.rb +57 -19
  20. data/fixtures/rails_3_2_12/spec/controllers/things_controller_spec.rb +18 -16
  21. data/fixtures/rails_3_2_12/spec/spec_helper.rb +1 -5
  22. data/fixtures/rails_3_2_12_no_init/Gemfile +0 -9
  23. data/fixtures/rails_3_2_12_no_init/app/controllers/other_things_controller.rb +1 -2
  24. data/fixtures/rails_3_2_12_no_init/app/controllers/things_controller.rb +1 -1
  25. data/fixtures/rails_3_2_12_no_init/app/views/layouts/application.html.erb +0 -2
  26. data/fixtures/rails_3_2_12_no_init/app/views/things/index.html.erb +0 -21
  27. data/fixtures/rails_3_2_12_no_init/config/application.rb +0 -51
  28. data/fixtures/rails_3_2_12_no_init/config/environments/test.rb +2 -2
  29. data/fixtures/rails_3_2_12_no_init/config/routes.rb +0 -57
  30. data/fixtures/rails_3_2_12_no_init/spec/controllers/other_things_controller_spec.rb +17 -12
  31. data/fixtures/rails_3_2_12_no_init/spec/controllers/things_controller_spec.rb +17 -12
  32. data/fixtures/rails_3_2_12_no_init/spec/spec_helper.rb +3 -18
  33. data/fixtures/rails_4_1_8/Gemfile +5 -0
  34. data/fixtures/rails_4_1_8/README.rdoc +28 -0
  35. data/fixtures/rails_4_1_8/Rakefile +6 -0
  36. data/fixtures/rails_4_1_8/app/controllers/application_controller.rb +4 -0
  37. data/fixtures/rails_4_1_8/app/controllers/concerns/.keep +0 -0
  38. data/fixtures/rails_4_1_8/app/controllers/other_things_controller.rb +5 -0
  39. data/fixtures/rails_4_1_8/app/controllers/things_controller.rb +5 -0
  40. data/fixtures/rails_4_1_8/app/models/.keep +0 -0
  41. data/fixtures/rails_4_1_8/app/models/concerns/.keep +0 -0
  42. data/fixtures/rails_4_1_8/app/views/layouts/application.html.erb +11 -0
  43. data/fixtures/rails_4_1_8/app/views/other_things/index.html.erb +2 -0
  44. data/fixtures/rails_4_1_8/app/views/things/index.html.erb +1 -0
  45. data/fixtures/rails_4_1_8/config/application.rb +15 -0
  46. data/fixtures/rails_4_1_8/config/boot.rb +4 -0
  47. data/fixtures/rails_4_1_8/config/environment.rb +5 -0
  48. data/fixtures/rails_4_1_8/config/environments/test.rb +10 -0
  49. data/fixtures/rails_4_1_8/config/initializers/secure_headers.rb +17 -0
  50. data/fixtures/rails_4_1_8/config/routes.rb +4 -0
  51. data/fixtures/rails_4_1_8/config/script_hashes.yml +5 -0
  52. data/fixtures/rails_4_1_8/config/secrets.yml +22 -0
  53. data/fixtures/rails_4_1_8/config.ru +4 -0
  54. data/fixtures/rails_4_1_8/lib/assets/.keep +0 -0
  55. data/fixtures/rails_4_1_8/lib/tasks/.keep +0 -0
  56. data/fixtures/rails_4_1_8/log/.keep +0 -0
  57. data/fixtures/rails_4_1_8/spec/controllers/other_things_controller_spec.rb +83 -0
  58. data/fixtures/rails_4_1_8/spec/controllers/things_controller_spec.rb +59 -0
  59. data/fixtures/rails_4_1_8/spec/spec_helper.rb +15 -0
  60. data/fixtures/rails_4_1_8/vendor/assets/javascripts/.keep +0 -0
  61. data/fixtures/rails_4_1_8/vendor/assets/stylesheets/.keep +0 -0
  62. data/lib/secure_headers/hash_helper.rb +7 -0
  63. data/lib/secure_headers/headers/content_security_policy/script_hash_middleware.rb +22 -0
  64. data/lib/secure_headers/headers/content_security_policy.rb +160 -161
  65. data/lib/secure_headers/headers/strict_transport_security.rb +2 -1
  66. data/lib/secure_headers/headers/x_download_options.rb +39 -0
  67. data/lib/secure_headers/headers/x_permitted_cross_domain_policies.rb +40 -0
  68. data/lib/secure_headers/headers/x_xss_protection.rb +2 -1
  69. data/lib/secure_headers/padrino.rb +14 -0
  70. data/lib/secure_headers/railtie.rb +19 -24
  71. data/lib/secure_headers/version.rb +1 -1
  72. data/lib/secure_headers/view_helper.rb +68 -0
  73. data/lib/secure_headers.rb +70 -29
  74. data/lib/tasks/tasks.rake +48 -0
  75. data/secure_headers.gemspec +4 -5
  76. data/spec/lib/secure_headers/headers/content_security_policy/script_hash_middleware_spec.rb +47 -0
  77. data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +127 -299
  78. data/spec/lib/secure_headers/headers/strict_transport_security_spec.rb +21 -20
  79. data/spec/lib/secure_headers/headers/x_content_type_options_spec.rb +12 -12
  80. data/spec/lib/secure_headers/headers/x_download_options_spec.rb +32 -0
  81. data/spec/lib/secure_headers/headers/x_frame_options_spec.rb +12 -13
  82. data/spec/lib/secure_headers/headers/x_permitted_cross_domain_policies_spec.rb +64 -0
  83. data/spec/lib/secure_headers/headers/x_xss_protection_spec.rb +20 -19
  84. data/spec/lib/secure_headers_spec.rb +68 -83
  85. data/spec/spec_helper.rb +29 -18
  86. metadata +51 -66
  87. data/.rvmrc +0 -1
  88. data/Guardfile +0 -10
  89. data/HISTORY.md +0 -100
  90. data/app/controllers/content_security_policy_controller.rb +0 -53
  91. data/config/curl-ca-bundle.crt +0 -5420
  92. data/config/routes.rb +0 -3
  93. data/fixtures/rails_3_2_12/Guardfile +0 -14
  94. data/fixtures/rails_3_2_12/app/models/thing.rb +0 -3
  95. data/fixtures/rails_3_2_12/config/database.yml +0 -25
  96. data/fixtures/rails_3_2_12/config/environments/development.rb +0 -37
  97. data/fixtures/rails_3_2_12/config/environments/production.rb +0 -67
  98. data/fixtures/rails_3_2_12/config/initializers/backtrace_silencers.rb +0 -7
  99. data/fixtures/rails_3_2_12/config/initializers/inflections.rb +0 -15
  100. data/fixtures/rails_3_2_12/config/initializers/mime_types.rb +0 -5
  101. data/fixtures/rails_3_2_12/config/initializers/secret_token.rb +0 -7
  102. data/fixtures/rails_3_2_12/config/initializers/session_store.rb +0 -8
  103. data/fixtures/rails_3_2_12/config/initializers/wrap_parameters.rb +0 -14
  104. data/fixtures/rails_3_2_12/config/locales/en.yml +0 -5
  105. data/fixtures/rails_3_2_12/db/schema.rb +0 -16
  106. data/fixtures/rails_3_2_12/db/seeds.rb +0 -7
  107. data/fixtures/rails_3_2_12_no_init/Guardfile +0 -14
  108. data/fixtures/rails_3_2_12_no_init/app/models/thing.rb +0 -3
  109. data/fixtures/rails_3_2_12_no_init/app/views/things/_form.html.erb +0 -17
  110. data/fixtures/rails_3_2_12_no_init/app/views/things/edit.html.erb +0 -6
  111. data/fixtures/rails_3_2_12_no_init/app/views/things/new.html.erb +0 -5
  112. data/fixtures/rails_3_2_12_no_init/app/views/things/show.html.erb +0 -5
  113. data/fixtures/rails_3_2_12_no_init/config/database.yml +0 -25
  114. data/fixtures/rails_3_2_12_no_init/config/environments/development.rb +0 -37
  115. data/fixtures/rails_3_2_12_no_init/config/environments/production.rb +0 -67
  116. data/fixtures/rails_3_2_12_no_init/config/initializers/backtrace_silencers.rb +0 -7
  117. data/fixtures/rails_3_2_12_no_init/config/initializers/inflections.rb +0 -15
  118. data/fixtures/rails_3_2_12_no_init/config/initializers/mime_types.rb +0 -5
  119. data/fixtures/rails_3_2_12_no_init/config/initializers/secret_token.rb +0 -7
  120. data/fixtures/rails_3_2_12_no_init/config/initializers/session_store.rb +0 -8
  121. data/fixtures/rails_3_2_12_no_init/config/initializers/wrap_parameters.rb +0 -14
  122. data/fixtures/rails_3_2_12_no_init/config/locales/en.yml +0 -5
  123. data/fixtures/rails_3_2_12_no_init/db/schema.rb +0 -16
  124. data/fixtures/rails_3_2_12_no_init/db/seeds.rb +0 -7
  125. data/lib/secure_headers/headers/content_security_policy/browser_strategy.rb +0 -78
  126. data/lib/secure_headers/headers/content_security_policy/firefox_browser_strategy.rb +0 -72
  127. data/lib/secure_headers/headers/content_security_policy/ie_browser_strategy.rb +0 -6
  128. data/lib/secure_headers/headers/content_security_policy/standard_browser_strategy.rb +0 -22
  129. data/spec/controllers/content_security_policy_controller_spec.rb +0 -74
@@ -1,33 +1,33 @@
1
1
  module SecureHeaders
2
2
  describe XContentTypeOptions do
3
- specify{ XContentTypeOptions.new.name.should == "X-Content-Type-Options" }
3
+ specify{ expect(XContentTypeOptions.new.name).to eq("X-Content-Type-Options") }
4
4
 
5
5
  describe "#value" do
6
- specify { XContentTypeOptions.new.value.should == XContentTypeOptions::Constants::DEFAULT_VALUE}
7
- specify { XContentTypeOptions.new("nosniff").value.should == "nosniff"}
8
- specify { XContentTypeOptions.new(:value => 'nosniff').value.should == "nosniff"}
6
+ specify { expect(XContentTypeOptions.new.value).to eq(XContentTypeOptions::Constants::DEFAULT_VALUE)}
7
+ specify { expect(XContentTypeOptions.new("nosniff").value).to eq("nosniff")}
8
+ specify { expect(XContentTypeOptions.new(:value => 'nosniff').value).to eq("nosniff")}
9
9
 
10
10
  context "invalid configuration values" do
11
11
  it "accepts nosniff" do
12
- lambda {
12
+ expect {
13
13
  XContentTypeOptions.new("nosniff")
14
- }.should_not raise_error
14
+ }.not_to raise_error
15
15
 
16
- lambda {
16
+ expect {
17
17
  XContentTypeOptions.new(:value => "nosniff")
18
- }.should_not raise_error
18
+ }.not_to raise_error
19
19
  end
20
20
 
21
21
  it "accepts nil" do
22
- lambda {
22
+ expect {
23
23
  XContentTypeOptions.new
24
- }.should_not raise_error
24
+ }.not_to raise_error
25
25
  end
26
26
 
27
27
  it "doesn't accept anything besides no-sniff" do
28
- lambda {
28
+ expect {
29
29
  XContentTypeOptions.new("donkey")
30
- }.should raise_error(XContentTypeOptionsBuildError)
30
+ }.to raise_error
31
31
  end
32
32
  end
33
33
  end
@@ -0,0 +1,32 @@
1
+ module SecureHeaders
2
+ describe XDownloadOptions do
3
+ specify { expect(XDownloadOptions.new.name).to eq(XDO_HEADER_NAME)}
4
+ specify { expect(XDownloadOptions.new.value).to eq("noopen")}
5
+ specify { expect(XDownloadOptions.new('noopen').value).to eq('noopen')}
6
+ specify { expect(XDownloadOptions.new(:value => 'noopen').value).to eq('noopen') }
7
+
8
+ context "invalid configuration values" do
9
+ it "accepts noopen" do
10
+ expect {
11
+ XDownloadOptions.new("noopen")
12
+ }.not_to raise_error
13
+
14
+ expect {
15
+ XDownloadOptions.new(:value => "noopen")
16
+ }.not_to raise_error
17
+ end
18
+
19
+ it "accepts nil" do
20
+ expect {
21
+ XDownloadOptions.new
22
+ }.not_to raise_error
23
+ end
24
+
25
+ it "doesn't accept anything besides noopen" do
26
+ expect {
27
+ XDownloadOptions.new("open")
28
+ }.to raise_error
29
+ end
30
+ end
31
+ end
32
+ end
@@ -2,35 +2,34 @@ require 'spec_helper'
2
2
 
3
3
  module SecureHeaders
4
4
  describe XFrameOptions do
5
- specify{ XFrameOptions.new.name.should == "X-Frame-Options" }
5
+ specify{ expect(XFrameOptions.new.name).to eq("X-Frame-Options") }
6
6
 
7
7
  describe "#value" do
8
- specify { XFrameOptions.new.value.should == XFrameOptions::Constants::DEFAULT_VALUE}
9
- specify { XFrameOptions.new("SAMEORIGIN").value.should == "SAMEORIGIN"}
10
- specify { XFrameOptions.new(:value => 'DENY').value.should == "DENY"}
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
- lambda {
14
+ expect {
15
15
  XFrameOptions.new("SAMEORIGIN").value
16
- }.should_not raise_error(XFOBuildError)
16
+ }.not_to raise_error
17
17
  end
18
18
 
19
19
  it "allows DENY" do
20
- lambda {
20
+ expect {
21
21
  XFrameOptions.new("DENY").value
22
- }.should_not raise_error(XFOBuildError)
23
- end
22
+ }.not_to raise_error end
24
23
 
25
24
  it "allows ALLOW-FROM*" do
26
- lambda {
25
+ expect {
27
26
  XFrameOptions.new("ALLOW-FROM: example.com").value
28
- }.should_not raise_error(XFOBuildError)
27
+ }.not_to raise_error
29
28
  end
30
29
  it "does not allow garbage" do
31
- lambda {
30
+ expect {
32
31
  XFrameOptions.new("I like turtles").value
33
- }.should raise_error(XFOBuildError)
32
+ }.to raise_error(XFOBuildError)
34
33
  end
35
34
  end
36
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.should == X_XSS_PROTECTION_HEADER_NAME}
4
- specify { XXssProtection.new.value.should == "1"}
5
- specify { XXssProtection.new("0").value.should == "0"}
6
- specify { XXssProtection.new(:value => 1, :mode => 'block').value.should == '1; mode=block' }
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
- lambda {
11
+ expect {
11
12
  XXssProtection.new("asdf")
12
- }.should raise_error(XXssProtectionBuildError)
13
+ }.to raise_error(XXssProtectionBuildError)
13
14
 
14
- lambda {
15
+ expect {
15
16
  XXssProtection.new("asdf; mode=donkey")
16
- }.should raise_error(XXssProtectionBuildError)
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
- lambda {
22
+ expect {
22
23
  XXssProtection.new(:value => '1')
23
- }.should_not raise_error
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
- lambda {
28
+ expect {
28
29
  XXssProtection.new(:value => 1)
29
- }.should_not raise_error
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
- lambda {
34
+ expect {
34
35
  XXssProtection.new(:mode => 'block')
35
- }.should raise_error(XXssProtectionBuildError)
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
- lambda {
40
+ expect {
40
41
  XXssProtection.new(:value => 123)
41
- }.should raise_error(XXssProtectionBuildError)
42
+ }.to raise_error(XXssProtectionBuildError)
42
43
  end
43
44
 
44
45
  it "should raise an error if mode != block" do
45
- lambda {
46
+ expect {
46
47
  XXssProtection.new(:value => 1, :mode => "donkey")
47
- }.should raise_error(XXssProtectionBuildError)
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.stub(:[])
17
- subject.stub(:response).and_return(response)
18
- subject.stub(:request).and_return(request)
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.stub_chain(:env, :[]).and_return(val)
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
- end
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,27 +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 = case name
96
- when :ie, :chrome
97
- 5
98
- when :ios5, :safari5, :safari5_1
99
- 3 # csp breaks these browsers
100
- else
101
- 4
102
- end
103
-
104
- subject.should_receive(:set_header).exactly(number_of_headers).times # a request for a given header
69
+ number_of_headers = 7
70
+ expect(subject).to receive(:set_header).exactly(number_of_headers).times # a request for a given header
105
71
  subject.set_csp_header
106
72
  subject.set_x_frame_options_header
107
73
  subject.set_hsts_header
108
74
  subject.set_x_xss_protection_header
109
75
  subject.set_x_content_type_options_header
76
+ subject.set_x_download_options_header
77
+ subject.set_x_permitted_cross_domain_policies_header
110
78
  end
111
79
  end
112
80
 
@@ -121,34 +89,47 @@ describe SecureHeaders do
121
89
  subject.set_x_xss_protection_header(false)
122
90
  end
123
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
+
124
97
  it "does not set the X-Frame-Options header if disabled" do
125
98
  should_not_assign_header(XFO_HEADER_NAME)
126
99
  subject.set_x_frame_options_header(false)
127
100
  end
128
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
+
129
107
  it "does not set the HSTS header if disabled" do
130
108
  should_not_assign_header(HSTS_HEADER_NAME)
131
109
  subject.set_hsts_header(false)
132
110
  end
133
111
 
134
112
  it "does not set the HSTS header if request is over HTTP" do
135
- subject.stub_chain(:request, :ssl?).and_return(false)
113
+ allow(subject).to receive_message_chain(:request, :ssl?).and_return(false)
136
114
  should_not_assign_header(HSTS_HEADER_NAME)
137
115
  subject.set_hsts_header({:include_subdomains => true})
138
116
  end
139
117
 
140
118
  it "does not set the CSP header if disabled" do
141
119
  stub_user_agent(USER_AGENTS[:chrome])
142
- should_not_assign_header(WEBKIT_CSP_HEADER_NAME)
143
- subject.set_csp_header(options_for(:csp).merge(:csp => false))
120
+ should_not_assign_header(HEADER_NAME)
121
+ subject.set_csp_header(false)
144
122
  end
145
123
 
146
- # apparently iOS5 safari with CSP in enforce mode causes nothing to render
147
- # it has no effect in report-only mode (as in no report is sent)
148
- it "does not set CSP header if using ios5" do
149
- stub_user_agent(USER_AGENTS[:ios5])
150
- subject.should_not_receive(:set_header)
151
- subject.set_csp_header(options_for(:csp))
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)
152
133
  end
153
134
 
154
135
  context "when disabled by configuration settings" do
@@ -159,8 +140,10 @@ describe SecureHeaders do
159
140
  config.x_content_type_options = false
160
141
  config.x_xss_protection = false
161
142
  config.csp = false
143
+ config.x_download_options = false
144
+ config.x_permitted_cross_domain_policies = false
162
145
  end
163
- subject.should_not_receive(:set_header)
146
+ expect(subject).not_to receive(:set_header)
164
147
  set_security_headers(subject)
165
148
  reset_config
166
149
  end
@@ -179,6 +162,18 @@ describe SecureHeaders do
179
162
  end
180
163
  end
181
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
+
182
177
  describe "#set_strict_transport_security" do
183
178
  it "sets the Strict-Transport-Security header" do
184
179
  should_assign_header(HSTS_HEADER_NAME, SecureHeaders::StrictTransportSecurity::Constants::DEFAULT_VALUE)
@@ -194,6 +189,11 @@ describe SecureHeaders do
194
189
  should_assign_header(HSTS_HEADER_NAME, "max-age=#{HSTS_MAX_AGE}; includeSubdomains")
195
190
  subject.set_hsts_header(:max_age => HSTS_MAX_AGE, :include_subdomains => true)
196
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
197
197
  end
198
198
 
199
199
  describe "#set_x_xss_protection" do
@@ -214,7 +214,7 @@ describe SecureHeaders do
214
214
  end
215
215
 
216
216
  describe "#set_x_content_type_options" do
217
- [:ie, :chrome].each do |useragent|
217
+ USER_AGENTS.each do |useragent|
218
218
  context "when using #{useragent}" do
219
219
  before(:each) do
220
220
  stub_user_agent(USER_AGENTS[useragent])
@@ -237,7 +237,7 @@ describe SecureHeaders do
237
237
  context "when using Firefox" do
238
238
  it "sets CSP headers" do
239
239
  stub_user_agent(USER_AGENTS[:firefox])
240
- should_assign_header(FIREFOX_CSP_HEADER_NAME + "-Report-Only", FIREFOX_CSP_HEADER)
240
+ should_assign_header(HEADER_NAME + "-Report-Only", DEFAULT_CSP_HEADER)
241
241
  subject.set_csp_header
242
242
  end
243
243
  end
@@ -245,7 +245,7 @@ describe SecureHeaders do
245
245
  context "when using Chrome" do
246
246
  it "sets default CSP header" do
247
247
  stub_user_agent(USER_AGENTS[:chrome])
248
- should_assign_header(WEBKIT_CSP_HEADER_NAME + "-Report-Only", WEBKIT_CSP_HEADER)
248
+ should_assign_header(HEADER_NAME + "-Report-Only", DEFAULT_CSP_HEADER)
249
249
  subject.set_csp_header
250
250
  end
251
251
  end
@@ -253,36 +253,21 @@ describe SecureHeaders do
253
253
  context "when using a browser besides chrome/firefox" do
254
254
  it "sets the CSP header" do
255
255
  stub_user_agent(USER_AGENTS[:opera])
256
- should_assign_header(WEBKIT_CSP_HEADER_NAME + "-Report-Only", WEBKIT_CSP_HEADER)
256
+ should_assign_header(HEADER_NAME + "-Report-Only", DEFAULT_CSP_HEADER)
257
257
  subject.set_csp_header
258
258
  end
259
259
  end
260
+ end
260
261
 
261
- context "when using the experimental key" do
262
- before(:each) do
263
- stub_user_agent(USER_AGENTS[:chrome])
264
- @opts = {
265
- :enforce => true,
266
- :default_src => 'self',
267
- :script_src => 'https://mycdn.example.com',
268
- :experimental => {
269
- :script_src => 'self',
270
- }
271
- }
272
- end
273
-
274
- it "does not set the header in enforce mode if experimental is supplied, but enforce is disabled" do
275
- opts = @opts.merge(:enforce => false)
276
- should_assign_header(WEBKIT_CSP_HEADER_NAME + "-Report-Only", anything)
277
- should_not_assign_header(WEBKIT_CSP_HEADER_NAME)
278
- subject.set_csp_header(opts)
279
- 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
280
267
 
281
- it "sets a header in enforce mode as well as report-only mode" do
282
- should_assign_header(WEBKIT_CSP_HEADER_NAME, anything)
283
- should_assign_header(WEBKIT_CSP_HEADER_NAME + "-Report-Only", anything)
284
- subject.set_csp_header(@opts)
285
- 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')
286
271
  end
287
272
  end
288
273
  end
data/spec/spec_helper.rb CHANGED
@@ -1,25 +1,36 @@
1
1
  require 'rubygems'
2
- require 'spork'
2
+ require 'rspec'
3
3
 
4
- unless Spork.using_spork?
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
- Spork.prefork do
12
- require 'pry'
13
- require 'rspec'
6
+ if defined?(Coveralls)
7
+ Coveralls.wear!
14
8
  end
15
9
 
16
- Spork.each_run do
17
- require File.join(File.dirname(__FILE__), '..', 'lib', 'secure_headers')
18
- require File.join(File.dirname(__FILE__), '..', 'app', 'controllers', 'content_security_policy_controller')
19
- include ::SecureHeaders::StrictTransportSecurity::Constants
20
- include ::SecureHeaders::ContentSecurityPolicy::Constants
21
- include ::SecureHeaders::XFrameOptions::Constants
22
- include ::SecureHeaders::XXssProtection::Constants
23
- include ::SecureHeaders::XContentTypeOptions::Constants
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