Skip to content

Commit 1afa7a5

Browse files
CopilotByron
andcommitted
fix: prevent dots from being URL-encoded in userinfo and add tests
Co-authored-by: Byron <63622+Byron@users.noreply.github.com>
1 parent f8435ea commit 1afa7a5

File tree

4 files changed

+68
-1
lines changed

4 files changed

+68
-1
lines changed

gix-url/src/lib.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,8 +328,37 @@ impl Url {
328328
}
329329
}
330330

331+
/// Characters that must be percent-encoded in the userinfo component of a URL.
332+
///
333+
/// According to RFC 3986, userinfo can contain:
334+
/// - unreserved characters: `A-Z a-z 0-9 - . _ ~`
335+
/// - percent-encoded characters
336+
/// - sub-delims: `! $ & ' ( ) * + , ; =`
337+
/// - `:`
338+
///
339+
/// This encode set encodes everything else, particularly `@` (userinfo delimiter),
340+
/// `/` `?` `#` (path/query/fragment delimiters), and various other special characters.
341+
const USERINFO_ENCODE_SET: &percent_encoding::AsciiSet = &percent_encoding::CONTROLS
342+
.add(b' ')
343+
.add(b'"')
344+
.add(b'#')
345+
.add(b'%')
346+
.add(b'/')
347+
.add(b'<')
348+
.add(b'>')
349+
.add(b'?')
350+
.add(b'@')
351+
.add(b'[')
352+
.add(b'\\')
353+
.add(b']')
354+
.add(b'^')
355+
.add(b'`')
356+
.add(b'{')
357+
.add(b'|')
358+
.add(b'}');
359+
331360
fn percent_encode(s: &str) -> Cow<'_, str> {
332-
percent_encoding::utf8_percent_encode(s, percent_encoding::NON_ALPHANUMERIC).into()
361+
percent_encoding::utf8_percent_encode(s, USERINFO_ENCODE_SET).into()
333362
}
334363

335364
/// Serialization

gix-url/tests/fixtures/make_baseline.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ done
5252
tests_windows+=("file://c:/repo")
5353
tests_windows+=("c:repo")
5454

55+
# Test URLs with dots in usernames (gitbutler issue #11419)
56+
# Dots should not be percent-encoded in userinfo
57+
tests+=("ssh://user.name@host/repo")
58+
tests+=("git://user.name@host/repo")
59+
tests+=("user.name@host:repo")
60+
5561
tests_unix+=("${tests[@]}")
5662
tests_windows+=("${tests[@]}")
5763

gix-url/tests/url/parse/http.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,27 @@ fn http_missing_path() -> crate::Result {
9494
assert_url("http://host.xz", url(Scheme::Http, None, "host.xz", None, b"/"))?;
9595
Ok(())
9696
}
97+
98+
#[test]
99+
fn username_with_dot_is_not_percent_encoded() -> crate::Result {
100+
assert_url_roundtrip(
101+
"http://user.name@example.com/repo",
102+
url(Scheme::Http, "user.name", "example.com", None, b"/repo"),
103+
)
104+
}
105+
106+
#[test]
107+
fn password_with_dot_is_not_percent_encoded() -> crate::Result {
108+
assert_url_roundtrip(
109+
"http://user:pass.word@example.com/repo",
110+
url_with_pass(Scheme::Http, "user", "pass.word", "example.com", None, b"/repo"),
111+
)
112+
}
113+
114+
#[test]
115+
fn username_and_password_with_dots_are_not_percent_encoded() -> crate::Result {
116+
assert_url_roundtrip(
117+
"http://user.name:pass.word@example.com/repo",
118+
url_with_pass(Scheme::Http, "user.name", "pass.word", "example.com", None, b"/repo"),
119+
)
120+
}

gix-url/tests/url/parse/ssh.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@ fn with_user_and_without_port() -> crate::Result {
5555
)
5656
}
5757

58+
#[test]
59+
fn username_with_dot_is_not_percent_encoded() -> crate::Result {
60+
assert_url_roundtrip(
61+
"ssh://user.name@host.xz/.git",
62+
url(Scheme::Ssh, "user.name", "host.xz", None, b"/.git"),
63+
)
64+
}
65+
5866
#[test]
5967
fn with_user_and_port_and_absolute_path() -> crate::Result {
6068
assert_url_roundtrip(

0 commit comments

Comments
 (0)