Skip to content

Commit 3334929

Browse files
keithamusdgreif
authored andcommitted
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. fix: allow URIs with schema to have trailing slashes normalised Co-authored-by: Dusty Greif <[email protected]>
1 parent 9bc0b6c commit 3334929

File tree

2 files changed

+30
-1
lines changed

2 files changed

+30
-1
lines changed

lib/secure_headers/headers/content_security_policy.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ def minify_source_list(directive, source_list)
129129
else
130130
source_list = populate_nonces(directive, source_list)
131131
source_list = reject_all_values_if_none(source_list)
132+
source_list = normalize_uri_paths(source_list)
132133

133134
unless directive == REPORT_URI || @preserve_schemes
134135
source_list = strip_source_schemes(source_list)
@@ -151,6 +152,26 @@ def reject_all_values_if_none(source_list)
151152
end
152153
end
153154

155+
def normalize_uri_paths(source_list)
156+
source_list.map do |source|
157+
# Normalize domains ending in a single / as without omitting the slash accomplishes the same.
158+
# https://www.w3.org/TR/CSP3/#match-paths § 6.6.2.10 Step 2
159+
begin
160+
uri = URI(source)
161+
if uri.path == "/"
162+
next source.chomp("/")
163+
end
164+
rescue URI::InvalidURIError
165+
end
166+
167+
if source.chomp("/").include?("/")
168+
source
169+
else
170+
source.chomp("/")
171+
end
172+
end
173+
end
174+
154175
# Private: append a nonce to the script/style directories if script_nonce
155176
# or style_nonce are provided.
156177
def populate_nonces(directive, source_list)

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/ wss://c.example.com/ c.example.net/foo/ b.example.co/bar wss://b.example.co/)
54+
}
55+
csp = ContentSecurityPolicy.new(config)
56+
expect(csp.value).to eq("default-src a.example.org b.example.com wss://c.example.com c.example.net/foo/ b.example.co/bar wss://b.example.co")
57+
end
58+
5159
it "does not minify 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 a.example.org b.example.org *.example.org")

0 commit comments

Comments
 (0)