From d1b56918faf73d926d66cf2b139e9a53503f110f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E8=87=AA=E8=BE=BE?= <980324341@qq.com> Date: Sun, 13 Sep 2026 03:01:56 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E8=B7=9F=E4=BB=B7):=20=E6=8C=87=E5=AE=9AAS?= =?UTF-8?q?IN=E6=96=87=E4=BB=B6=E6=8F=90=E4=BA=A4=E6=94=B9=20fileKey=20+?= =?UTF-8?q?=20=E6=9C=8D=E5=8A=A1=E7=AB=AF=E4=B8=89=E5=BD=A2=E6=80=81?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=E5=85=BC=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 前端 resolveAsinRequestPaths 改发上传返回的 fileKey(与去重/转换/采集一致), 修复安全加固后服务端按拼接解析绝对路径失败导致"ASIN 文件不存在或不可读" - Java parseAsinRowsByCountry 三种形态解析:绝对路径直读 → tempRoot 拼接 → fileKey 反查, 均过 isInsideTempDir 穿越校验,不放开临时目录外读取 --- .../service/PriceTrackTaskService.java | 19 +++++++++++++------ .../brand/components/BrandPriceTrackTab.vue | 6 ++++-- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/pricetrack/service/PriceTrackTaskService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/pricetrack/service/PriceTrackTaskService.java index 543c7dd8..8a20f91f 100644 --- a/backend-java/src/main/java/com/nanri/aiimage/modules/pricetrack/service/PriceTrackTaskService.java +++ b/backend-java/src/main/java/com/nanri/aiimage/modules/pricetrack/service/PriceTrackTaskService.java @@ -1463,13 +1463,20 @@ public class PriceTrackTaskService { if (rawPath == null || rawPath.isBlank()) { continue; } - // 只允许解析上传落库的临时目录文件:直接 new File(请求路径) 可被穿越读服务器任意 csv/xlsx - File file = new File(localFileStorageService.localTempRoot().getAbsolutePath(), rawPath); + // 上传文件路径解析(三种形态都过 isInsideTempDir 穿越校验,不放开临时目录外读取): + // ①绝对路径:老客户端提交上传接口返回的 localPath 时按原路径直读; + // ②相对路径/裸文件名:相对上传临时目录拼接; + // ③fileKey/索引键:按上传索引反查临时目录(与去重/转换等模块统一的口径)。 + File file = new File(rawPath); if (!file.isFile()) { - // 传入的是 fileKey/索引键时按上传索引反查临时目录 - File resolved = localFileStorageService.findLocalSourceFile(rawPath); - if (resolved != null) { - file = resolved; + File joined = new File(localFileStorageService.localTempRoot().getAbsolutePath(), rawPath); + if (joined.isFile()) { + file = joined; + } else { + File resolved = localFileStorageService.findLocalSourceFile(rawPath); + if (resolved != null) { + file = resolved; + } } } if (!file.isFile() || !isInsideTempDir(file)) { diff --git a/frontend-vue/src/pages/brand/components/BrandPriceTrackTab.vue b/frontend-vue/src/pages/brand/components/BrandPriceTrackTab.vue index f1f5432b..a39de0cd 100644 --- a/frontend-vue/src/pages/brand/components/BrandPriceTrackTab.vue +++ b/frontend-vue/src/pages/brand/components/BrandPriceTrackTab.vue @@ -569,8 +569,10 @@ async function uploadAsinPathsToJava(paths: Array item.localPath).filter((path) => !!path) + // 提交上传返回的 fileKey(与去重/转换/采集等模块一致):服务端按 key 反查上传临时目录, + // 不依赖上传返回的 localPath 绝对路径形态——安全加固后服务端按拼接解析绝对路径会失败。 + // 仅上传成功(上传接口返回 data)的文件才进入列表,未上传成功不兜底本地路径(服务器无法读取)。 + return asinUploadedFiles.value.map((item) => item.fileKey).filter((path) => !!path) } function resolveCountryCodesForRequest() {