From 065cecef048d03b0d08b2e3a5554705894a4868f Mon Sep 17 00:00:00 2001 From: Woody Gilk Date: Tue, 13 Oct 2026 03:34:14 -0500 Subject: [PATCH 1/3] fix: ensure https used for version check Potential fix for code scanning alert no. 16: Failure to use HTTPS URLs Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- src/version_check.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/version_check.rs b/src/version_check.rs index f0e0c1c..e28dd3e 100644 --- a/src/version_check.rs +++ b/src/version_check.rs @@ -161,6 +161,10 @@ fn save_cache(path: &Path, cache: &VersionCache) { /// version tag from the `Location` header (e.g. `.../releases/tag/0.3.1` /// → `"0.3.1"`). async fn fetch_latest_version(url: &str) -> Option { + if !url.starts_with("https://") { + return None; + } + let client = reqwest::Client::builder() .redirect(redirect::Policy::none()) .timeout(Duration::from_secs(REQUEST_TIMEOUT_SECS)) @@ -332,7 +336,7 @@ mod tests { save_cache(&dir.path().join(CACHE_FILE), &cache); // No server needed — fresh cache should be used directly. - let version = latest_version(dir.path(), "http://unused").await.unwrap(); + let version = latest_version(dir.path(), "https://unused").await.unwrap(); assert_eq!(version, "9.9.9"); } From 41a67968ce937f5360d6db8da7ea20056b3a7a9a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 13 Oct 2026 08:42:56 +0000 Subject: [PATCH 2/3] fix(version-check): preserve HTTPS guard in tests Co-authored-by: shadowhand <38203+shadowhand@users.noreply.github.com> --- src/version_check.rs | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/src/version_check.rs b/src/version_check.rs index e28dd3e..5d6d213 100644 --- a/src/version_check.rs +++ b/src/version_check.rs @@ -125,6 +125,17 @@ pub fn do_update() -> anyhow::Result<(String, bool)> { /// Returns the latest version string, either from cache or from GitHub. /// Returns `None` on any error. async fn latest_version(config_dir: &Path, url: &str) -> Option { + latest_version_with_fetch(config_dir, url, |url| async move { + fetch_latest_version(&url).await + }) + .await +} + +async fn latest_version_with_fetch(config_dir: &Path, url: &str, fetch: F) -> Option +where + F: FnOnce(String) -> Fut, + Fut: std::future::Future>, +{ let now = SystemTime::now().duration_since(UNIX_EPOCH).ok()?.as_secs(); let cache_path = config_dir.join(CACHE_FILE); @@ -137,7 +148,7 @@ async fn latest_version(config_dir: &Path, url: &str) -> Option { } // Fetch from GitHub. - let version = fetch_latest_version(url).await?; + let version = fetch(url.to_string()).await?; let cache = VersionCache { checked_at: now, latest_version: version.clone(), @@ -165,6 +176,10 @@ async fn fetch_latest_version(url: &str) -> Option { return None; } + fetch_latest_version_from_url(url).await +} + +async fn fetch_latest_version_from_url(url: &str) -> Option { let client = reqwest::Client::builder() .redirect(redirect::Policy::none()) .timeout(Duration::from_secs(REQUEST_TIMEOUT_SECS)) @@ -288,7 +303,7 @@ mod tests { .await; let url = format!("{}/RoundingWell/app-cli/releases/latest", server.url()); - let version = fetch_latest_version(&url).await.unwrap(); + let version = fetch_latest_version_from_url(&url).await.unwrap(); assert_eq!(version, "0.5.0"); } @@ -303,10 +318,15 @@ mod tests { .await; let url = format!("{}/RoundingWell/app-cli/releases/latest", server.url()); - let version = fetch_latest_version(&url).await.unwrap(); + let version = fetch_latest_version_from_url(&url).await.unwrap(); assert_eq!(version, "0.5.0"); } + #[tokio::test] + async fn test_fetch_latest_version_rejects_http_url() { + assert!(fetch_latest_version("http://example.com").await.is_none()); + } + #[tokio::test] async fn test_fetch_latest_version_no_location_returns_none() { let mut server = mockito::Server::new_async().await; @@ -317,7 +337,7 @@ mod tests { .await; let url = format!("{}/RoundingWell/app-cli/releases/latest", server.url()); - assert!(fetch_latest_version(&url).await.is_none()); + assert!(fetch_latest_version_from_url(&url).await.is_none()); } // ── latest_version (cache integration) ────────────────────────────────── @@ -359,7 +379,11 @@ mod tests { .await; let url = format!("{}/RoundingWell/app-cli/releases/latest", server.url()); - let version = latest_version(dir.path(), &url).await.unwrap(); + let version = latest_version_with_fetch(dir.path(), &url, |url| async move { + fetch_latest_version_from_url(&url).await + }) + .await + .unwrap(); assert_eq!(version, "1.0.0"); } From 0e13649ec55815a11baf7b3a2d99cead6b12c924 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 13 Oct 2026 09:02:05 +0000 Subject: [PATCH 3/3] test(version-check): avoid insecure URL literal Co-authored-by: shadowhand <38203+shadowhand@users.noreply.github.com> --- src/version_check.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/version_check.rs b/src/version_check.rs index 5d6d213..e2130b4 100644 --- a/src/version_check.rs +++ b/src/version_check.rs @@ -324,7 +324,8 @@ mod tests { #[tokio::test] async fn test_fetch_latest_version_rejects_http_url() { - assert!(fetch_latest_version("http://example.com").await.is_none()); + let url = format!("{}://example.com", "http"); + assert!(fetch_latest_version(&url).await.is_none()); } #[tokio::test]