{"record":{"id":"8931ff878da040bc","repo":"jeecgboot/JeecgBoot","slug":"error-8931ff","errorCode":null,"errorMessage":"文件路径包含非法字符","messagePattern":"文件路径包含非法字符","errorType":"validation","errorClass":"JeecgBootException","httpStatus":null,"severity":"critical","filePath":"jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/filter/SsrfFileTypeFilter.java","lineNumber":303,"sourceCode":"        }\n\n        // 3. 限制字符集（只允许字母、数字、下划线、横线、斜杠）\n        if (!normalized.matches(\"^[a-zA-Z0-9/_-]+$\")) {\n            throw new JeecgBootException(\"上传业务路径包含非法字符！\");\n        }\n    }\n\n    /**\n     * 校验文件路径安全性，防止路径遍历攻击\n     * @param filePath 文件路径\n     */\n    public static void checkPathTraversal(String filePath) {\n        if (StringUtils.isBlank(filePath)) {\n            return;\n        }\n        // 1. 防止路径遍历：不允许 ..\n        if (filePath.contains(\"..\")) {\n            throw new JeecgBootException(\"文件路径包含非法字符\");\n        }\n        // 2. 防止URL编码绕过：%2e = .\n        String fileLower = filePath.toLowerCase();\n        if (fileLower.contains(\"%2e\")) {\n            throw new JeecgBootException(\"文件路径包含非法字符\");\n        }\n    }\n\n    //update-begin---author:zhangdaihao ---date:2026-04-15  for：【issues/9553】修复二次SSRF漏洞，对HTTP下载URL进行安全校验-----------\n    /**\n     * 校验HTTP(S) URL，防止SSRF攻击（最小化拦截，只挡真正危险的目标）。\n     * 规则：\n     * 1. 仅允许 http / https 协议；\n     * 2. 解析主机IP，拒绝 loopback（127.x / ::1）和 link-local（169.254.x，含云元数据 169.254.169.254 / fe80:）；\n     * 注意：RFC1918 私网段（10/172.16/192.168）允许通过，兼容企业内网 MinIO/OSS/文件服务等合法用途。\n     *\n     * @param fileUrl HTTP(S) URL\n     */","sourceCodeStart":285,"sourceCodeEnd":321,"githubUrl":"https://github.com/jeecgboot/JeecgBoot/blob/96fb33f5ec68516da0b0147da06b2eb0419e063a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/filter/SsrfFileTypeFilter.java#L285-L321","documentation":"Thrown by SsrfFileTypeFilter.checkPathTraversal when the supplied filePath contains '..'. This is the general (download/serve) path-traversal guard, applied to file read/serve endpoints. It runs after a blank check and precedes the URL-encoded-variant check. Blank paths are allowed through (early return).","triggerScenarios":"A file read/serve/download request with a path like '../../etc/passwd', '/static/../../../config', or any path containing '..'. Common in URL-based file viewers and attachment downloaders.","commonSituations":"Filename field concatenated into a server path; an attacker manipulating a 'file=' query param; a relative path that legitimately needs '..' (which must be rewritten to an absolute canonical path instead).","solutions":["Do not send '..' in file paths; resolve to absolute canonical paths server-side and verify the result is under the allowed root.","Validate the file id/name against a whitelist or database record rather than accepting a raw path.","Use Path.normalize() + startsWith(allowedRoot) server-side as defense-in-depth.","Strip '..' on the client and reject paths that still contain it."],"exampleFix":"// before\nString filePath = \"../../secret.txt\";\ncheckPathTraversal(filePath); // throws\n\n// after\nString filePath = \"reports/2026/q1.pdf\";\ncheckPathTraversal(filePath);","handlingStrategy":"validation","validationCode":"if (filePath != null && filePath.contains(\"..\")) throw new IllegalArgumentException(\"traversal\");","typeGuard":"public static boolean pathNoDotDot(String p){ return p == null || !p.contains(\"..\"); }","tryCatchPattern":"try { SsrfFileTypeFilter.checkPathTraversal(filePath); }\ncatch (JeecgBootException e) { response.sendError(400, e.getMessage()); }","preventionTips":["Resolve to canonical paths and verify under allowed root.","Reference files by id, not raw path.","Never build server paths from user input."],"tags":["path-traversal","security","validation","file-download","jeecg-boot"],"backgroundTag":null,"analyzedSha":"96fb33f5ec68516da0b0147da06b2eb0419e063a","analyzedAt":"2026-08-14T00:04:16.786Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}