{"record":{"id":"b6bbd50c702891b8","repo":"jeecgboot/JeecgBoot","slug":"illegal-access-to-path-outside-of-base-directory","errorCode":null,"errorMessage":"Illegal access to path outside of base directory.","messagePattern":"Illegal access to path outside of base directory\\.","errorType":"validation","errorClass":"SecurityException","httpStatus":null,"severity":"critical","filePath":"jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/CommonUtils.java","lineNumber":74,"sourceCode":"    private static String FILE_NAME_REGEX = \"[^A-Za-z\\\\.\\\\(\\\\)\\\\-（）\\\\_0-9\\\\u4e00-\\\\u9fa5]\";\n\n    public static String uploadOnlineImage(byte[] data,String basePath,String bizPath,String uploadType){\n        String dbPath = null;\n        String fileName = \"image\" + Math.round(Math.random() * 100000000000L);\n        //update-begin---author:wangshuai---date:2026-01-08---for:【QQYUN-14535】ai生成图片的后缀不一致的，导致不展示---\n        fileName += \".\" + PoiPublicUtil.getFileExtendName(data).toLowerCase();\n        //update-end---author:wangshuai---date:2026-01-08---for:【QQYUN-14535】ai生成图片的后缀不一致的，导致不展示---\n        try {\n            if(CommonConstant.UPLOAD_TYPE_LOCAL.equals(uploadType)){\n                //update-begin---author:wangshuai---date:2026-03-30---for:【issues/9435】uploadOnlineImage路径遍历漏洞修复---\n                // 1. 使用已有的路径遍历检查\n                SsrfFileTypeFilter.checkPathTraversal(bizPath);\n                // 2. 标准化路径并校验是否在basePath范围内\n                Path root = Paths.get(basePath).toAbsolutePath().normalize();\n                Path targetDir = root.resolve(bizPath).toAbsolutePath().normalize();\n                if (!targetDir.startsWith(root)) {\n                    log.error(\"检测到路径遍历攻击！非法 bizPath: {}\", bizPath);\n                    throw new SecurityException(\"Illegal access to path outside of base directory.\");\n                }\n                File file = targetDir.toFile();\n                //update-end---author:wangshuai---date:2026-03-30---for:【issues/9435】uploadOnlineImage路径遍历漏洞修复---\n                if (!file.exists()) {\n                    file.mkdirs();// 创建文件根目录\n                }\n                String savePath = file.getPath() + File.separator + fileName;\n                File savefile = new File(savePath);\n                FileCopyUtils.copy(data, savefile);\n                dbPath = bizPath + File.separator + fileName;\n            }else {\n                InputStream in = new ByteArrayInputStream(data);\n                String relativePath = bizPath+\"/\"+fileName;\n                if(CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)){\n                    dbPath = MinioUtil.upload(in,relativePath);\n                }else if(CommonConstant.UPLOAD_TYPE_OSS.equals(uploadType)){\n                    dbPath = OssBootUtil.upload(in,relativePath);\n                }","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/jeecgboot/JeecgBoot/blob/96fb33f5ec68516da0b0147da06b2eb0419e063a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/CommonUtils.java#L56-L92","documentation":"This is a path-traversal (CWE-22) security guard in the local-upload path of CommonUtils.uploadOnlineImage. After running SsrfFileTypeFilter.checkPathTraversal on the bizPath, it normalizes both the basePath root and the resolved target directory and asserts targetDir.startsWith(root). If a crafted bizPath (e.g. ../../etc) escapes the base directory after normalization, it throws SecurityException, refusing the upload. Added for issues/9435.","triggerScenarios":"An upload request supplies a bizPath containing traversal sequences (../, ..\\, absolute paths, or URL-encoded variants) that, after Path.normalize(), resolves outside the configured upload basePath. This is an attacker-controlled input reaching the filesystem write.","commonSituations":"Penetration testing / security scans submitting crafted bizPath values; a buggy client constructing bizPath from user input without sanitization; integration with an upstream service that forwards raw paths; a legitimate bizPath that accidentally contains '..' segments.","solutions":["Sanitize bizPath on the client and server: reject '..' segments and absolute paths before calling upload.","If a legitimate nested path is needed, ensure it is relative and within the upload root.","Do not forward raw user input as bizPath — map it to a fixed set of allowed subdirectories.","Confirm basePath is configured to the intended upload root (application.yml upload path)."],"exampleFix":"// before — client sends raw user-controlled path\nbizPath = userInput.folder; // \"../../etc\"\n\n// after — whitelist/map to a safe relative dir\nbizPath = ALLOWED_FOLDERS.contains(userInput.folder) ? userInput.folder : \"default\";","handlingStrategy":"validation","validationCode":"// Reject traversal in bizPath before calling upload\nimport org.apache.commons.lang3.StringUtils;\npublic static String sanitizeBizPath(String bizPath) {\n  if (bizPath == null || bizPath.contains(\"..\") || bizPath.startsWith(\"/\") || bizPath.startsWith(\"\\\\\")) {\n    throw new IllegalArgumentException(\"Invalid bizPath\");\n  }\n  return bizPath;\n}","typeGuard":"public static boolean isBizPathSafe(String bizPath, Path root) {\n  Path resolved = root.resolve(bizPath).normalize();\n  return resolved.startsWith(root);\n}","tryCatchPattern":"try {\n  CommonUtils.uploadOnlineImage(data, bizPath, uploadType, basePath);\n} catch (SecurityException e) {\n  // log and reject the request with 400\n  response.sendError(400, \"Invalid upload path\");\n}","preventionTips":["Never forward raw user input as bizPath.","Whitelist allowed subdirectories for uploads.","Run path-traversal checks (SsrfFileTypeFilter.checkPathTraversal) early.","Keep basePath configured to a dedicated upload root."],"tags":["backend","security","path-traversal","cwe-22","upload","jeecg"],"backgroundTag":null,"analyzedSha":"96fb33f5ec68516da0b0147da06b2eb0419e063a","analyzedAt":"2026-08-14T00:04:16.786Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}