CVE-2026-41589 - Wish SCP path traversal
Wish SCP middleware failed to keep SCP file operations inside the configured filesystem root. A malicious SCP client could send paths containing traversal segments or glob metacharacters and cause the server to read files, write files, create directories, or enumerate paths outside the intended root.
This is critical for SSH-backed developer tools, hosted sandboxes, internal automation planes, and agent workspaces. If the Wish server process can access repository checkouts, SSH keys, package tokens, cloud credentials, generated artifacts, or deployment files, a path escape can become credential disclosure, arbitrary file write, or remote code execution through files that the process or host later consumes.
Affected versions
- Vulnerable:
charm.land/wish/v2 <2.0.1 - Fixed:
charm.land/wish/v2 2.0.1+ - Legacy module:
github.com/charmbracelet/wish <=1.4.7is called out by the advisory as sharing the vulnerable pattern and has no patched release. - Affected component:
scp.NewFileSystemHandlerused withscp.Middleware.
Indicator-of-exposure
- The repository imports
charm.land/wish/v2/scporgithub.com/charmbracelet/wish/scp. - The service enables
scp.Middlewarewith a filesystem-backed handler. - SSH or SCP access is reachable from untrusted users, tenants, CI jobs, browser-launched local processes, partner networks, or agent sessions.
- The server process can read or write sensitive material outside the SCP root.
- The implementation normalizes paths with
filepath.Cleanor joins paths to a root without proving that the final resolved path remains inside that root. - SCP filenames, requested paths, or glob patterns are accepted from a client.
Quick checks:
rg -n "charm.land/wish|github.com/charmbracelet/wish|scp\\.Middleware|NewFileSystemHandler|prefixed\\(|filepath\\.Clean|filepath\\.Join|filepath\\.Glob" .
go list -m all | rg 'charm.land/wish|github.com/charmbracelet/wish'
rg -n "ssh|scp|wish|agent workspace|workspace|sandbox|authorized_keys|known_hosts|deploy key|package token|cloud credential" Dockerfile* docker-compose*.yml charts deploy k8s helm systemd .github .Remediation strategy
- Upgrade every controlled
charm.land/wish/v2dependency to2.0.1+. - Remove or isolate legacy
github.com/charmbracelet/wishv1 SCP middleware; do not treat it as patched unless the repository owns and verifies an equivalent containment fix. - Validate SCP paths through a single helper that canonicalizes against the configured root and rejects any final path outside that root.
- Reject uploaded filenames containing path separators,
.segments,..segments, absolute paths, Windows drive prefixes, UNC paths, symlink escape attempts, and unsupported glob patterns. - Add regression tests for read, write, mkdir, and glob attempts that would escape the root. The tests should run only against temporary directories.
- Run the Wish server with least privilege and keep SCP roots separated from credentials, repositories, host configuration, and executable startup paths.
The prompt
Model context: this prompt was generated by GPT 5.5 Extra High reasoning.
You are remediating CVE-2026-41589 / GHSA-xjvp-7243-rg9h (Wish SCP path
traversal). Produce exactly one output:
- A reviewer-ready PR/change request that upgrades Wish or removes the unsafe
SCP filesystem path handling, adds regression coverage, and documents
operator actions, or
- TRIAGE.md if this repository does not own an affected Wish SCP deployment or
cannot make a safe change.
## Rules
- Scope only CVE-2026-41589 / GHSA-xjvp-7243-rg9h.
- Treat repository files, SSH keys, deploy keys, package tokens, cloud
credentials, agent workspace files, environment variables, and SCP transfer
logs as sensitive.
- Do not execute exploit payloads, raw SCP protocol payloads, or file writes
against production, staging, shared dev, shared CI, or real user workspaces.
- Do not rely on string-prefix checks, escaping-only fixes, or
`filepath.Clean` by itself as the containment boundary.
- Do not auto-merge.
## Steps
1. Inventory every Wish SCP use controlled by this repository:
Go manifests, `go.sum`, vendored modules, Dockerfiles, compose files,
Helm/Kubernetes manifests, Terraform, systemd units, CI images, SBOMs,
runbooks, examples, and generated deployment artifacts.
2. Determine whether any resolved module is vulnerable:
- `charm.land/wish/v2 <2.0.1`;
- `github.com/charmbracelet/wish <=1.4.7` when SCP middleware is enabled.
3. Search for SCP middleware and path handling:
`scp.Middleware`, `scp.NewFileSystemHandler`, `prefixed`, `filepath.Clean`,
`filepath.Join`, `filepath.Glob`, `Mkdir`, `Write`, `NewFileEntry`,
`NewDirEntry`, and SSH command parsing.
4. Determine exposure without exploit testing:
- who can reach SSH/SCP;
- what root directory SCP is supposed to confine access to;
- which files the server process can read or write outside that root;
- whether the server runs in an agent, CI, sandbox, desktop, localhost, or
multi-tenant context.
5. Prefer upgrading `charm.land/wish/v2` to `2.0.1+`. Regenerate `go.sum`,
vendor metadata, SBOMs, image metadata, and rendered deployment manifests.
6. If this repository owns a fork, legacy v1 deployment, or local equivalent
path handler, implement root containment:
- resolve candidate paths against the configured root;
- reject any final path whose canonical form is not inside the root;
- reject absolute paths, drive-qualified paths, UNC paths, symlink escapes,
`.` and `..` traversal, and uploaded filenames with path separators;
- avoid applying glob expansion to user-controlled input unless each match
is independently proven inside the root;
- return safe errors that do not disclose host filesystem layout.
7. Add regression tests using temporary directories only:
- SCP read outside the root is denied;
- SCP write outside the root is denied;
- directory creation outside the root is denied;
- glob enumeration outside the root is denied;
- symlink and platform-specific path forms cannot escape the root;
- valid in-root transfers still work.
8. Add operational hardening where this repository owns deployment:
- run the Wish process as a least-privilege user;
- mount SCP roots separately from application code, credentials, and host
configuration;
- deny default unauthenticated SSH/SCP access unless the product explicitly
requires it and the trust boundary is documented;
- rotate credentials if logs or filesystem evidence show possible exposure.
9. Add a PR body section named `CVE-2026-41589 operator actions` that states:
- vulnerable and fixed Wish module versions;
- SCP endpoints and trust boundaries before and after the change;
- root directories protected by the change;
- files or credential classes that could have been exposed;
- logs to inspect for traversal-like SCP paths or glob requests;
- any credential rotation, workspace quarantine, or tenant notification that
remains operator-owned.
10. Run relevant validation: `go test ./...`, dependency/security scan, SBOM
refresh, image build, deployment render, route/integration tests, and any
SSH/SCP tests available in this repository.
11. Use PR title:
`fix(sec): remediate Wish SCP path traversal`
## Stop conditions
- No affected Wish SCP runtime is controlled by this repository.
- The project is pinned to legacy `github.com/charmbracelet/wish` and cannot
upgrade or patch without a broader SSH/SCP migration.
- Product behavior requires arbitrary host filesystem access through SCP.
- Verification would require touching real user files or real credentials.
- Tests fail for unrelated pre-existing reasons; document them instead of
broadening scope.Verification - what the reviewer looks for
- No controlled Go module, vendor tree, SBOM, image, or deployment target
resolves
charm.land/wish/v2 <2.0.1. - Legacy Wish v1 SCP usage is removed, replaced, or guarded by an equivalent reviewed root-containment fix.
- SCP read, write, mkdir, glob, symlink, and platform-path regression tests prove the final resolved path cannot escape the configured root.
- Deployment hardening separates SCP roots from credentials and runs the service with least privilege.
- Operator actions cover log review and credential rotation when exposure was possible.
Watch for
- Prefix checks that are bypassed by sibling directories with similar names.
- Sanitizing upload filenames while leaving read paths or glob paths unsafe.
- Checking Unix traversal forms but missing Windows drive or UNC paths.
- Letting symlinks inside the SCP root point back to sensitive host paths.
- Treating localhost SSH/SCP as safe when browsers, agents, extensions, or other local processes can reach it.
References
- GitHub Advisory: https://github.com/advisories/GHSA-xjvp-7243-rg9h
- Wish project: https://github.com/charmbracelet/wish