AWS disclosed a vulnerability in its own open-source MCP server on October 1, 2026, and the mechanism is worth more than the advisory. A crafted value passed to a scan operation was interpreted as a command-line option rather than as the revision it was supposed to be. The result: an attacker could create, overwrite or truncate arbitrary files on the host, outside the directory the tool was supposed to confine itself to. The control that was supposed to hold — workspace confinement — did not fail on a path check. It failed on argument parsing.
CVE-2026-97662 affects awslabs.security-agent-mcp-server versions 0.1.1 and above but below 0.2.0, and is fixed in 0.2.0. AWS rates it “Important” and published no CVSS score. There is no workaround and no evidence of exploitation. The generalisable lesson is bigger than the CVE: MCP servers and similar LLM-facing tools routinely describe their arguments as data while implementing them as code, and every one that shells out inherits the argument-injection bug class. The mitigation is not a better directory check. It is passing an argument array instead of a command string.
Key takeaways:
- Affected:
awslabs.security-agent-mcp-server≥ 0.1.1 and < 0.2.0. Fixed in 0.2.0 on PyPI. - AWS rates it “Important (requires attention)” and published no CVSS vector.
- Attacker model is context-dependent: the model or agent must be fed attacker-controlled content. This is not an unauthenticated network attack.
- It is a containment failure, not remote code execution. Do not inflate it.
- This is an AWS Labs open-source tool, not an AWS cloud service. The distinction matters if you report it upward.
- No workaround exists. Upgrade, or isolate.
What AWS actually published
| Field | Value |
|---|---|
| CVE | CVE-2026-97662 |
| GHSA | GHSA-8g28-rj54-p5p2 |
| AWS bulletin | 2026-121-AWS |
| Published | 1 October 2026, 10:30 AM PDT |
| Content type | Important (requires attention) |
| Affected | awslabs.security-agent-mcp-server ≥ 0.1.1, < 0.2.0 |
| Fixed in | 0.2.0 (PyPI) |
| Workaround | None |
| Credited to | Mario Guzmán, independent researcher |
The advisory’s own description is precise about the mechanism: a crafted reference value supplied to the scan is interpreted as a command-line option rather than a revision. The stated effect is that an attacker can create, overwrite or truncate arbitrary files on the host outside the intended workspace directory, bypassing the server’s workspace-confinement control.
Why this class is structural, not a coding mistake
The underlying pattern is old and well understood: if user-controlled input is concatenated into a command line and that string is handed to a shell, then a value beginning with a dash is not a value, it is an option. Every downstream tool in the chain then honours it as one.
What makes this particular instance interesting is which control was supposed to stop it. Workspace confinement is a path-level control. It answers “where is this tool allowed to write?” The injection does not attack that question at all. It arranges for a write to a path the tool never validated, by making a different part of the command mean something other than what the tool intended.
That is why a workspace-path check is the wrong layer of defence, however carefully written. Validating the destination is validating the result of a decision that was already made incorrectly. If the argument parser can be persuaded that your revision is a flag, the path validator downstream is being asked the wrong question.
The fix that actually closes the class
Pass an argument array, never a shell string. In Python that means subprocess.run([...]) with shell=False as the default, rather than a string with your value interpolated into it. In Rust, Command::new with .arg() rather than .args(["sh", "-c", "..."]). In Go, exec.Command with separate arguments rather than sh -c.
With an argv array there is no parsing step, so a leading dash in your revision is simply a revision that happens to start with a dash. The value stays a value. The whole bug class evaporates without any validation of its content.
Two further hardening steps are worth adding, in this order of importance:
- Pin
argv[0]explicitly rather than letting it be inferred from the executable path, so a writable PATH entry cannot substitute a different program. - Reject values beginning with
-as defence in depth. This is a backstop, not the fix, and it will not help if your tool legitimately needs to pass an option through. - Never route through a shell when the child does not need one.
sh -cadds globbing, word splitting, redirection and command substitution to the set of things that can go wrong, none of which you need in order to rungit.
The MCP-specific lesson
MCP tool schemas describe parameters as typed data: a string here, an integer there, maybe an enum. That framing encourages implementors to think of them as data. But a tool’s implementation typically ends at a process invocation, and at that point the same string becomes a command-line token with syntax.
The gap between the schema and the exec call is the vulnerability. A schema that declares {"ref": {"type": "string"}} carries no constraint that would survive contact with an argument parser. Neither does a description saying “a git revision”.
This compounds with the agent threat model, and the combination is what makes it worth writing about rather than filing. Most MCP server hardening assumes the adversary is a malicious model — an agent tricked by prompt injection into calling a dangerous tool. This CVE is the other direction: the adversary controls the content the model is reading, and the model’s innocent, correct-looking tool call carries the payload. The model did exactly what it was asked. The bug was downstream of the model entirely.
Controls that sit on the model — approval prompts, tool allowlists, output filters — do not help when the model’s behaviour is correct. The control has to be in the tool’s own implementation.
What AWS recommends
- Upgrade to 0.2.0.
- Patch forks and derivatives you maintain yourself, which will not receive the upstream fix.
- Run diff scans only against repositories you trust, since attacker-controlled repository content is the delivery path.
- Run the tool with least privilege in an isolated environment.
Being precise about severity
AWS published no CVSS vector for this one, and that is worth preserving rather than filling in. Three constraints keep it below where a headline might place it:
- No exploitation is claimed. Nobody has reported this being used.
- It is arbitrary file write, not arbitrary code execution. On its own that is serious, but it is a different claim.
- It is a containment bypass in open-source tooling, not a vulnerability in a hosted AWS service. Nothing about your AWS account is affected unless you run this server.
The compensating control also matters: AWS’s own framing is that the attack requires the model or agent to be fed attacker-controlled content. That is a real precondition, and it is precisely the situation in which agent tooling is being deployed. Rare in production does not mean unimportant.
The audit to run on your own MCP servers
# Find shell-string construction in your own tooling
grep -rn "shell=True" .
grep -rn "sh -c|bash -c|/bin/sh" .
grep -rn "os.system(" .
# Python: prefer argv arrays
subprocess.run(["git", "diff", ref], shell=False)
Two findings are worth acting on immediately. Any tool that takes a revision, branch, path or URL from model-visible content and builds a command string from it has this bug class. Any tool that resolves its executable through PATH has the argv[0] substitution variant, which is arguably easier to exploit because it needs no injection at all.
If you want the broader picture of how tool calls reach an execution boundary, how AI tool calling actually works is the right starting point, and AI agent security: Linux permissions, sandboxing and least privilege covers the controls that do hold when a tool does misbehave.
References
- AWS Security Bulletin 2026-121-AWS — 1 October 2026
- GitHub Security Advisory GHSA-8g28-rj54-p5p2 — GitHub Advisory Database
awslabs.security-agent-mcp-server— PyPI package page- MITRE ATT&CK T1059 — Command and Scripting Interpreter
Related Tools
Try these companion tools on DevStackIO Tools:
- CSP generator — build Content Security Policy headers to contain workspace escape attacks.