jeecgboot/JeecgBoot · critical · IOException

ZIP 路径穿越攻击被阻止:{entryName}

Error message

ZIP 路径穿越攻击被阻止:{entryName}

What it means

Thrown by AiragKnowledgeDocServiceImpl.safeResolve() when a zip entry name resolves to a path outside the target extraction directory. The method resolves entryName against targetDir, normalizes the result, and verifies it still starts with targetDir. This is the classic Zip-Slip vulnerability defense.

Source

Thrown at jeecg-boot/jeecg-boot-module/jeecg-boot-module-airag/src/main/java/org/jeecg/modules/airag/llm/service/impl/AiragKnowledgeDocServiceImpl.java:484

        String fileName = Paths.get(normalizedName).getFileName().toString();
        return fileName.startsWith("._") || fileName.equals(".DS_Store");
    }
    //update-end---author:scott ---date:2026-04-16  for:【issues/9551】macOS压缩包隐藏文件过滤-----------

    /**
     * 安全解析路径,防止Zip Slip攻击
     *
     * @param targetDir
     * @param entryName
     * @return
     * @throws IOException
     * @author chenrui
     * @date 2025/4/28 16:46
     */
    private static Path safeResolve(Path targetDir, String entryName) throws IOException {
        Path resolvedPath = targetDir.resolve(entryName).normalize();
        if (!resolvedPath.startsWith(targetDir)) {
            throw new IOException("ZIP 路径穿越攻击被阻止:" + entryName);
        }
        return resolvedPath;
    }

    /**
     * 复制输入流到输出流,并限制最大字节数
     *
     * @param in
     * @param out
     * @param maxBytes
     * @return
     * @throws IOException
     * @author chenrui
     * @date 2025/4/28 17:03
     */
    private static long copyLimited(InputStream in, OutputStream out, long maxBytes) throws IOException {
        byte[] buffer = new byte[8192];
        long totalCopied = 0;

View on GitHub (pinned to 96fb33f5ec)

Solutions

  1. Re-create the zip archive ensuring all entry paths are relative and do not contain '..' segments.
  2. Use a standard zip tool that produces clean relative paths (avoid tools that embed absolute paths).
  3. If processing untrusted archives is required, the current safeResolve guard already prevents exploitation — the error is the guard working as intended.
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-validate zip entries before extraction
try (ZipFile zf = new ZipFile(zipFile.toFile())) {
    Path target = targetDir;
    Enumeration<ZipArchiveEntry> entries = zf.getEntries();
    while (entries.hasMoreElements()) {
        String name = entries.nextElement().getName();
        Path resolved = target.resolve(name).normalize();
        if (!resolved.startsWith(target)) {
            throw new JeecgBootException("压缩包包含非法路径条目: " + name);
        }
    }
}

Try / catch

try {
    unzipFile(zipFilePath, targetDir, callback);
} catch (IOException e) {
    if (e.getMessage().contains("路径穿越攻击")) {
        log.error("Zip slip attack detected: {}", e.getMessage());
        throw new JeecgBootException("压缩包包含不安全的路径,已被拒绝");
    }
    throw e;
}

Prevention

When it happens

Trigger: A zip archive containing an entry with a name like '../../../etc/passwd' or '..\..\windows\system32' that, when resolved against the target directory, escapes it. The normalize() call collapses the '..' segments, and the startsWith check detects the escape.

Common situations: A maliciously crafted zip designed to overwrite system files (Zip-Slip attack); an archive created with absolute paths or entries using relative '../' segments; some older zip tools that embed full paths.

Related errors


AI-assisted analysis of jeecgboot/JeecgBoot@96fb33f5ec (2026-08-14). Data as JSON: /api/errors/e8f783dc89e97636. Report an issue: GitHub.