secure_headers 0.1.1 → 0.2.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 (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