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

  1. Provide an output path that is a child of the configured sandbox directory
  2. Check the resolved path with filepath.Clean and confirm it starts with filepath.Clean(sandboxDir)+Separator before issuing the command
  3. If you need a different location, restart nebula with the sandbox configured to (or above) that directory
  4. 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

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


AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03). Data as JSON: /api/errors/366c960729aa6960. Report an issue: GitHub.