slackhq/nebula · error
path %q is outside the sandbox directory %q
Error message
path %q is outside the sandbox directory %q
What it means
sshSanitizeFilePath ensures the requested profile output path stays within the sandbox directory using a prefix check against cleanedSandbox + the OS separator. Any path that escapes the sandbox (via '..', absolute paths elsewhere, or sibling directories sharing a name prefix) is rejected. It protects the SSH debug profile handlers from arbitrary filesystem writes.
Source
Thrown at ssh.go:551
// 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())
}
file, err := os.Create(filePath)
if err != nil {View on GitHub (pinned to dd8f660c0a)
Solutions
- Provide an output path that is a child of the configured sandbox directory
- Check the resolved path with filepath.Clean and confirm it starts with filepath.Clean(sandboxDir)+Separator before issuing the command
- If you need a different location, restart nebula with the sandbox configured to (or above) that directory
- Use relative paths only after cd-ing logically under the sandbox root; remember the guard applies to the cleaned absolute result
Example fix
// before profile heap /tmp/heap.pprof // after profile heap /var/lib/nebula-sandbox/heap.pprof
Defensive patterns
Strategy: validation
Validate before calling
func withinSandbox(p, sandboxDir string) bool {
c := filepath.Clean(p)
if c == filepath.Clean(sandboxDir) {
return false
}
return strings.HasPrefix(c, filepath.Clean(sandboxDir)+string(filepath.Separator))
} Prevention
- Pre-validate profile output paths client-side against the configured sandbox before sending the command
- Never use absolute paths outside the sandbox for SSH debug commands
- Watch for sibling directories with shared name prefixes — the separator-aware prefix check will reject them
When it happens
Trigger: SSH debug commands (cpu/heap/mutex profile) given a file path whose cleaned value does not start with sandboxDir + '/', e.g. '/tmp/other/x.pprof', '/var/lib/nebula-sandbox-evil/x' when sandbox is '/var/lib/nebula-sandbox', or anything containing a leading '..' traversal.
Common situations: Typing an absolute path outside the sandbox in a profile command; assuming a similarly-named sibling directory is allowed; running the SSH interface with a default sandbox while passing home-directory paths.
Related errors
- path %q resolves to the sandbox directory itself %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/366c960729aa6960.
Report an issue: GitHub.