jeecgboot/JeecgBoot · critical · JeecgBootException

文件路径包含非法字符

Error message

文件路径包含非法字符

What it means

This error is thrown when path traversal is detected during AI attachment file resolution. The code resolves the fileRef against the upload directory root, normalizes the path, and checks if the resolved target still starts with the root directory. If not (indicating a ../ escape attempt), it rejects the request. This is a security guard against CWE-22 path traversal attacks on AI chat attachment files.

Source

Thrown at jeecg-boot/jeecg-boot-module/jeecg-boot-module-airag/src/main/java/org/jeecg/modules/airag/app/service/impl/AiragChatServiceImpl.java:2197

            }
            String tempFilePath = tempDir + safeFileName;
            //update-begin---author:zhangdaihao ---date:20260427  for:[issues/9578]AI附件下载 SSRF 校验,拒绝 loopback/link-local------------
            // /airag/chat/send 端点为 @IgnoreAuth 无认证,AI 聊天解析附件存在 SSRF 风险;
            // 沿用与 #9553 一致的基础 SSRF 校验(拒绝 loopback / link-local),保留对企业内网 MinIO/OSS 的兼容。
            SsrfFileTypeFilter.checkSsrfHttpUrl(fileRef);
            //update-end-----author:zhangdaihao ---date:20260427  for:[issues/9578]AI附件下载 SSRF 校验,拒绝 loopback/link-local------------
            FileDownloadUtils.download2DiskFromNet(fileRef, tempFilePath);
            return new File(tempFilePath);
        }
        //update-begin---author:wangshuai ---date:2026-04-13  for:【issues/9519】AI附件处理路径遍历漏洞:规范化路径并强制校验沙箱范围---
        // 本地附件:1) 先做字符级路径遍历检查;2) 规范化路径后必须仍在 uploadpath 下,阻止 ../ 逃逸
        java.nio.file.Path root = Paths.get(uploadpath).toAbsolutePath().normalize();
        SsrfFileTypeFilter.checkPathTraversal(fileRef);
        String relativePath = fileRef.replaceAll("^[\\\\/]+", "");
        java.nio.file.Path target = root.resolve(relativePath).toAbsolutePath().normalize();
        if (!target.startsWith(root)) {
            log.error("检测到路径遍历攻击! fileRef: {}, 解析后: {}", relativePath, target);
            throw new JeecgBootException("文件路径包含非法字符");
        }
        return target.toFile();
        //update-end---author:wangshuai ---date:2026-04-13  for:【issues/9519】AI附件处理路径遍历漏洞:规范化路径并强制校验沙箱范围---
    }
    //================================================= end【QQYUN-14261】【AI】AI助手,支持多模态能力- 文档========================================


    /**
     * ai创作
     *
     * @param aiWriteGenerateVo
     * @return
     */
    @Override
    public SseEmitter genAiWriter(AiWriteGenerateVo aiWriteGenerateVo) {
        String activeMode = "compose";
        String reply = "reply";
        ChatSendParams sendParams = new ChatSendParams();

View on GitHub (pinned to 96fb33f5ec)

Solutions

  1. This is a security guard working correctly — do not disable it. Investigate the source of the malicious fileRef value.
  2. If the fileRef is legitimate, ensure it does not contain ../, ..\, or absolute path prefixes — use a simple relative filename only.
  3. Audit the client-side code that generates the fileRef to ensure it only produces safe relative paths.
  4. Check the server logs for the full fileRef and resolved target path to understand the attempted traversal.

Example fix

// before (client) — sending a path with traversal characters
String fileRef = "../../uploads/document.pdf";

// after — sending a clean relative filename only
String fileRef = "document.pdf";
Defensive patterns

Strategy: validation

Validate before calling

// Server-side: validate fileRef before path resolution
public static boolean isSafeFileRef(String fileRef) {
    if (fileRef == null || fileRef.isEmpty()) return false;
    // Reject path traversal patterns
    if (fileRef.contains("..") || fileRef.contains("%2e%2e")) return false;
    // Reject absolute paths
    if (fileRef.startsWith("/") || fileRef.startsWith("\\")) return false;
    // Only allow alphanumeric, dash, underscore, dot, and standard separators
    return fileRef.matches("[a-zA-Z0-9._\\-/]+");
}

Type guard

// Normalize and validate that resolved path stays within root
public static boolean isWithinUploadRoot(String uploadpath, String fileRef) {
    try {
        Path root = Paths.get(uploadpath).toAbsolutePath().normalize();
        Path target = root.resolve(fileRef.replaceAll("^[\\\\/]+", "")).toAbsolutePath().normalize();
        return target.startsWith(root);
    } catch (Exception e) {
        return false;
    }
}

Try / catch

// This is a security guard — the exception should not be caught and suppressed.
// Instead, log the security event and return an error to the client.
try {
    File file = resolveAttachmentFile(fileRef, uploadpath);
    // process file
} catch (JeecgBootException e) {
    if (e.getMessage().contains("非法字符")) {
        log.warn("[SECURITY] Path traversal attempt blocked: fileRef={}", fileRef);
        auditLogService.recordSecurityEvent("PATH_TRAVERSAL_ATTEMPT", fileRef);
    }
    throw e;
}

Prevention

When it happens

Trigger: A user sends a file reference (fileRef) like '../../../etc/passwd' or '..\..\config\application.yml' to an AI chat endpoint that processes local file attachments. SsrfFileTypeFilter.checkPathTraversal first catches character-level traversal patterns, and the normalized path check (target.startsWith(root)) catches any remaining escape attempts.

Common situations: Malicious input attempting directory traversal to access system files. Legitimate file references with unusual but safe path characters that trigger a false positive in SsrfFileTypeFilter. Race condition where the upload directory path changes between resolution and normalization.

Related errors


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