Wish has SCP Path Traversal that allows arbitrary file read/write
Date: 2026-04
Affected: go/github.com/charmbracelet/wish, go/charm.land/wish/v2
Attack vector: path-traversal
Category: vulnerability-disclosure
Severity: Critical
CVE: CVE-2026-41589
CWE: CWE-22
CVSS: 9.6 (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N)

## Summary The SCP middleware in `charm.land/wish/v2` is vulnerable to path traversal attacks. A malicious SCP client can read arbitrary files from the server, write arbitrary files to the server, and create directories outside the configured root directory by sending crafted filenames containing `../` sequences over the SCP protocol. ## Affected Versions - `charm.land/wish/v2` — all versions through commit `72d67e6` (current `main`) - `github.com/charmbracelet/wish` — likely all v1 versions (same code pattern) ## Details ### Root Cause The `fileSystemHandler.prefixed()` method in `scp/filesystem.go:42-48` is intended to confine all file operations to a configured root directory. However, it fails to validate that the resolved path remains within the root: ```go func (h *fileSystemHandler) prefixed(path string) string { path = filepath.Clean(path) if strings.HasPrefix(path, h.root) { return path } return filepath.Join(h.root, path) } ``` When `path` contains `../` components, `filepath.Clean` resolves them but does not reject them. The subsequent `filepath.Join(h.root, path)` produces a path that escapes the root directory. ### Attack Vector 1: Arbitrary File Write (scp -t) When receiving files from a client (`scp -t`), filenames are parsed from the SCP protocol wire using regexes that accept arbitrary strings: ```go reNewFile = regexp.MustCompile(`^C(\d{4}) (\d+) (.*)$`) reNewFolder = regexp.MustCompile(`^D(\d{4}) 0 (.*)$`) ``` The captu


Mitigations:

Architectural layers (MAESTRO):