{"record":{"id":"1c9eccc1a18b0aff","repo":"alibaba/nacos","slug":"path-traversal-detected-path","errorCode":null,"errorMessage":"Path traversal detected: {path}","messagePattern":"Path traversal detected: (.+?)","errorType":"exception","errorClass":"SecurityException","httpStatus":null,"severity":"critical","filePath":"api/src/main/java/com/alibaba/nacos/api/ai/model/skills/SkillUtils.java","lineNumber":173,"sourceCode":"        } else {\n            entryPath = skillName + \"/\" + resource.getName();\n        }\n        validatePathSafety(entryPath);\n        return entryPath;\n    }\n    \n    /**\n     * Validate that a path does not contain path traversal sequences or absolute path indicators.\n     *\n     * @param path the path to validate\n     * @throws SecurityException if path contains unsafe sequences\n     */\n    public static void validatePathSafety(String path) {\n        if (path == null) {\n            return;\n        }\n        if (path.contains(PATH_TRAVERSAL_SEQUENCE)) {\n            throw new SecurityException(\"Path traversal detected: \" + path);\n        }\n        if (path.startsWith(\"/\") || path.startsWith(\"\\\\\")) {\n            throw new SecurityException(\"Absolute path not allowed: \" + path);\n        }\n    }\n    \n    /**\n     * Validate that a resolved path stays within the expected base directory.\n     *\n     * @param baseDir the base directory that must contain the target\n     * @param target  the resolved target path\n     * @throws SecurityException if target escapes baseDir\n     */\n    public static void validatePathContainment(Path baseDir, Path target) {\n        if (!target.normalize().startsWith(baseDir.normalize())) {\n            throw new SecurityException(\n                \"Path escapes target directory: \" + target + \" is outside \" + baseDir);\n        }","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/alibaba/nacos/blob/9b989acdf181d00898f2e8839257bb2b2a3cefe3/api/src/main/java/com/alibaba/nacos/api/ai/model/skills/SkillUtils.java#L155-L191","documentation":"SkillUtils.validatePathSafety throws SecurityException when a resource path contains the '..' sequence. This is a path-traversal guard that prevents ZIP-slip attacks during skill packaging and extraction — a malicious or malformed entry name could escape the target directory.","triggerScenarios":"A Skill resource path contains '..', e.g. '../../etc/passwd' or 'subdir/../other'. Also triggered during ZIP extraction if a ZipEntry name contains traversal sequences. A user-uploaded skill ZIP includes entries with relative-parent references.","commonSituations":"Processing an untrusted or user-uploaded skill ZIP. A resource path was constructed by concatenating user input without sanitization. A legitimate path that contains '..' as part of a directory name (e.g. 'my..app/resource').","solutions":["Sanitize or reject resource paths containing '..' before adding them to a Skill.","When extracting ZIPs, canonicalize each entry path and verify it starts with the base directory.","Reject any ZIP entry whose name contains '..' at upload/validation time.","Educate users that '..' in resource paths is not permitted in skill packaging."],"exampleFix":"// before\nskill.getResource().add(new SkillResource(\"../../secret.txt\", data));\nSkillUtils.validatePathSafety(\"../../secret.txt\"); // SecurityException\n\n// after -- reject at upload\nfor (SkillResource r : skill.getResource()) {\n    SkillUtils.validatePathSafety(r.getPath()); // fails fast\n    // or sanitize: r.setPath(r.getPath().replace(\"..\", \"\"));\n}\n\n// For ZIP extraction:\nPath resolved = baseDir.resolve(entryName).normalize();\nif (!resolved.startsWith(baseDir)) {\n    throw new SecurityException(\"ZIP slip: \" + entryName);\n}","handlingStrategy":"validation","validationCode":"// Pre-check before adding resources or extracting ZIP entries\npublic static void assertPathSafe(String path) {\n    if (path != null && path.contains(\"..\")) {\n        throw new SecurityException(\"Rejected path with traversal: \" + path);\n    }\n}\n\n// For ZIP extraction:\nPath resolved = baseDir.resolve(entryName).normalize();\nif (!resolved.startsWith(baseDir)) {\n    throw new SecurityException(\"Entry escapes base dir: \" + entryName);\n}","typeGuard":"public static boolean isPathSafe(String path) {\n    if (path == null) return true;\n    if (path.contains(\"..\")) return false;\n    if (path.startsWith(\"/\") || path.startsWith(\"\\\\\")) return false;\n    return true;\n}","tryCatchPattern":"try {\n    SkillUtils.validatePathSafety(resourcePath);\n} catch (SecurityException e) {\n    logger.error(\"Rejected unsafe path: {}\", resourcePath);\n    // skip this entry or abort the operation\n    throw new ResponseStatusException(HttpStatus.BAD_REQUEST,\n        \"Resource path contains forbidden traversal sequence\");\n}","preventionTips":["Treat all user-supplied paths and ZIP entry names as untrusted.","Canonicalize and verify containment within baseDir for every extracted path.","Reject paths containing '..' at upload time, not just at extraction.","Keep SkillUtils.validatePathSafety in the extraction hot path as defense-in-depth."],"tags":["skill","security","path-traversal","zip-slip","validation"],"backgroundTag":null,"analyzedSha":"9b989acdf181d00898f2e8839257bb2b2a3cefe3","analyzedAt":"2026-08-14T07:17:31.569Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}