{"record":{"id":"f80cf1a3a4bb116a","repo":"hashicorp/nomad","slug":"file-path-escapes-capture-directory","errorCode":null,"errorMessage":"file path escapes capture directory","messagePattern":"file path escapes capture directory","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"command/operator_debug.go","lineNumber":797,"sourceCode":"\n\treturn nil\n}\n\n// path returns platform specific paths in the tmp root directory\nfunc (c *OperatorDebugCommand) path(paths ...string) string {\n\tps := []string{c.collectDir}\n\tps = append(ps, paths...)\n\treturn filepath.Join(ps...)\n}\n\n// mkdir creates directories in the tmp root directory\nfunc (c *OperatorDebugCommand) mkdir(paths ...string) error {\n\tjoinedPath := c.path(paths...)\n\n\t// Ensure path doesn't escape the sandbox of the capture directory\n\tescapes := escapingfs.PathEscapesSandbox(c.collectDir, joinedPath)\n\tif escapes {\n\t\treturn fmt.Errorf(\"file path escapes capture directory\")\n\t}\n\n\treturn escapingfs.EnsurePath(joinedPath, true)\n}\n\n// startMonitors starts go routines for each node and client\nfunc (c *OperatorDebugCommand) startMonitors(client *api.Client) {\n\t// if requested, start monitor export first\n\tif c.logLookback != 0 || c.logFileExport {\n\t\tfor _, id := range c.nodeIDs {\n\t\t\tgo c.startMonitorExport(clientDir, \"node_id\", id, client)\n\t\t}\n\n\t\tfor _, id := range c.serverIDs {\n\t\t\tgo c.startMonitorExport(serverDir, \"server_id\", id, client)\n\t\t}\n\t}\n\tfor _, id := range c.nodeIDs {","sourceCodeStart":779,"sourceCodeEnd":815,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/command/operator_debug.go#L779-L815","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nc.mkdir(\"..\", \"outside.txt\")\n// after\nsafe := filepath.Base(rawName) // strips traversal\nc.mkdir(safe)\n","handlingStrategy":"validation","validationCode":"rel, err := filepath.Rel(collectDir, filepath.Join(collectDir, name))\nif err != nil || strings.HasPrefix(rel, \"..\") {\n\treturn fmt.Errorf(\"path %q escapes capture dir\", name)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Build output names with filepath.Base to strip any traversal components","Reject names containing '/', '\\\\', or '..' before use","Avoid symlinks inside the capture directory"],"tags":["filesystem","security","path-traversal"],"backgroundTag":"path-traversal-blocked","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}