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-launch executes 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_summary will 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. prod in 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 git binary 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:

PackageRuntime dependencies
@usegate/corezod
@usegate/rules@usegate/core
@usegate/adapters@usegate/core
@usegate/telemetry@usegate/core, zod
@usegate/mcp@usegate/core, @modelcontextprotocol/sdk
@usegate/clithe 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.

Was this page helpful?