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.
- checksums.yaml +4 -4
- data/.rspec +1 -0
- data/.travis.yml +2 -1
- data/Gemfile +9 -16
- data/README.md +154 -331
- data/Rakefile +2 -36
- data/lib/secure_headers/configuration.rb +189 -0
- data/lib/secure_headers/headers/content_security_policy.rb +341 -254
- data/lib/secure_headers/headers/public_key_pins.rb +43 -58
- data/lib/secure_headers/headers/strict_transport_security.rb +21 -49
- data/lib/secure_headers/headers/x_content_type_options.rb +18 -33
- data/lib/secure_headers/headers/x_download_options.rb +18 -33
- data/lib/secure_headers/headers/x_frame_options.rb +24 -34
- data/lib/secure_headers/headers/x_permitted_cross_domain_policies.rb +19 -34
- data/lib/secure_headers/headers/x_xss_protection.rb +17 -48
- data/lib/secure_headers/middleware.rb +15 -0
- data/lib/secure_headers/padrino.rb +1 -2
- data/lib/secure_headers/railtie.rb +9 -6
- data/lib/secure_headers/view_helper.rb +27 -43
- data/lib/secure_headers.rb +254 -61
- data/secure_headers.gemspec +7 -12
- data/spec/lib/secure_headers/configuration_spec.rb +80 -0
- data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +111 -276
- data/spec/lib/secure_headers/headers/public_key_pins_spec.rb +17 -17
- data/spec/lib/secure_headers/headers/strict_transport_security_spec.rb +11 -43
- data/spec/lib/secure_headers/headers/x_content_type_options_spec.rb +11 -18
- data/spec/lib/secure_headers/headers/x_download_options_spec.rb +13 -17
- data/spec/lib/secure_headers/headers/x_frame_options_spec.rb +15 -17
- data/spec/lib/secure_headers/headers/x_permitted_cross_domain_policies_spec.rb +22 -39
- data/spec/lib/secure_headers/headers/x_xss_protection_spec.rb +20 -30
- data/spec/lib/secure_headers/middleware_spec.rb +40 -0
- data/spec/lib/secure_headers_spec.rb +201 -339
- data/spec/spec_helper.rb +30 -30
- data/upgrading-to-3-0.md +35 -0
- metadata +14 -100
- data/fixtures/rails_3_2_22/.rspec +0 -1
- data/fixtures/rails_3_2_22/Gemfile +0 -6
- data/fixtures/rails_3_2_22/README.rdoc +0 -261
- data/fixtures/rails_3_2_22/Rakefile +0 -7
- data/fixtures/rails_3_2_22/app/controllers/application_controller.rb +0 -4
- data/fixtures/rails_3_2_22/app/controllers/other_things_controller.rb +0 -5
- data/fixtures/rails_3_2_22/app/controllers/things_controller.rb +0 -5
- data/fixtures/rails_3_2_22/app/models/.gitkeep +0 -0
- data/fixtures/rails_3_2_22/app/views/layouts/application.html.erb +0 -11
- data/fixtures/rails_3_2_22/app/views/other_things/index.html.erb +0 -2
- data/fixtures/rails_3_2_22/app/views/things/index.html.erb +0 -1
- data/fixtures/rails_3_2_22/config/application.rb +0 -14
- data/fixtures/rails_3_2_22/config/boot.rb +0 -6
- data/fixtures/rails_3_2_22/config/environment.rb +0 -5
- data/fixtures/rails_3_2_22/config/environments/test.rb +0 -37
- data/fixtures/rails_3_2_22/config/initializers/secure_headers.rb +0 -16
- data/fixtures/rails_3_2_22/config/routes.rb +0 -4
- data/fixtures/rails_3_2_22/config/script_hashes.yml +0 -5
- data/fixtures/rails_3_2_22/config.ru +0 -7
- data/fixtures/rails_3_2_22/lib/assets/.gitkeep +0 -0
- data/fixtures/rails_3_2_22/lib/tasks/.gitkeep +0 -0
- data/fixtures/rails_3_2_22/log/.gitkeep +0 -0
- data/fixtures/rails_3_2_22/spec/controllers/other_things_controller_spec.rb +0 -83
- data/fixtures/rails_3_2_22/spec/controllers/things_controller_spec.rb +0 -54
- data/fixtures/rails_3_2_22/spec/spec_helper.rb +0 -15
- data/fixtures/rails_3_2_22/vendor/assets/javascripts/.gitkeep +0 -0
- data/fixtures/rails_3_2_22/vendor/assets/stylesheets/.gitkeep +0 -0
- data/fixtures/rails_3_2_22/vendor/plugins/.gitkeep +0 -0
- data/fixtures/rails_3_2_22_no_init/.rspec +0 -1
- data/fixtures/rails_3_2_22_no_init/Gemfile +0 -6
- data/fixtures/rails_3_2_22_no_init/README.rdoc +0 -261
- data/fixtures/rails_3_2_22_no_init/Rakefile +0 -7
- data/fixtures/rails_3_2_22_no_init/app/controllers/application_controller.rb +0 -4
- data/fixtures/rails_3_2_22_no_init/app/controllers/other_things_controller.rb +0 -20
- data/fixtures/rails_3_2_22_no_init/app/controllers/things_controller.rb +0 -5
- data/fixtures/rails_3_2_22_no_init/app/models/.gitkeep +0 -0
- data/fixtures/rails_3_2_22_no_init/app/views/layouts/application.html.erb +0 -12
- data/fixtures/rails_3_2_22_no_init/app/views/other_things/index.html.erb +0 -1
- data/fixtures/rails_3_2_22_no_init/app/views/things/index.html.erb +0 -0
- data/fixtures/rails_3_2_22_no_init/config/application.rb +0 -17
- data/fixtures/rails_3_2_22_no_init/config/boot.rb +0 -6
- data/fixtures/rails_3_2_22_no_init/config/environment.rb +0 -5
- data/fixtures/rails_3_2_22_no_init/config/environments/test.rb +0 -37
- data/fixtures/rails_3_2_22_no_init/config/routes.rb +0 -4
- data/fixtures/rails_3_2_22_no_init/config.ru +0 -4
- data/fixtures/rails_3_2_22_no_init/lib/assets/.gitkeep +0 -0
- data/fixtures/rails_3_2_22_no_init/lib/tasks/.gitkeep +0 -0
- data/fixtures/rails_3_2_22_no_init/log/.gitkeep +0 -0
- data/fixtures/rails_3_2_22_no_init/spec/controllers/other_things_controller_spec.rb +0 -56
- data/fixtures/rails_3_2_22_no_init/spec/controllers/things_controller_spec.rb +0 -54
- data/fixtures/rails_3_2_22_no_init/spec/spec_helper.rb +0 -5
- data/fixtures/rails_3_2_22_no_init/vendor/assets/javascripts/.gitkeep +0 -0
- data/fixtures/rails_3_2_22_no_init/vendor/assets/stylesheets/.gitkeep +0 -0
- data/fixtures/rails_3_2_22_no_init/vendor/plugins/.gitkeep +0 -0
- data/fixtures/rails_4_1_8/Gemfile +0 -5
- data/fixtures/rails_4_1_8/README.rdoc +0 -28
- data/fixtures/rails_4_1_8/Rakefile +0 -6
- data/fixtures/rails_4_1_8/app/controllers/application_controller.rb +0 -4
- data/fixtures/rails_4_1_8/app/controllers/concerns/.keep +0 -0
- data/fixtures/rails_4_1_8/app/controllers/other_things_controller.rb +0 -5
- data/fixtures/rails_4_1_8/app/controllers/things_controller.rb +0 -5
- data/fixtures/rails_4_1_8/app/models/.keep +0 -0
- data/fixtures/rails_4_1_8/app/models/concerns/.keep +0 -0
- data/fixtures/rails_4_1_8/app/views/layouts/application.html.erb +0 -11
- data/fixtures/rails_4_1_8/app/views/other_things/index.html.erb +0 -2
- data/fixtures/rails_4_1_8/app/views/things/index.html.erb +0 -1
- data/fixtures/rails_4_1_8/config/application.rb +0 -15
- data/fixtures/rails_4_1_8/config/boot.rb +0 -4
- data/fixtures/rails_4_1_8/config/environment.rb +0 -5
- data/fixtures/rails_4_1_8/config/environments/test.rb +0 -10
- data/fixtures/rails_4_1_8/config/initializers/secure_headers.rb +0 -16
- data/fixtures/rails_4_1_8/config/routes.rb +0 -4
- data/fixtures/rails_4_1_8/config/script_hashes.yml +0 -5
- data/fixtures/rails_4_1_8/config/secrets.yml +0 -22
- data/fixtures/rails_4_1_8/config.ru +0 -4
- data/fixtures/rails_4_1_8/lib/assets/.keep +0 -0
- data/fixtures/rails_4_1_8/lib/tasks/.keep +0 -0
- data/fixtures/rails_4_1_8/log/.keep +0 -0
- data/fixtures/rails_4_1_8/spec/controllers/other_things_controller_spec.rb +0 -83
- data/fixtures/rails_4_1_8/spec/controllers/things_controller_spec.rb +0 -59
- data/fixtures/rails_4_1_8/spec/spec_helper.rb +0 -15
- data/fixtures/rails_4_1_8/vendor/assets/javascripts/.keep +0 -0
- data/fixtures/rails_4_1_8/vendor/assets/stylesheets/.keep +0 -0
- data/lib/secure_headers/controller_extension.rb +0 -158
- data/lib/secure_headers/hash_helper.rb +0 -7
- data/lib/secure_headers/header.rb +0 -5
- data/lib/secure_headers/headers/content_security_policy/script_hash_middleware.rb +0 -22
- data/lib/secure_headers/version.rb +0 -3
- data/lib/tasks/tasks.rake +0 -48
- 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
|
-
|
|
10
|
+
t.rspec_opts = "--format progress"
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
task :
|
|
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
|