fix(update): allow GitHub release asset redirects

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
zyycn
2026-07-25 03:43:19 +08:00
co-authored by Codex
parent b9335ea89f
commit d1a875c19a
3 changed files with 19 additions and 2 deletions
@@ -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";
@@ -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 {
@@ -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;