slackhq/nebula · error
path %q resolves to the sandbox directory itself %q
Error message
path %q resolves to the sandbox directory itself %q
What it means
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.
Source
Thrown at ssh.go:548
}
// sshSanitizeFilePath validates that the given file path is within the sandbox directory.
// If sandboxDir is empty, the path is returned as-is for backwards compatibility.
func sshSanitizeFilePath(sandboxDir, filePath string) (string, error) {
if sandboxDir == "" {
return filePath, nil
}
// Clean and resolve the path relative to the sandbox directory
if !filepath.IsAbs(filePath) {
filePath = filepath.Join(sandboxDir, filePath)
}
cleaned := filepath.Clean(filePath)
// Ensure the resolved path is within the sandbox directory
cleanedSandbox := filepath.Clean(sandboxDir)
if cleaned == cleanedSandbox {
return "", fmt.Errorf("path %q resolves to the sandbox directory itself %q", filePath, sandboxDir)
}
if !strings.HasPrefix(cleaned, cleanedSandbox+string(filepath.Separator)) {
return "", fmt.Errorf("path %q is outside the sandbox directory %q", filePath, sandboxDir)
}
return cleaned, nil
}
func sshStartCpuProfile(sandboxDir string, fs any, a []string, w sshd.StringWriter) error {
if len(a) == 0 {
err := w.WriteLine("No path to write profile provided")
return err
}
filePath, err := sshSanitizeFilePath(sandboxDir, a[0])
if err != nil {
return w.WriteLine(err.Error())
}View on GitHub (pinned to dd8f660c0a)
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
Example fix
// before (over SSH debug command) profile cpu /var/lib/nebula-sandbox // after profile cpu /var/lib/nebula-sandbox/cpu.pprof
Defensive patterns
Strategy: validation
Validate before calling
func isSandboxItself(filePath, sandboxDir string) bool {
return filepath.Clean(filePath) == filepath.Clean(sandboxDir)
}
// only issue the profile command when !isSandboxItself(p, sandboxDir) && strings.HasPrefix(filepath.Clean(p), filepath.Clean(sandboxDir)+string(filepath.Separator)) Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- path %q is outside the sandbox directory %q
- ErrPeerRejected
- ErrPublicKeyMismatch
- unable to find host
- unable to find host with relay
AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03).
Data as JSON: /api/errors/8104e66e583706af.
Report an issue: GitHub.