{"record":{"id":"8104e66e583706af","repo":"slackhq/nebula","slug":"path-q-resolves-to-the-sandbox-directory-itself","errorCode":null,"errorMessage":"path %q resolves to the sandbox directory itself %q","messagePattern":"path %q resolves to the sandbox directory itself %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ssh.go","lineNumber":548,"sourceCode":"}\n\n// sshSanitizeFilePath validates that the given file path is within the sandbox directory.\n// If sandboxDir is empty, the path is returned as-is for backwards compatibility.\nfunc sshSanitizeFilePath(sandboxDir, filePath string) (string, error) {\n\tif sandboxDir == \"\" {\n\t\treturn filePath, nil\n\t}\n\n\t// Clean and resolve the path relative to the sandbox directory\n\tif !filepath.IsAbs(filePath) {\n\t\tfilePath = filepath.Join(sandboxDir, filePath)\n\t}\n\tcleaned := filepath.Clean(filePath)\n\n\t// Ensure the resolved path is within the sandbox directory\n\tcleanedSandbox := filepath.Clean(sandboxDir)\n\tif cleaned == cleanedSandbox {\n\t\treturn \"\", fmt.Errorf(\"path %q resolves to the sandbox directory itself %q\", filePath, sandboxDir)\n\t}\n\tif !strings.HasPrefix(cleaned, cleanedSandbox+string(filepath.Separator)) {\n\t\treturn \"\", fmt.Errorf(\"path %q is outside the sandbox directory %q\", filePath, sandboxDir)\n\t}\n\n\treturn cleaned, nil\n}\n\nfunc sshStartCpuProfile(sandboxDir string, fs any, a []string, w sshd.StringWriter) error {\n\tif len(a) == 0 {\n\t\terr := w.WriteLine(\"No path to write profile provided\")\n\t\treturn err\n\t}\n\n\tfilePath, err := sshSanitizeFilePath(sandboxDir, a[0])\n\tif err != nil {\n\t\treturn w.WriteLine(err.Error())\n\t}","sourceCodeStart":530,"sourceCodeEnd":566,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/ssh.go#L530-L566","documentation":"sshSanitizeFilePath validates a file path supplied over the SSH debug interface before it is used for profile output. It rejects the path if, after filepath.Clean, it equals the sandbox directory itself, since writing there would clobber the sandbox root. This is a path-traversal safety guard for handlers like sshStartCpuProfile, sshGetHeapProfile, and sshGetMutexProfile.","triggerScenarios":"Calling an SSH debug command (cpu profile, heap profile, mutex profile output) with a file argument that cleans to exactly the sandbox directory, e.g. passing the sandbox dir itself, '.', './', or a trailing-slash/traversal form like '/sandbox/dir/..' that resolves to it.","commonSituations":"Operators pass the sandbox root or a '.'-relative path when specifying profile output files over `nebula -ssh` debug channels; symlinks or '..' components silently normalize to the sandbox directory.","solutions":["Pass a path strictly inside the sandbox directory, e.g. <sandboxDir>/cpu.pprof, not the sandbox dir itself","Avoid '.' or '..' path components and trailing separators in the file argument","Create a dedicated profiles subdirectory under the sandbox and write there","If a symlink or mount makes the intended path resolve to the sandbox root, use the real target path instead"],"exampleFix":"// before (over SSH debug command)\nprofile cpu /var/lib/nebula-sandbox\n// after\nprofile cpu /var/lib/nebula-sandbox/cpu.pprof","handlingStrategy":"validation","validationCode":"func isSandboxItself(filePath, sandboxDir string) bool {\n\treturn filepath.Clean(filePath) == filepath.Clean(sandboxDir)\n}\n// only issue the profile command when !isSandboxItself(p, sandboxDir) && strings.HasPrefix(filepath.Clean(p), filepath.Clean(sandboxDir)+string(filepath.Separator))","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always include a filename, never pass the sandbox directory itself as the output target","Avoid '.' and '..' components; pre-clean paths with filepath.Clean","Keep a dedicated profiles/ subdirectory under the sandbox for dump files"],"tags":["path-validation","ssh-debug","security","nebula"],"backgroundTag":"path-outside-sandbox","analyzedSha":"dd8f660c0ac37903ec4080ca4d3c861ba9342ceb","analyzedAt":"2026-09-03T11:13:55.444Z","contentChangedAt":"2026-09-03T11:13:55.444Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}