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

  1. Ensure both baseDir and target are absolute and resolved (toRealPath) before comparing.
  2. Remove symlinks inside the base directory or resolve them and re-validate against the real base.
  3. 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

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


AI-assisted analysis of alibaba/nacos@9b989acdf1 (2026-08-14). Data as JSON: /api/errors/2a913403f4890310. Report an issue: GitHub.