hashicorp/nomad · error
file path escapes capture directory
Error message
file path escapes capture directory
What it means
operator debug writes its capture bundle under a collection directory and treats that directory as a filesystem sandbox. Before creating any path, mkdir joins the requested segments and asks escapingfs.PathEscapesSandbox whether the joined path still resolves inside c.collectDir; if not (e.g. via ".." segments, absolute paths, or symlinked components), creation is refused with this error to prevent the capture from writing outside the bundle.
Source
Thrown at command/operator_debug.go:797
return nil
}
// path returns platform specific paths in the tmp root directory
func (c *OperatorDebugCommand) path(paths ...string) string {
ps := []string{c.collectDir}
ps = append(ps, paths...)
return filepath.Join(ps...)
}
// mkdir creates directories in the tmp root directory
func (c *OperatorDebugCommand) mkdir(paths ...string) error {
joinedPath := c.path(paths...)
// Ensure path doesn't escape the sandbox of the capture directory
escapes := escapingfs.PathEscapesSandbox(c.collectDir, joinedPath)
if escapes {
return fmt.Errorf("file path escapes capture directory")
}
return escapingfs.EnsurePath(joinedPath, true)
}
// startMonitors starts go routines for each node and client
func (c *OperatorDebugCommand) startMonitors(client *api.Client) {
// if requested, start monitor export first
if c.logLookback != 0 || c.logFileExport {
for _, id := range c.nodeIDs {
go c.startMonitorExport(clientDir, "node_id", id, client)
}
for _, id := range c.serverIDs {
go c.startMonitorExport(serverDir, "server_id", id, client)
}
}
for _, id := range c.nodeIDs {View on GitHub (pinned to 482b49bf1a)
Solutions
- Sanitize or reject node/file names before passing them to mkdir (strip '/', '\\', and '..' segments)
- Compute output paths relative to the capture dir with filepath.Join and validate with filepath.Rel that the result has no '..' prefix
- Remove or replace symlinks inside the capture directory that point outside it
- If you control the caller, pass simple flat names (letters, digits, dash, underscore) for dirs/files
Example fix
// before
c.mkdir("..", "outside.txt")
// after
safe := filepath.Base(rawName) // strips traversal
c.mkdir(safe)
Defensive patterns
Strategy: validation
Validate before calling
rel, err := filepath.Rel(collectDir, filepath.Join(collectDir, name))
if err != nil || strings.HasPrefix(rel, "..") {
return fmt.Errorf("path %q escapes capture dir", name)
} Prevention
- Build output names with filepath.Base to strip any traversal components
- Reject names containing '/', '\\', or '..' before use
- Avoid symlinks inside the capture directory
When it happens
Trigger: Calling mkdir with path segments containing "..", an absolute segment, or names that resolve (through symlinks) outside the capture dir. Internally reachable from startMonitor, startMonitorExport and captureEventStream when composing output filenames from node names or event data that contain path separators or traversal sequences.
Common situations: Node/member names or export prefixes derived from external data containing '/' or '..'; a misconfigured output subdirectory value; symlink inside collectDir pointing at /tmp or another mount; Windows-style separators sneaking into names.
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
- path escapes the alloc directory
- file path %q escapes capture directory %q
- Reading secret file prohibited: %s
- archive contains object that escapes alloc dir
- archive contains symlink that escapes alloc dir
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/f80cf1a3a4bb116a.
Report an issue: GitHub.