AI agent infrastructure · High, 8.4 of 10
One tool call out of the AI sandbox
An MCP server is how an AI agent gets hands on a machine. This one let an agent read, write and delete files in a project folder, and promised in its own documentation that anything outside that folder would be refused. For absolute paths, the check behind that promise looked at how a path was spelled, never at where it led, so a single tool call could reach any file the server could. It is public as GHSA-rxhw-rjp6-53vj, rated High at 8.4 of 10, with RIA Labs among the credited researchers.
Read this part first
If you run an MCP server that confines an agent’s file access to a project folder, read the advisory. It names the project and the affected versions. A fix shipped in version 0.1.13 on 7 September 2026. Update to it or later and both escapes this post describes are closed.
The rest of this is about the pattern, not the project. There is no exploit code below.
How bad it is
8.4 of 10. The vector is CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H, and the three Hs at the end are the whole story: anything able to shape a tool call could read, overwrite or delete any file the server process could reach. No account and no click were needed. Two things keep it below Critical. One is AV:L: the server talks to its client over a local pipe, not the network. The other is our own call: claiming a scope change would have scored it 9.3, and our report deliberately did not.
A server like this runs on the developer’s own machine, launched by their AI client, so “any file it can reach” is whatever that account can reach. If SSH keys, cloud credentials or other repositories sit on that machine under that account, the server could reach them too. Write access is the heavier half: the agent could create and overwrite files outside the project, including new folders that did not exist before.
The 8.4 assumes the tool call goes through without a person approving it. If the client is set to ask before every single call, the vector takes UI:R and the score is 7.8, still High.
Why an AI agent makes this worse
A path traversal bug in a web form needs someone to type the bad value. An MCP tool’s arguments are picked by a model, from whatever text is in its context, and that text includes things neither the model nor the operator wrote, because fetching outside text is what tools are for. This server ships a tool that returns the text of a GitHub issue, and anyone who can open an issue writes that text.
So the bad path never has to come from the user. It only has to reach something the model reads before its next tool call. That is prompt injection, and it is the realistic route our report named. For a server like this the project folder is the security boundary, the one thing its documentation promises, and the model deciding what to call is exactly the party it has to hold against.
We did not steer a live model into making that call, and did not try. We called the tool directly, over the same protocol a client uses, and it did what its own documentation says it will refuse.
One boundary, checked two different ways
Every read, write and delete went through one confinement check first. For a relative path that check was built right: work out where the path really points, work out where the project folder really is, and compare the two. For an absolute path it took a shorter branch that compared the text of the path with the text of the project folder and never asked where the path actually led.
Two kinds of path pass that comparison and still land outside. One starts inside the project and then climbs out of it. The other names a symlink that lives inside the project and points somewhere else. The second has nothing odd in its spelling at all, so a filter that blocks .. closes the first and leaves the second wide open. Only resolving the path before comparing closes both, and it is what the relative branch of the same function already did.
How we know
A description is not a finding. We started the published release as a real subprocess and drove it over MCP’s JSON-RPC messages, the way a client would. Controls came first: a file inside the project read fine, and a relative path climbing out was refused, so the correct branch of the check was working before anything else ran. Then both escapes. Each read, write and delete was confirmed by looking at the filesystem afterward, not by trusting the tool’s reply. Naming the same symlink by a relative path was refused, which pins the cause on the missing resolve step and nothing else. No one else’s machine was involved at any point.
The advisory the project’s maintainer published is our report, signed RIA Labs, with our vector left unchanged. That makes the 8.4 our score, accepted, not an independent rescoring. The advisory lists five credits. RIA Labs is credited as a reporter, and one other account as the finder. The maintainer assigns those labels and they say nothing about order, so we claim neither first nor alone. The record carries no CVE identifier, and our report did not ask for one.
Three questions for anything that fences in an agent’s tools
None of this is specific to one server. If anything you run promises that an agent’s tools will stay inside a folder, ask these of whatever enforces it.
- Does every branch that accepts a path resolve it, or only some of them? Code often splits relative and absolute paths into two branches and tests only one of them. Test both.
- Does the check catch a symlink, or only the characters
..? A string filter reads like a fix and passes a quick manual test. It does nothing about a path that never contains those two characters. - What text can reach this tool’s arguments without a person typing it? If any tool in the same agent returns text a stranger wrote, that text is a plausible source for the next call’s arguments, not just the operator’s own input.
None of it needs exotic tooling. It needs both branches of the same check tested with the same care, and an honest answer about where each argument can come from.
What it proves, and what it doesn’t
It proves we test AI infrastructure itself, not only the applications an agent gets pointed at: a Python MCP server, JSON-RPC tool calls in place of HTTP requests, and the same positive and negative controls any web finding gets. A boundary a project states in its own documentation is exactly the kind of claim worth testing against the code.
It does not prove a live model was ever steered into this call through prompt injection, because that chain was not attempted. It does not show what any particular machine keeps outside its project folder. And with five credits on the record, it is not a claim to have found this alone or first.
If you ship an MCP server, an agent tool or anything else a model gets to call, we’ll read it. Nominate a target and the first scan costs nothing.
Published 26 September 2026. GHSA-rxhw-rjp6-53vj · CVSS 3.1 base score 8.4 (CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) · CWE-22 (Path Traversal) · RIA Labs credited as a reporter, one of five credits, published by the project’s maintainer. Status as of 27 September 2026. Corrections, or an MCP server you maintain and want looked at: info@ria-labs.com.
See what a scan turns up
Nominate a target. The first scan costs nothing, and we’ll walk you through it.