alibaba/nacos · critical · SecurityException
Path escapes target directory: {target} is outside {baseDir}
Error message
Path escapes target directory: {target} is outside {baseDir} What it means
Thrown by SkillUtils.validatePathContainment as a SecurityException when a resolved target Path, after normalization, does not start with the normalized base directory. This is the containment backstop that catches path-escape attempts the lexical '..'/absolute checks might miss (e.g. symlink resolution or platform-specific normalization).
Source
Thrown at api/src/main/java/com/alibaba/nacos/api/ai/model/skills/SkillUtils.java:189
}
if (path.contains(PATH_TRAVERSAL_SEQUENCE)) {
throw new SecurityException("Path traversal detected: " + path);
}
if (path.startsWith("/") || path.startsWith("\\")) {
throw new SecurityException("Absolute path not allowed: " + path);
}
}
/**
* Validate that a resolved path stays within the expected base directory.
*
* @param baseDir the base directory that must contain the target
* @param target the resolved target path
* @throws SecurityException if target escapes baseDir
*/
public static void validatePathContainment(Path baseDir, Path target) {
if (!target.normalize().startsWith(baseDir.normalize())) {
throw new SecurityException(
"Path escapes target directory: " + target + " is outside " + baseDir);
}
}
/**
* Validate that byte array is a valid ZIP file by checking the magic number header.
*
* @param data the byte array to validate
* @throws IllegalArgumentException if data is null, too short, or does not have ZIP magic header
*/
public static void validateZipBytes(byte[] data) {
if (data == null || data.length < ZIP_MIN_SIZE) {
throw new IllegalArgumentException(
"Invalid ZIP data: too short (" + (data == null ? 0 : data.length) + " bytes)");
}
for (int i = 0; i < ZIP_MAGIC.length; i++) {
if (data[i] != ZIP_MAGIC[i]) {
throw new IllegalArgumentException(View on GitHub (pinned to 9b989acdf1)
Solutions
- Ensure both baseDir and target are absolute and resolved (toRealPath) before comparing.
- Remove symlinks inside the base directory or resolve them and re-validate against the real base.
- Use Paths.get(baseDir).resolve(name).normalize() consistently so target is always derived from baseDir.
Example fix
// before
Path base = Paths.get("/skills");
Path target = Paths.get("/skills/../etc/hosts");
SkillUtils.validatePathContainment(base, target); // throws
// after
Path base = Paths.get("/skills").toAbsolutePath().normalize();
Path target = base.resolve("skillName/SKILL.md").normalize();
SkillUtils.validatePathContainment(base, target); // ok Defensive patterns
Strategy: validation
Validate before calling
// Derive target from base so containment always holds Path base = Paths.get(baseDir).toAbsolutePath().normalize(); Path target = base.resolve(relativeName).normalize(); SkillUtils.validatePathContainment(base, target);
Type guard
static boolean isContained(Path base, Path target) {
Path b = base.toAbsolutePath().normalize();
Path t = target.toAbsolutePath().normalize();
return t.startsWith(b);
} Try / catch
try {
SkillUtils.validatePathContainment(baseDir, target);
} catch (SecurityException e) {
// containment violation — do not write the file
throw new IOException("Refusing to write outside base dir", e);
} Prevention
- Always derive target paths via base.resolve(name).normalize() rather than accepting caller-supplied absolute paths.
- Resolve symlinks (toRealPath) under the base before containment checks.
- Treat a containment failure as a security incident, not a recoverable error.
When it happens
Trigger: Calling validatePathContainment(baseDir, target) where target.normalize().startsWith(baseDir.normalize()) is false — for example base=/skills, target=/skills/../etc/hosts (resolved), or a symlink in the skill tree resolves outside base.
Common situations: A base directory containing a symlink that points outside the tree; a resolved resource path that escapes after normalization due to '..' segments surviving an earlier weak check; mismatched absolute vs relative base and target paths.
Related errors
- Absolute path not allowed: {path}
- Skill directory already exists: {skillDir}
- Failed to delete: {path}
- Invalid ZIP data: too short ({length} bytes)
- Invalid ZIP data: missing ZIP magic header (PK\x03\x04)
AI-assisted analysis of alibaba/nacos@9b989acdf1 (2026-08-14).
Data as JSON: /api/errors/2a913403f4890310.
Report an issue: GitHub.