spring-projects/spring-ai · error · IOException

Invalid filename for file '': resolves outside target direct

Error message

Invalid filename for file '': resolves outside target directory ''

What it means

Final hardening check in resolveSafeChildPath: after resolving the single-segment name against the normalized absolute target directory, it throws this IOException if the resolved path does not remain under the base directory. This backstops any future rule changes or platform path quirks that could let a file land outside the target directory (a path-traversal attack).

Source

Thrown at models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicSkillsResponseHelper.java:177

		}
		if (name.isAbsolute() || name.getRoot() != null) {
			throw new IOException("Invalid filename for file '" + fileId + "': absolute path '" + rawName + "'");
		}
		if (name.getNameCount() != 1) {
			throw new IOException(
					"Invalid filename for file '" + fileId + "': must be a single path segment '" + rawName + "'");
		}
		String only = name.getName(0).toString();
		if (only.equals(".") || only.equals("..")) {
			throw new IOException("Invalid filename for file '" + fileId + "': '" + rawName + "'");
		}

		// One extra hardening check to make sure nothing fell through the cracks above
		// (future tweaks to the rules, odd platform path quirks, etc.).
		Path base = targetDir.toAbsolutePath().normalize();
		Path resolved = base.resolve(only).normalize();
		if (!resolved.startsWith(base)) {
			throw new IOException(
					"Invalid filename for file '" + fileId + "': resolves outside target directory '" + rawName + "'");
		}
		return resolved;
	}

	private static void extractFileIdsFromBashResult(BashCodeExecutionToolResultBlock resultBlock,
			List<String> fileIds) {
		BashCodeExecutionToolResultBlock.Content content = resultBlock.content();
		if (content.isBashCodeExecutionResultBlock()) {
			for (BashCodeExecutionOutputBlock outputBlock : content.asBashCodeExecutionResultBlock().content()) {
				fileIds.add(outputBlock.fileId());
			}
		}
	}

	private static void extractFileIdsFromCodeExecutionResult(CodeExecutionToolResultBlock resultBlock,
			List<String> fileIds) {
		CodeExecutionToolResultBlockContent content = resultBlock.content();

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Treat the filename as untrusted: reject any name containing separators, '..', or non-portable characters before calling filePath.
  2. Verify your target directory is not itself a symlink whose parent chain changes resolution semantics.
  3. Audit where the filename originates (API response field vs. user input) and whitelist alphanumerics/dots/dashes/hyphens.
  4. Catch the IOException, log the rejected name, and abort processing that file.

Example fix

// before
Path p = helper.filePath(fileId, apiReturnedName);
// after
if (!apiReturnedName.matches("[A-Za-z0-9._-]+")) {
    throw new IllegalArgumentException("Unsafe filename: " + apiReturnedName);
}
Path p = helper.filePath(fileId, apiReturnedName);
Defensive patterns

Strategy: validation

Validate before calling

static boolean staysUnderBase(Path base, String name) {
    return base.toAbsolutePath().normalize()
        .resolve(name).normalize().startsWith(base.toAbsolutePath().normalize());
}

Type guard

static String strictSafeName(String name) {
    if (name == null || name.contains("/") || name.contains("\\")
        || !name.matches("[A-Za-z0-9._-]+")) {
        throw new IllegalArgumentException("Unsafe filename: " + name);
    }
    return name;
}

Try / catch

try {
    Path p = helper.filePath(fileId, name);
} catch (IOException e) {
    securityLog.warn("Path traversal attempt rejected: {}", name);
    throw e;
}

Prevention

When it happens

Trigger: A filename that, after normalize(), escapes the base dir — e.g. via platform-specific quirks or a name that survived earlier checks such as a case where resolve introduces '..' behavior (symbolic-link-adjacent or driver/root-relative oddities).

Common situations: Hostile filenames returned by a compromised or mocked API endpoint; unusual filesystems (Windows drive-relative paths); regression after changing validation rules.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11). Data as JSON: /api/errors/2b9143cb7edf1a1b. Report an issue: GitHub.