zed: Support IPv6 hosts in SCP-style SSH URLs (#62157)
# Objective - Support opening remote paths from SCP-style SSH URLs that use bracketed IPv6 hosts, such as `ssh://[2600::]:~/foo`. ## Solution - Validate SSH hosts with `url::Host::parse`, which supports bracketed IPv6 addresses while continuing to reject malformed or ambiguous hosts. - Add test coverage for IPv6 URLs with usernames, home-relative paths, absolute paths, and explicit ports. - Add rejection tests for unbracketed IPv6 addresses and ambiguous port-plus-SCP-path syntax. ## Testing - Ran `cargo fmt --all -- --check`. - Ran `cargo test -p zed test_parse_ssh_urls -- --nocapture`. - Ran `cargo test -p zed test_reject_ssh_urls -- --nocapture`. - All targeted tests passed on macOS. No additional platform-specific testing is expected to be necessary because the change only affects URL parsing. Release Notes: - Fixed opening remote paths from SCP-style SSH URLs with IPv6 hosts. Signed-off-by: Xiaobo Liu <cppcoffee@gmail.com>
This commit is contained in:
1 parent
32a0e813a5
commit
a58fff1334
1 file changed
+36
-4
@@ -329,8 +329,7 @@ fn parse_ssh_url(url: &str) -> Result<url::Url> {
|
||||
// They are unsupported by Url::parse, but can be normalized into a Url.
|
||||
// SCPUrl("ssh://user@host:~/relpath") => Url("ssh://user@host/~/relpath")
|
||||
// SCPUrl("ssh://user@host:/abs/path") => Url("ssh://user@host/abs/path")
|
||||
//
|
||||
// TODO: Add IPv6 support: "ssh://[2600::]:~/foo"
|
||||
// SCPUrl("ssh://[2600::]:~/foo") => Url("ssh://[2600::]/~/foo")
|
||||
let ssh_target = url
|
||||
.strip_prefix("ssh://")
|
||||
.with_context(|| format!("invalid ssh url: {url}"))?;
|
||||
@@ -347,7 +346,7 @@ fn parse_ssh_url(url: &str) -> Result<url::Url> {
|
||||
.rsplit_once('@')
|
||||
.map_or((None, authority), |(userinfo, host)| (Some(userinfo), host));
|
||||
anyhow::ensure!(
|
||||
!host.is_empty() && !host.starts_with('[') && !host.contains(':'),
|
||||
!host.is_empty() && url::Host::parse(host).is_ok(),
|
||||
"invalid ssh url: {url}"
|
||||
);
|
||||
|
||||
@@ -1235,6 +1234,38 @@ mod tests {
|
||||
None,
|
||||
"/project",
|
||||
),
|
||||
(
|
||||
"ssh://[2600::]:~/foo",
|
||||
Some("ssh://[2600::]/~/foo"),
|
||||
"2600::",
|
||||
None,
|
||||
None,
|
||||
"/~/foo",
|
||||
),
|
||||
(
|
||||
"ssh://me@[2001:db8::1]:~/project",
|
||||
Some("ssh://me@[2001:db8::1]/~/project"),
|
||||
"2001:db8::1",
|
||||
Some("me"),
|
||||
None,
|
||||
"/~/project",
|
||||
),
|
||||
(
|
||||
"ssh://me@[::1]:/tmp/file",
|
||||
Some("ssh://me@[::1]/tmp/file"),
|
||||
"::1",
|
||||
Some("me"),
|
||||
None,
|
||||
"/tmp/file",
|
||||
),
|
||||
(
|
||||
"ssh://[2001:db8::2]:2222/tmp",
|
||||
Some("ssh://[2001:db8::2]:2222/tmp"),
|
||||
"2001:db8::2",
|
||||
None,
|
||||
Some(2222),
|
||||
"/tmp",
|
||||
),
|
||||
];
|
||||
|
||||
for (input, expected_url, host, username, port, path) in cases {
|
||||
@@ -1362,7 +1393,8 @@ mod tests {
|
||||
for input in [
|
||||
"ssh://me@localhost:code/vibes/mine-bot",
|
||||
"ssh://me@localhost:2222:~/project",
|
||||
"ssh://me@[2001:db8::1]:~/project",
|
||||
"ssh://2600:::~/foo",
|
||||
"ssh://[2600::]:2222:~/foo",
|
||||
] {
|
||||
let result = cx.update(|cx| {
|
||||
OpenRequest::parse(
|
||||
|
||||
Reference in new issue
Block a user