jeecgboot/JeecgBoot · critical · JeecgBootException
非法文件路径,禁止访问上传目录之外的文件:
Error message
非法文件路径,禁止访问上传目录之外的文件:
What it means
This error is thrown by AIChatHandler.getFirstImageBase64() when the resolved canonical path of a local image file does not start with the upload directory's canonical path. This is a path traversal security guard (CWE-22, issues/9431) for the image-edit pipeline. The code resolves uploadpath + imageUrl to a canonical File, then checks if it's within the upload directory.
Source
Thrown at jeecg-boot/jeecg-boot-module/jeecg-boot-module-airag/src/main/java/org/jeecg/modules/airag/llm/handler/AIChatHandler.java:628
java.io.ByteArrayOutputStream buffer = new java.io.ByteArrayOutputStream();
int nRead;
byte[] data = new byte[1024];
while ((nRead = in.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
buffer.flush();
fileContent = buffer.toByteArray();
}
} else {
//update-begin---author:liusq ---date:2026-03-30 for:【issues/9431】修复getFirstImageBase64路径遍历漏洞(CWE-22)-----------
// 本地文件
String filePath = uploadpath + File.separator + imageUrl;
SsrfFileTypeFilter.checkPathTraversal(filePath);
// 路径遍历校验:规范化后确保文件在uploadpath目录内
File uploadDir = new File(uploadpath).getCanonicalFile();
File targetFile = new File(filePath).getCanonicalFile();
if (!targetFile.toPath().startsWith(uploadDir.toPath())) {
throw new JeecgBootException("非法文件路径,禁止访问上传目录之外的文件: " + imageUrl);
}
fileContent = Files.readAllBytes(targetFile.toPath());
//update-end---author:liusq ---date:2026-03-30 for:【issues/9431】修复getFirstImageBase64路径遍历漏洞(CWE-22)-----------
}
originalImageBase64List.add(Base64.getEncoder().encodeToString(fileContent));
} catch (Exception e) {
log.error("图片读取失败: {}", imageUrl, e);
throw new JeecgBootException("图片读取失败: " + imageUrl);
}
}
}
return originalImageBase64List;
}
//================================================= end 【QQYUN-12145】【AI】AI 绘画创作 ========================================
/**
* 将 LLM 调用异常统一翻译为友好的 JeecgBootException。
* <p>View on GitHub (pinned to 96fb33f5ec)
Solutions
- This is a security guard functioning correctly — investigate the source of the imageUrl input.
- Ensure client-side code only sends simple filenames (e.g. 'image_123.jpg'), not paths.
- Verify the uploadpath configuration is correct and images are actually stored there.
- Check for symlinks within the upload directory that could resolve outside it.
- If legitimate images are stored elsewhere, update uploadpath or move the images into the configured directory.
Example fix
// before (client) — sending traversal path images: ['../../../data/secret.png'] // after — sending clean filename images: ['user_upload_123.png']
Defensive patterns
Strategy: validation
Validate before calling
// Server-side: validate imageUrl is safe before path construction
public static boolean isSafeLocalImageRef(String imageUrl, String uploadpath) {
if (imageUrl == null || imageUrl.isEmpty()) return false;
if (imageUrl.contains("..")) return false;
try {
File uploadDir = new File(uploadpath).getCanonicalFile();
File target = new File(uploadpath + File.separator + imageUrl).getCanonicalFile();
return target.toPath().startsWith(uploadDir.toPath());
} catch (IOException e) {
return false;
}
} Type guard
public static boolean isWithinUploadDir(String uploadpath, String imageUrl) {
try {
File uploadDir = new File(uploadpath).getCanonicalFile();
File target = new File(uploadpath + File.separator + imageUrl).getCanonicalFile();
return target.toPath().startsWith(uploadDir.toPath());
} catch (Exception e) {
return false;
}
} Try / catch
// This is a security guard — do not suppress the exception.
// Log it as a security event.
try {
List<String> base64List = getFirstImageBase64(images);
} catch (JeecgBootException e) {
if (e.getMessage().contains("非法文件路径")) {
log.warn("[SECURITY] Path traversal in image edit blocked: {}", imageUrl);
auditLogService.recordSecurityEvent("IMG_PATH_TRAVERSAL", imageUrl);
}
throw e;
} Prevention
- Only accept simple filenames as imageUrl, never paths
- Store images with UUID-based names to eliminate traversal possibilities
- Validate at the API boundary that imageUrl matches a safe pattern (alphanumeric + dots)
- Check for symlinks within the upload directory that could resolve outside it
- Log all path-traversal rejections as security events
When it happens
Trigger: An image-edit request provides a local imageUrl (not matching WEB_PATTERN for URLs) that resolves outside the upload directory after canonicalization. For example: imageUrl='../../../etc/passwd' resolves to /etc/passwd which does not start with the upload directory. This triggers after SsrfFileTypeFilter.checkPathTraversal but the canonical path check is a second layer of defense.
Common situations: Malicious input attempting to access files outside the upload directory via ../ sequences or symlinks. A symlink within the upload directory points outside, causing canonical resolution to escape the root. Legitimate but incorrectly configured uploadpath where image files are stored in a sibling directory.
Related errors
- 文件路径包含非法字符
- Illegal access to path outside of base directory.
- 非法业务路径,禁止访问上传目录之外的路径: ${bizPath}
- 非法存储路径,路径包含遍历字符: {storePath}
- 非法文件路径,禁止访问上传目录之外的文件: {imageUrl}
AI-assisted analysis of jeecgboot/JeecgBoot@96fb33f5ec (2026-08-14).
Data as JSON: /api/errors/2722da3756d716be.
Report an issue: GitHub.