Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Start splitting up ContentSecurityPolicy. WIP #10

Closed
wants to merge 7 commits into from

3 participants

@bemurphy

This is total WIP on gh-6, just submitting PR to get feedback if on the right
track or not....

Still not done but split out a bunch so far. Factorized with
.build instead of calling .new to get the right class.

I think the factory could further split the FF into 2 classes
for standard and non-standard, to further kill conditional checks
in the class. Perhaps it should call a class method like .buildable?
on classes so it can roll through and pick the first that responds true.

bemurphy added some commits
@bemurphy bemurphy Start splitting up ContentSecurityPolicy. WIP
Still not done but split out a bunch so far.  Factorized with
.build instead of calling .new to get the right class.  Still
struggling with the browser ivar.

I think the factory could further split the FF into 2 classes
for standard and non-standard, to further kill conditional checks
in the class.  Perhaps it should call a class method like .buildable?
on classes so it can roll through and pick the first that responds true.
3d8f9cb
@bemurphy bemurphy Don't unnecessarily filter directives fa063ae
@bemurphy bemurphy Remove #supports_standard? outside of Firefox 7a6cbd5
@bemurphy bemurphy Move FF related constants to appropriate class
As a side note, I'm smelling the Constant modules a little, we can
probably clean this up later.  It would probably be better to have
constants available in the modules that aren't prefixed with a vendor
name, because it will cut out subclass methods that are just one
liners going for the constant.
92a38b0
@bemurphy bemurphy Pull out empty template methods only needed for FF
Use an #after_configure method to handle the last bits of config
work
ac180ac
@bemurphy bemurphy Join strings rather than building using += 97733fd
@bemurphy bemurphy Get rid of tempvar in #generic_directives 80eccc3
@bemurphy

I think there's some chrome specific stuff (like the extensions) that could be split out to webkit as well. Maybe have a general #browser_extensions method or something that can handle that kind of thing?

@bemurphy

BTW, here's my feature branch flogged on the file currently:

180.2: flog total
7.5: flog/method average

Here's master:
250.1: flog total
8.9: flog/method average

@oreoshake
Collaborator

:+1:

Definitely what I wanted to do.

RE: browser_extensions, there's some debate as to whether this will be part of the spec (i.e. don't report on extension activities at all). It might be premature abstraction, which I'm not completely against. Likely the method to manipulate extensions across browsers will not be standardized very soon which justifies this entirely.

@bemurphy

@oreoshake ok I'll keep trucking with it tonight then. I think I can get it far enough it's worthy of master (it's pretty close already) and then we can keep pulling in features for little extractions here and there.

I think the tests were pretty helpful btw (I hope!) because I've relied on them entirely for the refactoring so far. I do need to do another sweep on them though to make sure nothing is breaking and stubs are lying to me or something...

@oreoshake
Collaborator

While you're at it, let me know if you figure out why #3 wasn't caught in tests? I thought I had tests for exactly that. Either they are invalid or missing.

@oreoshake oreoshake commented on the diff
...ecure_headers/headers/content_security_policy_spec.rb
@@ -119,22 +119,16 @@ def request_for user_agent, request_uri = nil
end
describe "#supports_standard?" do
- ['IE', 'Safari', 'Chrome'].each do |browser_name|
@oreoshake Collaborator

Got beef with this test?

I ripped supports_standard? from the other classes, it's only a private for FF now because that was the only place using it. Does this make sense?

It was in commit 7a6cbd5

@oreoshake Collaborator

I like.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@bemurphy

@oreoshake hah, yeah that issue just caught my eye, too. I'll check it out.

@oreoshake
Collaborator

fwiw that code is some jank. It's used in different contexts and for different purposes IIRC

@oreoshake
Collaborator

Not to put any pressure on you... but I was about to integrate the code to apply two headers (on that enforces, and an "experimental" one that only generates reports).

We will have a mega conflict, but I can easily resolve my conflicts with this change so I'll hold off until then.

@oreoshake oreoshake commented on the diff
...re_headers/headers/firefox_content_security_policy.rb
((29 lines not shown))
+ private
+
+ def supports_standard?
+ browser.version.to_i >= 18
+ end
+
+ def build_impl_specific_directives
+ header_value = ""
+ default = expect_directive_value(:default_src)
+ header_value += build_preamble(default) || ''
+ header_value
+ end
+
+ def build_preamble(default_src_value)
+ header_value = ''
+ if supports_standard?
@oreoshake Collaborator

honestly this whole condition isn't needed, which reduces the need for the browser ivar. I doubt firefox will drop support for "allow" anytime soon. @imelven?

@imelven
imelven added a note

The tentative plan is to deprecate the old header (including the old syntax e.g 'allow') somewhere around Firefox 24 which is an ESR, but there's a lot of dependencies that need to be addressed first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@oreoshake
Collaborator

Just rebased, conflicts were not trivial to resolve :crying_cat_face:, as in I gave up quickly. I'll try to take a stab at this soon

@oreoshake
Collaborator

Yo, just merge in the smelly_csp branch and this should be good :) just merged in real_tests and everything but travis is working! (I think i know what is causing travis fail)

Splitting up the tests into separate classes would be awesome but I think the genericizing (aka coupling) of the classes could be justifiable for now :deciduous_tree:

@bemurphy

@oreoshake sounds good. A couple questions/points first:

  • I split into a different implementation Thursday night at the hackfest. It's in my branch at: https://github.com/bemurphy/secureheaders/tree/content_security_policy_refactor

  • See if you have an implementation preference. I will say in the new branch, I was having trouble pushing #normalize_reporting_endpoint down into a strategy so that says something.

  • I agree that ultimately the tests should be split out but that it's a win for now.

  • Lastly, the one thing that led me to the different attempt last thursday was that the ContentSecurityPolicy. initialize went to 3 optional arguments, which broke the notions of a factory from the browser in the build class method. Maybe that request really ought to never be nil, but also, that method signature is a bit messy feeling to me now. The messiness bit is something that can be addressed later.

p.s. that's right...there was hacking at the hackfest!

@bemurphy

thought about this some more, fwiw it's a good idea to look at the new implementation but if the old branch will work I think it'll be less headache, I'll tell you more later.

@oreoshake
Collaborator

I like the new impl more. The normalize_reporting_endpoint is a little janky, but it's definitely an improvement.

See if you have an implementation preference. I will say in the new branch, I was having trouble pushing #normalize_reporting_endpoint down into a strategy so that says something.

Meh no biggie, I don't think there's anything horribly wrong w/ that. I like the strategy pattern. The tests pass. I'm going to do some manual testing but it looks like it's ready to merge if you wanna open a PR

@oreoshake
Collaborator

merged your other branch in 29cba15

@oreoshake oreoshake closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jan 31, 2013
  1. @bemurphy

    Start splitting up ContentSecurityPolicy. WIP

    bemurphy authored
    Still not done but split out a bunch so far.  Factorized with
    .build instead of calling .new to get the right class.  Still
    struggling with the browser ivar.
    
    I think the factory could further split the FF into 2 classes
    for standard and non-standard, to further kill conditional checks
    in the class.  Perhaps it should call a class method like .buildable?
    on classes so it can roll through and pick the first that responds true.
  2. @bemurphy
  3. @bemurphy
  4. @bemurphy

    Move FF related constants to appropriate class

    bemurphy authored
    As a side note, I'm smelling the Constant modules a little, we can
    probably clean this up later.  It would probably be better to have
    constants available in the modules that aren't prefixed with a vendor
    name, because it will cut out subclass methods that are just one
    liners going for the constant.
  5. @bemurphy

    Pull out empty template methods only needed for FF

    bemurphy authored
    Use an #after_configure method to handle the last bits of config
    work
  6. @bemurphy
  7. @bemurphy
This page is out of date. Refresh to see the latest.
View
4 lib/secure_headers.rb
@@ -51,7 +51,7 @@ def set_csp_header(request, options=nil)
options = self.class.options_for :csp, options
return if options == false
- header = ContentSecurityPolicy.new(request, options)
+ header = ContentSecurityPolicy.build(request, options)
set_header(header.name, header.value)
end
@@ -95,6 +95,8 @@ def broken_implementation?(browser)
require "secure_headers/version"
require "secure_headers/headers/content_security_policy"
+require "secure_headers/headers/firefox_content_security_policy"
+require "secure_headers/headers/webkit_content_security_policy"
require "secure_headers/headers/x_frame_options"
require "secure_headers/headers/strict_transport_security"
require "secure_headers/headers/x_xss_protection"
View
134 lib/secure_headers/headers/content_security_policy.rb
@@ -3,25 +3,17 @@
module SecureHeaders
class ContentSecurityPolicyBuildError < StandardError; end
+
class ContentSecurityPolicy
module Constants
- WEBKIT_CSP_HEADER = "default-src https: data: 'unsafe-inline' 'unsafe-eval'; frame-src https://* about: javascript:; img-src chrome-extension:"
- FIREFOX_CSP_HEADER = "options eval-script inline-script; allow https://* data:; frame-src https://* about: javascript:; img-src chrome-extension:"
-
- FIREFOX_CSP_HEADER_NAME = 'X-Content-Security-Policy'
- WEBKIT_CSP_HEADER_NAME = 'X-WebKit-CSP'
+ STANDARD_CSP_HEADER = "default-src https: data: 'unsafe-inline' 'unsafe-eval'; frame-src https://* about: javascript:; img-src chrome-extension:"
STANDARD_HEADER_NAME = "Content-Security-Policy"
-
- FF_CSP_ENDPOINT = "/content_security_policy/forward_report"
- WEBKIT_DIRECTIVES = DIRECTIVES = [:default_src, :script_src, :frame_src, :style_src, :img_src, :media_src, :font_src, :object_src, :connect_src]
- FIREFOX_DIRECTIVES = DIRECTIVES + [:xhr_src, :frame_ancestors] - [:connect_src]
+ DIRECTIVES = [:default_src, :script_src, :frame_src, :style_src, :img_src, :media_src, :font_src, :object_src, :connect_src]
META = [:enforce, :http_additions, :disable_chrome_extension, :disable_fill_missing, :forward_endpoint]
end
include Constants
- META.each do |meta|
- attr_accessor meta
- end
+ attr_accessor *META
attr_reader :browser, :ssl_request, :report_uri, :request_uri
alias :enforce? :enforce
@@ -29,6 +21,20 @@ module Constants
alias :disable_fill_missing? :disable_fill_missing
alias :ssl_request? :ssl_request
+ def self.build(request, config)
+ browser = Brwsr::Browser.new(:ua => request.env['HTTP_USER_AGENT'])
+
+ klass = case
+ when browser.ie?
+ self
+ when browser.firefox?
+ FirefoxContentSecurityPolicy
+ else
+ WebkitContentSecurityPolicy
+ end
+
+ klass.new(request, config)
+ end
def initialize request = nil, config = nil
if config
@@ -43,42 +49,44 @@ def configure request, opts
parse_request request
META.each do |meta|
- self.send(meta.to_s + "=", @config.delete(meta))
+ self.send("#{meta}=", @config.delete(meta))
end
@report_uri = @config.delete(:report_uri)
+ after_configure
+ end
+
+ def after_configure
normalize_csp_options
- normalize_reporting_endpoint if report_uri && forward_endpoint
- filter_unsupported_directives
end
- def name
- base = if browser.ie?
- STANDARD_HEADER_NAME
- elsif browser.firefox?
- # can't use supports_standard because FF18 does not support this part of the standard.
- FIREFOX_CSP_HEADER_NAME
- else
- WEBKIT_CSP_HEADER_NAME
- end
+ def base_name
+ STANDARD_HEADER_NAME
+ end
+ def name
+ base = base_name
base += "-Report-Only" unless enforce
base
end
+ def directives
+ DIRECTIVES
+ end
+
def value
return @config if @config.is_a?(String)
+
if @config.nil?
- return supports_standard? ? WEBKIT_CSP_HEADER : FIREFOX_CSP_HEADER
+ csp_header
+ else
+ build_value
end
-
- build_value
end
- def directives
- # can't use supports_standard because FF18 does not support this part of the standard.
- browser.firefox? ? FIREFOX_DIRECTIVES : WEBKIT_DIRECTIVES
+ def csp_header
+ STANDARD_CSP_HEADER
end
private
@@ -88,9 +96,11 @@ def build_value
add_missing_chrome_extension_values unless disable_chrome_extension?
append_http_additions unless ssl_request?
- header_value = build_impl_specific_directives
- header_value += generic_directives(@config)
- header_value += report_uri_directive(@report_uri)
+ header_value = [
+ build_impl_specific_directives,
+ generic_directives(@config),
+ report_uri_directive(@report_uri),
+ ].join
#store the value for next time
@config = header_value
@@ -138,15 +148,6 @@ def normalize_csp_options
end
end
- def filter_unsupported_directives
- if browser.firefox?
- # can't use supports_standard because FF18 does not support this part of the standard.
- @config[:xhr_src] = @config.delete(:connect_src) if @config[:connect_src]
- else
- @config.delete(:frame_ancestors)
- end
- end
-
# translates 'inline','self', 'none' and 'eval' to their respective impl-specific values.
def translate_dir_value val
if %w{inline eval}.include?(val)
@@ -161,51 +162,13 @@ def translate_dir_value val
# inline/eval => impl-specific values
def translate_inline_or_eval val
- # can't use supports_standard because FF18 does not support this part of the standard.
- if browser.firefox?
- val == 'inline' ? 'inline-script' : 'eval-script'
- else
- val == 'inline' ? "'unsafe-inline'" : "'unsafe-eval'"
- end
- end
-
- # if we have a forwarding endpoint setup and we are not on the same origin as our report_uri
- # or only a path was supplied (in which case we assume cross-host)
- # we need to forward the request for Firefox.
- def normalize_reporting_endpoint
- # can't use supports_standard because FF18 does not support cross-origin posting.
- if browser.firefox? && (!same_origin? || URI.parse(report_uri).host.nil?)
- @report_uri = (@forward_endpoint || FF_CSP_ENDPOINT)
- end
- end
-
- def supports_standard?
- !browser.firefox? || (browser.firefox? && browser.version.to_i >= 18)
+ val == 'inline' ? "'unsafe-inline'" : "'unsafe-eval'"
end
def build_impl_specific_directives
header_value = ""
default = expect_directive_value(:default_src)
- # firefox 18 still requires the use of the options value, but can substitute default-src for allow
- if browser.firefox?
- header_value += build_firefox_specific_preamble(default) || ''
- else
- header_value += "default-src #{default.join(" ")}; " if default.any?
- end
-
- header_value
- end
-
- def build_firefox_specific_preamble(default_src_value)
- header_value = ''
- if supports_standard?
- header_value += "default-src #{default_src_value.join(" ")}; " if default_src_value.any?
- elsif default_src_value
- header_value += "allow #{default_src_value.join(" ")}; " if default_src_value.any?
- end
-
- options_directive = build_options_directive
- header_value += "options #{options_directive.join(" ")}; " if options_directive.any?
+ header_value += "default-src #{default.join(" ")}; " if default.any?
header_value
end
@@ -251,14 +214,11 @@ def report_uri_directive(report_uri)
report_uri.nil? ? '' : "report-uri #{report_uri};"
end
-
def generic_directives(config)
- header_value = ''
- config.keys.sort_by{|k| k.to_s}.each do |k| # ensure consistent ordering
+ # ensure consistent ordering
+ config.keys.sort_by{|k| k.to_s}.inject('') do |header_value, k|
header_value += "#{symbol_to_hyphen_case(k)} #{config[k].join(" ")}; "
end
-
- header_value
end
def symbol_to_hyphen_case sym
View
75 lib/secure_headers/headers/firefox_content_security_policy.rb
@@ -0,0 +1,75 @@
+module SecureHeaders
+ class FirefoxContentSecurityPolicy < ContentSecurityPolicy
+ module Constants
+ FIREFOX_CSP_HEADER = "options eval-script inline-script; allow https://* data:; frame-src https://* about: javascript:; img-src chrome-extension:"
+ FIREFOX_CSP_HEADER_NAME = 'X-Content-Security-Policy'
+ FF_CSP_ENDPOINT = "/content_security_policy/forward_report"
+ FIREFOX_DIRECTIVES = ContentSecurityPolicy::DIRECTIVES + [:xhr_src, :frame_ancestors] - [:connect_src]
+ end
+ include Constants
+
+ def after_configure
+ super
+ normalize_reporting_endpoint if report_uri && forward_endpoint
+ filter_unsupported_directives
+ end
+
+ def base_name
+ FIREFOX_CSP_HEADER_NAME
+ end
+
+ def directives
+ FIREFOX_DIRECTIVES
+ end
+
+ def csp_header
+ return supports_standard? ? STANDARD_CSP_HEADER : FIREFOX_CSP_HEADER
+ end
+
+ private
+
+ def supports_standard?
+ browser.version.to_i >= 18
+ end
+
+ def build_impl_specific_directives
+ header_value = ""
+ default = expect_directive_value(:default_src)
+ header_value += build_preamble(default) || ''
+ header_value
+ end
+
+ def build_preamble(default_src_value)
+ header_value = ''
+ if supports_standard?
@oreoshake Collaborator

honestly this whole condition isn't needed, which reduces the need for the browser ivar. I doubt firefox will drop support for "allow" anytime soon. @imelven?

@imelven
imelven added a note

The tentative plan is to deprecate the old header (including the old syntax e.g 'allow') somewhere around Firefox 24 which is an ESR, but there's a lot of dependencies that need to be addressed first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ header_value += "default-src #{default_src_value.join(" ")}; " if default_src_value.any?
+ elsif default_src_value
+ header_value += "allow #{default_src_value.join(" ")}; " if default_src_value.any?
+ end
+
+ options_directive = build_options_directive
+ header_value += "options #{options_directive.join(" ")}; " if options_directive.any?
+ header_value
+ end
+
+ def filter_unsupported_directives
+ @config[:xhr_src] = @config.delete(:connect_src) if @config[:connect_src]
+ end
+
+ # inline/eval => impl-specific values
+ def translate_inline_or_eval val
+ # can't use supports_standard because FF18 does not support this part of the standard.
+ val == 'inline' ? 'inline-script' : 'eval-script'
+ end
+
+ # if we have a forwarding endpoint setup and we are not on the same origin as our report_uri
+ # or only a path was supplied (in which case we assume cross-host)
+ # we need to forward the request for Firefox.
+ def normalize_reporting_endpoint
+ # can't use supports_standard because FF18 does not support cross-origin posting.
+ if (!same_origin? || URI.parse(report_uri).host.nil?)
+ @report_uri = (@forward_endpoint || FF_CSP_ENDPOINT)
+ end
+ end
+ end
+end
View
12 lib/secure_headers/headers/webkit_content_security_policy.rb
@@ -0,0 +1,12 @@
+module SecureHeaders
+ class WebkitContentSecurityPolicy < ContentSecurityPolicy
+ module Constants
+ WEBKIT_CSP_HEADER_NAME = 'X-WebKit-CSP'
+ end
+ include Constants
+
+ def base_name
+ WEBKIT_CSP_HEADER_NAME
+ end
+ end
+end
View
88 spec/lib/secure_headers/headers/content_security_policy_spec.rb
@@ -29,19 +29,19 @@ def request_for user_agent, request_uri = nil
describe "#name" do
context "when in report-only mode" do
- specify { ContentSecurityPolicy.new(request_for(IE), default_opts).name.should == STANDARD_HEADER_NAME + "-Report-Only"}
- specify { ContentSecurityPolicy.new(request_for(FIREFOX), default_opts).name.should == FIREFOX_CSP_HEADER_NAME + "-Report-Only"}
- specify { ContentSecurityPolicy.new(request_for(FIREFOX_18), default_opts).name.should == FIREFOX_CSP_HEADER_NAME + "-Report-Only"}
- specify { ContentSecurityPolicy.new(request_for(CHROME), default_opts).name.should == WEBKIT_CSP_HEADER_NAME + "-Report-Only"}
+ specify { ContentSecurityPolicy.build(request_for(IE), default_opts).name.should == STANDARD_HEADER_NAME + "-Report-Only"}
+ specify { ContentSecurityPolicy.build(request_for(FIREFOX), default_opts).name.should == FIREFOX_CSP_HEADER_NAME + "-Report-Only"}
+ specify { ContentSecurityPolicy.build(request_for(FIREFOX_18), default_opts).name.should == FIREFOX_CSP_HEADER_NAME + "-Report-Only"}
+ specify { ContentSecurityPolicy.build(request_for(CHROME), default_opts).name.should == WEBKIT_CSP_HEADER_NAME + "-Report-Only"}
end
context "when in enforce mode" do
let(:opts) { default_opts.merge(:enforce => true)}
- specify { ContentSecurityPolicy.new(request_for(IE), opts).name.should == STANDARD_HEADER_NAME}
- specify { ContentSecurityPolicy.new(request_for(FIREFOX), opts).name.should == FIREFOX_CSP_HEADER_NAME}
- specify { ContentSecurityPolicy.new(request_for(FIREFOX_18), opts).name.should == FIREFOX_CSP_HEADER_NAME}
- specify { ContentSecurityPolicy.new(request_for(CHROME), opts).name.should == WEBKIT_CSP_HEADER_NAME}
+ specify { ContentSecurityPolicy.build(request_for(IE), opts).name.should == STANDARD_HEADER_NAME}
+ specify { ContentSecurityPolicy.build(request_for(FIREFOX), opts).name.should == FIREFOX_CSP_HEADER_NAME}
+ specify { ContentSecurityPolicy.build(request_for(FIREFOX_18), opts).name.should == FIREFOX_CSP_HEADER_NAME}
+ specify { ContentSecurityPolicy.build(request_for(CHROME), opts).name.should == WEBKIT_CSP_HEADER_NAME}
end
end
@@ -50,20 +50,20 @@ def request_for user_agent, request_uri = nil
let(:expected) {{}}
it "fills empty directives with the 'allow' directive in Firefox" do
- csp = ContentSecurityPolicy.new(request_for(FIREFOX), opts)
+ csp = ContentSecurityPolicy.build(request_for(FIREFOX), opts)
csp.directives.each {|dir| expected[dir] = ["https://*"]}
csp.send(:fill_directives).should == expected.merge(opts)
end
it "fills empty directives with the 'default' directive in Chrome" do
- csp = ContentSecurityPolicy.new(request_for(CHROME), opts)
+ csp = ContentSecurityPolicy.build(request_for(CHROME), opts)
csp.directives.each {|dir| expected[dir] = ["https://*"]}
csp.send(:fill_directives).should == expected.merge(opts)
end
it "does not overwrite supplied values" do
options = opts.merge(:img_src => ['https://twitter.com'])
- csp = ContentSecurityPolicy.new(request_for(CHROME), options)
+ csp = ContentSecurityPolicy.build(request_for(CHROME), options)
csp.directives.each {|dir| expected[dir] = ["https://*"]}
csp.send(:fill_directives).should == expected.merge(options)
end
@@ -79,7 +79,7 @@ def request_for user_agent, request_uri = nil
context "X-Content-Security-Policy" do
it "converts the script values to their equivilents" do
- csp = ContentSecurityPolicy.new(request_for(FIREFOX), @opts)
+ csp = ContentSecurityPolicy.build(request_for(FIREFOX), @opts)
csp.value.should include("script-src https://* data: 'self' 'none'")
csp.value.should include('options inline-script eval-script')
end
@@ -87,7 +87,7 @@ def request_for user_agent, request_uri = nil
context "X-Webkit-CSP" do
it "converts the script values to their equivilents" do
- csp = ContentSecurityPolicy.new(request_for(CHROME), @opts)
+ csp = ContentSecurityPolicy.build(request_for(CHROME), @opts)
csp.value.should include("script-src 'unsafe-inline' 'unsafe-eval' https://* data: 'self' 'none' chrome-extension")
end
end
@@ -100,7 +100,7 @@ def request_for user_agent, request_uri = nil
:default_src => 'https://*',
:script_src => "inline eval https://*"
}
- csp = ContentSecurityPolicy.new(request_for(FIREFOX), opts)
+ csp = ContentSecurityPolicy.build(request_for(FIREFOX), opts)
browser_specific = csp.send :build_impl_specific_directives
browser_specific.should include('options inline-script eval-script;')
end
@@ -110,7 +110,7 @@ def request_for user_agent, request_uri = nil
:default_src => 'https://*',
:style_src => "inline eval https://*"
}
- csp = ContentSecurityPolicy.new(request_for(FIREFOX), opts)
+ csp = ContentSecurityPolicy.build(request_for(FIREFOX), opts)
browser_specific = csp.send :build_impl_specific_directives
browser_specific.should_not include('inline-script')
browser_specific.should_not include('eval-script')
@@ -119,22 +119,16 @@ def request_for user_agent, request_uri = nil
end
describe "#supports_standard?" do
- ['IE', 'Safari', 'Chrome'].each do |browser_name|
@oreoshake Collaborator

Got beef with this test?

I ripped supports_standard? from the other classes, it's only a private for FF now because that was the only place using it. Does this make sense?

It was in commit 7a6cbd5

@oreoshake Collaborator

I like.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
- it "returns true for #{browser_name}" do
- browser = Brwsr::Browser.new(:ua => browser_name)
- subject.stub(:browser).and_return(browser)
- subject.send(:supports_standard?).should be_true
- end
- end
-
it "returns true for Firefox v >= 18" do
browser = Brwsr::Browser.new(:ua => "Firefox 18")
+ subject = FirefoxContentSecurityPolicy.new
subject.stub(:browser).and_return(browser)
subject.send(:supports_standard?).should be_true
end
it "returns false for Firefox v < 18" do
browser = Brwsr::Browser.new(:ua => "Firefox 17")
+ subject = FirefoxContentSecurityPolicy.new
subject.stub(:browser).and_return(browser)
subject.send(:supports_standard?).should be_false
end
@@ -144,47 +138,47 @@ def request_for user_agent, request_uri = nil
let(:origin) {"https://example.com:123"}
it "matches when host, scheme, and port match" do
- csp = ContentSecurityPolicy.new(request_for(FIREFOX, "https://example.com"), {:report_uri => 'https://example.com'})
+ csp = ContentSecurityPolicy.build(request_for(FIREFOX, "https://example.com"), {:report_uri => 'https://example.com'})
csp.send(:same_origin?).should be_true
- csp = ContentSecurityPolicy.new(request_for(FIREFOX, "https://example.com:443"), {:report_uri => 'https://example.com'})
+ csp = ContentSecurityPolicy.build(request_for(FIREFOX, "https://example.com:443"), {:report_uri => 'https://example.com'})
csp.send(:same_origin?).should be_true
- csp = ContentSecurityPolicy.new(request_for(FIREFOX, "https://example.com:123"), {:report_uri => 'https://example.com:123'})
+ csp = ContentSecurityPolicy.build(request_for(FIREFOX, "https://example.com:123"), {:report_uri => 'https://example.com:123'})
csp.send(:same_origin?).should be_true
- csp = ContentSecurityPolicy.new(request_for(FIREFOX, "http://example.com"), {:report_uri => 'http://example.com'})
+ csp = ContentSecurityPolicy.build(request_for(FIREFOX, "http://example.com"), {:report_uri => 'http://example.com'})
csp.send(:same_origin?).should be_true
- csp = ContentSecurityPolicy.new(request_for(FIREFOX, "http://example.com"), {:report_uri => 'http://example.com:80'})
+ csp = ContentSecurityPolicy.build(request_for(FIREFOX, "http://example.com"), {:report_uri => 'http://example.com:80'})
csp.send(:same_origin?).should be_true
- csp = ContentSecurityPolicy.new(request_for(FIREFOX, "http://example.com:80"), {:report_uri => 'http://example.com'})
+ csp = ContentSecurityPolicy.build(request_for(FIREFOX, "http://example.com:80"), {:report_uri => 'http://example.com'})
csp.send(:same_origin?).should be_true
end
it "does not match port mismatches" do
- csp = ContentSecurityPolicy.new(request_for(FIREFOX, "http://example.com:81"), {:report_uri => 'http://example.com'})
+ csp = ContentSecurityPolicy.build(request_for(FIREFOX, "http://example.com:81"), {:report_uri => 'http://example.com'})
csp.send(:same_origin?).should be_false
end
it "does not match host mismatches" do
- csp = ContentSecurityPolicy.new(request_for(FIREFOX, "http://example.com"), {:report_uri => 'http://twitter.com'})
+ csp = ContentSecurityPolicy.build(request_for(FIREFOX, "http://example.com"), {:report_uri => 'http://twitter.com'})
csp.send(:same_origin?).should be_false
end
it "does not match host mismatches because of subdomains" do
- csp = ContentSecurityPolicy.new(request_for(FIREFOX, "http://sub.example.com"), {:report_uri => 'http://example.com'})
+ csp = ContentSecurityPolicy.build(request_for(FIREFOX, "http://sub.example.com"), {:report_uri => 'http://example.com'})
csp.send(:same_origin?).should be_false
end
it "does not match scheme mismatches" do
- csp = ContentSecurityPolicy.new(request_for(FIREFOX, "ftp://example.com"), {:report_uri => 'https://example.com'})
+ csp = ContentSecurityPolicy.build(request_for(FIREFOX, "ftp://example.com"), {:report_uri => 'https://example.com'})
csp.send(:same_origin?).should be_false
end
it "does not match on substring collisions" do
- csp = ContentSecurityPolicy.new(request_for(FIREFOX, "https://anotherexample.com"), {:report_uri => 'https://example.com'})
+ csp = ContentSecurityPolicy.build(request_for(FIREFOX, "https://anotherexample.com"), {:report_uri => 'https://example.com'})
csp.send(:same_origin?).should be_false
end
end
@@ -194,11 +188,13 @@ def request_for user_agent, request_uri = nil
context "when using firefox" do
it "updates the report-uri when posting to a different host" do
+ subject = FirefoxContentSecurityPolicy.new
subject.configure(request_for(FIREFOX, "https://anexample.com"), opts)
subject.report_uri.should == FF_CSP_ENDPOINT
end
it "updates the report-uri when posting to a different host for Firefox >= 18" do
+ subject = FirefoxContentSecurityPolicy.new
subject.configure(request_for(FIREFOX, "https://anexample.com"), opts)
subject.report_uri.should == FF_CSP_ENDPOINT
end
@@ -226,20 +222,20 @@ def request_for user_agent, request_uri = nil
it "fills in directives without values with default-src value" do
options = default_opts.merge(:disable_fill_missing => false)
- csp = ContentSecurityPolicy.new(request_for(CHROME), options)
+ csp = ContentSecurityPolicy.build(request_for(CHROME), options)
default = "default-src https://*;"
value = "default-src https://*; connect-src https://*; font-src https://*; frame-src https://*; img-src https://*; 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;"
csp.value.should == value
end
it "sends the chrome csp header if an unknown browser is supplied" do
- csp = ContentSecurityPolicy.new(request_for(IE), default_opts)
+ csp = ContentSecurityPolicy.build(request_for(IE), default_opts)
csp.value.should == "default-src https://*; script-src 'unsafe-inline' 'unsafe-eval' https://* data:; style-src 'unsafe-inline' https://* chrome-extension: about:; report-uri /csp_report;"
end
context "X-Content-Security-Policy" do
it "builds a csp header for firefox" do
- csp = ContentSecurityPolicy.new(request_for(FIREFOX), default_opts)
+ csp = ContentSecurityPolicy.build(request_for(FIREFOX), default_opts)
csp.value.should == "allow https://*; options inline-script eval-script; script-src https://* data:; style-src https://* chrome-extension: about:; report-uri /csp_report;"
end
@@ -250,7 +246,7 @@ def request_for user_agent, request_uri = nil
:disable_chrome_extension => true,
:disable_fill_missing => true
}
- csp = ContentSecurityPolicy.new(request_for(FIREFOX), opts)
+ csp = ContentSecurityPolicy.build(request_for(FIREFOX), opts)
csp.value.should == "allow http://twitter.com; xhr-src 'self' http://*.localhost.com:*;"
end
@@ -261,30 +257,30 @@ def request_for user_agent, request_uri = nil
:disable_chrome_extension => true,
:disable_fill_missing => true
}
- csp = ContentSecurityPolicy.new(request_for(FIREFOX_18), opts)
+ csp = ContentSecurityPolicy.build(request_for(FIREFOX_18), opts)
csp.value.should == "default-src http://twitter.com; xhr-src 'self' http://*.localhost.com:*;"
end
it "builds a w3c-style-ish header for Firefox > version 18" do
- csp = ContentSecurityPolicy.new(request_for(FIREFOX_18), default_opts)
+ csp = ContentSecurityPolicy.build(request_for(FIREFOX_18), default_opts)
csp.value.should == "default-src https://*; options inline-script eval-script; script-src https://* data:; style-src https://* chrome-extension: about:; report-uri /csp_report;"
end
# cross-host posting not allowed in FF < 18
it "changes the report-uri to the local forwarder path if cross-host" do
- csp = ContentSecurityPolicy.new(request_for(FIREFOX), @options_with_forwarding)
+ csp = ContentSecurityPolicy.build(request_for(FIREFOX), @options_with_forwarding)
csp.value.should == "allow https://*; options inline-script eval-script; script-src https://* data:; style-src https://* chrome-extension: about:; report-uri #{@options_with_forwarding[:forward_endpoint]};"
end
end
context "X-Webkit-CSP" do
it "builds a csp header for chrome" do
- csp = ContentSecurityPolicy.new(request_for(CHROME), default_opts)
+ csp = ContentSecurityPolicy.build(request_for(CHROME), default_opts)
csp.value.should == "default-src https://*; script-src 'unsafe-inline' 'unsafe-eval' https://* data:; style-src 'unsafe-inline' https://* chrome-extension: about:; report-uri /csp_report;"
end
it "ignores :forward_endpoint settings" do
- csp = ContentSecurityPolicy.new(request_for(CHROME), @options_with_forwarding)
+ csp = ContentSecurityPolicy.build(request_for(CHROME), @options_with_forwarding)
csp.value.should == "default-src https://*; script-src 'unsafe-inline' 'unsafe-eval' https://* data:; style-src 'unsafe-inline' https://* chrome-extension: about:; report-uri #{@options_with_forwarding[:report_uri]};"
end
@@ -297,7 +293,7 @@ def request_for user_agent, request_uri = nil
:style_src => "inline https://* chrome-extension: about:"
}
- csp = ContentSecurityPolicy.new(request_for(CHROME), opts)
+ csp = ContentSecurityPolicy.build(request_for(CHROME), opts)
csp.value.should == "default-src https://* chrome-extension:; script-src 'unsafe-inline' 'unsafe-eval' https://* data: chrome-extension:; style-src 'unsafe-inline' https://* chrome-extension: about:; report-uri /csp_report;"
end
end
@@ -313,14 +309,14 @@ def request_for user_agent, request_uri = nil
}
it "adds directive values for headers on http" do
- csp = ContentSecurityPolicy.new(request_for(CHROME), options)
+ csp = ContentSecurityPolicy.build(request_for(CHROME), options)
csp.value.should == "default-src https://*; frame-src http://*; img-src http://*; script-src 'unsafe-inline' 'unsafe-eval' https://* data:; style-src 'unsafe-inline' https://* chrome-extension: about:; report-uri /csp_report;"
end
it "does not add the directive values if requesting https" do
request = request_for(CHROME)
request.stub(:ssl?).and_return(true)
- csp = ContentSecurityPolicy.new(request, options)
+ csp = ContentSecurityPolicy.build(request, options)
csp.value.should == "default-src https://*; script-src 'unsafe-inline' 'unsafe-eval' https://* data:; style-src 'unsafe-inline' https://* chrome-extension: about:; report-uri /csp_report;"
end
end
View
4 spec/lib/secure_headers_spec.rb
@@ -209,7 +209,7 @@ def reset_config
context "when using Chrome" do
it "sets default CSP header" do
stub_user_agent(USER_AGENTS[:chrome])
- should_assign_header(WEBKIT_CSP_HEADER_NAME + "-Report-Only", WEBKIT_CSP_HEADER)
+ should_assign_header(WEBKIT_CSP_HEADER_NAME + "-Report-Only", STANDARD_CSP_HEADER)
subject.set_csp_header request
end
end
@@ -217,7 +217,7 @@ def reset_config
context "when using a browser besides chrome/firefox" do
it "sets the CSP header" do
stub_user_agent(USER_AGENTS[:opera])
- should_assign_header(WEBKIT_CSP_HEADER_NAME + "-Report-Only", WEBKIT_CSP_HEADER)
+ should_assign_header(WEBKIT_CSP_HEADER_NAME + "-Report-Only", STANDARD_CSP_HEADER)
subject.set_csp_header request
end
end
View
2  spec/spec_helper.rb
@@ -18,6 +18,8 @@
require File.join(File.dirname(__FILE__), '..', 'app', 'controllers', 'content_security_policy_controller')
include ::SecureHeaders::StrictTransportSecurity::Constants
include ::SecureHeaders::ContentSecurityPolicy::Constants
+ include ::SecureHeaders::WebkitContentSecurityPolicy::Constants
+ include ::SecureHeaders::FirefoxContentSecurityPolicy::Constants
include ::SecureHeaders::XFrameOptions::Constants
include ::SecureHeaders::XXssProtection::Constants
include ::SecureHeaders::XContentTypeOptions::Constants
Something went wrong with that request. Please try again.