From d1a875c19ae1683df1207c4739f2c8d49619f3d2 Mon Sep 17 00:00:00 2001 From: zyycn Date: Sat, 25 Jul 2026 03:43:19 +0800 Subject: [PATCH] fix(update): allow GitHub release asset redirects Co-authored-by: Codex --- .../crates/gateway-host/src/system_update/mod.rs | 2 ++ .../gateway-host/src/system_update/release.rs | 4 +++- .../gateway-host/tests/system_update/mod.rs | 15 ++++++++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/backend/crates/gateway-host/src/system_update/mod.rs b/backend/crates/gateway-host/src/system_update/mod.rs index 34e7358c8..7d89d2d85 100644 --- a/backend/crates/gateway-host/src/system_update/mod.rs +++ b/backend/crates/gateway-host/src/system_update/mod.rs @@ -39,6 +39,8 @@ use self::state::{ }; use self::swap::{replace_release_files, rollback_release}; +pub use self::release::validate_download_url; + const APP_BINARY_NAME: &str = "codex-proxy-rs"; const DEFAULT_WEB_DIST_DIR: &str = "/app/web/dist"; const DEFAULT_GITHUB_API_BASE: &str = "https://api.github.com/repos"; diff --git a/backend/crates/gateway-host/src/system_update/release.rs b/backend/crates/gateway-host/src/system_update/release.rs index 9c701c7dd..ebf736d16 100644 --- a/backend/crates/gateway-host/src/system_update/release.rs +++ b/backend/crates/gateway-host/src/system_update/release.rs @@ -177,7 +177,8 @@ pub(crate) fn select_archive<'a>( }) } -pub(crate) fn validate_download_url(raw: &str, api_base: &str) -> Result<(), OperationError> { +/// 校验 GitHub Release 资产 URL 及其重定向目标是否位于受信任边界内。 +pub fn validate_download_url(raw: &str, api_base: &str) -> Result<(), OperationError> { let url = reqwest::Url::parse(raw) .map_err(|error| invalid(format!("invalid download URL: {error}")))?; if local_download_allowed(&url, api_base) { @@ -294,6 +295,7 @@ fn github_download_host_allowed(host: &str) -> bool { || host.ends_with(".github.com") || host == "objects.githubusercontent.com" || host.ends_with(".objects.githubusercontent.com") + || host == "release-assets.githubusercontent.com" } fn local_download_allowed(url: &reqwest::Url, api_base: &str) -> bool { diff --git a/backend/crates/gateway-host/tests/system_update/mod.rs b/backend/crates/gateway-host/tests/system_update/mod.rs index fe1895781..a589ad48b 100644 --- a/backend/crates/gateway-host/tests/system_update/mod.rs +++ b/backend/crates/gateway-host/tests/system_update/mod.rs @@ -10,7 +10,9 @@ use gateway_admin::model::system::{ }; use gateway_admin::ports::system::{SystemOperationErrorKind, SystemOperations}; use gateway_core::engine::CancellationToken; -use gateway_host::system_update::{ProcessSystemOperations, SystemUpdateConfig}; +use gateway_host::system_update::{ + ProcessSystemOperations, SystemUpdateConfig, validate_download_url, +}; use sha2::{Digest as _, Sha256}; use tar::{Builder, EntryType, Header}; use wiremock::matchers::{method, path}; @@ -280,6 +282,17 @@ async fn update_should_reject_release_archive_from_untrusted_host() { ); } +#[test] +fn update_should_trust_github_release_asset_redirect_host() { + assert!( + validate_download_url( + "https://release-assets.githubusercontent.com/github-production-release-asset/archive", + "https://api.github.com/repos", + ) + .is_ok() + ); +} + #[tokio::test] async fn update_should_reject_release_archive_with_unsafe_path() { let server = MockServer::start().await;