Skip to content

Commit 3823787

Browse files
committed
Endpoint() constructor: Roundtrip the URL through URIs.resolvereference(), and make sure it is unchanged
1 parent 814fa59 commit 3823787

2 files changed

Lines changed: 31 additions & 18 deletions

File tree

Project.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,20 @@ HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3"
99
JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1"
1010
StructTypes = "856f2bd8-1eba-4b0a-8007-ebc267875bd4"
1111
TimeZones = "f269a46b-ccf7-5d73-abea-4c690281aa53"
12+
URIs = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4"
1213
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
1314

1415
[compat]
1516
HTTP = "0.9, 1"
1617
JSON3 = "1"
1718
StructTypes = "1"
1819
TimeZones = "1"
20+
URIs = "1.6" # Must be >= 1.6, to get https://github.com/JuliaWeb/URIs.jl/pull/66
1921
julia = "1.6"
2022

2123
[extras]
22-
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
2324
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
25+
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
2426

2527
[targets]
2628
test = ["Test", "Logging"]

src/forge.jl

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,33 @@ function JSON3.write(ctx::FieldContext{FORGE, T}, buf, pos, len, x::T; kw...) wh
154154
return buf, pos, len
155155
end
156156

157+
function assert_check_endpoint_url(url::AbstractString)
158+
# Do not allow path navigation in URLs
159+
# Disallowed pattern: ..
160+
if occursin(r"\.\.", url)
161+
throw(ArgumentError("URLs cannot contain path navigation"))
162+
end
163+
164+
# Additional disallowed patterns:
165+
# ../, ..\, /.., \.., ./, .\, /./, \.\
166+
PATH_TRAVERSAL = r"(?:\.{2,}[\/\\]|\.{1,}[\/\\]|[\/\\]\.{2,}|[\/\\]\.{1,}[\/\\])"
167+
if occursin(PATH_TRAVERSAL, url)
168+
throw(ArgumentError("URLs cannot contain path navigation"))
169+
end
170+
171+
# do not allow new lines or carriage returns in URLs
172+
if occursin(r"\s", url)
173+
throw(ArgumentError("URLs cannot contain line breaks"))
174+
end
175+
176+
# Roundtrip the URL through `URIs.resolvereference()`, and make sure it is unchanged
177+
if url != URIs.resolvereference("https://example.invalid/", url).path
178+
throw(ArgumentError("URLs cannot contain path navigation"))
179+
end
180+
181+
return nothing
182+
end
183+
157184
"""
158185
Endpoint(
159186
method::Symbol,
@@ -187,23 +214,7 @@ struct Endpoint
187214
query::Dict=Dict(),
188215
allow_404::Bool=false,
189216
)
190-
# Do not allow path navigation in URLs
191-
# Disallowed pattern: ..
192-
if occursin(r"\.\.", url)
193-
throw(ArgumentError("URLs cannot contain path navigation"))
194-
end
195-
196-
# Additional disallowed patterns:
197-
# ../, ..\, /.., \.., ./, .\, /./, \.\
198-
PATH_TRAVERSAL = r"(?:\.{2,}[\/\\]|\.{1,}[\/\\]|[\/\\]\.{2,}|[\/\\]\.{1,}[\/\\])"
199-
if occursin(PATH_TRAVERSAL, url)
200-
throw(ArgumentError("URLs cannot contain path navigation"))
201-
end
202-
203-
# do not allow new lines or carriage returns in URLs
204-
if occursin(r"\s", url)
205-
throw(ArgumentError("URLs cannot contain line breaks"))
206-
end
217+
assert_check_endpoint_url(url)
207218
return new(method, url, headers, query, allow_404)
208219
end
209220
end

0 commit comments

Comments
 (0)