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

  1. Sanitize or reject node/file names before passing them to mkdir (strip '/', '\\', and '..' segments)
  2. Compute output paths relative to the capture dir with filepath.Join and validate with filepath.Rel that the result has no '..' prefix
  3. Remove or replace symlinks inside the capture directory that point outside it
  4. 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

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


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/f80cf1a3a4bb116a. Report an issue: GitHub.