· via dev.to (home feed)
MCP scanner's path-traversal rule missed every 2026 write-side MCP CVE, including a 9.1 RCE
A dev.to post shows the mcpscan MCP security scanner's path-traversal rule only watched read sinks, letting every 2026 write-side MCP CVE slip past it. A write-sink regex patch closes the gap.

A security scanner built to catch bugs in MCP servers turned out to mirror the flaws it hunts. In a dev.to post, the author of mcpscan — a static analyzer that scans Model Context Protocol servers for command injection, SSRF and path traversal — audited the tool against the four path-traversal CVEs disclosed against MCP servers in 2026. Its path-traversal rule would have missed every one, including an unauthenticated remote code execution flaw rated CVSS 9.1. The post walks through the gap and ships a one-rule fix.
The blind spot
According to the post, the four CVEs hit four different maintainers but share one root cause: a file path assembled from caller-controlled input and written with no check that it stays inside an intended directory.
The list covers CVE-2026-40576 in excel-mcp-server (path traversal via file write), CVE-2026-84201 in appium-mcp-server (path traversal via a write_file tool), CVE-2026-44336 in PraisonAI MCP (RCE by writing a Python .pth file into site-packages) and CVE-2026-27825 in mcp-atlassian, where the confluence_download_attachment path allowed an unauthenticated RCE. The post says that flaw chains with SSRF issue CVE-2026-27826 to overwrite ~/.ssh/authorized_keys or drop a cron entry — no authentication and no restart required.
The reason mcpscan missed all four sits in the rule's own docstring, the author writes: MCP007 is documented as detecting path traversal in file-reading tools. It was scoped to reads from day one, while every real exploit in 2026 worked the write side.
How the rule works
mcpscan's rules are simple by design: line-by-line regex matching with no AST, so one rule runs across every language the tool supports. A path-traversal finding requires three regex layers to agree — a SINK pattern matching file-open or read calls, an INTERP pattern matching dynamically built paths (f-strings, concatenation, .format, os.path.join with a variable), and a TRAVERSAL pattern that escalates severity when a literal ../ appears on the line.
The gap lives in the sink layer. The Python pattern matched open(, .read_text(, .read_bytes(, send_file( and FileResponse(; the JavaScript pattern matched the readFile family. Since Python's open() also serves as the write sink, some writes slipped through incidentally — but write_text, write_bytes, shutil.copy and os.rename were not matched at all. That omission let the mcp-atlassian bug shape through.
The fix
The patch is additive: a second family of sink regexes feeding the same pipeline. The Python write pattern covers .write_text, .write_bytes, open() calls with w/a/x mode strings, shutil copy and move calls, and os.rename and os.replace. The JavaScript pattern covers fs.writeFile, appendFile, createWriteStream, copyFile and renameSync. Each line is checked against both sink families, and any write-sink hit escalates to HIGH even without a literal ../ — the author argues an attacker-controlled destination is a worse primitive than an attacker-controlled source, because the payload usually rides along in the same request.
The finding text now directs developers to resolve the path (the post suggests os.path.realpath plus a prefix check) and confirm it stays within an allowed base directory before writing. As verification, the author added a fixture shaped like the mcp-atlassian bug — a function writing caller-supplied bytes to /data/attachments/ plus a caller-controlled filename. It produced zero findings before the patch and a HIGH write-sink finding after.
Known limitations
The post is candid about the edges. open(path, 'r+') is both read and write, and the regex does not parse complex mode strings. Legitimate atomic-save patterns such as writing to a path plus '.tmp' will now flag, which the author considers correct: it is still an unvalidated destination. Escalating all write hits to HIGH will grow triage backlogs, which is deliberate. And because the rule is regex-based, it still cannot catch a path assembled in a different function from where it is used — a limitation the author says belongs in the documentation so users do not over-trust a clean scan.
Why it matters
For anyone running MCP servers, the practical point is that write paths are the dangerous half of path traversal. The 2026 CVE set shows what attacker-controlled destinations enable in practice: .pth injection into site-packages for code execution on import, and authorized_keys or cron overwrites for persistent shell access. Any MCP tool that accepts a filename and file content in a single call should resolve and bound the destination before writing.
For scanner maintainers, the closing lesson is to periodically replay rules against a list of recent real CVEs. Rules go stale silently: nothing breaks, they just quietly stop catching the vulnerabilities actually being disclosed.
- #mcp
- #security
- #static-analysis
- #vulnerabilities
- #rce