conductor-oss/conductor · critical · DocumentAccessDeniedException
Access denied: path traversal sequences are not allowed
Error message
Access denied: path traversal sequences are not allowed
What it means
Thrown by DocumentAccessPolicy.checkPathTraversal when the normalized local path contains '/../' as a segment, ends with '/..', or starts with '../'. This blocks classic path-traversal attacks even when the base directory is allowed. DocumentAccessDeniedException (SecurityException).
Source
Thrown at ai/src/main/java/org/conductoross/conductor/ai/document/DocumentAccessPolicy.java:384
+ addr.getHostAddress()
+ ")");
}
} catch (DocumentAccessDeniedException e) {
throw e;
} catch (Exception e) {
// DNS resolution failure — allow the request to proceed and fail naturally
log.debug(
"Could not resolve host '{}' for access policy check: {}",
host,
e.getMessage());
}
}
private void checkPathTraversal(String normalizedPath) {
if (normalizedPath.contains("/../")
|| normalizedPath.endsWith("/..")
|| normalizedPath.startsWith("../")) {
throw new DocumentAccessDeniedException(
"Access denied: path traversal sequences are not allowed");
}
}
/**
* Only local filesystem paths under the effective allowed directories (file-storage parentDir +
* any additional configured directories) are permitted. HTTP/HTTPS URLs are not subject to this
* check.
*/
private void checkAllowedDirectories(String originalLocation, String normalizedPath) {
List<String> dirs = effectiveAllowedDirectories;
if (dirs == null || dirs.isEmpty()) {
return;
}
// Only apply to local filesystem paths, not HTTP URLs
if (originalLocation.startsWith("http://") || originalLocation.startsWith("https://")) {
return;
}View on GitHub (pinned to cf7c3e4a8a)
Solutions
- Sanitize/reject '..' in any path component before passing it to the loader.
- Construct paths with Path.resolve against an allowed root and verify the result still startsWith the root after normalization.
- Never concatenate untrusted input directly into a filesystem path.
Example fix
// before
Path p = Path.of(baseDir, userInput); // userInput may contain ..
// after
Path root = Path.of(baseDir).normalize();
Path p = root.resolve(userInput).normalize();
if (!p.startsWith(root)) throw new IllegalArgumentException("outside allowed root"); Defensive patterns
Strategy: validation
Validate before calling
// Build paths safely: resolve under a root and confirm containment after normalization
java.nio.file.Path root = java.nio.file.Path.of(baseDir).normalize();
java.nio.file.Path resolved = root.resolve(userInput).normalize();
if (!resolved.startsWith(root) || resolved.toString().contains("/../")) {
throw new IllegalArgumentException("Refusing path outside allowed root: " + userInput);
} Try / catch
try {
loader.download(path);
} catch (SecurityException e) {
// traversal attempt — do not relax; reject and log the input as hostile
throw new SecurityException("Path traversal blocked: " + path, e);
} Prevention
- Never concatenate untrusted input into a filesystem path.
- Always resolve against a fixed root and verify startsWith after normalize().
- Reject '..' segments in any user/LLM-supplied filename.
When it happens
Trigger: A file location like /data/allowed/../../etc/passwd, /data/allowed/foo/.., or ../../secret is passed to a document loader or upload. The check runs on the Path.of(...).normalize() result, so redundant segments collapse before the test.
Common situations: User/LLM-controlled filenames concatenated to a base directory without sanitization; a path built from query params that allows '..'; attempts to escape a sandboxed document root.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Access denied: path matches blocked prefix '{prefix}'
- Access denied: file name '{fileName}' is blocked
- Access denied: host '{host}' is blocked
- Access denied: link-local address range is blocked (host res
- Access denied: loopback address is blocked (host resolves to
AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14).
Data as JSON: /api/errors/01864e01ced69795.
Report an issue: GitHub.