Skip to content

Commit 0d44152

Browse files
committed
normalize domains with trailing slashes
CSP3 more explicitly calls this out: > If path A consists of one character that is equal to the U+002F > SOLIDUS character (/) and path B is empty, return "Matches". A URL like `example.com/foo` will match a connect-src of `example.com`, as well as `example.com/`, so having two connect-srcs listed like this is redundant.
1 parent 7f89df2 commit 0d44152

File tree

2 files changed

+23
-1
lines changed

2 files changed

+23
-1
lines changed

lib/secure_headers/headers/content_security_policy.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ def minify_source_list(directive, source_list)
125125
else
126126
source_list = populate_nonces(directive, source_list)
127127
source_list = reject_all_values_if_none(source_list)
128+
source_list = normalize_uri_paths(source_list)
128129

129130
unless directive == REPORT_URI || @preserve_schemes
130131
source_list = strip_source_schemes(source_list)
@@ -147,6 +148,19 @@ def reject_all_values_if_none(source_list)
147148
end
148149
end
149150

151+
def normalize_uri_paths(source_list)
152+
source_list.map do |source|
153+
# Normalize domains ending in a single / as without omitting the slash accomplisheg the same.
154+
# https://www.w3.org/TR/CSP3/#match-paths § 6.6.2.10 Step 2
155+
if source.chomp("/").include?("/")
156+
source
157+
else
158+
source.chomp("/")
159+
end
160+
end
161+
end
162+
163+
150164
# Removes duplicates and sources that already match an existing wild card.
151165
#
152166
# e.g. *.github.com asdf.github.com becomes *.github.com

spec/lib/secure_headers/headers/content_security_policy_spec.rb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,17 @@ module SecureHeaders
4848
expect(csp.value).to eq("default-src * 'unsafe-inline' 'unsafe-eval' data: blob:")
4949
end
5050

51+
it "normalizes source expressions that end with a trailing /" do
52+
config = {
53+
default_src: %w(a.example.org/ b.example.com/ c.example.net/foo/ b.example.co/bar)
54+
}
55+
csp = ContentSecurityPolicy.new(config)
56+
expect(csp.value).to eq("default-src a.example.org b.example.com c.example.net/foo/ b.example.co/bar")
57+
end
58+
5159
it "minifies source expressions based on overlapping wildcards" do
5260
config = {
53-
default_src: %w(a.example.org b.example.org *.example.org https://*.example.org)
61+
default_src: %w(a.example.org b.example.org *.example.org https://*.example.org c.example.org/)
5462
}
5563
csp = ContentSecurityPolicy.new(config)
5664
expect(csp.value).to eq("default-src *.example.org")

0 commit comments

Comments
 (0)