v0.12.0 — three defects found by attacking the tool, not by the test suite
[0.12.0] — 2026-09-07
Three defects found by adversarial testing, not by the test suite.
Fixed — a mistyped flag looked like a crash and exited 1
Nine of thirteen argument-validation paths printed a raw Node stack trace and
exited 1. Exit 1 means "findings at or above the threshold", so a typo was
reported to CI as a security failure — wrong, and the kind of wrong that
quietly devalues every other exit code the tool produces.
parse() is synchronous while every action is async, so a rejected action
escaped as an unhandled rejection. All usage errors now print a message and
exit 2.
Fixed — --ignore-rule accepted ids that do not exist
--rules NOPE-999 errored; --ignore-rule NOPE-999 was silently accepted. A
suppression that quietly does nothing is worse in a security tool than an
error: the user believes a rule is muted and it is not. Both are validated now.
Added — MCPG-202/206/208 detect terminal control sequences
Found by pointing a deliberately hostile MCP server at --live. A description
carrying ESC[8m ("conceal"), a lone CR (overwrites the line just printed) or
BS (erases what precedes it) reads one way to a human in a terminal and another
way to the model. Same attack as a zero-width character, different mechanism.
guardmcp was already sanitising these on output, so a reviewer's terminal
was never at risk — but nothing reported that the server had sent them.
CRLF is excluded: it is a Windows line ending, and flagging it would fire on a
large share of honest descriptions.
Verification performed
| Method | Result |
|---|---|
| Deletion mutation, 27 rules | 27/27 killed |
| Always-fire mutation, 9 detectors | 9/9 killed |
| Fuzzing, 24 adversarial configs (deep nesting, 20k servers, 200MB strings, prototype pollution, BOM, surrogates, ANSI, path traversal) | 24/24 handled, no crash, no hang |
| Hostile MCP server, 6 modes (100k tools, 200MB description, ANSI injection, 50k-deep schema, garbage JSON-RPC, silent, slowloris) | 6/6 survived, timeouts honoured, 0 escape bytes reached stdout |
| Prototype pollution, in-process | Object.prototype untouched, __proto__ key dropped |
| Real-world scan, 80 official-registry servers | 0 findings |
| Determinism | three runs byte-identical, fingerprints stable |
| Idempotence | a scan writes nothing to disk |