secure_headers 2.5.3 → 3.0.0.pre

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 (125) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +1 -0
  3. data/.travis.yml +2 -1
  4. data/Gemfile +9 -16
  5. data/README.md +154 -331
  6. data/Rakefile +2 -36
  7. data/lib/secure_headers/configuration.rb +189 -0
  8. data/lib/secure_headers/headers/content_security_policy.rb +341 -254
  9. data/lib/secure_headers/headers/public_key_pins.rb +43 -58
  10. data/lib/secure_headers/headers/strict_transport_security.rb +21 -49
  11. data/lib/secure_headers/headers/x_content_type_options.rb +18 -33
  12. data/lib/secure_headers/headers/x_download_options.rb +18 -33
  13. data/lib/secure_headers/headers/x_frame_options.rb +24 -34
  14. data/lib/secure_headers/headers/x_permitted_cross_domain_policies.rb +19 -34
  15. data/lib/secure_headers/headers/x_xss_protection.rb +17 -48
  16. data/lib/secure_headers/middleware.rb +15 -0
  17. data/lib/secure_headers/padrino.rb +1 -2
  18. data/lib/secure_headers/railtie.rb +9 -6
  19. data/lib/secure_headers/view_helper.rb +27 -43
  20. data/lib/secure_headers.rb +254 -61
  21. data/secure_headers.gemspec +7 -12
  22. data/spec/lib/secure_headers/configuration_spec.rb +80 -0
  23. data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +111 -276
  24. data/spec/lib/secure_headers/headers/public_key_pins_spec.rb +17 -17
  25. data/spec/lib/secure_headers/headers/strict_transport_security_spec.rb +11 -43
  26. data/spec/lib/secure_headers/headers/x_content_type_options_spec.rb +11 -18
  27. data/spec/lib/secure_headers/headers/x_download_options_spec.rb +13 -17
  28. data/spec/lib/secure_headers/headers/x_frame_options_spec.rb +15 -17
  29. data/spec/lib/secure_headers/headers/x_permitted_cross_domain_policies_spec.rb +22 -39
  30. data/spec/lib/secure_headers/headers/x_xss_protection_spec.rb +20 -30
  31. data/spec/lib/secure_headers/middleware_spec.rb +40 -0
  32. data/spec/lib/secure_headers_spec.rb +201 -339
  33. data/spec/spec_helper.rb +30 -30
  34. data/upgrading-to-3-0.md +35 -0
  35. metadata +14 -100
  36. data/fixtures/rails_3_2_22/.rspec +0 -1
  37. data/fixtures/rails_3_2_22/Gemfile +0 -6
  38. data/fixtures/rails_3_2_22/README.rdoc +0 -261
  39. data/fixtures/rails_3_2_22/Rakefile +0 -7
  40. data/fixtures/rails_3_2_22/app/controllers/application_controller.rb +0 -4
  41. data/fixtures/rails_3_2_22/app/controllers/other_things_controller.rb +0 -5
  42. data/fixtures/rails_3_2_22/app/controllers/things_controller.rb +0 -5
  43. data/fixtures/rails_3_2_22/app/models/.gitkeep +0 -0
  44. data/fixtures/rails_3_2_22/app/views/layouts/application.html.erb +0 -11
  45. data/fixtures/rails_3_2_22/app/views/other_things/index.html.erb +0 -2
  46. data/fixtures/rails_3_2_22/app/views/things/index.html.erb +0 -1
  47. data/fixtures/rails_3_2_22/config/application.rb +0 -14
  48. data/fixtures/rails_3_2_22/config/boot.rb +0 -6
  49. data/fixtures/rails_3_2_22/config/environment.rb +0 -5
  50. data/fixtures/rails_3_2_22/config/environments/test.rb +0 -37
  51. data/fixtures/rails_3_2_22/config/initializers/secure_headers.rb +0 -16
  52. data/fixtures/rails_3_2_22/config/routes.rb +0 -4
  53. data/fixtures/rails_3_2_22/config/script_hashes.yml +0 -5
  54. data/fixtures/rails_3_2_22/config.ru +0 -7
  55. data/fixtures/rails_3_2_22/lib/assets/.gitkeep +0 -0
  56. data/fixtures/rails_3_2_22/lib/tasks/.gitkeep +0 -0
  57. data/fixtures/rails_3_2_22/log/.gitkeep +0 -0
  58. data/fixtures/rails_3_2_22/spec/controllers/other_things_controller_spec.rb +0 -83
  59. data/fixtures/rails_3_2_22/spec/controllers/things_controller_spec.rb +0 -54
  60. data/fixtures/rails_3_2_22/spec/spec_helper.rb +0 -15
  61. data/fixtures/rails_3_2_22/vendor/assets/javascripts/.gitkeep +0 -0
  62. data/fixtures/rails_3_2_22/vendor/assets/stylesheets/.gitkeep +0 -0
  63. data/fixtures/rails_3_2_22/vendor/plugins/.gitkeep +0 -0
  64. data/fixtures/rails_3_2_22_no_init/.rspec +0 -1
  65. data/fixtures/rails_3_2_22_no_init/Gemfile +0 -6
  66. data/fixtures/rails_3_2_22_no_init/README.rdoc +0 -261
  67. data/fixtures/rails_3_2_22_no_init/Rakefile +0 -7
  68. data/fixtures/rails_3_2_22_no_init/app/controllers/application_controller.rb +0 -4
  69. data/fixtures/rails_3_2_22_no_init/app/controllers/other_things_controller.rb +0 -20
  70. data/fixtures/rails_3_2_22_no_init/app/controllers/things_controller.rb +0 -5
  71. data/fixtures/rails_3_2_22_no_init/app/models/.gitkeep +0 -0
  72. data/fixtures/rails_3_2_22_no_init/app/views/layouts/application.html.erb +0 -12
  73. data/fixtures/rails_3_2_22_no_init/app/views/other_things/index.html.erb +0 -1
  74. data/fixtures/rails_3_2_22_no_init/app/views/things/index.html.erb +0 -0
  75. data/fixtures/rails_3_2_22_no_init/config/application.rb +0 -17
  76. data/fixtures/rails_3_2_22_no_init/config/boot.rb +0 -6
  77. data/fixtures/rails_3_2_22_no_init/config/environment.rb +0 -5
  78. data/fixtures/rails_3_2_22_no_init/config/environments/test.rb +0 -37
  79. data/fixtures/rails_3_2_22_no_init/config/routes.rb +0 -4
  80. data/fixtures/rails_3_2_22_no_init/config.ru +0 -4
  81. data/fixtures/rails_3_2_22_no_init/lib/assets/.gitkeep +0 -0
  82. data/fixtures/rails_3_2_22_no_init/lib/tasks/.gitkeep +0 -0
  83. data/fixtures/rails_3_2_22_no_init/log/.gitkeep +0 -0
  84. data/fixtures/rails_3_2_22_no_init/spec/controllers/other_things_controller_spec.rb +0 -56
  85. data/fixtures/rails_3_2_22_no_init/spec/controllers/things_controller_spec.rb +0 -54
  86. data/fixtures/rails_3_2_22_no_init/spec/spec_helper.rb +0 -5
  87. data/fixtures/rails_3_2_22_no_init/vendor/assets/javascripts/.gitkeep +0 -0
  88. data/fixtures/rails_3_2_22_no_init/vendor/assets/stylesheets/.gitkeep +0 -0
  89. data/fixtures/rails_3_2_22_no_init/vendor/plugins/.gitkeep +0 -0
  90. data/fixtures/rails_4_1_8/Gemfile +0 -5
  91. data/fixtures/rails_4_1_8/README.rdoc +0 -28
  92. data/fixtures/rails_4_1_8/Rakefile +0 -6
  93. data/fixtures/rails_4_1_8/app/controllers/application_controller.rb +0 -4
  94. data/fixtures/rails_4_1_8/app/controllers/concerns/.keep +0 -0
  95. data/fixtures/rails_4_1_8/app/controllers/other_things_controller.rb +0 -5
  96. data/fixtures/rails_4_1_8/app/controllers/things_controller.rb +0 -5
  97. data/fixtures/rails_4_1_8/app/models/.keep +0 -0
  98. data/fixtures/rails_4_1_8/app/models/concerns/.keep +0 -0
  99. data/fixtures/rails_4_1_8/app/views/layouts/application.html.erb +0 -11
  100. data/fixtures/rails_4_1_8/app/views/other_things/index.html.erb +0 -2
  101. data/fixtures/rails_4_1_8/app/views/things/index.html.erb +0 -1
  102. data/fixtures/rails_4_1_8/config/application.rb +0 -15
  103. data/fixtures/rails_4_1_8/config/boot.rb +0 -4
  104. data/fixtures/rails_4_1_8/config/environment.rb +0 -5
  105. data/fixtures/rails_4_1_8/config/environments/test.rb +0 -10
  106. data/fixtures/rails_4_1_8/config/initializers/secure_headers.rb +0 -16
  107. data/fixtures/rails_4_1_8/config/routes.rb +0 -4
  108. data/fixtures/rails_4_1_8/config/script_hashes.yml +0 -5
  109. data/fixtures/rails_4_1_8/config/secrets.yml +0 -22
  110. data/fixtures/rails_4_1_8/config.ru +0 -4
  111. data/fixtures/rails_4_1_8/lib/assets/.keep +0 -0
  112. data/fixtures/rails_4_1_8/lib/tasks/.keep +0 -0
  113. data/fixtures/rails_4_1_8/log/.keep +0 -0
  114. data/fixtures/rails_4_1_8/spec/controllers/other_things_controller_spec.rb +0 -83
  115. data/fixtures/rails_4_1_8/spec/controllers/things_controller_spec.rb +0 -59
  116. data/fixtures/rails_4_1_8/spec/spec_helper.rb +0 -15
  117. data/fixtures/rails_4_1_8/vendor/assets/javascripts/.keep +0 -0
  118. data/fixtures/rails_4_1_8/vendor/assets/stylesheets/.keep +0 -0
  119. data/lib/secure_headers/controller_extension.rb +0 -158
  120. data/lib/secure_headers/hash_helper.rb +0 -7
  121. data/lib/secure_headers/header.rb +0 -5
  122. data/lib/secure_headers/headers/content_security_policy/script_hash_middleware.rb +0 -22
  123. data/lib/secure_headers/version.rb +0 -3
  124. data/lib/tasks/tasks.rake +0 -48
  125. data/spec/lib/secure_headers/headers/content_security_policy/script_hash_middleware_spec.rb +0 -46
data/Rakefile CHANGED
@@ -7,44 +7,10 @@ require 'net/https'
7
7
  desc "Run RSpec"
8
8
  RSpec::Core::RakeTask.new do |t|
9
9
  t.verbose = false
10
- t.rspec_opts = "--format progress"
10
+ t.rspec_opts = "--format progress"
11
11
  end
12
12
 
13
- task :default => :all_spec
14
-
15
- desc "Run all specs, and test fixture apps"
16
- task :all_spec => :spec do
17
- pwd = Dir.pwd
18
- Dir.chdir 'fixtures/rails_3_2_22'
19
- puts Dir.pwd
20
- str = `bundle install >> /dev/null; bundle exec rspec spec`
21
- puts str
22
- unless $? == 0
23
- Dir.chdir pwd
24
- fail "Header tests with app not using initializer failed exit code: #{$?}"
25
- end
26
-
27
- Dir.chdir pwd
28
- Dir.chdir 'fixtures/rails_3_2_22_no_init'
29
- puts Dir.pwd
30
- puts `bundle install >> /dev/null; bundle exec rspec spec`
31
-
32
- unless $? == 0
33
- fail "Header tests with app not using initializer failed"
34
- Dir.chdir pwd
35
- end
36
-
37
- Dir.chdir pwd
38
- Dir.chdir 'fixtures/rails_4_1_8'
39
- puts Dir.pwd
40
- puts `bundle install >> /dev/null; bundle exec rspec spec`
41
-
42
- unless $? == 0
43
- fail "Header tests with Rails 4 failed"
44
- Dir.chdir pwd
45
- end
46
-
47
- end
13
+ task default: :spec
48
14
 
49
15
  begin
50
16
  require 'rdoc/task'
@@ -0,0 +1,189 @@
1
+ module SecureHeaders
2
+ class Configuration
3
+ DEFAULT_CONFIG = :default
4
+ NOOP_CONFIGURATION = "secure_headers_noop_config"
5
+ class NotYetConfiguredError < StandardError; end
6
+ class << self
7
+ # Public: Set the global default configuration.
8
+ #
9
+ # Optionally supply a block to override the defaults set by this library.
10
+ #
11
+ # Returns the newly created config.
12
+ def default(&block)
13
+ config = new(&block)
14
+ add_noop_configuration
15
+ add_configuration(DEFAULT_CONFIG, config)
16
+ end
17
+ alias_method :configure, :default
18
+
19
+ # Public: create a named configuration that overrides the default config.
20
+ #
21
+ # name - use an idenfier for the override config.
22
+ # base - override another existing config, or override the default config
23
+ # if no value is supplied.
24
+ #
25
+ # Returns: the newly created config
26
+ def override(name, base = DEFAULT_CONFIG)
27
+ unless get(base)
28
+ raise NotYetConfiguredError, "#{base} policy not yet supplied"
29
+ end
30
+ override = @configurations[base].dup
31
+ yield(override)
32
+ add_configuration(name, override)
33
+ end
34
+
35
+ # Public: retrieve a global configuration object
36
+ #
37
+ # Returns the configuration with a given name or raises a
38
+ # NotYetConfiguredError if `default` has not been called.
39
+ def get(name = DEFAULT_CONFIG)
40
+ if @configurations.nil?
41
+ raise NotYetConfiguredError, "Default policy not yet supplied"
42
+ end
43
+ @configurations[name]
44
+ end
45
+
46
+ # Public: perform a basic deep dup. The shallow copy provided by dup/clone
47
+ # can lead to modifying parent objects.
48
+ def deep_copy(config)
49
+ config.each_with_object({}) do |(key, value), hash|
50
+ hash[key] = if value.is_a?(Array)
51
+ value.dup
52
+ else
53
+ value
54
+ end
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ # Private: add a valid configuration to the global set of named configs.
61
+ #
62
+ # config - the config to store
63
+ # name - the lookup value for this config
64
+ #
65
+ # Raises errors if the config is invalid or if a config named `name`
66
+ # already exists.
67
+ #
68
+ # Returns the config, if valid
69
+ def add_configuration(name, config)
70
+ config.validate_config!
71
+ @configurations ||= {}
72
+ config.send(:cache_headers!)
73
+ config.freeze
74
+ @configurations[name] = config
75
+ end
76
+
77
+ # Private: Automatically add an "opt-out of everything" override.
78
+ #
79
+ # Returns the noop config
80
+ def add_noop_configuration
81
+ noop_config = new do |config|
82
+ ALL_HEADER_CLASSES.each do |klass|
83
+ config.send("#{klass::CONFIG_KEY}=", OPT_OUT)
84
+ end
85
+ end
86
+
87
+ add_configuration(NOOP_CONFIGURATION, noop_config)
88
+ end
89
+ end
90
+
91
+ attr_accessor :hsts, :x_frame_options, :x_content_type_options,
92
+ :x_xss_protection, :csp, :x_download_options, :x_permitted_cross_domain_policies,
93
+ :hpkp
94
+ attr_reader :cached_headers
95
+
96
+ def initialize(&block)
97
+ self.hpkp = OPT_OUT
98
+ self.csp = self.class.deep_copy(CSP::DEFAULT_CONFIG)
99
+ instance_eval &block if block_given?
100
+ end
101
+
102
+ # Public: copy everything but the cached headers
103
+ #
104
+ # Returns a deep-dup'd copy of this configuration.
105
+ def dup
106
+ copy = self.class.new
107
+ copy.hsts = hsts
108
+ copy.x_frame_options = x_frame_options
109
+ copy.x_content_type_options = x_content_type_options
110
+ copy.x_xss_protection = x_xss_protection
111
+ copy.x_download_options = x_download_options
112
+ copy.x_permitted_cross_domain_policies = x_permitted_cross_domain_policies
113
+ copy.csp = if csp.is_a?(Hash)
114
+ self.class.deep_copy(csp)
115
+ else
116
+ csp
117
+ end
118
+
119
+ copy.hpkp = if hpkp.is_a?(Hash)
120
+ self.class.deep_copy(hpkp)
121
+ else
122
+ hpkp
123
+ end
124
+ copy
125
+ end
126
+
127
+ # Public: Retrieve a config based on the CONFIG_KEY for a class
128
+ #
129
+ # Returns the value if available, and returns a dup of any hash values.
130
+ def fetch(key)
131
+ config = send(key)
132
+ config = self.class.deep_copy(config) if config.is_a?(Hash)
133
+ config
134
+ end
135
+
136
+ # Public: validates all configurations values.
137
+ #
138
+ # Raises various configuration errors if any invalid config is detected.
139
+ #
140
+ # Returns nothing
141
+ def validate_config!
142
+ StrictTransportSecurity.validate_config!(hsts)
143
+ ContentSecurityPolicy.validate_config!(csp)
144
+ XFrameOptions.validate_config!(x_frame_options)
145
+ XContentTypeOptions.validate_config!(x_content_type_options)
146
+ XXssProtection.validate_config!(x_xss_protection)
147
+ XDownloadOptions.validate_config!(x_download_options)
148
+ XPermittedCrossDomainPolicies.validate_config!(x_permitted_cross_domain_policies)
149
+ PublicKeyPins.validate_config!(hpkp)
150
+ end
151
+
152
+ # Public: Precompute the header names and values for this configuraiton.
153
+ # Ensures that headers generated at configure time, not on demand.
154
+ #
155
+ # Returns the cached headers
156
+ def cache_headers!
157
+ # generate defaults for the "easy" headers
158
+ headers = (ALL_HEADERS_BESIDES_CSP).each_with_object({}) do |klass, hash|
159
+ config = fetch(klass::CONFIG_KEY)
160
+ unless config == OPT_OUT
161
+ hash[klass::CONFIG_KEY] = klass.make_header(config).freeze
162
+ end
163
+ end
164
+
165
+ generate_csp_headers(headers)
166
+
167
+ headers.freeze
168
+ @cached_headers = headers
169
+ end
170
+
171
+ # Private: adds CSP headers for each variation of CSP support.
172
+ #
173
+ # headers - generated headers are added to this hash namespaced by The
174
+ # different variations
175
+ #
176
+ # Returns nothing
177
+ def generate_csp_headers(headers)
178
+ unless csp == OPT_OUT
179
+ headers[CSP::CONFIG_KEY] = {}
180
+
181
+ CSP::VARIATIONS.each do |name, _|
182
+ csp_config = fetch(CSP::CONFIG_KEY)
183
+ csp = CSP.make_header(csp_config, UserAgent.parse(name))
184
+ headers[CSP::CONFIG_KEY][name] = csp.freeze
185
+ end
186
+ end
187
+ end
188
+ end
189
+ end