Threat model
Gate reads configuration files, MCP tool descriptions and remote server responses. All of that is untrusted, attacker-influenceable input, and Gate is frequently run against a repository the operator did not write.
This document is about threats to Gate itself. For threats to your agents, see measuring blast radius.
What Gate is trusted with
When you run gate scan, Gate has:
- read access to your repository, including files git ignores
- the contents of every credential written into agent configuration
- the ability to write
.gate/baseline.json - your terminal
- optionally, one outbound HTTPS request
That is a meaningful amount of trust for a tool you invoked with npx.
Adversaries
- Name
A hostile repository- Type
- primary
- Description
You run Gate on code you did not write, reviewing a pull request from a fork, auditing a dependency, triaging a report. Every byte Gate reads is chosen by someone else.
- Name
A malicious MCP server- Type
- under --inspect
- Description
A remote server that returns hostile tool names and descriptions, or a local command line crafted to be executed.
- Name
A compromised dependency- Type
- supply chain
- Description
Something in Gate's own tree that wants what Gate can see.
- Name
A passive observer- Type
- network
- Description
Anyone who sees Gate's telemetry request, or the CI logs Gate writes to.
Threats and mitigations
Credentials leak into Gate's output
Mitigation. Redaction is a single primitive in @usegate/core, applied at
the adapter boundary. Everything downstream works with classifications, never
values. The --json path re-scans its own output for credential shapes and
refuses to emit anything that matches. Gate does not print a prefix or suffix of
a secret, only a salted one-way digest. Heavily tested, including a test that
scans every rendering of every fixture.
Credentials leak into telemetry
Mitigation. The event is built by an explicit constructor of named scalar fields, validated by a strict schema that rejects unknown keys, and scanned for credential shapes before sending. The server applies the same allowlist again. See privacy.
Executing a malicious MCP server
A configuration file is a list of command lines. A scanner that runs them has a remote code execution feature.
Mitigation. gate scan never spawns a process. --inspect connects only to
remote servers. Launching a local stdio server requires a second explicit
flag, prints the exact command, and asks for confirmation. Gate cannot sandbox
that execution today, so it refuses by default rather than shipping it unsafely.
Terminal escape injection
A tool description containing ESC [ 2 J clears your screen. One containing a
carriage return overwrites the line Gate just printed. Enough to hide a
CRITICAL finding. One containing right-to-left override characters can display
execute_query as something else entirely: the Trojan
Source class.
Mitigation. Every string derived from scanned content passes through a sanitiser that strips ANSI/VT sequences, C0 and C1 control characters (carriage return included), bidirectional overrides, zero-width characters and the byte-order mark, before it can reach a terminal, a JSON file or a baseline.
Path traversal via configuration
A config file can name any path. ../../../etc/passwd is a valid string.
Mitigation. Adapters cannot read outside the repository root: the scan
context resolves and rejects, so it is an enforcement point rather than a
convenience wrapper. Symbolic links are not followed during discovery. Gate
classifies ~/.ssh as a grant; it never goes and looks.
Denial of service via a hostile repository
Deeply nested JSON, a million files, a 4 GB config.
Mitigation. Caps on file count (2000), file size (2 MB), directory depth (8), servers per file (200), tools per server (500), evidence depth (6) and breadth (100). Hitting a cap produces a warning, so a partial answer never looks like a complete one.
Prototype pollution via parsed JSON
JSON.parse('{"__proto__": ...}') creates a real own property.
Mitigation. The evidence sanitiser drops __proto__, constructor and
prototype keys when copying untrusted objects.
Baseline tampering
A baseline is a file in the repository, and a repository is writable by whoever sends the pull request.
Mitigation. A baseline is advisory. It can mark a finding as known; it cannot lower a severity, disable a rule, or change a blast radius. The most a tampered baseline achieves is making findings look new.
Gate output leaking through CI logs
Gate output lands in pull request logs, frequently public.
Mitigation. Gate's output is designed to be safe in a public log: rule IDs, counts, severities, file paths and field paths. Never a value. The GitHub Action uploads nothing.
Accepted limitations
These are open. Gate does not claim otherwise.
- Gate cannot sandbox a launched stdio server.
--allow-stdio-launchexecutes a command from the config being audited. It is documented as unsafe and off by default. There is no seccomp profile, Job Object or container in this release. - Gate cannot verify that a tool does what it says. Classification is based
on names, known-server mappings and descriptions. A server that names a
destructive tool
get_summarywill be misclassified. GATE016 catches the obvious cases and nothing catches the subtle ones. - Gate cannot see runtime behaviour. It reads configuration. An agent that is handed a credential at runtime through a channel Gate never sees is invisible to it.
- Production detection is a heuristic.
prodin a hostname is a guess, so GATE014 says possible production access. - The repository fingerprint is not anonymity. It is a salted one-way hash of your root commit. Someone who already has your repository can confirm a match; they cannot recover anything from the hash alone.
- Gate trusts your
gitbinary and your Node runtime. If those are compromised, nothing here helps.
Gate's own supply chain
Gate is a security tool that runs with access to your credentials, so its own dependency tree is part of your attack surface. It is kept small:
| Package | Runtime dependencies |
|---|---|
@usegate/core | zod |
@usegate/rules | @usegate/core |
@usegate/adapters | @usegate/core |
@usegate/telemetry | @usegate/core, zod |
@usegate/mcp | @usegate/core, @modelcontextprotocol/sdk |
@usegate/cli | the above, plus commander |
Terminal colours, glob matching and TOML parsing are implemented in Gate rather than pulled in, because each of those would be another edge in the graph for very little code.
Install scripts are denied by default in Gate's own workspace, with a short allowlist of packages that genuinely need them.