Windsurf path-traversal via prompt-injected README — Cascade reads/writes arbitrary files (CVE-2025-62353, Oct 2025)
TL;DR
CVE-2025-62353 (CVSS 9.8) — HiddenLayer found that Windsurf's Cascade agent followed instructions hidden in a project's README.md to change its workspace path to the filesystem root (/ or C:\) and then use write_to_file (or its read sibling) to read and write arbitrary files on the developer's machine — even with Auto-Execution OFF and write_to_file on the explicit deny list. The classic "confused deputy": Cascade has the user's privileges and faithfully carried out an attacker's text. Affects all Windsurf ≤ 1.12.12. Distinct discrete bug from Windsurf zero-click MCP RCE (CVE-2026-30615). Update Windsurf and treat any repo you ever opened with old Windsurf as a potential exfil surface.
What happened
HiddenLayer demonstrated indirect prompt injection against the Cascade AI agent inside Windsurf. Their PoC placed natural-language instructions inside an HTML comment in a project's README.md — invisible to a human reader, but plain text to the model. When Cascade processed the README during routine code analysis (not a deliberate user action), the embedded prompt did two things:
- Changed Cascade's workspace path to the filesystem root (
/on Linux/macOS,C:\on Windows). - Directed
write_to_file(and the equivalent read tool) at a path well outside the original project —~/.ssh/id_rsa,~/.aws/credentials, browser cookie stores, etc.
Cascade obeyed both. The auth/permission layer that should have caught this never fired: the deny-list check used Cascade's current workspace, which the injection had already rewritten (HiddenLayer-credited writeups via Vibe Graveyard, Witness AI).
Two properties make this a sharp class:
- Auto-Execution OFF did not help. The tool-deny check ran on the wrong scope.
- write_to_file on the deny list did not help. Same scope confusion (Vibe Graveyard).
Same "two parsers, one string" family as the Claude Code argv-smuggling deeplink RCE, Claude Code SOCKS5 null-byte sandbox bypass, and Starlette BadHost (CVE-2026-48710): the validator and the executor disagreed on the canonical value, the security check ran on the wrong one.
Am I affected?
# Windsurf version
windsurf --version
# or: Help → About inside the IDE
# Vulnerable: ≤ 1.12.12 (all versions, per HiddenLayer)
# Patched: see Windsurf changelog for the fixed release line after Oct 2025
If you ever opened an untrusted repo in Windsurf ≤ 1.12.12 — public clones, customer-submitted bug repros, contractor code — your local secrets were reachable. Even repos that look clean to a human can carry the payload because the injection lives in invisible-comment text.
# Quick local check for prompt-injection markers in README.md across repos
grep -RnE '<!--.*(ignore previous|system prompt|workspace|deny.list|write_to_file|/etc/|~/.ssh|~/.aws)' \
--include 'README*' ~/projects 2>/dev/null
# Also grep for zero-width Unicode in agent-touched files (cf. TrapDoor)
perl -nlE 'print "$ARGV:$.:$_" if /[\x{200B}\x{200C}\x{200D}\x{FEFF}\x{2060}-\x{206F}]/' \
$(find ~/projects -name 'README*' 2>/dev/null) 2>/dev/null
If you are affected
- Upgrade Windsurf to the latest release (Windsurf shipped a fix; pin to the current available version — silent auto-update is fine for security but lagged installs are exposed).
- Rotate every credential reachable from the developer machine. SSH keys, cloud profiles, GitHub/GitLab PATs, browser-stored session cookies, npm/PyPI tokens.
- Audit any repo you opened in Windsurf ≤ 1.12.12 for invisible-comment or zero-width-Unicode prompt injection in README/CONTRIBUTING/
.cursorrules/AGENTS.md/CLAUDE.md/WINDSURFRULES.md. Same write-target surface as TrapDoor. - Don't open arbitrary repos in an AI IDE with cloud creds loaded. Use a fresh user profile, a devcontainer, or a separate machine for untrusted code.
Prevention
→ prevention/agent-sandboxing.md → prevention/mcp-hygiene.md
Pattern to internalize: an AI agent that can change its own workspace scope from text inside a file it's reading has no usable permission model. The deny list runs on the current scope; the injection rewrites the scope; deny list fails open. The structural fix is to bind security decisions to the initial user-confirmed workspace and refuse mid-session scope mutations from agent input — the same lesson as Starlette BadHost (canonical scope is the security boundary, not the derived/rebuilt one).
Sources
- NVD — CVE-2025-62353 detail
- Vibe Graveyard — Windsurf AI editor critical path traversal enables data exfiltration
- Witness AI — Windsurf security: risks, CVEs, and enterprise governance
- SG Cyber Security Agency — Security Bulletin 22 October 2025 — official advisory listing CVE-2025-62353.
- Windsurf — Security page — vendor security disclosures index.