fix: bound VNC server-announced sizes and only open http(s) links from network data

- The VNC client refuses desktop sizes over 64 Mpx (ServerInit and
  DesktopSize/ExtendedDesktopSize) and ZRLE rectangles over 64 MiB before
  allocating, so a hostile server can't force a multi-gigabyte allocation.
- Release URLs from the update manifest and repository URLs from the GitHub
  API are handed to the system opener only when they are http(s) links.
This commit is contained in:
Andy Dixon
2026-10-12 07:23:40 +01:00
parent 0a18f7e578
commit aac986d78b
4 changed files with 73 additions and 5 deletions
+5 -1
View File
@@ -268,7 +268,11 @@ impl GithubView {
});
match action {
Some(Action::Zip(r)) => self.download(s, &r),
Some(Action::Visit(url)) if !url.is_empty() => ui.ctx().open_url(egui::OpenUrl::new_tab(url)),
Some(Action::Visit(url)) if !url.is_empty() => {
if !widgets::open_web_url(ui.ctx(), &url) {
s.error("The repository link is not a web address");
}
}
Some(Action::Copy(url)) => {
ui.ctx().copy_text(url);
s.info("URL copied");
+5 -2
View File
@@ -559,8 +559,11 @@ impl Settings {
});
});
}
if !info.release_url.is_empty() && widgets::button(ui, icon::ARROW_SQUARE_OUT, "View release").clicked() {
ui.ctx().open_url(egui::OpenUrl::new_tab(&info.release_url));
if widgets::is_web_url(&info.release_url)
&& widgets::button(ui, icon::ARROW_SQUARE_OUT, "View release").clicked()
&& !widgets::open_web_url(ui.ctx(), &info.release_url)
{
s.error("The release link is not a web address");
}
} else {
ui.label(RichText::new("You are running the latest version.").color(t.success));
+28
View File
@@ -125,6 +125,24 @@ pub fn group(n: u64) -> String {
out
}
/// Whether `url` is a plain web link. Links that arrive from the network (update manifests,
/// GitHub API responses) are only handed to the system opener when they are http(s), so a
/// hostile or spoofed server can't launch `file:`, `ssh:` or custom-scheme handlers.
pub fn is_web_url(url: &str) -> bool {
let u = url.trim();
(u.len() > 8 && u[..8].eq_ignore_ascii_case("https://")) || (u.len() > 7 && u[..7].eq_ignore_ascii_case("http://"))
}
/// Opens `url` in the browser if it is a web link; returns false (and opens nothing) otherwise.
pub fn open_web_url(ctx: &egui::Context, url: &str) -> bool {
if !is_web_url(url) {
eprintln!("refusing to open non-web URL {url:?}");
return false;
}
ctx.open_url(egui::OpenUrl::new_tab(url.trim()));
true
}
pub fn human_size(n: u64) -> String {
const U: [&str; 5] = ["B", "KiB", "MiB", "GiB", "TiB"];
let mut v = n as f64;
@@ -142,4 +160,14 @@ mod tests {
fn helpers() {
assert_eq!(super::group(1234567), "1,234,567");
}
#[test]
fn only_web_urls_are_opened() {
for ok in ["https://dixon.cx/downloads/nexus/", "http://localhost:3000/x", " HTTPS://Example.com "] {
assert!(super::is_web_url(ok), "{ok}");
}
for bad in ["", "https://", "file:///etc/passwd", "ssh://evil", "javascript:alert(1)", "ms-msdt:/id x", "ftp://host/", "//host/path"] {
assert!(!super::is_web_url(bad), "{bad}");
}
}
}