secure_headers 6.3.4 → 7.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -2
- data/Gemfile +2 -0
- data/README.md +81 -15
- data/lib/secure_headers/configuration.rb +64 -13
- data/lib/secure_headers/headers/clear_site_data.rb +31 -33
- data/lib/secure_headers/headers/content_security_policy.rb +80 -46
- data/lib/secure_headers/headers/content_security_policy_config.rb +17 -58
- data/lib/secure_headers/headers/cookie.rb +4 -6
- data/lib/secure_headers/headers/expect_certificate_transparency.rb +20 -22
- data/lib/secure_headers/headers/policy_management.rb +66 -18
- data/lib/secure_headers/headers/referrer_policy.rb +20 -22
- data/lib/secure_headers/headers/reporting_endpoints.rb +54 -0
- data/lib/secure_headers/headers/strict_transport_security.rb +13 -15
- data/lib/secure_headers/headers/x_content_type_options.rb +14 -16
- data/lib/secure_headers/headers/x_download_options.rb +14 -16
- data/lib/secure_headers/headers/x_frame_options.rb +14 -16
- data/lib/secure_headers/headers/x_permitted_cross_domain_policies.rb +14 -16
- data/lib/secure_headers/headers/x_xss_protection.rb +14 -16
- data/lib/secure_headers/middleware.rb +11 -7
- data/lib/secure_headers/railtie.rb +11 -8
- data/lib/secure_headers/task_helper.rb +65 -0
- data/lib/secure_headers/version.rb +1 -1
- data/lib/secure_headers/view_helper.rb +7 -6
- data/lib/secure_headers.rb +26 -2
- data/lib/tasks/tasks.rake +4 -53
- data/secure_headers.gemspec +19 -7
- metadata +38 -70
- data/.github/ISSUE_TEMPLATE.md +0 -41
- data/.github/PULL_REQUEST_TEMPLATE.md +0 -20
- data/.github/workflows/build.yml +0 -24
- data/.github/workflows/github-release.yml +0 -28
- data/.gitignore +0 -13
- data/.rspec +0 -3
- data/.rubocop.yml +0 -4
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/CODE_OF_CONDUCT.md +0 -46
- data/CONTRIBUTING.md +0 -41
- data/Guardfile +0 -13
- data/Rakefile +0 -32
- data/docs/cookies.md +0 -65
- data/docs/hashes.md +0 -64
- data/docs/named_overrides_and_appends.md +0 -104
- data/docs/per_action_configuration.md +0 -139
- data/docs/sinatra.md +0 -25
- data/docs/upgrading-to-3-0.md +0 -42
- data/docs/upgrading-to-4-0.md +0 -35
- data/docs/upgrading-to-5-0.md +0 -15
- data/docs/upgrading-to-6-0.md +0 -50
- data/spec/lib/secure_headers/configuration_spec.rb +0 -121
- data/spec/lib/secure_headers/headers/clear_site_data_spec.rb +0 -87
- data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +0 -190
- data/spec/lib/secure_headers/headers/cookie_spec.rb +0 -179
- data/spec/lib/secure_headers/headers/expect_certificate_transparency_spec.rb +0 -42
- data/spec/lib/secure_headers/headers/policy_management_spec.rb +0 -264
- data/spec/lib/secure_headers/headers/referrer_policy_spec.rb +0 -91
- data/spec/lib/secure_headers/headers/strict_transport_security_spec.rb +0 -33
- data/spec/lib/secure_headers/headers/x_content_type_options_spec.rb +0 -31
- data/spec/lib/secure_headers/headers/x_download_options_spec.rb +0 -29
- data/spec/lib/secure_headers/headers/x_frame_options_spec.rb +0 -36
- data/spec/lib/secure_headers/headers/x_permitted_cross_domain_policies_spec.rb +0 -48
- data/spec/lib/secure_headers/headers/x_xss_protection_spec.rb +0 -47
- data/spec/lib/secure_headers/middleware_spec.rb +0 -117
- data/spec/lib/secure_headers/view_helpers_spec.rb +0 -191
- data/spec/lib/secure_headers_spec.rb +0 -516
- data/spec/spec_helper.rb +0 -64
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
module SecureHeaders
|
|
3
3
|
class XFOConfigError < StandardError; end
|
|
4
4
|
class XFrameOptions
|
|
5
|
-
HEADER_NAME = "
|
|
5
|
+
HEADER_NAME = "x-frame-options".freeze
|
|
6
6
|
SAMEORIGIN = "sameorigin"
|
|
7
7
|
DENY = "deny"
|
|
8
8
|
ALLOW_FROM = "allow-from"
|
|
@@ -10,22 +10,20 @@ module SecureHeaders
|
|
|
10
10
|
DEFAULT_VALUE = SAMEORIGIN
|
|
11
11
|
VALID_XFO_HEADER = /\A(#{SAMEORIGIN}\z|#{DENY}\z|#{ALLOW_ALL}\z|#{ALLOW_FROM}[:\s])/i
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
end
|
|
13
|
+
# Public: generate an X-Frame-Options header.
|
|
14
|
+
#
|
|
15
|
+
# Returns a default header if no configuration is provided, or a
|
|
16
|
+
# header name and value based on the config.
|
|
17
|
+
def self.make_header(config = nil, user_agent = nil)
|
|
18
|
+
return if config == OPT_OUT
|
|
19
|
+
[HEADER_NAME, config || DEFAULT_VALUE]
|
|
20
|
+
end
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
end
|
|
22
|
+
def self.validate_config!(config)
|
|
23
|
+
return if config.nil? || config == OPT_OUT
|
|
24
|
+
raise TypeError.new("Must be a string. Found #{config.class}: #{config}") unless config.is_a?(String)
|
|
25
|
+
unless config =~ VALID_XFO_HEADER
|
|
26
|
+
raise XFOConfigError.new("Value must be SAMEORIGIN|DENY|ALLOW-FROM:|ALLOWALL")
|
|
29
27
|
end
|
|
30
28
|
end
|
|
31
29
|
end
|
|
@@ -2,26 +2,24 @@
|
|
|
2
2
|
module SecureHeaders
|
|
3
3
|
class XPCDPConfigError < StandardError; end
|
|
4
4
|
class XPermittedCrossDomainPolicies
|
|
5
|
-
HEADER_NAME = "
|
|
5
|
+
HEADER_NAME = "x-permitted-cross-domain-policies".freeze
|
|
6
6
|
DEFAULT_VALUE = "none"
|
|
7
7
|
VALID_POLICIES = %w(all none master-only by-content-type by-ftp-filename)
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
end
|
|
9
|
+
# Public: generate an x-permitted-cross-domain-policies header.
|
|
10
|
+
#
|
|
11
|
+
# Returns a default header if no configuration is provided, or a
|
|
12
|
+
# header name and value based on the config.
|
|
13
|
+
def self.make_header(config = nil, user_agent = nil)
|
|
14
|
+
return if config == OPT_OUT
|
|
15
|
+
[HEADER_NAME, config || DEFAULT_VALUE]
|
|
16
|
+
end
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
end
|
|
18
|
+
def self.validate_config!(config)
|
|
19
|
+
return if config.nil? || config == OPT_OUT
|
|
20
|
+
raise TypeError.new("Must be a string. Found #{config.class}: #{config}") unless config.is_a?(String)
|
|
21
|
+
unless VALID_POLICIES.include?(config.downcase)
|
|
22
|
+
raise XPCDPConfigError.new("Value can only be one of #{VALID_POLICIES.join(', ')}")
|
|
25
23
|
end
|
|
26
24
|
end
|
|
27
25
|
end
|
|
@@ -2,25 +2,23 @@
|
|
|
2
2
|
module SecureHeaders
|
|
3
3
|
class XXssProtectionConfigError < StandardError; end
|
|
4
4
|
class XXssProtection
|
|
5
|
-
HEADER_NAME = "
|
|
6
|
-
DEFAULT_VALUE = "
|
|
5
|
+
HEADER_NAME = "x-xss-protection".freeze
|
|
6
|
+
DEFAULT_VALUE = "0".freeze
|
|
7
7
|
VALID_X_XSS_HEADER = /\A[01](; mode=block)?(; report=.*)?\z/
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
end
|
|
9
|
+
# Public: generate an X-Xss-Protection header.
|
|
10
|
+
#
|
|
11
|
+
# Returns a default header if no configuration is provided, or a
|
|
12
|
+
# header name and value based on the config.
|
|
13
|
+
def self.make_header(config = nil, user_agent = nil)
|
|
14
|
+
return if config == OPT_OUT
|
|
15
|
+
[HEADER_NAME, config || DEFAULT_VALUE]
|
|
16
|
+
end
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
end
|
|
18
|
+
def self.validate_config!(config)
|
|
19
|
+
return if config.nil? || config == OPT_OUT
|
|
20
|
+
raise TypeError.new("Must be a string. Found #{config.class}: #{config}") unless config.is_a?(String)
|
|
21
|
+
raise XXssProtectionConfigError.new("Invalid format (see VALID_X_XSS_HEADER)") unless config.to_s =~ VALID_X_XSS_HEADER
|
|
24
22
|
end
|
|
25
23
|
end
|
|
26
24
|
end
|
|
@@ -10,6 +10,12 @@ module SecureHeaders
|
|
|
10
10
|
req = Rack::Request.new(env)
|
|
11
11
|
status, headers, response = @app.call(env)
|
|
12
12
|
|
|
13
|
+
# Rack::Headers is available in Rack 3.x and later
|
|
14
|
+
# So we should pull the headers into that structure if possible
|
|
15
|
+
if defined?(Rack::Headers)
|
|
16
|
+
headers = Rack::Headers[headers]
|
|
17
|
+
end
|
|
18
|
+
|
|
13
19
|
config = SecureHeaders.config_for(req)
|
|
14
20
|
flag_cookies!(headers, override_secure(env, config.cookies)) unless config.cookies == OPT_OUT
|
|
15
21
|
headers.merge!(SecureHeaders.header_hash_for(req))
|
|
@@ -20,14 +26,12 @@ module SecureHeaders
|
|
|
20
26
|
|
|
21
27
|
# inspired by https://github.com/tobmatth/rack-ssl-enforcer/blob/6c014/lib/rack/ssl-enforcer.rb#L183-L194
|
|
22
28
|
def flag_cookies!(headers, config)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
cookies = cookies.split("\n") unless cookies.is_a?(Array)
|
|
29
|
+
cookies = headers["Set-Cookie"]
|
|
30
|
+
return unless cookies
|
|
26
31
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
end
|
|
32
|
+
cookies_array = cookies.is_a?(Array) ? cookies : cookies.split("\n")
|
|
33
|
+
secured_cookies = cookies_array.map { |cookie| SecureHeaders::Cookie.new(cookie, config).to_s }
|
|
34
|
+
headers["Set-Cookie"] = cookies.is_a?(Array) ? secured_cookies : secured_cookies.join("\n")
|
|
31
35
|
end
|
|
32
36
|
|
|
33
37
|
# disable Secure cookies for non-https requests
|
|
@@ -4,11 +4,11 @@ if defined?(Rails::Railtie)
|
|
|
4
4
|
module SecureHeaders
|
|
5
5
|
class Railtie < Rails::Railtie
|
|
6
6
|
isolate_namespace SecureHeaders if defined? isolate_namespace # rails 3.0
|
|
7
|
-
conflicting_headers = ["
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
7
|
+
conflicting_headers = ["x-frame-options", "x-xss-protection",
|
|
8
|
+
"x-permitted-cross-domain-policies", "x-download-options",
|
|
9
|
+
"x-content-type-options", "strict-transport-security",
|
|
10
|
+
"content-security-policy", "content-security-policy-report-only",
|
|
11
|
+
"public-key-pins", "public-key-pins-report-only", "referrer-policy"]
|
|
12
12
|
|
|
13
13
|
initializer "secure_headers.middleware" do
|
|
14
14
|
Rails.application.config.middleware.insert_before 0, SecureHeaders::Middleware
|
|
@@ -22,9 +22,12 @@ if defined?(Rails::Railtie)
|
|
|
22
22
|
ActiveSupport.on_load(:action_controller) do
|
|
23
23
|
include SecureHeaders
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
default_headers = Rails.application.config.action_dispatch.default_headers
|
|
26
|
+
unless default_headers.nil?
|
|
27
|
+
default_headers.each_key do |header|
|
|
28
|
+
if conflicting_headers.include?(header.downcase)
|
|
29
|
+
default_headers.delete(header)
|
|
30
|
+
end
|
|
28
31
|
end
|
|
29
32
|
end
|
|
30
33
|
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SecureHeaders
|
|
4
|
+
module TaskHelper
|
|
5
|
+
include SecureHeaders::HashHelper
|
|
6
|
+
|
|
7
|
+
INLINE_SCRIPT_REGEX = /(<script(\s*(?!src)([\w\-])+=([\"\'])[^\"\']+\4)*\s*>)(.*?)<\/script>/mx
|
|
8
|
+
INLINE_STYLE_REGEX = /(<style[^>]*>)(.*?)<\/style>/mx
|
|
9
|
+
INLINE_HASH_SCRIPT_HELPER_REGEX = /<%=\s?hashed_javascript_tag(.*?)\s+do\s?%>(.*?)<%\s*end\s*%>/mx
|
|
10
|
+
INLINE_HASH_STYLE_HELPER_REGEX = /<%=\s?hashed_style_tag(.*?)\s+do\s?%>(.*?)<%\s*end\s*%>/mx
|
|
11
|
+
|
|
12
|
+
def generate_inline_script_hashes(filename)
|
|
13
|
+
hashes = []
|
|
14
|
+
|
|
15
|
+
hashes.concat find_inline_content(filename, INLINE_SCRIPT_REGEX, false)
|
|
16
|
+
hashes.concat find_inline_content(filename, INLINE_HASH_SCRIPT_HELPER_REGEX, true)
|
|
17
|
+
|
|
18
|
+
hashes
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def generate_inline_style_hashes(filename)
|
|
22
|
+
hashes = []
|
|
23
|
+
|
|
24
|
+
hashes.concat find_inline_content(filename, INLINE_STYLE_REGEX, false)
|
|
25
|
+
hashes.concat find_inline_content(filename, INLINE_HASH_STYLE_HELPER_REGEX, true)
|
|
26
|
+
|
|
27
|
+
hashes
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def dynamic_content?(filename, inline_script)
|
|
31
|
+
!!(
|
|
32
|
+
(is_mustache?(filename) && inline_script =~ /\{\{.*\}\}/) ||
|
|
33
|
+
(is_erb?(filename) && inline_script =~ /<%.*%>/)
|
|
34
|
+
)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def find_inline_content(filename, regex, strip_trailing_whitespace)
|
|
40
|
+
hashes = []
|
|
41
|
+
file = File.read(filename)
|
|
42
|
+
file.scan(regex) do # TODO don't use gsub
|
|
43
|
+
inline_script = Regexp.last_match.captures.last
|
|
44
|
+
inline_script.gsub!(/(\r?\n)[\t ]+\z/, '\1') if strip_trailing_whitespace
|
|
45
|
+
if dynamic_content?(filename, inline_script)
|
|
46
|
+
puts "Looks like there's some dynamic content inside of a tag :-/"
|
|
47
|
+
puts "That pretty much means the hash value will never match."
|
|
48
|
+
puts "Code: " + inline_script
|
|
49
|
+
puts "=" * 20
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
hashes << hash_source(inline_script)
|
|
53
|
+
end
|
|
54
|
+
hashes
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def is_erb?(filename)
|
|
58
|
+
filename =~ /\.erb\Z/
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def is_mustache?(filename)
|
|
62
|
+
filename =~ /\.mustache\Z/
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -147,12 +147,13 @@ module SecureHeaders
|
|
|
147
147
|
|
|
148
148
|
def nonced_tag(type, content_or_options, block)
|
|
149
149
|
options = {}
|
|
150
|
-
content =
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
150
|
+
content =
|
|
151
|
+
if block
|
|
152
|
+
options = content_or_options
|
|
153
|
+
capture(&block)
|
|
154
|
+
else
|
|
155
|
+
content_or_options.html_safe # :'(
|
|
156
|
+
end
|
|
156
157
|
content_tag type, content, options.merge(nonce: _content_security_policy_nonce(type))
|
|
157
158
|
end
|
|
158
159
|
|
data/lib/secure_headers.rb
CHANGED
|
@@ -11,6 +11,7 @@ require "secure_headers/headers/x_permitted_cross_domain_policies"
|
|
|
11
11
|
require "secure_headers/headers/referrer_policy"
|
|
12
12
|
require "secure_headers/headers/clear_site_data"
|
|
13
13
|
require "secure_headers/headers/expect_certificate_transparency"
|
|
14
|
+
require "secure_headers/headers/reporting_endpoints"
|
|
14
15
|
require "secure_headers/middleware"
|
|
15
16
|
require "secure_headers/railtie"
|
|
16
17
|
require "secure_headers/view_helper"
|
|
@@ -133,6 +134,7 @@ module SecureHeaders
|
|
|
133
134
|
# request.
|
|
134
135
|
#
|
|
135
136
|
# StrictTransportSecurity is not applied to http requests.
|
|
137
|
+
# upgrade_insecure_requests is not applied to http requests.
|
|
136
138
|
# See #config_for to determine which config is used for a given request.
|
|
137
139
|
#
|
|
138
140
|
# Returns a hash of header names => header values. The value
|
|
@@ -146,6 +148,11 @@ module SecureHeaders
|
|
|
146
148
|
|
|
147
149
|
if request.scheme != HTTPS
|
|
148
150
|
headers.delete(StrictTransportSecurity::HEADER_NAME)
|
|
151
|
+
|
|
152
|
+
# Remove upgrade_insecure_requests from CSP headers for HTTP requests
|
|
153
|
+
# as it doesn't make sense to upgrade requests when the page itself is served over HTTP
|
|
154
|
+
remove_upgrade_insecure_requests_from_csp!(headers, config.csp)
|
|
155
|
+
remove_upgrade_insecure_requests_from_csp!(headers, config.csp_report_only)
|
|
149
156
|
end
|
|
150
157
|
headers
|
|
151
158
|
end
|
|
@@ -178,7 +185,7 @@ module SecureHeaders
|
|
|
178
185
|
content_security_policy_nonce(request, ContentSecurityPolicy::STYLE_SRC)
|
|
179
186
|
end
|
|
180
187
|
|
|
181
|
-
# Public:
|
|
188
|
+
# Public: Retrieves the config for a given header type:
|
|
182
189
|
#
|
|
183
190
|
# Checks to see if there is an override for this request, then
|
|
184
191
|
# Checks to see if a named override is used for this request, then
|
|
@@ -208,7 +215,7 @@ module SecureHeaders
|
|
|
208
215
|
|
|
209
216
|
def config_and_target(request, target)
|
|
210
217
|
config = config_for(request)
|
|
211
|
-
target
|
|
218
|
+
target ||= guess_target(config)
|
|
212
219
|
raise_on_unknown_target(target)
|
|
213
220
|
[config, target]
|
|
214
221
|
end
|
|
@@ -242,6 +249,23 @@ module SecureHeaders
|
|
|
242
249
|
def override_secure_headers_request_config(request, config)
|
|
243
250
|
request.env[SECURE_HEADERS_CONFIG] = config
|
|
244
251
|
end
|
|
252
|
+
|
|
253
|
+
# Private: removes upgrade_insecure_requests directive from a CSP config
|
|
254
|
+
# if it's present, and updates the headers hash with the modified CSP.
|
|
255
|
+
#
|
|
256
|
+
# headers - the headers hash to update
|
|
257
|
+
# csp_config - the CSP config to check and potentially modify
|
|
258
|
+
#
|
|
259
|
+
# Returns nothing (modifies headers in place)
|
|
260
|
+
def remove_upgrade_insecure_requests_from_csp!(headers, csp_config)
|
|
261
|
+
return if csp_config.opt_out?
|
|
262
|
+
return unless csp_config.directive_value(ContentSecurityPolicy::UPGRADE_INSECURE_REQUESTS)
|
|
263
|
+
|
|
264
|
+
modified_config = csp_config.dup
|
|
265
|
+
modified_config.update_directive(ContentSecurityPolicy::UPGRADE_INSECURE_REQUESTS, false)
|
|
266
|
+
header_name, value = ContentSecurityPolicy.make_header(modified_config)
|
|
267
|
+
headers[header_name] = value if header_name && value
|
|
268
|
+
end
|
|
245
269
|
end
|
|
246
270
|
|
|
247
271
|
# These methods are mixed into controllers and delegate to the class method
|
data/lib/tasks/tasks.rake
CHANGED
|
@@ -1,58 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
INLINE_STYLE_REGEX = /(<style[^>]*>)(.*?)<\/style>/mx unless defined? INLINE_STYLE_REGEX
|
|
4
|
-
INLINE_HASH_SCRIPT_HELPER_REGEX = /<%=\s?hashed_javascript_tag(.*?)\s+do\s?%>(.*?)<%\s*end\s*%>/mx unless defined? INLINE_HASH_SCRIPT_HELPER_REGEX
|
|
5
|
-
INLINE_HASH_STYLE_HELPER_REGEX = /<%=\s?hashed_style_tag(.*?)\s+do\s?%>(.*?)<%\s*end\s*%>/mx unless defined? INLINE_HASH_STYLE_HELPER_REGEX
|
|
2
|
+
require "secure_headers/task_helper"
|
|
6
3
|
|
|
7
4
|
namespace :secure_headers do
|
|
8
|
-
include SecureHeaders::
|
|
9
|
-
|
|
10
|
-
def is_erb?(filename)
|
|
11
|
-
filename =~ /\.erb\Z/
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def is_mustache?(filename)
|
|
15
|
-
filename =~ /\.mustache\Z/
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def dynamic_content?(filename, inline_script)
|
|
19
|
-
(is_mustache?(filename) && inline_script =~ /\{\{.*\}\}/) ||
|
|
20
|
-
(is_erb?(filename) && inline_script =~ /<%.*%>/)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def find_inline_content(filename, regex, hashes, strip_trailing_whitespace)
|
|
24
|
-
file = File.read(filename)
|
|
25
|
-
file.scan(regex) do # TODO don't use gsub
|
|
26
|
-
inline_script = Regexp.last_match.captures.last
|
|
27
|
-
inline_script.gsub!(/(\r?\n)[\t ]+\z/, '\1') if strip_trailing_whitespace
|
|
28
|
-
if dynamic_content?(filename, inline_script)
|
|
29
|
-
puts "Looks like there's some dynamic content inside of a tag :-/"
|
|
30
|
-
puts "That pretty much means the hash value will never match."
|
|
31
|
-
puts "Code: " + inline_script
|
|
32
|
-
puts "=" * 20
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
hashes << hash_source(inline_script)
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def generate_inline_script_hashes(filename)
|
|
40
|
-
hashes = []
|
|
41
|
-
|
|
42
|
-
find_inline_content(filename, INLINE_SCRIPT_REGEX, hashes, false)
|
|
43
|
-
find_inline_content(filename, INLINE_HASH_SCRIPT_HELPER_REGEX, hashes, true)
|
|
44
|
-
|
|
45
|
-
hashes
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def generate_inline_style_hashes(filename)
|
|
49
|
-
hashes = []
|
|
50
|
-
|
|
51
|
-
find_inline_content(filename, INLINE_STYLE_REGEX, hashes, false)
|
|
52
|
-
find_inline_content(filename, INLINE_HASH_STYLE_HELPER_REGEX, hashes, true)
|
|
53
|
-
|
|
54
|
-
hashes
|
|
55
|
-
end
|
|
5
|
+
include SecureHeaders::TaskHelper
|
|
56
6
|
|
|
57
7
|
desc "Generate #{SecureHeaders::Configuration::HASH_CONFIG_FILE}"
|
|
58
8
|
task :generate_hashes do |t, args|
|
|
@@ -77,6 +27,7 @@ namespace :secure_headers do
|
|
|
77
27
|
file.write(script_hashes.to_yaml)
|
|
78
28
|
end
|
|
79
29
|
|
|
80
|
-
|
|
30
|
+
file_count = (script_hashes["scripts"].keys + script_hashes["styles"].keys).uniq.count
|
|
31
|
+
puts "Script and style hashes from #{file_count} files added to #{SecureHeaders::Configuration::HASH_CONFIG_FILE}"
|
|
81
32
|
end
|
|
82
33
|
end
|
data/secure_headers.gemspec
CHANGED
|
@@ -9,15 +9,27 @@ Gem::Specification.new do |gem|
|
|
|
9
9
|
gem.version = SecureHeaders::VERSION
|
|
10
10
|
gem.authors = ["Neil Matatall"]
|
|
11
11
|
gem.email = ["neil.matatall@gmail.com"]
|
|
12
|
-
gem.
|
|
13
|
-
gem.
|
|
12
|
+
gem.summary = "Manages application of security headers with many safe defaults."
|
|
13
|
+
gem.description = 'Add easily configured security headers to responses
|
|
14
14
|
including content-security-policy, x-frame-options,
|
|
15
15
|
strict-transport-security, etc.'
|
|
16
|
-
gem.homepage = "https://github.com/
|
|
17
|
-
gem.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
gem.homepage = "https://github.com/github/secure_headers"
|
|
17
|
+
gem.metadata = {
|
|
18
|
+
"bug_tracker_uri" => "https://github.com/github/secure_headers/issues",
|
|
19
|
+
"changelog_uri" => "https://github.com/github/secure_headers/blob/master/CHANGELOG.md",
|
|
20
|
+
"documentation_uri" => "https://rubydoc.info/gems/secure_headers",
|
|
21
|
+
"homepage_uri" => gem.homepage,
|
|
22
|
+
"source_code_uri" => "https://github.com/github/secure_headers",
|
|
23
|
+
"rubygems_mfa_required" => "true",
|
|
24
|
+
}
|
|
25
|
+
gem.license = "MIT"
|
|
26
|
+
|
|
27
|
+
gem.files = Dir["bin/**/*", "lib/**/*", "README.md", "CHANGELOG.md", "LICENSE", "Gemfile", "secure_headers.gemspec"]
|
|
21
28
|
gem.require_paths = ["lib"]
|
|
29
|
+
|
|
30
|
+
gem.extra_rdoc_files = Dir["README.md", "CHANGELOG.md", "LICENSE"]
|
|
31
|
+
|
|
32
|
+
gem.add_dependency "cgi", ">= 0.1"
|
|
33
|
+
|
|
22
34
|
gem.add_development_dependency "rake"
|
|
23
35
|
end
|
metadata
CHANGED
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: secure_headers
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 7.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Neil Matatall
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: cgi
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0.1'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0.1'
|
|
13
26
|
- !ruby/object:Gem::Dependency
|
|
14
27
|
name: rake
|
|
15
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -24,39 +37,23 @@ dependencies:
|
|
|
24
37
|
- - ">="
|
|
25
38
|
- !ruby/object:Gem::Version
|
|
26
39
|
version: '0'
|
|
27
|
-
description:
|
|
40
|
+
description: |-
|
|
41
|
+
Add easily configured security headers to responses
|
|
42
|
+
including content-security-policy, x-frame-options,
|
|
43
|
+
strict-transport-security, etc.
|
|
28
44
|
email:
|
|
29
45
|
- neil.matatall@gmail.com
|
|
30
46
|
executables: []
|
|
31
47
|
extensions: []
|
|
32
|
-
extra_rdoc_files:
|
|
48
|
+
extra_rdoc_files:
|
|
49
|
+
- CHANGELOG.md
|
|
50
|
+
- LICENSE
|
|
51
|
+
- README.md
|
|
33
52
|
files:
|
|
34
|
-
- ".github/ISSUE_TEMPLATE.md"
|
|
35
|
-
- ".github/PULL_REQUEST_TEMPLATE.md"
|
|
36
|
-
- ".github/workflows/build.yml"
|
|
37
|
-
- ".github/workflows/github-release.yml"
|
|
38
|
-
- ".gitignore"
|
|
39
|
-
- ".rspec"
|
|
40
|
-
- ".rubocop.yml"
|
|
41
|
-
- ".ruby-gemset"
|
|
42
|
-
- ".ruby-version"
|
|
43
53
|
- CHANGELOG.md
|
|
44
|
-
- CODE_OF_CONDUCT.md
|
|
45
|
-
- CONTRIBUTING.md
|
|
46
54
|
- Gemfile
|
|
47
|
-
- Guardfile
|
|
48
55
|
- LICENSE
|
|
49
56
|
- README.md
|
|
50
|
-
- Rakefile
|
|
51
|
-
- docs/cookies.md
|
|
52
|
-
- docs/hashes.md
|
|
53
|
-
- docs/named_overrides_and_appends.md
|
|
54
|
-
- docs/per_action_configuration.md
|
|
55
|
-
- docs/sinatra.md
|
|
56
|
-
- docs/upgrading-to-3-0.md
|
|
57
|
-
- docs/upgrading-to-4-0.md
|
|
58
|
-
- docs/upgrading-to-5-0.md
|
|
59
|
-
- docs/upgrading-to-6-0.md
|
|
60
57
|
- lib/secure_headers.rb
|
|
61
58
|
- lib/secure_headers/configuration.rb
|
|
62
59
|
- lib/secure_headers/hash_helper.rb
|
|
@@ -67,6 +64,7 @@ files:
|
|
|
67
64
|
- lib/secure_headers/headers/expect_certificate_transparency.rb
|
|
68
65
|
- lib/secure_headers/headers/policy_management.rb
|
|
69
66
|
- lib/secure_headers/headers/referrer_policy.rb
|
|
67
|
+
- lib/secure_headers/headers/reporting_endpoints.rb
|
|
70
68
|
- lib/secure_headers/headers/strict_transport_security.rb
|
|
71
69
|
- lib/secure_headers/headers/x_content_type_options.rb
|
|
72
70
|
- lib/secure_headers/headers/x_download_options.rb
|
|
@@ -75,33 +73,22 @@ files:
|
|
|
75
73
|
- lib/secure_headers/headers/x_xss_protection.rb
|
|
76
74
|
- lib/secure_headers/middleware.rb
|
|
77
75
|
- lib/secure_headers/railtie.rb
|
|
76
|
+
- lib/secure_headers/task_helper.rb
|
|
78
77
|
- lib/secure_headers/utils/cookies_config.rb
|
|
79
78
|
- lib/secure_headers/version.rb
|
|
80
79
|
- lib/secure_headers/view_helper.rb
|
|
81
80
|
- lib/tasks/tasks.rake
|
|
82
81
|
- secure_headers.gemspec
|
|
83
|
-
|
|
84
|
-
- spec/lib/secure_headers/headers/clear_site_data_spec.rb
|
|
85
|
-
- spec/lib/secure_headers/headers/content_security_policy_spec.rb
|
|
86
|
-
- spec/lib/secure_headers/headers/cookie_spec.rb
|
|
87
|
-
- spec/lib/secure_headers/headers/expect_certificate_transparency_spec.rb
|
|
88
|
-
- spec/lib/secure_headers/headers/policy_management_spec.rb
|
|
89
|
-
- spec/lib/secure_headers/headers/referrer_policy_spec.rb
|
|
90
|
-
- spec/lib/secure_headers/headers/strict_transport_security_spec.rb
|
|
91
|
-
- spec/lib/secure_headers/headers/x_content_type_options_spec.rb
|
|
92
|
-
- spec/lib/secure_headers/headers/x_download_options_spec.rb
|
|
93
|
-
- spec/lib/secure_headers/headers/x_frame_options_spec.rb
|
|
94
|
-
- spec/lib/secure_headers/headers/x_permitted_cross_domain_policies_spec.rb
|
|
95
|
-
- spec/lib/secure_headers/headers/x_xss_protection_spec.rb
|
|
96
|
-
- spec/lib/secure_headers/middleware_spec.rb
|
|
97
|
-
- spec/lib/secure_headers/view_helpers_spec.rb
|
|
98
|
-
- spec/lib/secure_headers_spec.rb
|
|
99
|
-
- spec/spec_helper.rb
|
|
100
|
-
homepage: https://github.com/twitter/secureheaders
|
|
82
|
+
homepage: https://github.com/github/secure_headers
|
|
101
83
|
licenses:
|
|
102
|
-
-
|
|
103
|
-
metadata:
|
|
104
|
-
|
|
84
|
+
- MIT
|
|
85
|
+
metadata:
|
|
86
|
+
bug_tracker_uri: https://github.com/github/secure_headers/issues
|
|
87
|
+
changelog_uri: https://github.com/github/secure_headers/blob/master/CHANGELOG.md
|
|
88
|
+
documentation_uri: https://rubydoc.info/gems/secure_headers
|
|
89
|
+
homepage_uri: https://github.com/github/secure_headers
|
|
90
|
+
source_code_uri: https://github.com/github/secure_headers
|
|
91
|
+
rubygems_mfa_required: 'true'
|
|
105
92
|
rdoc_options: []
|
|
106
93
|
require_paths:
|
|
107
94
|
- lib
|
|
@@ -116,26 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
116
103
|
- !ruby/object:Gem::Version
|
|
117
104
|
version: '0'
|
|
118
105
|
requirements: []
|
|
119
|
-
rubygems_version: 3.
|
|
120
|
-
signing_key:
|
|
106
|
+
rubygems_version: 3.6.9
|
|
121
107
|
specification_version: 4
|
|
122
|
-
summary:
|
|
123
|
-
|
|
124
|
-
test_files:
|
|
125
|
-
- spec/lib/secure_headers/configuration_spec.rb
|
|
126
|
-
- spec/lib/secure_headers/headers/clear_site_data_spec.rb
|
|
127
|
-
- spec/lib/secure_headers/headers/content_security_policy_spec.rb
|
|
128
|
-
- spec/lib/secure_headers/headers/cookie_spec.rb
|
|
129
|
-
- spec/lib/secure_headers/headers/expect_certificate_transparency_spec.rb
|
|
130
|
-
- spec/lib/secure_headers/headers/policy_management_spec.rb
|
|
131
|
-
- spec/lib/secure_headers/headers/referrer_policy_spec.rb
|
|
132
|
-
- spec/lib/secure_headers/headers/strict_transport_security_spec.rb
|
|
133
|
-
- spec/lib/secure_headers/headers/x_content_type_options_spec.rb
|
|
134
|
-
- spec/lib/secure_headers/headers/x_download_options_spec.rb
|
|
135
|
-
- spec/lib/secure_headers/headers/x_frame_options_spec.rb
|
|
136
|
-
- spec/lib/secure_headers/headers/x_permitted_cross_domain_policies_spec.rb
|
|
137
|
-
- spec/lib/secure_headers/headers/x_xss_protection_spec.rb
|
|
138
|
-
- spec/lib/secure_headers/middleware_spec.rb
|
|
139
|
-
- spec/lib/secure_headers/view_helpers_spec.rb
|
|
140
|
-
- spec/lib/secure_headers_spec.rb
|
|
141
|
-
- spec/spec_helper.rb
|
|
108
|
+
summary: Manages application of security headers with many safe defaults.
|
|
109
|
+
test_files: []
|