secure_headers 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of secure_headers might be problematic. Click here for more details.

Files changed (104) hide show
  1. data/.gitignore +9 -0
  2. data/.travis.yml +3 -2
  3. data/Gemfile +4 -0
  4. data/Guardfile +1 -1
  5. data/HISTORY.md +14 -0
  6. data/README.md +2 -2
  7. data/Rakefile +41 -1
  8. data/app/controllers/content_security_policy_controller.rb +19 -10
  9. data/fixtures/rails_3_2_12/.rspec +1 -0
  10. data/fixtures/rails_3_2_12/Gemfile +14 -0
  11. data/fixtures/rails_3_2_12/Guardfile +14 -0
  12. data/fixtures/rails_3_2_12/README.rdoc +261 -0
  13. data/fixtures/rails_3_2_12/Rakefile +7 -0
  14. data/fixtures/rails_3_2_12/app/controllers/application_controller.rb +4 -0
  15. data/fixtures/rails_3_2_12/app/controllers/other_things_controller.rb +5 -0
  16. data/fixtures/rails_3_2_12/app/controllers/things_controller.rb +6 -0
  17. data/fixtures/rails_3_2_12/app/models/.gitkeep +0 -0
  18. data/fixtures/rails_3_2_12/app/models/thing.rb +3 -0
  19. data/fixtures/rails_3_2_12/app/views/layouts/application.html.erb +14 -0
  20. data/fixtures/rails_3_2_12/app/views/other_things/index.html.erb +1 -0
  21. data/fixtures/rails_3_2_12/app/views/things/index.html.erb +21 -0
  22. data/fixtures/rails_3_2_12/config.ru +4 -0
  23. data/fixtures/rails_3_2_12/config/application.rb +68 -0
  24. data/fixtures/rails_3_2_12/config/boot.rb +6 -0
  25. data/fixtures/rails_3_2_12/config/database.yml +25 -0
  26. data/fixtures/rails_3_2_12/config/environment.rb +5 -0
  27. data/fixtures/rails_3_2_12/config/environments/development.rb +37 -0
  28. data/fixtures/rails_3_2_12/config/environments/production.rb +67 -0
  29. data/fixtures/rails_3_2_12/config/environments/test.rb +37 -0
  30. data/fixtures/rails_3_2_12/config/initializers/backtrace_silencers.rb +7 -0
  31. data/fixtures/rails_3_2_12/config/initializers/inflections.rb +15 -0
  32. data/fixtures/rails_3_2_12/config/initializers/mime_types.rb +5 -0
  33. data/fixtures/rails_3_2_12/config/initializers/secret_token.rb +7 -0
  34. data/fixtures/rails_3_2_12/config/initializers/secure_headers.rb +15 -0
  35. data/fixtures/rails_3_2_12/config/initializers/session_store.rb +8 -0
  36. data/fixtures/rails_3_2_12/config/initializers/wrap_parameters.rb +14 -0
  37. data/fixtures/rails_3_2_12/config/locales/en.yml +5 -0
  38. data/fixtures/rails_3_2_12/config/routes.rb +61 -0
  39. data/fixtures/rails_3_2_12/db/schema.rb +16 -0
  40. data/fixtures/rails_3_2_12/db/seeds.rb +7 -0
  41. data/fixtures/rails_3_2_12/lib/assets/.gitkeep +0 -0
  42. data/fixtures/rails_3_2_12/lib/tasks/.gitkeep +0 -0
  43. data/fixtures/rails_3_2_12/log/.gitkeep +0 -0
  44. data/fixtures/rails_3_2_12/spec/controllers/other_things_controller_spec.rb +40 -0
  45. data/fixtures/rails_3_2_12/spec/controllers/things_controller_spec.rb +47 -0
  46. data/fixtures/rails_3_2_12/spec/spec_helper.rb +19 -0
  47. data/fixtures/rails_3_2_12/vendor/assets/javascripts/.gitkeep +0 -0
  48. data/fixtures/rails_3_2_12/vendor/assets/stylesheets/.gitkeep +0 -0
  49. data/fixtures/rails_3_2_12/vendor/plugins/.gitkeep +0 -0
  50. data/fixtures/rails_3_2_12_no_init/.rspec +1 -0
  51. data/fixtures/rails_3_2_12_no_init/Gemfile +14 -0
  52. data/fixtures/rails_3_2_12_no_init/Guardfile +14 -0
  53. data/fixtures/rails_3_2_12_no_init/README.rdoc +261 -0
  54. data/fixtures/rails_3_2_12_no_init/Rakefile +7 -0
  55. data/fixtures/rails_3_2_12_no_init/app/controllers/application_controller.rb +4 -0
  56. data/fixtures/rails_3_2_12_no_init/app/controllers/other_things_controller.rb +7 -0
  57. data/fixtures/rails_3_2_12_no_init/app/controllers/things_controller.rb +5 -0
  58. data/fixtures/rails_3_2_12_no_init/app/models/.gitkeep +0 -0
  59. data/fixtures/rails_3_2_12_no_init/app/models/thing.rb +3 -0
  60. data/fixtures/rails_3_2_12_no_init/app/views/layouts/application.html.erb +14 -0
  61. data/fixtures/rails_3_2_12_no_init/app/views/other_things/index.html.erb +1 -0
  62. data/fixtures/rails_3_2_12_no_init/app/views/things/_form.html.erb +17 -0
  63. data/fixtures/rails_3_2_12_no_init/app/views/things/edit.html.erb +6 -0
  64. data/fixtures/rails_3_2_12_no_init/app/views/things/index.html.erb +21 -0
  65. data/fixtures/rails_3_2_12_no_init/app/views/things/new.html.erb +5 -0
  66. data/fixtures/rails_3_2_12_no_init/app/views/things/show.html.erb +5 -0
  67. data/fixtures/rails_3_2_12_no_init/config.ru +4 -0
  68. data/fixtures/rails_3_2_12_no_init/config/application.rb +68 -0
  69. data/fixtures/rails_3_2_12_no_init/config/boot.rb +6 -0
  70. data/fixtures/rails_3_2_12_no_init/config/database.yml +25 -0
  71. data/fixtures/rails_3_2_12_no_init/config/environment.rb +5 -0
  72. data/fixtures/rails_3_2_12_no_init/config/environments/development.rb +37 -0
  73. data/fixtures/rails_3_2_12_no_init/config/environments/production.rb +67 -0
  74. data/fixtures/rails_3_2_12_no_init/config/environments/test.rb +37 -0
  75. data/fixtures/rails_3_2_12_no_init/config/initializers/backtrace_silencers.rb +7 -0
  76. data/fixtures/rails_3_2_12_no_init/config/initializers/inflections.rb +15 -0
  77. data/fixtures/rails_3_2_12_no_init/config/initializers/mime_types.rb +5 -0
  78. data/fixtures/rails_3_2_12_no_init/config/initializers/secret_token.rb +7 -0
  79. data/fixtures/rails_3_2_12_no_init/config/initializers/session_store.rb +8 -0
  80. data/fixtures/rails_3_2_12_no_init/config/initializers/wrap_parameters.rb +14 -0
  81. data/fixtures/rails_3_2_12_no_init/config/locales/en.yml +5 -0
  82. data/fixtures/rails_3_2_12_no_init/config/routes.rb +61 -0
  83. data/fixtures/rails_3_2_12_no_init/db/schema.rb +16 -0
  84. data/fixtures/rails_3_2_12_no_init/db/seeds.rb +7 -0
  85. data/fixtures/rails_3_2_12_no_init/lib/assets/.gitkeep +0 -0
  86. data/fixtures/rails_3_2_12_no_init/lib/tasks/.gitkeep +0 -0
  87. data/fixtures/rails_3_2_12_no_init/log/.gitkeep +0 -0
  88. data/fixtures/rails_3_2_12_no_init/spec/controllers/other_things_controller_spec.rb +40 -0
  89. data/fixtures/rails_3_2_12_no_init/spec/controllers/things_controller_spec.rb +44 -0
  90. data/fixtures/rails_3_2_12_no_init/spec/spec_helper.rb +20 -0
  91. data/fixtures/rails_3_2_12_no_init/vendor/assets/javascripts/.gitkeep +0 -0
  92. data/fixtures/rails_3_2_12_no_init/vendor/assets/stylesheets/.gitkeep +0 -0
  93. data/fixtures/rails_3_2_12_no_init/vendor/plugins/.gitkeep +0 -0
  94. data/lib/secure_headers.rb +19 -15
  95. data/lib/secure_headers/headers/content_security_policy.rb +54 -113
  96. data/lib/secure_headers/headers/content_security_policy/browser_strategy.rb +70 -0
  97. data/lib/secure_headers/headers/content_security_policy/firefox_browser_strategy.rb +72 -0
  98. data/lib/secure_headers/headers/content_security_policy/ie_browser_strategy.rb +6 -0
  99. data/lib/secure_headers/headers/content_security_policy/webkit_browser_strategy.rb +9 -0
  100. data/lib/secure_headers/version.rb +1 -1
  101. data/{secure-headers.gemspec → secure_headers.gemspec} +0 -0
  102. data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +72 -84
  103. data/travis.sh +10 -0
  104. metadata +93 -3
@@ -0,0 +1,70 @@
1
+ require "forwardable"
2
+
3
+ module SecureHeaders
4
+ class ContentSecurityPolicy
5
+ class BrowserStrategy
6
+ extend Forwardable
7
+
8
+ def_delegators :@content_security_policy, :browser, :experimental, :enforce, :config
9
+
10
+ def self.build(content_security_policy)
11
+ browser = content_security_policy.browser
12
+ klass = if browser.ie?
13
+ IeBrowserStrategy
14
+ elsif browser.firefox?
15
+ FirefoxBrowserStrategy
16
+ else
17
+ WebkitBrowserStrategy
18
+ end
19
+
20
+ klass.new content_security_policy
21
+ end
22
+
23
+ def initialize(content_security_policy)
24
+ @content_security_policy = content_security_policy
25
+ end
26
+
27
+ def base_name
28
+ SecureHeaders::ContentSecurityPolicy::STANDARD_HEADER_NAME
29
+ end
30
+
31
+ def name
32
+ base = base_name
33
+ if !enforce || experimental
34
+ base += "-Report-Only"
35
+ end
36
+ base
37
+ end
38
+
39
+ def csp_header
40
+ SecureHeaders::ContentSecurityPolicy::WEBKIT_CSP_HEADER
41
+ end
42
+
43
+ def directives
44
+ SecureHeaders::ContentSecurityPolicy::WEBKIT_DIRECTIVES
45
+ end
46
+
47
+ def filter_unsupported_directives(config)
48
+ config = config.dup
49
+ config.delete(:frame_ancestors)
50
+ config
51
+ end
52
+
53
+ def translate_inline_or_eval val
54
+ val == 'inline' ? "'unsafe-inline'" : "'unsafe-eval'"
55
+ end
56
+
57
+ def build_impl_specific_directives(default)
58
+ if default.any?
59
+ "default-src #{default.join(" ")}; "
60
+ else
61
+ ""
62
+ end
63
+ end
64
+
65
+ def normalize_reporting_endpoint?
66
+ false
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,72 @@
1
+ module SecureHeaders
2
+ class ContentSecurityPolicy
3
+ class FirefoxBrowserStrategy < BrowserStrategy
4
+ def base_name
5
+ SecureHeaders::ContentSecurityPolicy::FIREFOX_CSP_HEADER_NAME
6
+ end
7
+
8
+ def csp_header
9
+ SecureHeaders::ContentSecurityPolicy::FIREFOX_CSP_HEADER
10
+ end
11
+
12
+ def directives
13
+ SecureHeaders::ContentSecurityPolicy::FIREFOX_DIRECTIVES
14
+ end
15
+
16
+ def filter_unsupported_directives(config)
17
+ config = config.dup
18
+ config[:xhr_src] = config.delete(:connect_src) if config[:connect_src]
19
+ config
20
+ end
21
+
22
+ def translate_inline_or_eval val
23
+ val == 'inline' ? 'inline-script' : 'eval-script'
24
+ end
25
+
26
+ def build_impl_specific_directives(default)
27
+ build_firefox_specific_preamble(default) || ''
28
+ end
29
+
30
+ def build_firefox_specific_preamble(default_src_value)
31
+ header_value = ''
32
+ header_value += "allow #{default_src_value.join(" ")}; " if default_src_value.any?
33
+
34
+ options_directive = build_options_directive
35
+ header_value += "options #{options_directive.join(" ")}; " if options_directive.any?
36
+ header_value
37
+ end
38
+
39
+ # moves inline/eval values from script-src to options
40
+ # discards those values in the style-src directive
41
+ def build_options_directive
42
+ options_directive = []
43
+ config.each do |directive, val|
44
+ next if val.is_a?(String)
45
+ new_val = []
46
+ val.each do |token|
47
+ if ['inline-script', 'eval-script'].include?(token)
48
+ # Firefox does not support blocking inline styles ATM
49
+ # https://bugzilla.mozilla.org/show_bug.cgi?id=763879
50
+ unless directive?(directive, "style_src") || options_directive.include?(token)
51
+ options_directive << token
52
+ end
53
+ else
54
+ new_val << token
55
+ end
56
+ end
57
+ config[directive] = new_val
58
+ end
59
+
60
+ options_directive
61
+ end
62
+
63
+ def directive? val, name
64
+ val.to_s.casecmp(name) == 0
65
+ end
66
+
67
+ def normalize_reporting_endpoint?
68
+ true
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,6 @@
1
+ module SecureHeaders
2
+ class ContentSecurityPolicy
3
+ class IeBrowserStrategy < BrowserStrategy
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,9 @@
1
+ module SecureHeaders
2
+ class ContentSecurityPolicy
3
+ class WebkitBrowserStrategy < BrowserStrategy
4
+ def base_name
5
+ SecureHeaders::ContentSecurityPolicy::WEBKIT_CSP_HEADER_NAME
6
+ end
7
+ end
8
+ end
9
+ end
@@ -1,3 +1,3 @@
1
1
  module SecureHeaders
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
File without changes
@@ -28,28 +28,31 @@ module SecureHeaders
28
28
  end
29
29
 
30
30
  describe "#name" do
31
+ context "when supplying options to override request" do
32
+ specify { ContentSecurityPolicy.new(default_opts, :ua => IE).name.should == STANDARD_HEADER_NAME + "-Report-Only"}
33
+ specify { ContentSecurityPolicy.new(default_opts, :ua => FIREFOX).name.should == FIREFOX_CSP_HEADER_NAME + "-Report-Only"}
34
+ specify { ContentSecurityPolicy.new(default_opts, :ua => CHROME).name.should == WEBKIT_CSP_HEADER_NAME + "-Report-Only"}
35
+ end
36
+
31
37
  context "when in report-only mode" do
32
- specify { ContentSecurityPolicy.new(request_for(IE), default_opts).name.should == STANDARD_HEADER_NAME + "-Report-Only"}
33
- specify { ContentSecurityPolicy.new(request_for(FIREFOX), default_opts).name.should == FIREFOX_CSP_HEADER_NAME + "-Report-Only"}
34
- specify { ContentSecurityPolicy.new(request_for(FIREFOX_18), default_opts).name.should == FIREFOX_CSP_HEADER_NAME + "-Report-Only"}
35
- specify { ContentSecurityPolicy.new(request_for(CHROME), default_opts).name.should == WEBKIT_CSP_HEADER_NAME + "-Report-Only"}
38
+ specify { ContentSecurityPolicy.new(default_opts, :request => request_for(IE)).name.should == STANDARD_HEADER_NAME + "-Report-Only"}
39
+ specify { ContentSecurityPolicy.new(default_opts, :request => request_for(FIREFOX)).name.should == FIREFOX_CSP_HEADER_NAME + "-Report-Only"}
40
+ specify { ContentSecurityPolicy.new(default_opts, :request => request_for(CHROME)).name.should == WEBKIT_CSP_HEADER_NAME + "-Report-Only"}
36
41
  end
37
42
 
38
43
  context "when in enforce mode" do
39
44
  let(:opts) { default_opts.merge(:enforce => true)}
40
45
 
41
- specify { ContentSecurityPolicy.new(request_for(IE), opts).name.should == STANDARD_HEADER_NAME}
42
- specify { ContentSecurityPolicy.new(request_for(FIREFOX), opts).name.should == FIREFOX_CSP_HEADER_NAME}
43
- specify { ContentSecurityPolicy.new(request_for(FIREFOX_18), opts).name.should == FIREFOX_CSP_HEADER_NAME}
44
- specify { ContentSecurityPolicy.new(request_for(CHROME), opts).name.should == WEBKIT_CSP_HEADER_NAME}
46
+ specify { ContentSecurityPolicy.new(opts, :request => request_for(IE)).name.should == STANDARD_HEADER_NAME}
47
+ specify { ContentSecurityPolicy.new(opts, :request => request_for(FIREFOX)).name.should == FIREFOX_CSP_HEADER_NAME}
48
+ specify { ContentSecurityPolicy.new(opts, :request => request_for(CHROME)).name.should == WEBKIT_CSP_HEADER_NAME}
45
49
  end
46
50
 
47
51
  context "when in experimental mode" do
48
52
  let(:opts) { default_opts.merge(:enforce => true).merge(:experimental => {})}
49
- specify { ContentSecurityPolicy.new(request_for(IE), opts, :experimental => true).name.should == STANDARD_HEADER_NAME + "-Report-Only"}
50
- specify { ContentSecurityPolicy.new(request_for(FIREFOX), opts, :experimental => true).name.should == FIREFOX_CSP_HEADER_NAME + "-Report-Only"}
51
- specify { ContentSecurityPolicy.new(request_for(FIREFOX_18), opts, :experimental => true).name.should == FIREFOX_CSP_HEADER_NAME + "-Report-Only"}
52
- specify { ContentSecurityPolicy.new(request_for(CHROME), opts, :experimental => true).name.should == WEBKIT_CSP_HEADER_NAME + "-Report-Only"}
53
+ specify { ContentSecurityPolicy.new(opts, {:experimental => true, :request => request_for(IE)}).name.should == STANDARD_HEADER_NAME + "-Report-Only"}
54
+ specify { ContentSecurityPolicy.new(opts, {:experimental => true, :request => request_for(FIREFOX)}).name.should == FIREFOX_CSP_HEADER_NAME + "-Report-Only"}
55
+ specify { ContentSecurityPolicy.new(opts, {:experimental => true, :request => request_for(CHROME)}).name.should == WEBKIT_CSP_HEADER_NAME + "-Report-Only"}
53
56
  end
54
57
  end
55
58
 
@@ -63,7 +66,7 @@ module SecureHeaders
63
66
 
64
67
  context "X-Content-Security-Policy" do
65
68
  it "converts the script values to their equivilents" do
66
- csp = ContentSecurityPolicy.new(request_for(FIREFOX), @opts)
69
+ csp = ContentSecurityPolicy.new(@opts, :request => request_for(FIREFOX))
67
70
  csp.value.should include("script-src https://* data: 'self' 'none'")
68
71
  csp.value.should include('options inline-script eval-script')
69
72
  end
@@ -71,7 +74,7 @@ module SecureHeaders
71
74
 
72
75
  context "X-Webkit-CSP" do
73
76
  it "converts the script values to their equivilents" do
74
- csp = ContentSecurityPolicy.new(request_for(CHROME), @opts)
77
+ csp = ContentSecurityPolicy.new(@opts, :request => request_for(CHROME))
75
78
  csp.value.should include("script-src 'unsafe-inline' 'unsafe-eval' https://* data: 'self' 'none' chrome-extension")
76
79
  end
77
80
  end
@@ -84,7 +87,7 @@ module SecureHeaders
84
87
  :default_src => 'https://*',
85
88
  :script_src => "inline eval https://*"
86
89
  }
87
- csp = ContentSecurityPolicy.new(request_for(FIREFOX), opts)
90
+ csp = ContentSecurityPolicy.new(opts, :request => request_for(FIREFOX))
88
91
  browser_specific = csp.send :build_impl_specific_directives
89
92
  browser_specific.should include('options inline-script eval-script;')
90
93
  end
@@ -94,7 +97,7 @@ module SecureHeaders
94
97
  :default_src => 'https://*',
95
98
  :style_src => "inline eval https://*"
96
99
  }
97
- csp = ContentSecurityPolicy.new(request_for(FIREFOX), opts)
100
+ csp = ContentSecurityPolicy.new(opts, :request => request_for(FIREFOX))
98
101
  browser_specific = csp.send :build_impl_specific_directives
99
102
  browser_specific.should_not include('inline-script')
100
103
  browser_specific.should_not include('eval-script')
@@ -102,75 +105,55 @@ module SecureHeaders
102
105
  end
103
106
  end
104
107
 
105
- describe "#supports_standard?" do
106
- ['IE', 'Safari', 'Chrome'].each do |browser_name|
107
- it "returns true for #{browser_name}" do
108
- browser = Brwsr::Browser.new(:ua => browser_name)
109
- subject.stub(:browser).and_return(browser)
110
- subject.send(:supports_standard?).should be_true
111
- end
112
- end
113
-
114
- it "returns true for Firefox v >= 18" do
115
- browser = Brwsr::Browser.new(:ua => "Firefox 18")
116
- subject.stub(:browser).and_return(browser)
117
- subject.send(:supports_standard?).should be_true
118
- end
119
-
120
- it "returns false for Firefox v < 18" do
121
- browser = Brwsr::Browser.new(:ua => "Firefox 17")
122
- subject.stub(:browser).and_return(browser)
123
- subject.send(:supports_standard?).should be_false
124
- end
125
- end
126
-
127
108
  describe "#same_origin?" do
128
109
  let(:origin) {"https://example.com:123"}
129
110
 
130
111
  it "matches when host, scheme, and port match" do
131
- csp = ContentSecurityPolicy.new(request_for(FIREFOX, "https://example.com"), {:report_uri => 'https://example.com'})
112
+ csp = ContentSecurityPolicy.new({:report_uri => 'https://example.com'}, :request => request_for(FIREFOX, "https://example.com"))
132
113
  csp.send(:same_origin?).should be_true
133
114
 
134
- csp = ContentSecurityPolicy.new(request_for(FIREFOX, "https://example.com:443"), {:report_uri => 'https://example.com'})
115
+ csp = ContentSecurityPolicy.new({:report_uri => 'https://example.com'}, :request => request_for(FIREFOX, "https://example.com:443"))
135
116
  csp.send(:same_origin?).should be_true
136
117
 
137
- csp = ContentSecurityPolicy.new(request_for(FIREFOX, "https://example.com:123"), {:report_uri => 'https://example.com:123'})
118
+ csp = ContentSecurityPolicy.new({:report_uri => 'https://example.com:123'}, :request => request_for(FIREFOX, "https://example.com:123"))
138
119
  csp.send(:same_origin?).should be_true
139
120
 
140
- csp = ContentSecurityPolicy.new(request_for(FIREFOX, "http://example.com"), {:report_uri => 'http://example.com'})
121
+ csp = ContentSecurityPolicy.new({:report_uri => 'http://example.com'}, :request => request_for(FIREFOX, "http://example.com"))
141
122
  csp.send(:same_origin?).should be_true
142
123
 
143
- csp = ContentSecurityPolicy.new(request_for(FIREFOX, "http://example.com"), {:report_uri => 'http://example.com:80'})
124
+ csp = ContentSecurityPolicy.new({:report_uri => 'http://example.com:80'}, :request => request_for(FIREFOX, "http://example.com"))
144
125
  csp.send(:same_origin?).should be_true
145
126
 
146
- csp = ContentSecurityPolicy.new(request_for(FIREFOX, "http://example.com:80"), {:report_uri => 'http://example.com'})
127
+ csp = ContentSecurityPolicy.new({:report_uri => 'http://example.com'}, :request => request_for(FIREFOX, "http://example.com:80"))
147
128
  csp.send(:same_origin?).should be_true
148
129
  end
149
130
 
150
131
  it "does not match port mismatches" do
151
- csp = ContentSecurityPolicy.new(request_for(FIREFOX, "http://example.com:81"), {:report_uri => 'http://example.com'})
132
+ csp = ContentSecurityPolicy.new({:report_uri => 'http://example.com'}, :request => request_for(FIREFOX, "http://example.com:81"))
152
133
  csp.send(:same_origin?).should be_false
153
134
  end
154
135
 
155
136
  it "does not match host mismatches" do
156
- csp = ContentSecurityPolicy.new(request_for(FIREFOX, "http://example.com"), {:report_uri => 'http://twitter.com'})
137
+ csp = ContentSecurityPolicy.new({:report_uri => 'http://twitter.com'}, :request => request_for(FIREFOX, "http://example.com"))
157
138
  csp.send(:same_origin?).should be_false
158
139
  end
159
140
 
160
141
  it "does not match host mismatches because of subdomains" do
161
- csp = ContentSecurityPolicy.new(request_for(FIREFOX, "http://sub.example.com"), {:report_uri => 'http://example.com'})
142
+ csp = ContentSecurityPolicy.new({:report_uri => 'http://example.com'}, :request => request_for(FIREFOX, "http://sub.example.com"))
162
143
  csp.send(:same_origin?).should be_false
163
144
  end
164
145
 
165
146
  it "does not match scheme mismatches" do
166
- csp = ContentSecurityPolicy.new(request_for(FIREFOX, "ftp://example.com"), {:report_uri => 'https://example.com'})
147
+ csp = ContentSecurityPolicy.new({:report_uri => 'https://example.com'}, :request => request_for(FIREFOX, "ftp://example.com"))
167
148
  csp.send(:same_origin?).should be_false
168
149
  end
169
150
 
170
151
  it "does not match on substring collisions" do
171
- csp = ContentSecurityPolicy.new(request_for(FIREFOX, "https://anotherexample.com"), {:report_uri => 'https://example.com'})
152
+ csp = ContentSecurityPolicy.new({:report_uri => 'https://example.com'}, :request => request_for(FIREFOX, "https://anotherexample.com"))
172
153
  csp.send(:same_origin?).should be_false
173
154
  end
155
+
156
+
174
157
  end
175
158
 
176
159
  describe "#normalize_reporting_endpoint" do
@@ -178,69 +161,69 @@ module SecureHeaders
178
161
 
179
162
  context "when using firefox" do
180
163
  it "updates the report-uri when posting to a different host" do
181
- subject.configure(request_for(FIREFOX, "https://anexample.com"), opts)
182
- subject.report_uri.should == FF_CSP_ENDPOINT
164
+ csp = ContentSecurityPolicy.new(opts, :request => request_for(FIREFOX, "https://anexample.com"))
165
+ csp.report_uri.should == FF_CSP_ENDPOINT
183
166
  end
184
167
 
185
- it "updates the report-uri when posting to a different host for Firefox >= 18" do
186
- subject.configure(request_for(FIREFOX, "https://anexample.com"), opts)
187
- subject.report_uri.should == FF_CSP_ENDPOINT
168
+ it "doesn't set report-uri if no forward_endpoint is supplied" do
169
+ csp = ContentSecurityPolicy.new({:report_uri => "https://another.example.com"}, :request => request_for(FIREFOX, "https://anexample.com"))
170
+ csp.report_uri.should be_nil
188
171
  end
189
172
 
190
- it "doesn't set report-uri if no forward_endpoint is supplied" do
191
- subject.configure(request_for(FIREFOX, "https://example.com"), :report_uri => "https://another.example.com")
192
- subject.report_uri.should be_nil
173
+ it "forwards if the request_uri is set to a non-matching value" do
174
+ csp = ContentSecurityPolicy.new({:report_uri => "https://another.example.com", :forward_endpoint => '/somewhere'}, :ua => "Firefox", :request_uri => "https://anexample.com")
175
+ csp.report_uri.should == FF_CSP_ENDPOINT
193
176
  end
194
177
  end
195
178
 
196
179
  it "does not update the URI is the report_uri is on the same origin" do
197
180
  opts = {:report_uri => 'https://example.com/csp', :forward_endpoint => 'https://anotherexample.com'}
198
- subject.configure(request_for(FIREFOX, "https://example.com/somewhere"), opts)
199
- subject.report_uri.should == 'https://example.com/csp'
181
+ csp = ContentSecurityPolicy.new(opts, :request => request_for(FIREFOX, "https://example.com/somewhere"))
182
+ csp.report_uri.should == 'https://example.com/csp'
200
183
  end
201
184
 
202
185
  it "does not update the report-uri when using a non-firefox browser" do
203
- subject.configure(request_for(CHROME), opts)
204
- subject.report_uri.should == 'https://example.com/csp'
186
+ csp = ContentSecurityPolicy.new(opts, :request => request_for(CHROME))
187
+ csp.report_uri.should == 'https://example.com/csp'
205
188
  end
206
189
  end
207
190
 
208
191
  describe "#value" do
209
192
  it "raises an exception when default-src is missing" do
210
- subject.configure(request_for(CHROME), {:script_src => 'anything'})
193
+ csp = ContentSecurityPolicy.new({:script_src => 'anything'}, :request => request_for(CHROME))
211
194
  lambda {
212
- subject.value
195
+ csp.value
213
196
  }.should raise_error(ContentSecurityPolicyBuildError, "Couldn't build CSP header :( Expected to find default_src directive value")
214
197
  end
215
198
 
216
199
  context "auto-whitelists data: uris for img-src" do
217
200
 
218
201
  it "sets the value if no img-src specified" do
219
- csp = ContentSecurityPolicy.new(request_for(CHROME), {:default_src => 'self', :disable_fill_missing => true, :disable_chrome_extension => true})
202
+ csp = ContentSecurityPolicy.new({:default_src => 'self', :disable_fill_missing => true, :disable_chrome_extension => true}, :request => request_for(CHROME))
220
203
  csp.value.should == "default-src 'self'; img-src data:;"
221
204
  end
222
205
 
223
206
  it "appends the value if img-src is specified" do
224
- csp = ContentSecurityPolicy.new(request_for(CHROME), {:default_src => 'self', :img_src => 'self', :disable_fill_missing => true, :disable_chrome_extension => true})
207
+ csp = ContentSecurityPolicy.new({:default_src => 'self', :img_src => 'self', :disable_fill_missing => true, :disable_chrome_extension => true}, :request => request_for(CHROME))
225
208
  csp.value.should == "default-src 'self'; img-src 'self' data:;"
226
209
  end
227
210
  end
228
211
 
229
212
  it "fills in directives without values with default-src value" do
230
213
  options = default_opts.merge(:disable_fill_missing => false)
231
- csp = ContentSecurityPolicy.new(request_for(CHROME), options)
214
+ csp = ContentSecurityPolicy.new(options, :request => request_for(CHROME))
232
215
  value = "default-src https://*; connect-src https://*; font-src https://*; frame-src https://*; img-src https://* data:; media-src https://*; object-src https://*; script-src 'unsafe-inline' 'unsafe-eval' https://* data:; style-src 'unsafe-inline' https://* chrome-extension: about:; report-uri /csp_report;"
233
216
  csp.value.should == value
234
217
  end
235
218
 
236
219
  it "sends the chrome csp header if an unknown browser is supplied" do
237
- csp = ContentSecurityPolicy.new(request_for(IE), default_opts)
220
+ csp = ContentSecurityPolicy.new(default_opts, :request => request_for(IE))
238
221
  csp.value.should match "default-src"
239
222
  end
240
223
 
241
224
  context "X-Content-Security-Policy" do
242
225
  it "builds a csp header for firefox" do
243
- csp = ContentSecurityPolicy.new(request_for(FIREFOX), default_opts)
226
+ csp = ContentSecurityPolicy.new(default_opts, :request => request_for(FIREFOX))
244
227
  csp.value.should == "allow https://*; options inline-script eval-script; img-src data:; script-src https://* data:; style-src https://* chrome-extension: about:;"
245
228
  end
246
229
 
@@ -251,7 +234,7 @@ module SecureHeaders
251
234
  :disable_chrome_extension => true,
252
235
  :disable_fill_missing => true
253
236
  }
254
- csp = ContentSecurityPolicy.new(request_for(FIREFOX), opts)
237
+ csp = ContentSecurityPolicy.new(opts, :request => request_for(FIREFOX))
255
238
  csp.value.should =~ /xhr-src 'self' http:/
256
239
  end
257
240
 
@@ -262,24 +245,19 @@ module SecureHeaders
262
245
  :disable_chrome_extension => true,
263
246
  :disable_fill_missing => true
264
247
  }
265
- csp = ContentSecurityPolicy.new(request_for(FIREFOX_18), opts)
248
+ csp = ContentSecurityPolicy.new(opts, :request => request_for(FIREFOX_18))
266
249
  csp.value.should =~ /xhr-src 'self' http:\/\/\*\.localhost\.com:\*/
267
250
  end
268
-
269
- it "builds a w3c-style-ish header for Firefox > version 18" do
270
- csp = ContentSecurityPolicy.new(request_for(FIREFOX_18), default_opts)
271
- csp.value.should =~ /default-src/
272
- end
273
251
  end
274
252
 
275
253
  context "X-Webkit-CSP" do
276
254
  it "builds a csp header for chrome" do
277
- csp = ContentSecurityPolicy.new(request_for(CHROME), default_opts)
255
+ csp = ContentSecurityPolicy.new(default_opts, :request => request_for(CHROME))
278
256
  csp.value.should == "default-src https://*; img-src data:; script-src 'unsafe-inline' 'unsafe-eval' https://* data:; style-src 'unsafe-inline' https://* chrome-extension: about:; report-uri /csp_report;"
279
257
  end
280
258
 
281
259
  it "ignores :forward_endpoint settings" do
282
- csp = ContentSecurityPolicy.new(request_for(CHROME), @options_with_forwarding)
260
+ csp = ContentSecurityPolicy.new(@options_with_forwarding, :request => request_for(CHROME))
283
261
  csp.value.should =~ /report-uri #{@options_with_forwarding[:report_uri]};/
284
262
  end
285
263
 
@@ -291,7 +269,7 @@ module SecureHeaders
291
269
  :style_src => "inline https://* chrome-extension: about:"
292
270
  }
293
271
 
294
- csp = ContentSecurityPolicy.new(request_for(CHROME), opts)
272
+ csp = ContentSecurityPolicy.new(opts, :request => request_for(CHROME))
295
273
 
296
274
  # ignore the report-uri directive
297
275
  csp.value.split(';')[0...-1].each{|directive| directive.should =~ /chrome-extension:/}
@@ -311,12 +289,12 @@ module SecureHeaders
311
289
 
312
290
  let(:header) {}
313
291
  it "returns the original value" do
314
- header = ContentSecurityPolicy.new(request_for(CHROME), options)
292
+ header = ContentSecurityPolicy.new(options, :request => request_for(CHROME))
315
293
  header.value.should == "default-src 'self'; img-src data:; script-src https://*;"
316
294
  end
317
295
 
318
296
  it "it returns the experimental value if requested" do
319
- header = ContentSecurityPolicy.new(request_for(CHROME), options, :experimental => true)
297
+ header = ContentSecurityPolicy.new(options, {:request => request_for(CHROME), :experimental => true})
320
298
  header.value.should_not =~ /https/
321
299
  end
322
300
  end
@@ -332,12 +310,17 @@ module SecureHeaders
332
310
  }
333
311
 
334
312
  it "adds directive values for headers on http" do
335
- csp = ContentSecurityPolicy.new(request_for(CHROME), options)
313
+ csp = ContentSecurityPolicy.new(options, :request => request_for(CHROME))
336
314
  csp.value.should == "default-src https://*; frame-src http://*; img-src http://* data:; script-src 'unsafe-inline' 'unsafe-eval' https://* data:; style-src 'unsafe-inline' https://* chrome-extension: about:; report-uri /csp_report;"
337
315
  end
338
316
 
339
317
  it "does not add the directive values if requesting https" do
340
- csp = ContentSecurityPolicy.new(request_for(CHROME, '/', :ssl => true), options)
318
+ csp = ContentSecurityPolicy.new(options, :request => request_for(CHROME, '/', :ssl => true))
319
+ csp.value.should_not =~ /http:/
320
+ end
321
+
322
+ it "does not add the directive values if requesting https" do
323
+ csp = ContentSecurityPolicy.new(options, :ua => "Chrome", :ssl => true)
341
324
  csp.value.should_not =~ /http:/
342
325
  end
343
326
 
@@ -367,12 +350,17 @@ module SecureHeaders
367
350
  # "allow 'self; script-src https://* http://*" for http requests
368
351
 
369
352
  it "uses the value in the experimental block over SSL" do
370
- csp = ContentSecurityPolicy.new(request_for(FIREFOX, '/', :ssl => true), options, :experimental => true)
353
+ csp = ContentSecurityPolicy.new(options, :experimental => true, :request => request_for(FIREFOX, '/', :ssl => true))
354
+ csp.value.should == "allow 'self'; img-src data:; script-src 'self';"
355
+ end
356
+
357
+ it "detects the :ssl => true option" do
358
+ csp = ContentSecurityPolicy.new(options, :experimental => true, :ua => FIREFOX, :ssl => true)
371
359
  csp.value.should == "allow 'self'; img-src data:; script-src 'self';"
372
360
  end
373
361
 
374
362
  it "merges the values from experimental/http_additions when not over SSL" do
375
- csp = ContentSecurityPolicy.new(request_for(FIREFOX), options, :experimental => true)
363
+ csp = ContentSecurityPolicy.new(options, :experimental => true, :request => request_for(FIREFOX))
376
364
  csp.value.should == "allow 'self'; img-src data:; script-src 'self' https://mycdn.example.com;"
377
365
  end
378
366
  end