@@ -74,6 +74,9 @@ public class Utils {
7474 */
7575 // 泛型去重方法
7676 public static <T > List <T > removeDuplicates (List <T > list ) {
77+ if (list == null ) {
78+ return new ArrayList <>();
79+ }
7780 // 使用 Set 去重
7881 Set <T > set = new LinkedHashSet <>(list );
7982 // 返回去重后的 List
@@ -191,7 +194,7 @@ public static List<FileInfo> unzip(MultipartFile multipartFile) throws IOExcepti
191194 * @return File the File
192195 * @throws IOException IOException
193196 */
194- private static File createTempDirectory () throws IOException {
197+ static File createTempDirectory () throws IOException {
195198 return Files .createTempDirectory ("unzip" ).toFile ();
196199 }
197200
@@ -202,7 +205,7 @@ private static File createTempDirectory() throws IOException {
202205 * @return File the File
203206 * @throws IOException IOException
204207 */
205- private static File convertMultipartFileToFile (MultipartFile multipartFile ) throws IOException {
208+ static File convertMultipartFileToFile (MultipartFile multipartFile ) throws IOException {
206209 File tempFile = File .createTempFile ("temp" , null );
207210 tempFile .deleteOnExit ();
208211 try (FileOutputStream fos = new FileOutputStream (tempFile )) {
@@ -219,18 +222,46 @@ private static File convertMultipartFileToFile(MultipartFile multipartFile) thro
219222 * @return List<FileInfo> the List<FileInfo>
220223 * @throws IOException IOException
221224 */
222- private static List <FileInfo > processZipEntries (ZipInputStream zis , File tempDir ) throws IOException {
225+ static List <FileInfo > processZipEntries (ZipInputStream zis , File tempDir ) throws IOException {
223226 List <FileInfo > fileInfoList = new ArrayList <>();
224227 ZipEntry zipEntry ;
228+ // 将 tempDir 转为规范路径(例如解析符号链接、父目录等)
229+ Path safeDir = tempDir .toPath ().toRealPath ();
230+ log .info ("Created temporary directory at: {}, real path: {}" , tempDir .getAbsolutePath (), safeDir );
225231
226232 while ((zipEntry = zis .getNextEntry ()) != null ) {
227- File newFile = new File (tempDir , zipEntry .getName ());
233+ // 获取 ZIP 条目中的路径(可能包含 ../ 或绝对路径)
234+ String entryName = zipEntry .getName ();
235+
236+ // 拼接并规范化路径
237+ Path targetPath = safeDir .resolve (entryName ).normalize ();
238+
239+ log .info ("Processing ZIP entry: {}, target path: {}" , entryName , targetPath );
240+
241+ // 关键校验:确保目标路径仍在 safeDir 之下
242+ if (!targetPath .startsWith (safeDir )) {
243+ throw new SecurityException ("检测到跨目录攻击: " + entryName );
244+ }
228245
229246 if (zipEntry .isDirectory ()) {
230- fileInfoList .add (new FileInfo (newFile .getName (), "" , true )); // 添加目录
247+ // 创建目录(同时确保父目录存在)
248+ Files .createDirectories (targetPath );
249+ // 存储目录信息(使用最后一级名称,保持与原行为一致)
250+ String dirName = targetPath .getFileName ().toString ();
251+ fileInfoList .add (new FileInfo (dirName , "" , true ));
231252 } else {
232- extractFile (zis , newFile ); // 解压文件
233- fileInfoList .add (new FileInfo (newFile .getName (), readFileContent (newFile ), false )); // 添加文件内容
253+ // 确保父目录存在
254+ Path parent = targetPath .getParent ();
255+ if (parent != null ) {
256+ Files .createDirectories (parent );
257+ }
258+ // 解压文件到目标路径(使用已验证的 targetPath)
259+ extractFile (zis , targetPath .toFile ());
260+ // 读取文件内容(同样使用已验证的路径)
261+ String content = readFileContent (targetPath .toFile ());
262+ // 存储文件信息(使用最后一级文件名)
263+ String fileName = targetPath .getFileName ().toString ();
264+ fileInfoList .add (new FileInfo (fileName , content , false ));
234265 }
235266 zis .closeEntry ();
236267 }
@@ -273,7 +304,7 @@ public static String readFileContent(File file) {
273304 }
274305
275306 // 清理临时文件和目录
276- private static void cleanUp (File zipFile , File tempDir ) {
307+ static void cleanUp (File zipFile , File tempDir ) {
277308 // 删除临时的 zip 文件
278309 if (zipFile .exists ()) {
279310 if (!zipFile .delete ()) {
0 commit comments