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,5 @@
1
+ # Sample localization file for English. Add more files in this directory for other locales.
2
+ # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
3
+
4
+ en:
5
+ hello: "Hello world"
@@ -0,0 +1,61 @@
1
+ Rails3212::Application.routes.draw do
2
+ resources :things
3
+
4
+
5
+ # The priority is based upon order of creation:
6
+ # first created -> highest priority.
7
+
8
+ # Sample of regular route:
9
+ # match 'products/:id' => 'catalog#view'
10
+ # Keep in mind you can assign values other than :controller and :action
11
+
12
+ # Sample of named route:
13
+ # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
14
+ # This route can be invoked with purchase_url(:id => product.id)
15
+
16
+ # Sample resource route (maps HTTP verbs to controller actions automatically):
17
+ # resources :products
18
+
19
+ # Sample resource route with options:
20
+ # resources :products do
21
+ # member do
22
+ # get 'short'
23
+ # post 'toggle'
24
+ # end
25
+ #
26
+ # collection do
27
+ # get 'sold'
28
+ # end
29
+ # end
30
+
31
+ # Sample resource route with sub-resources:
32
+ # resources :products do
33
+ # resources :comments, :sales
34
+ # resource :seller
35
+ # end
36
+
37
+ # Sample resource route with more complex sub-resources
38
+ # resources :products do
39
+ # resources :comments
40
+ # resources :sales do
41
+ # get 'recent', :on => :collection
42
+ # end
43
+ # end
44
+
45
+ # Sample resource route within a namespace:
46
+ # namespace :admin do
47
+ # # Directs /admin/products/* to Admin::ProductsController
48
+ # # (app/controllers/admin/products_controller.rb)
49
+ # resources :products
50
+ # end
51
+
52
+ # You can have the root of your site routed with "root"
53
+ # just remember to delete public/index.html.
54
+ # root :to => 'welcome#index'
55
+
56
+ # See how all your routes lay out with "rake routes"
57
+
58
+ # This is a legacy wild controller route that's not recommended for RESTful applications.
59
+ # Note: This route will make all actions in every controller accessible via GET requests.
60
+ match ':controller(/:action(/:id))(.:format)'
61
+ end
@@ -0,0 +1,16 @@
1
+ # encoding: UTF-8
2
+ # This file is auto-generated from the current state of the database. Instead
3
+ # of editing this file, please use the migrations feature of Active Record to
4
+ # incrementally modify your database, and then regenerate this schema definition.
5
+ #
6
+ # Note that this schema.rb definition is the authoritative source for your
7
+ # database schema. If you need to create the application database on another
8
+ # system, you should be using db:schema:load, not running all the migrations
9
+ # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10
+ # you'll amass, the slower it'll run and the greater likelihood for issues).
11
+ #
12
+ # It's strongly recommended to check this file into your version control system.
13
+
14
+ ActiveRecord::Schema.define(:version => 0) do
15
+
16
+ end
@@ -0,0 +1,7 @@
1
+ # This file should contain all the record creation needed to seed the database with its default values.
2
+ # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
3
+ #
4
+ # Examples:
5
+ #
6
+ # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
7
+ # Mayor.create(name: 'Emanuel', city: cities.first)
File without changes
File without changes
File without changes
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ describe OtherThingsController do
4
+ describe "headers" do
5
+ before(:each) do
6
+ # Chrome
7
+ request.env['HTTP_USER_AGENT'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.56 Safari/536.5'
8
+ end
9
+
10
+ it "sets the X-XSS-PROTECTION header" do
11
+ get :index
12
+ response.headers['X-XSS-Protection'].should == SecureHeaders::XXssProtection::Constants::DEFAULT_VALUE
13
+ end
14
+
15
+ it "sets the X-FRAME-OPTIONS header" do
16
+ get :index
17
+ response.headers['X-FRAME-OPTIONS'].should == SecureHeaders::XFrameOptions::Constants::DEFAULT_VALUE
18
+ end
19
+
20
+ it "sets the X-WebKit-CSP header" do
21
+ get :index
22
+ response.headers['X-WebKit-CSP-Report-Only'].should == "default-src 'self'; img-src data:;"
23
+ end
24
+
25
+ #mock ssl
26
+ it "sets the STRICT-TRANSPORT-SECURITY header" do
27
+ request.env['HTTPS'] = 'on'
28
+ get :index
29
+ response.headers['Strict-Transport-Security'].should == SecureHeaders::StrictTransportSecurity::Constants::DEFAULT_VALUE
30
+ end
31
+
32
+ context "using IE" do
33
+ it "sets the X-CONTENT-TYPE-OPTIONS header" do
34
+ request.env['HTTP_USER_AGENT'] = "Mozilla/5.0 (compatible; MSIE 10.6; Windows NT 6.1; Trident/5.0; InfoPath.2; SLCC1; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 2.0.50727) 3gpp-gba UNTRUSTED/1.0"
35
+ get :index
36
+ response.headers['X-Content-Type-Options'].should == "nosniff"
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ # This controller is meant to be something that inherits config from application controller
4
+ # all values are defaulted because no initializer is configured, and the values in app controller
5
+ # only provide csp => false
6
+
7
+ describe ThingsController do
8
+ describe "headers" do
9
+ before(:each) do
10
+ # Chrome
11
+ request.env['HTTP_USER_AGENT'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.56 Safari/536.5'
12
+ end
13
+
14
+ it "sets the X-XSS-PROTECTION header" do
15
+ get :index
16
+ response.headers['X-XSS-Protection'].should == SecureHeaders::XXssProtection::Constants::DEFAULT_VALUE
17
+ end
18
+
19
+ it "sets the X-FRAME-OPTIONS header" do
20
+ get :index
21
+ response.headers['X-FRAME-OPTIONS'].should == SecureHeaders::XFrameOptions::Constants::DEFAULT_VALUE
22
+ end
23
+
24
+ it "sets the X-WebKit-CSP header" do
25
+ get :index
26
+ response.headers['X-WebKit-CSP-Report-Only'].should == nil
27
+ end
28
+
29
+ #mock ssl
30
+ it "sets the STRICT-TRANSPORT-SECURITY header" do
31
+ request.env['HTTPS'] = 'on'
32
+ get :index
33
+ response.headers['Strict-Transport-Security'].should == SecureHeaders::StrictTransportSecurity::Constants::DEFAULT_VALUE
34
+ end
35
+
36
+ context "using IE" do
37
+ it "sets the X-CONTENT-TYPE-OPTIONS header" do
38
+ request.env['HTTP_USER_AGENT'] = "Mozilla/5.0 (compatible; MSIE 10.6; Windows NT 6.1; Trident/5.0; InfoPath.2; SLCC1; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 2.0.50727) 3gpp-gba UNTRUSTED/1.0"
39
+ get :index
40
+ response.headers['X-Content-Type-Options'].should == "nosniff"
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,20 @@
1
+ require 'rubygems'
2
+ require 'spork'
3
+ #uncomment the following line to use spork with the debugger
4
+ #require 'spork/ext/ruby-debug'
5
+
6
+ Spork.prefork do
7
+ # Loading more in this block will cause your tests to run faster. However,
8
+ # if you change any configuration or code from libraries loaded here, you'll
9
+ # need to restart spork for it take effect.
10
+ # This file is copied to spec/ when you run 'rails generate rspec:install'
11
+ ENV["RAILS_ENV"] ||= 'test'
12
+ require File.expand_path("../../config/environment", __FILE__)
13
+ require 'rspec/rails'
14
+ require 'rspec/autorun'
15
+ # require 'ruby-debug'
16
+ end
17
+
18
+ Spork.each_run do
19
+
20
+ end
@@ -13,25 +13,25 @@ module SecureHeaders
13
13
  class << self
14
14
  def append_features(base)
15
15
  base.module_eval do
16
- @@secure_headers_options = nil
17
-
18
16
  extend ClassMethods
19
17
  include InstanceMethods
20
-
21
- # jank?
22
- def self.secure_headers_options=(opts)
23
- @@secure_headers_options = opts
24
- end
25
-
26
- def self.secure_headers_options
27
- @@secure_headers_options
28
- end
29
18
  end
30
19
  end
31
20
  end
32
21
 
33
22
  module ClassMethods
34
- def ensure_security_headers options = {}, *args
23
+ attr_writer :secure_headers_options
24
+ def secure_headers_options
25
+ if @secure_headers_options
26
+ @secure_headers_options
27
+ elsif superclass.respond_to?(:secure_headers_options) # stop at application_controller
28
+ superclass.secure_headers_options
29
+ else
30
+ {}
31
+ end
32
+ end
33
+
34
+ def ensure_security_headers options = {}
35
35
  self.secure_headers_options = options
36
36
  before_filter :set_security_headers
37
37
  end
@@ -44,7 +44,7 @@ module SecureHeaders
44
44
  end
45
45
 
46
46
  module InstanceMethods
47
- def set_security_headers(options = self.class.secure_headers_options || {})
47
+ def set_security_headers(options = self.class.secure_headers_options)
48
48
  brwsr = Brwsr::Browser.new(:ua => request.env['HTTP_USER_AGENT'])
49
49
  set_hsts_header(options[:hsts]) if request.ssl?
50
50
  set_x_frame_options_header(options[:x_frame_options])
@@ -59,10 +59,10 @@ module SecureHeaders
59
59
  options = self.class.options_for :csp, options
60
60
  return if options == false
61
61
 
62
- header = ContentSecurityPolicy.new(request, options)
62
+ header = ContentSecurityPolicy.new(options, :request => request)
63
63
  set_header(header.name, header.value)
64
64
  if options && options[:experimental] && options[:enforce]
65
- header = ContentSecurityPolicy.new(request, options, :experimental => true)
65
+ header = ContentSecurityPolicy.new(options, :experimental => true, :request => request)
66
66
  set_header(header.name, header.value)
67
67
  end
68
68
  end
@@ -107,6 +107,10 @@ end
107
107
 
108
108
  require "secure_headers/version"
109
109
  require "secure_headers/headers/content_security_policy"
110
+ require "secure_headers/headers/content_security_policy/browser_strategy"
111
+ require "secure_headers/headers/content_security_policy/firefox_browser_strategy"
112
+ require "secure_headers/headers/content_security_policy/ie_browser_strategy"
113
+ require "secure_headers/headers/content_security_policy/webkit_browser_strategy"
110
114
  require "secure_headers/headers/x_frame_options"
111
115
  require "secure_headers/headers/strict_transport_security"
112
116
  require "secure_headers/headers/x_xss_protection"
@@ -19,27 +19,39 @@ module SecureHeaders
19
19
  end
20
20
  include Constants
21
21
 
22
- META.each do |meta|
23
- attr_accessor meta
24
- end
25
- attr_reader :browser, :ssl_request, :report_uri, :request_uri
22
+ attr_accessor *META
23
+ attr_reader :browser, :ssl_request, :report_uri, :request_uri, :experimental, :config
26
24
 
27
- alias :enforce? :enforce
28
25
  alias :disable_chrome_extension? :disable_chrome_extension
29
26
  alias :disable_fill_missing? :disable_fill_missing
30
27
  alias :ssl_request? :ssl_request
31
28
 
32
-
33
- def initialize(request=nil, config=nil, options={})
29
+ # +options+ param contains
30
+ # :experimental use experimental block for config
31
+ # :ssl_request used to determine if http_additions should be used
32
+ # :request_uri used to determine if firefox should send the report directly
33
+ # or use the forwarding endpoint
34
+ # :ua the user agent (or just use Firefox/Chrome/MSIE/etc)
35
+ #
36
+ # :report used to determine what :ssl_request, :ua, and :request_uri are set to
37
+ def initialize(config=nil, options={})
34
38
  @experimental = !!options.delete(:experimental)
35
- if config
36
- configure request, config
37
- elsif request
38
- parse_request request
39
+ if options[:request]
40
+ parse_request(options[:request])
41
+ else
42
+ @browser = Brwsr::Browser.new(:ua => options[:ua])
43
+ # fails open, assumes http. Bad idea? Will always include http additions.
44
+ # could also fail if not supplied.
45
+ @ssl_request = !!options.delete(:ssl)
46
+ # a nil value here means we always assume we are not on the same host,
47
+ # which causes all FF csp reports to go through the forwarder
48
+ @request_uri = options.delete(:request_uri)
39
49
  end
50
+
51
+ configure(config) if config
40
52
  end
41
53
 
42
- def configure request, opts
54
+ def configure opts
43
55
  @config = opts.dup
44
56
 
45
57
  experimental_config = @config.delete(:experimental)
@@ -48,9 +60,8 @@ module SecureHeaders
48
60
  @config.merge!(experimental_config)
49
61
  end
50
62
 
51
- parse_request request
52
63
  META.each do |meta|
53
- self.send(meta.to_s + "=", @config.delete(meta))
64
+ self.send("#{meta}=", @config.delete(meta))
54
65
  end
55
66
 
56
67
  @report_uri = @config.delete(:report_uri)
@@ -61,45 +72,39 @@ module SecureHeaders
61
72
  end
62
73
 
63
74
  def name
64
- base = if browser.ie?
65
- STANDARD_HEADER_NAME
66
- elsif browser.firefox?
67
- # can't use supports_standard because FF18 does not support this part of the standard.
68
- FIREFOX_CSP_HEADER_NAME
69
- else
70
- WEBKIT_CSP_HEADER_NAME
71
- end
72
-
73
- if !enforce || @experimental
74
- base += "-Report-Only"
75
- end
76
- base
75
+ browser_strategy.name
77
76
  end
78
77
 
79
78
  def value
80
79
  return @config if @config.is_a?(String)
81
- if @config.nil?
82
- return supports_standard? ? WEBKIT_CSP_HEADER : FIREFOX_CSP_HEADER
80
+
81
+ if @config
82
+ build_value
83
+ else
84
+ browser_strategy.csp_header
83
85
  end
86
+ end
87
+
88
+ private
84
89
 
85
- build_value
90
+ def browser_strategy
91
+ @browser_strategy ||= BrowserStrategy.build(self)
86
92
  end
87
93
 
88
94
  def directives
89
- # can't use supports_standard because FF18 does not support this part of the standard.
90
- browser.firefox? ? FIREFOX_DIRECTIVES : WEBKIT_DIRECTIVES
95
+ browser_strategy.directives
91
96
  end
92
97
 
93
- private
94
-
95
98
  def build_value
96
99
  fill_directives unless disable_fill_missing?
97
100
  add_missing_chrome_extension_values unless disable_chrome_extension?
98
101
  append_http_additions unless ssl_request?
99
102
 
100
- header_value = build_impl_specific_directives
101
- header_value += generic_directives(@config)
102
- header_value += report_uri_directive(@report_uri)
103
+ header_value = [
104
+ build_impl_specific_directives,
105
+ generic_directives(@config),
106
+ report_uri_directive(@report_uri)
107
+ ].join
103
108
 
104
109
  #store the value for next time
105
110
  @config = header_value
@@ -148,12 +153,7 @@ module SecureHeaders
148
153
  end
149
154
 
150
155
  def filter_unsupported_directives
151
- if browser.firefox?
152
- # can't use supports_standard because FF18 does not support this part of the standard.
153
- @config[:xhr_src] = @config.delete(:connect_src) if @config[:connect_src]
154
- else
155
- @config.delete(:frame_ancestors)
156
- end
156
+ @config = browser_strategy.filter_unsupported_directives(@config)
157
157
  end
158
158
 
159
159
  # translates 'inline','self', 'none' and 'eval' to their respective impl-specific values.
@@ -168,104 +168,45 @@ module SecureHeaders
168
168
  end
169
169
  end
170
170
 
171
- # inline/eval => impl-specific values
172
171
  def translate_inline_or_eval val
173
- # can't use supports_standard because FF18 does not support this part of the standard.
174
- if browser.firefox?
175
- val == 'inline' ? 'inline-script' : 'eval-script'
176
- else
177
- val == 'inline' ? "'unsafe-inline'" : "'unsafe-eval'"
178
- end
172
+ browser_strategy.translate_inline_or_eval(val)
179
173
  end
180
174
 
181
175
  # if we have a forwarding endpoint setup and we are not on the same origin as our report_uri
182
176
  # or only a path was supplied (in which case we assume cross-host)
183
177
  # we need to forward the request for Firefox.
184
178
  def normalize_reporting_endpoint
185
- if browser.firefox? && (!same_origin? || URI.parse(report_uri).host.nil?)
186
- if forward_endpoint
187
- @report_uri = FF_CSP_ENDPOINT
188
- else
189
- @report_uri = nil
190
- end
191
- end
192
- end
179
+ return unless browser_strategy.normalize_reporting_endpoint?
180
+ return unless !same_origin? || URI.parse(report_uri).host.nil?
193
181
 
194
- def supports_standard?
195
- !browser.firefox?
196
- end
197
-
198
- def build_impl_specific_directives
199
- header_value = ""
200
- default = expect_directive_value(:default_src)
201
- # firefox 18 still requires the use of the options value, but can substitute default-src for allow
202
- if browser.firefox?
203
- header_value += build_firefox_specific_preamble(default) || ''
182
+ if forward_endpoint
183
+ @report_uri = FF_CSP_ENDPOINT
204
184
  else
205
- header_value += "default-src #{default.join(" ")}; " if default.any?
185
+ @report_uri = nil
206
186
  end
207
-
208
- header_value
209
187
  end
210
188
 
211
- def build_firefox_specific_preamble(default_src_value)
212
- header_value = ''
213
- if supports_standard?
214
- header_value += "default-src #{default_src_value.join(" ")}; " if default_src_value.any?
215
- elsif default_src_value
216
- header_value += "allow #{default_src_value.join(" ")}; " if default_src_value.any?
217
- end
218
-
219
- options_directive = build_options_directive
220
- header_value += "options #{options_directive.join(" ")}; " if options_directive.any?
221
- header_value
189
+ def build_impl_specific_directives
190
+ default = expect_directive_value(:default_src)
191
+ browser_strategy.build_impl_specific_directives(default)
222
192
  end
223
193
 
224
194
  def expect_directive_value key
225
195
  @config.delete(key) {|k| raise ContentSecurityPolicyBuildError.new("Expected to find #{k} directive value")}
226
196
  end
227
197
 
228
- # moves inline/eval values from script-src to options
229
- # discards those values in the style-src directive
230
- def build_options_directive
231
- options_directive = []
232
- @config.each do |directive, val|
233
- next if val.is_a?(String)
234
- new_val = []
235
- val.each do |token|
236
- if ['inline-script', 'eval-script'].include?(token)
237
- # Firefox does not support blocking inline styles ATM
238
- # https://bugzilla.mozilla.org/show_bug.cgi?id=763879
239
- unless directive?(directive, "style_src") || options_directive.include?(token)
240
- options_directive << token
241
- end
242
- else
243
- new_val << token
244
- end
245
- end
246
- @config[directive] = new_val
247
- end
248
-
249
- options_directive
250
- end
251
-
252
198
  def same_origin?
253
- return if report_uri.nil?
199
+ return unless report_uri && request_uri
254
200
 
255
201
  origin = URI.parse(request_uri)
256
202
  uri = URI.parse(report_uri)
257
203
  uri.host == origin.host && origin.port == uri.port && origin.scheme == uri.scheme
258
204
  end
259
205
 
260
- def directive? val, name
261
- val.to_s.casecmp(name) == 0
262
- end
263
-
264
206
  def report_uri_directive(report_uri)
265
- report_uri.nil? ? '' : "report-uri #{report_uri};"
207
+ report_uri ? "report-uri #{report_uri};" : ''
266
208
  end
267
209
 
268
-
269
210
  def generic_directives(config)
270
211
  header_value = ''
271
212
  if config[:img_src]