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

  1. Pass a path strictly inside the sandbox directory, e.g. <sandboxDir>/cpu.pprof, not the sandbox dir itself
  2. Avoid '.' or '..' path components and trailing separators in the file argument
  3. Create a dedicated profiles subdirectory under the sandbox and write there
  4. 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

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


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