secure_headers 0.5.0 → 1.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.
Potentially problematic release.
This version of secure_headers might be problematic. Click here for more details.
- data/HISTORY.md +14 -2
- data/lib/secure_headers.rb +18 -12
- data/lib/secure_headers/header.rb +5 -0
- data/lib/secure_headers/headers/content_security_policy.rb +1 -1
- data/lib/secure_headers/headers/content_security_policy/browser_strategy.rb +6 -2
- data/lib/secure_headers/headers/content_security_policy/{webkit_browser_strategy.rb → standard_browser_strategy.rb} +6 -2
- data/lib/secure_headers/headers/strict_transport_security.rb +1 -1
- data/lib/secure_headers/headers/x_content_type_options.rb +1 -1
- data/lib/secure_headers/headers/x_frame_options.rb +1 -1
- data/lib/secure_headers/headers/x_xss_protection.rb +1 -1
- data/lib/secure_headers/version.rb +1 -1
- data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +18 -13
- data/spec/lib/secure_headers_spec.rb +18 -5
- metadata +4 -3
data/HISTORY.md
CHANGED
@@ -1,12 +1,24 @@
|
|
1
|
+
1.0.0
|
2
|
+
======
|
3
|
+
|
4
|
+
Features:
|
5
|
+
|
6
|
+
- Use non-prefixed header names for Firefox >= 23, Chrome >= 25
|
7
|
+
- Use csp 1.0 compliant header for firefox >= 23
|
8
|
+
|
9
|
+
Bug Fix:
|
10
|
+
|
11
|
+
- Stop sending CSP on safari 5.1+
|
12
|
+
|
1
13
|
0.5.0
|
2
14
|
======
|
3
15
|
|
4
|
-
X-Content-Type-Options also applied to Chrome requests
|
16
|
+
- X-Content-Type-Options also applied to Chrome requests
|
5
17
|
|
6
18
|
0.4.3
|
7
19
|
======
|
8
20
|
|
9
|
-
Safari 5 is just completely broken when CSP is used, both mobile and desktop versions
|
21
|
+
- Safari 5 is just completely broken when CSP is used, both mobile and desktop versions
|
10
22
|
|
11
23
|
0.4.2
|
12
24
|
======
|
data/lib/secure_headers.rb
CHANGED
@@ -59,6 +59,8 @@ module SecureHeaders
|
|
59
59
|
# set_csp_header(+Hash+) - uses the request accessor and options from parameters
|
60
60
|
# set_csp_header(+Rack::Request+, +Hash+)
|
61
61
|
def set_csp_header(req = nil, options=nil)
|
62
|
+
return if broken_implementation?(brwsr)
|
63
|
+
|
62
64
|
if req.is_a?(Hash)
|
63
65
|
options = req
|
64
66
|
elsif req
|
@@ -66,17 +68,15 @@ module SecureHeaders
|
|
66
68
|
end
|
67
69
|
|
68
70
|
options = self.class.secure_headers_options[:csp] if options.nil?
|
69
|
-
|
70
|
-
return if broken_implementation?(brwsr)
|
71
|
-
|
72
71
|
options = self.class.options_for :csp, options
|
72
|
+
|
73
73
|
return if options == false
|
74
74
|
|
75
|
-
|
76
|
-
set_header(
|
75
|
+
csp_header = ContentSecurityPolicy.new(options, :request => request, :controller => self)
|
76
|
+
set_header(csp_header)
|
77
77
|
if options && options[:experimental] && options[:enforce]
|
78
|
-
|
79
|
-
set_header(
|
78
|
+
experimental_header = ContentSecurityPolicy.new(options, :experimental => true, :request => request, :controller => self)
|
79
|
+
set_header(experimental_header)
|
80
80
|
end
|
81
81
|
end
|
82
82
|
|
@@ -105,26 +105,32 @@ module SecureHeaders
|
|
105
105
|
return if options == false
|
106
106
|
|
107
107
|
header = klass.new(options)
|
108
|
-
set_header(header
|
108
|
+
set_header(header)
|
109
109
|
end
|
110
110
|
|
111
|
-
def set_header(
|
112
|
-
|
111
|
+
def set_header(name_or_header, value=nil)
|
112
|
+
if name_or_header.is_a?(Header)
|
113
|
+
header = name_or_header
|
114
|
+
response.headers[header.name] = header.value
|
115
|
+
else
|
116
|
+
response.headers[name_or_header] = value
|
117
|
+
end
|
113
118
|
end
|
114
119
|
|
115
120
|
def broken_implementation?(browser)
|
116
|
-
return browser.ios5? || (browser.safari? && browser.version == 5)
|
121
|
+
return browser.ios5? || (browser.safari? && browser.version == '5')
|
117
122
|
end
|
118
123
|
end
|
119
124
|
end
|
120
125
|
|
121
126
|
|
122
127
|
require "secure_headers/version"
|
128
|
+
require "secure_headers/header"
|
123
129
|
require "secure_headers/headers/content_security_policy"
|
124
130
|
require "secure_headers/headers/content_security_policy/browser_strategy"
|
125
131
|
require "secure_headers/headers/content_security_policy/firefox_browser_strategy"
|
126
132
|
require "secure_headers/headers/content_security_policy/ie_browser_strategy"
|
127
|
-
require "secure_headers/headers/content_security_policy/
|
133
|
+
require "secure_headers/headers/content_security_policy/standard_browser_strategy"
|
128
134
|
require "secure_headers/headers/x_frame_options"
|
129
135
|
require "secure_headers/headers/strict_transport_security"
|
130
136
|
require "secure_headers/headers/x_xss_protection"
|
@@ -3,7 +3,7 @@ require 'brwsr'
|
|
3
3
|
|
4
4
|
module SecureHeaders
|
5
5
|
class ContentSecurityPolicyBuildError < StandardError; end
|
6
|
-
class ContentSecurityPolicy
|
6
|
+
class ContentSecurityPolicy < Header
|
7
7
|
module Constants
|
8
8
|
WEBKIT_CSP_HEADER = "default-src https: data: 'unsafe-inline' 'unsafe-eval'; frame-src https://* about: javascript:; img-src chrome-extension:"
|
9
9
|
FIREFOX_CSP_HEADER = "options eval-script inline-script; allow https://* data:; frame-src https://* about: javascript:; img-src chrome-extension:"
|
@@ -12,9 +12,13 @@ module SecureHeaders
|
|
12
12
|
klass = if browser.ie?
|
13
13
|
IeBrowserStrategy
|
14
14
|
elsif browser.firefox?
|
15
|
-
|
15
|
+
if browser.version.to_i >= 23
|
16
|
+
StandardBrowserStrategy
|
17
|
+
else
|
18
|
+
FirefoxBrowserStrategy
|
19
|
+
end
|
16
20
|
else
|
17
|
-
|
21
|
+
StandardBrowserStrategy
|
18
22
|
end
|
19
23
|
|
20
24
|
klass.new content_security_policy
|
@@ -1,8 +1,12 @@
|
|
1
1
|
module SecureHeaders
|
2
2
|
class ContentSecurityPolicy
|
3
|
-
class
|
3
|
+
class StandardBrowserStrategy < BrowserStrategy
|
4
4
|
def base_name
|
5
|
-
|
5
|
+
if (browser.firefox? && browser.version.to_i >= 23) || (browser.chrome? && browser.version.to_i >= 25)
|
6
|
+
SecureHeaders::ContentSecurityPolicy::STANDARD_HEADER_NAME
|
7
|
+
else
|
8
|
+
SecureHeaders::ContentSecurityPolicy::WEBKIT_CSP_HEADER_NAME
|
9
|
+
end
|
6
10
|
end
|
7
11
|
|
8
12
|
def add_missing_extension_values
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module SecureHeaders
|
2
2
|
class XContentTypeOptionsBuildError < StandardError; end
|
3
3
|
# IE only
|
4
|
-
class XContentTypeOptions
|
4
|
+
class XContentTypeOptions < Header
|
5
5
|
module Constants
|
6
6
|
X_CONTENT_TYPE_OPTIONS_HEADER_NAME = "X-Content-Type-Options"
|
7
7
|
DEFAULT_VALUE = "nosniff"
|
@@ -16,8 +16,10 @@ module SecureHeaders
|
|
16
16
|
|
17
17
|
IE = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)"
|
18
18
|
FIREFOX = "Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.9.0.2) Gecko/20121223 Ubuntu/9.25 (jaunty) Firefox/3.8"
|
19
|
-
|
19
|
+
FIREFOX_23 = "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:23.0) Gecko/20131011 Firefox/23.0"
|
20
20
|
CHROME = "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.99 Safari/533.4"
|
21
|
+
CHROME_25 = "Mozilla/5.0 (Macintosh; Intel Mac OS X 1084) AppleWebKit/537.22 (KHTML like Gecko) Chrome/25.0.1364.99 Safari/537.22"
|
22
|
+
|
21
23
|
|
22
24
|
def request_for user_agent, request_uri=nil, options={:ssl => false}
|
23
25
|
double(:ssl? => options[:ssl], :env => {'HTTP_USER_AGENT' => user_agent}, :url => (request_uri || 'http://areallylongdomainexample.com') )
|
@@ -31,13 +33,17 @@ module SecureHeaders
|
|
31
33
|
context "when supplying options to override request" do
|
32
34
|
specify { ContentSecurityPolicy.new(default_opts, :ua => IE).name.should == STANDARD_HEADER_NAME + "-Report-Only"}
|
33
35
|
specify { ContentSecurityPolicy.new(default_opts, :ua => FIREFOX).name.should == FIREFOX_CSP_HEADER_NAME + "-Report-Only"}
|
36
|
+
specify { ContentSecurityPolicy.new(default_opts, :ua => FIREFOX_23).name.should == STANDARD_HEADER_NAME + "-Report-Only"}
|
34
37
|
specify { ContentSecurityPolicy.new(default_opts, :ua => CHROME).name.should == WEBKIT_CSP_HEADER_NAME + "-Report-Only"}
|
38
|
+
specify { ContentSecurityPolicy.new(default_opts, :ua => CHROME_25).name.should == STANDARD_HEADER_NAME + "-Report-Only"}
|
35
39
|
end
|
36
40
|
|
37
41
|
context "when in report-only mode" do
|
38
42
|
specify { ContentSecurityPolicy.new(default_opts, :request => request_for(IE)).name.should == STANDARD_HEADER_NAME + "-Report-Only"}
|
39
43
|
specify { ContentSecurityPolicy.new(default_opts, :request => request_for(FIREFOX)).name.should == FIREFOX_CSP_HEADER_NAME + "-Report-Only"}
|
44
|
+
specify { ContentSecurityPolicy.new(default_opts, :request => request_for(FIREFOX_23)).name.should == STANDARD_HEADER_NAME + "-Report-Only"}
|
40
45
|
specify { ContentSecurityPolicy.new(default_opts, :request => request_for(CHROME)).name.should == WEBKIT_CSP_HEADER_NAME + "-Report-Only"}
|
46
|
+
specify { ContentSecurityPolicy.new(default_opts, :request => request_for(CHROME_25)).name.should == STANDARD_HEADER_NAME + "-Report-Only"}
|
41
47
|
end
|
42
48
|
|
43
49
|
context "when in enforce mode" do
|
@@ -45,14 +51,18 @@ module SecureHeaders
|
|
45
51
|
|
46
52
|
specify { ContentSecurityPolicy.new(opts, :request => request_for(IE)).name.should == STANDARD_HEADER_NAME}
|
47
53
|
specify { ContentSecurityPolicy.new(opts, :request => request_for(FIREFOX)).name.should == FIREFOX_CSP_HEADER_NAME}
|
54
|
+
specify { ContentSecurityPolicy.new(opts, :request => request_for(FIREFOX_23)).name.should == STANDARD_HEADER_NAME}
|
48
55
|
specify { ContentSecurityPolicy.new(opts, :request => request_for(CHROME)).name.should == WEBKIT_CSP_HEADER_NAME}
|
56
|
+
specify { ContentSecurityPolicy.new(opts, :request => request_for(CHROME_25)).name.should == STANDARD_HEADER_NAME}
|
49
57
|
end
|
50
58
|
|
51
59
|
context "when in experimental mode" do
|
52
60
|
let(:opts) { default_opts.merge(:enforce => true).merge(:experimental => {})}
|
53
61
|
specify { ContentSecurityPolicy.new(opts, {:experimental => true, :request => request_for(IE)}).name.should == STANDARD_HEADER_NAME + "-Report-Only"}
|
54
62
|
specify { ContentSecurityPolicy.new(opts, {:experimental => true, :request => request_for(FIREFOX)}).name.should == FIREFOX_CSP_HEADER_NAME + "-Report-Only"}
|
63
|
+
specify { ContentSecurityPolicy.new(opts, {:experimental => true, :request => request_for(FIREFOX_23)}).name.should == STANDARD_HEADER_NAME + "-Report-Only"}
|
55
64
|
specify { ContentSecurityPolicy.new(opts, {:experimental => true, :request => request_for(CHROME)}).name.should == WEBKIT_CSP_HEADER_NAME + "-Report-Only"}
|
65
|
+
specify { ContentSecurityPolicy.new(opts, {:experimental => true, :request => request_for(CHROME_25)}).name.should == STANDARD_HEADER_NAME + "-Report-Only"}
|
56
66
|
end
|
57
67
|
end
|
58
68
|
|
@@ -224,7 +234,6 @@ module SecureHeaders
|
|
224
234
|
end
|
225
235
|
|
226
236
|
context "auto-whitelists data: uris for img-src" do
|
227
|
-
|
228
237
|
it "sets the value if no img-src specified" do
|
229
238
|
csp = ContentSecurityPolicy.new({:default_src => 'self', :disable_fill_missing => true, :disable_chrome_extension => true}, :request => request_for(CHROME))
|
230
239
|
csp.value.should == "default-src 'self'; img-src data:;"
|
@@ -248,7 +257,7 @@ module SecureHeaders
|
|
248
257
|
csp.value.should match "default-src"
|
249
258
|
end
|
250
259
|
|
251
|
-
context "
|
260
|
+
context "Firefox" do
|
252
261
|
it "builds a csp header for firefox" do
|
253
262
|
csp = ContentSecurityPolicy.new(default_opts, :request => request_for(FIREFOX))
|
254
263
|
csp.value.should == "allow https://*; options inline-script eval-script; img-src data:; script-src https://* data:; style-src https://* about:; report-uri /csp_report;"
|
@@ -270,19 +279,15 @@ module SecureHeaders
|
|
270
279
|
csp.value.should =~ /xhr-src 'self' http:/
|
271
280
|
end
|
272
281
|
|
273
|
-
|
274
|
-
|
275
|
-
:
|
276
|
-
|
277
|
-
|
278
|
-
:disable_fill_missing => true
|
279
|
-
}
|
280
|
-
csp = ContentSecurityPolicy.new(opts, :request => request_for(FIREFOX_18))
|
281
|
-
csp.value.should =~ /xhr-src 'self' http:\/\/\*\.localhost\.com:\*/
|
282
|
+
context "Firefox >= 23" do
|
283
|
+
it "builds a csp header for firefox" do
|
284
|
+
csp = ContentSecurityPolicy.new(default_opts, :request => request_for(FIREFOX_23))
|
285
|
+
csp.value.should == "default-src https://*; img-src data:; script-src 'unsafe-inline' 'unsafe-eval' https://* data:; style-src 'unsafe-inline' https://* about:; report-uri /csp_report;"
|
286
|
+
end
|
282
287
|
end
|
283
288
|
end
|
284
289
|
|
285
|
-
context "
|
290
|
+
context "Chrome" do
|
286
291
|
it "builds a csp header for chrome" do
|
287
292
|
csp = ContentSecurityPolicy.new(default_opts, :request => request_for(CHROME))
|
288
293
|
csp.value.should == "default-src https://*; img-src data:; script-src 'unsafe-inline' 'unsafe-eval' https://* data:; style-src 'unsafe-inline' https://* about:; report-uri /csp_report;"
|
@@ -27,15 +27,16 @@ describe SecureHeaders do
|
|
27
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
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
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",
|
30
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"
|
31
32
|
}
|
32
33
|
|
33
34
|
def should_assign_header name, value
|
34
|
-
|
35
|
+
response.headers.should_receive(:[]=).with(name, value)
|
35
36
|
end
|
36
37
|
|
37
38
|
def should_not_assign_header name
|
38
|
-
|
39
|
+
response.headers.should_not_receive(:[]=).with(name, anything)
|
39
40
|
end
|
40
41
|
|
41
42
|
def stub_user_agent val
|
@@ -72,6 +73,18 @@ describe SecureHeaders do
|
|
72
73
|
end
|
73
74
|
end
|
74
75
|
|
76
|
+
describe "#set_header" do
|
77
|
+
it "accepts name/value pairs" do
|
78
|
+
should_assign_header("X-Hipster-Ipsum", "kombucha")
|
79
|
+
subject.send(:set_header, "X-Hipster-Ipsum", "kombucha")
|
80
|
+
end
|
81
|
+
|
82
|
+
it "accepts header objects" do
|
83
|
+
should_assign_header("Strict-Transport-Security", SecureHeaders::StrictTransportSecurity::Constants::DEFAULT_VALUE)
|
84
|
+
subject.send(:set_header, SecureHeaders::StrictTransportSecurity.new)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
75
88
|
describe "#set_security_headers" do
|
76
89
|
before(:each) do
|
77
90
|
SecureHeaders::ContentSecurityPolicy.stub(:new).and_return(double.as_null_object)
|
@@ -82,7 +95,7 @@ describe SecureHeaders do
|
|
82
95
|
number_of_headers = case name
|
83
96
|
when :ie, :chrome
|
84
97
|
5
|
85
|
-
when :ios5, :safari5
|
98
|
+
when :ios5, :safari5, :safari5_1
|
86
99
|
3 # csp breaks these browsers
|
87
100
|
else
|
88
101
|
4
|
@@ -262,13 +275,13 @@ describe SecureHeaders do
|
|
262
275
|
opts = @opts.merge(:enforce => false)
|
263
276
|
should_assign_header(WEBKIT_CSP_HEADER_NAME + "-Report-Only", anything)
|
264
277
|
should_not_assign_header(WEBKIT_CSP_HEADER_NAME)
|
265
|
-
subject.set_csp_header
|
278
|
+
subject.set_csp_header(opts)
|
266
279
|
end
|
267
280
|
|
268
281
|
it "sets a header in enforce mode as well as report-only mode" do
|
269
282
|
should_assign_header(WEBKIT_CSP_HEADER_NAME, anything)
|
270
283
|
should_assign_header(WEBKIT_CSP_HEADER_NAME + "-Report-Only", anything)
|
271
|
-
subject.set_csp_header
|
284
|
+
subject.set_csp_header(@opts)
|
272
285
|
end
|
273
286
|
end
|
274
287
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: secure_headers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-06-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: brwsr
|
@@ -148,11 +148,12 @@ files:
|
|
148
148
|
- fixtures/rails_3_2_12_no_init/vendor/assets/stylesheets/.gitkeep
|
149
149
|
- fixtures/rails_3_2_12_no_init/vendor/plugins/.gitkeep
|
150
150
|
- lib/secure_headers.rb
|
151
|
+
- lib/secure_headers/header.rb
|
151
152
|
- lib/secure_headers/headers/content_security_policy.rb
|
152
153
|
- lib/secure_headers/headers/content_security_policy/browser_strategy.rb
|
153
154
|
- lib/secure_headers/headers/content_security_policy/firefox_browser_strategy.rb
|
154
155
|
- lib/secure_headers/headers/content_security_policy/ie_browser_strategy.rb
|
155
|
-
- lib/secure_headers/headers/content_security_policy/
|
156
|
+
- lib/secure_headers/headers/content_security_policy/standard_browser_strategy.rb
|
156
157
|
- lib/secure_headers/headers/strict_transport_security.rb
|
157
158
|
- lib/secure_headers/headers/x_content_type_options.rb
|
158
159
|
- lib/secure_headers/headers/x_frame_options.rb
|