{"record":{"id":"f37d5930ed148b56","repo":"hashicorp/nomad","slug":"path-escapes-the-alloc-directory","errorCode":null,"errorMessage":"path escapes the alloc directory","messagePattern":"path escapes the alloc directory","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/allocdir/alloc_dir.go","lineNumber":427,"sourceCode":"\n// sanitizePath checks that the path does not escape the alloc directory,\n// does not read into the secrets or private directories and returns an absolute\n// path of the provided path.\nfunc (a *AllocDir) sanitizePath(path string) (string, error) {\n\t// In some non linux systmes, directories like /var and /tmp resolve to\n\t// /private/var and /private/tmp.\n\tresolvedAllocDir, err := filepath.EvalSymlinks(a.AllocDir)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to resolve alloc directory: %w\", err)\n\t}\n\n\trequestedPath, err := filepath.Abs(filepath.Join(resolvedAllocDir, path))\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to resolve requested path: %w\", err)\n\t}\n\n\tif err := escapingfs.ChildEscapesParentDir(resolvedAllocDir, requestedPath); err != nil {\n\t\treturn \"\", fmt.Errorf(\"path escapes the alloc directory\")\n\t}\n\n\ta.mu.RLock()\n\tdefer a.mu.RUnlock()\n\n\t// Check it does not access the secrets or private directories\n\tfor _, taskDir := range a.TaskDirs {\n\t\trps := strings.ReplaceAll(requestedPath, \"/Secrets\", \"/secrets\")\n\t\tif err := escapingfs.ChildEscapesParentDir(taskDir.SecretsDir, rps); err == nil {\n\t\t\treturn \"\", fmt.Errorf(\"Reading secret file prohibited: %s\", path)\n\t\t}\n\n\t\trpp := strings.ReplaceAll(requestedPath, \"/Private\", \"/private\")\n\t\tif err := escapingfs.ChildEscapesParentDir(taskDir.PrivateDir, rpp); err == nil {\n\t\t\treturn \"\", fmt.Errorf(\"Reading secret file prohibited: %s\", path)\n\t\t}\n\t}\n","sourceCodeStart":409,"sourceCodeEnd":445,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/allocdir/alloc_dir.go#L409-L445","documentation":"sanitizePath() enforces containment: escapingfs.ChildEscapesParentDir verifies the requested path stays within the resolved alloc directory. A path that escapes (e.g. via \"..\") returns the fixed error \"path escapes the alloc directory\" — a security guard against arbitrary file access.","triggerScenarios":"Calling List/Stat/ReadAt/BlockUntilExists/ChangeEvents with a relative path containing enough \"..\" components to resolve outside the alloc dir, or a path that follows a symlink out of it.","commonSituations":"Misbehaving or malicious task/artifact config referencing paths like ../../etc/passwd; templates or filesystem APIs given host paths instead of alloc-relative paths.","solutions":["Use paths relative to the alloc dir and never start with / or contain .. that exits it","Use task-local dirs (NOMAD_ALLOC_DIR env, task dir) for file access","Remove or fix symlinks inside the alloc dir that point outside","Validate user-supplied paths in tooling built on the Nomad filesystem API"],"exampleFix":"// before\nallocDir.ReadAt(\"../../../../etc/passwd\", 0)\n// after\nallocDir.ReadAt(\"task/mytask/out.txt\", 0)","handlingStrategy":"validation","validationCode":"func safeRel(base, p string) error {\n    abs, err := filepath.Abs(filepath.Join(base, p))\n    if err != nil { return err }\n    rel, err := filepath.Rel(base, abs)\n    if err != nil { return err }\n    if rel == \"..\" || strings.HasPrefix(rel, \"..\"+string(os.PathSeparator)) {\n        return fmt.Errorf(\"path escapes base\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"f, err := allocDir.ReadAt(relPath, 0)\nif err != nil && strings.Contains(err.Error(), \"path escapes the alloc directory\") {\n    return ErrPathTraversalRejected // do not retry; reject the request\n}","preventionTips":["Never pass absolute or ..-containing paths to the alloc file API","Use NOMAD_ALLOC_DIR / task-dir-relative paths","Avoid symlinks that point outside the alloc dir","Treat this error as a request bug or probe, and reject without retry"],"tags":["security","path-traversal","filesystem"],"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"}