From 42d69dd4bd569e96321807755228e6b26cdecc00 Mon Sep 17 00:00:00 2001 From: Yuyi-Oak <1722157266@qq.com> Date: Thu, 16 Jul 2026 08:03:40 -0700 Subject: [PATCH] =?UTF-8?q?fix(official-sync):=20=E6=8B=92=E7=BB=9D?= =?UTF-8?q?=E6=90=BA=E5=B8=A6=20query/fragment=20=E7=9A=84=E8=B5=84?= =?UTF-8?q?=E6=BA=90=20URL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit destination_for_url 原来对每个路径片段静默剥除 query/fragment,导致仅 query 不同的两个官方 URL 映射到同一目标文件而相互覆盖,并使每轮 hash 复用校验失配、 反复重下。官方资源 URL 本不携带 query(发现 URL 由固定拼接生成),改为直接拒绝, 消除该碰撞路径。新增单测。 对应 issue #18 维护清单 2-3。 Co-Authored-By: Claude Fable 5 --- infrastructure/src/official_download.rs | 32 ++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/infrastructure/src/official_download.rs b/infrastructure/src/official_download.rs index d19d1da..dce51bf 100644 --- a/infrastructure/src/official_download.rs +++ b/infrastructure/src/official_download.rs @@ -1320,9 +1320,11 @@ impl OfficialResourcePullService { return Err(format!("官方 URL 包含不安全路径片段:{url}")); } - let segment = segment.split(['?', '#']).next().unwrap_or(segment); - if segment.is_empty() { - continue; + // 官方资源 URL 不携带 query/fragment。若出现则直接拒绝,而非静默剥除—— + // 否则仅 query 不同的两个 URL 会映射到同一目标文件而相互覆盖, + // 并导致每轮 hash 复用校验失配、反复重下。 + if segment.contains('?') || segment.contains('#') { + return Err(format!("官方资源 URL 不允许包含 query 或 fragment:{url}")); } relative_destination.push(sanitize_segment(segment)); } @@ -2193,6 +2195,30 @@ exit 22 assert!(error.contains("危险路径")); } + #[test] + fn rejects_resource_url_with_query_or_fragment() { + let temp = TempDir::new().unwrap(); + let service = OfficialResourcePullService::new(temp.path().join("out")); + + for url in [ + "https://prod-clientpatch.bluearchiveyostar.com/r93_token/TableBundles/TableCatalog.bytes?v=1", + "https://prod-clientpatch.bluearchiveyostar.com/r93_token/TableBundles/TableCatalog.bytes#frag", + ] { + let error = service.destination_for_url(url).unwrap_err(); + assert!( + error.contains("query 或 fragment"), + "unexpected error for {url}: {error}" + ); + } + + // 无 query 的正常资源 URL 仍可映射。 + assert!(service + .destination_for_url( + "https://prod-clientpatch.bluearchiveyostar.com/r93_token/TableBundles/TableCatalog.bytes", + ) + .is_ok()); + } + #[cfg(unix)] #[test] fn pull_rejects_symlink_parent_escape() {