https://github.com/saagpatel/cross-provider-egress-guard
Destination-aware, default-deny egress control for AI coding agents, at the tool-dispatch layer across Claude Code and Codex from one shared policy.
https://github.com/saagpatel/cross-provider-egress-guard
agent-security ai-agents ai-safety claude-code codex data-exfiltration default-deny devsecops egress-filtering hooks llm-security mcp model-context-protocol prompt-injection security-tools
Last synced: about 6 hours ago
JSON representation
Destination-aware, default-deny egress control for AI coding agents, at the tool-dispatch layer across Claude Code and Codex from one shared policy.
- Host: GitHub
- URL: https://github.com/saagpatel/cross-provider-egress-guard
- Owner: saagpatel
- License: apache-2.0
- Created: 2026-06-20T10:30:55.000Z (8 days ago)
- Default Branch: main
- Last Pushed: 2026-06-20T19:02:55.000Z (8 days ago)
- Last Synced: 2026-06-20T21:04:34.857Z (8 days ago)
- Topics: agent-security, ai-agents, ai-safety, claude-code, codex, data-exfiltration, default-deny, devsecops, egress-filtering, hooks, llm-security, mcp, model-context-protocol, prompt-injection, security-tools
- Language: Shell
- Size: 238 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Security: SECURITY.md
- Notice: NOTICE
Awesome Lists containing this project
README
# cross-provider-egress-guard
**Destination-aware, default-deny egress control for AI coding agents, enforced at the
agent's own tool-dispatch boundary, across both Claude Code and Codex.**
[](https://github.com/saagpatel/cross-provider-egress-guard/actions/workflows/ci.yml)
[](https://github.com/saagpatel/cross-provider-egress-guard/releases/latest)
[](LICENSE)
> A default-deny firewall for your coding agent's tool calls: a hijacked or prompt-injected
> agent can't ship your data to a host you didn't allow-list, and the **same policy covers
> both Claude Code and Codex**.

*Six representative tool calls against the example policy. Reproduce it offline with `bash demo/demo.sh`.*
---
## The threat
An AI coding agent holds broad capability: it reads your files, calls tools, and reaches the
network through MCP connectors and the shell. A single hostile instruction (whether the
model goes off the rails or a **prompt-injection payload rides in tool output**) can turn
into one tool call that ships data to an attacker-controlled host. Most agent setups have
**no destination control** on that call: if the agent can name a URL, it can reach it.
This guard closes that: **network/send-class tool calls and shell network commands are denied
by default unless their destination is on an allow-list you control.**
## What it is
A small set of **PreToolUse hooks** (Claude Code) and an equivalent **hook patch** (Codex)
that classify every tool call and gate the network-shaped ones against one shared JSON
allow-list policy. It runs *inside the agent's own dispatch cycle* (no network
reconfiguration, no proxy to stand up, no per-app SDK changes) and enforces the **same
policy across both agents** from a single source of truth.
- **Default-deny** for network/send-class tools; everything else is unaffected.
- **Per-destination** host allow-listing and **per-connector** scoping (including resource
owner scoping, e.g. only your GitHub org).
- **Fail-closed**: a missing, unreadable, or invalid policy denies; it never falls open.
- **Cross-provider**: Claude Code (`mcp-guard.sh` + `bash-egress-guard.sh`) and Codex
(`codex-egress.patch`) read the *same* `mcp-gate-policy.json`, so the two agents can't drift
to different blast radii.
## Where it sits (honest positioning)
Agent egress can be controlled at two layers, and they are **complementary**:
- **Network-proxy layer**: a forward proxy / firewall / DLP outside the agent (e.g.
[Pipelock](https://github.com/luckyPipewrench/pipelock), Promptfoo's MCP proxy, MCP
gateways). Independent of the agent; strong, but needs network plumbing and lives outside
the agent's semantics.
- **Agent hook layer**: *this project*. Gates each tool call **before dispatch**, with
per-tool/per-connector/per-owner semantics, with no network reconfiguration.
Nothing published (as of mid-2026) does default-deny, per-destination egress control at the
**hook layer, cross-provider across Claude Code *and* Codex**, off one shared policy. That's
the gap this fills. For real defense in depth, run **both** layers; see
[LIMITATIONS.md](LIMITATIONS.md) on why an in-process hook is not a substitute for a network
choke point.
## How it works
A tool call is classified into exactly one mode (checked in order):
1. **URL-host**: tools carrying an explicit URL (browser navigate, fetch). Allowed iff every
extracted host ∈ `allow_hosts`. Deceptive hosts (userinfo-spoof, trailing-dot, punycode,
IP-literal) are normalized to their true host first; no extractable host ⇒ deny.
2. **Connector-class**: fixed-backend connectors with no URL in the payload. Allowed iff the
full tool name matches an `allow_connectors` glob; unknown/renamed connector ⇒ deny.
Optional `connector_owner_scope` further restricts a connector to allow-listed resource
owners.
3. **Generic-network catch-all**: any tool whose *name* signals network/send behavior but
matches neither mode above ⇒ **fail-closed deny** unless its server is local
(`non_egress_servers`).
4. **Unknown tool carrying a `scheme://host` payload** ⇒ fail-closed deny.
The shell hook (`bash-egress-guard.sh`, and the Codex side) applies the same allow-list to
`curl`/`wget`/`ssh` and owner/host-scopes `git push` / `gh` **writes** (reads are never
gated). Full model: [docs/THREAT-MODEL.md](docs/THREAT-MODEL.md) and
[docs/DESIGN.md](docs/DESIGN.md). Mapped to the
[OWASP Top 10 for LLM Applications (2025)](docs/OWASP-LLM-MAPPING.md).
## Quickstart
No install required — runs entirely offline against the in-repo hooks:
```bash
git clone https://github.com/saagpatel/cross-provider-egress-guard
cd cross-provider-egress-guard
bash tests/run-all.sh # runs the full deterministic suite against the in-repo hooks
```
You'll watch egress denies fire across every mode (navigation to a non-allow-listed host,
unknown connectors, deceptive GitHub hosts, oversized novel-host payloads) and legitimate
allow-listed calls pass. Requires only `bash` and `jq` (preinstalled on macOS/Linux).
This same suite (200+ assertions across both agents' enforcement, plus a cross-provider
parity check proving they read one shared policy) is the CI gate.
## Install
Quick deploy (three steps):
1. Copy `policy/mcp-gate-policy.example.json` to `~/.claude/mcp-gate-policy.json` and set your `allow_hosts`, `allow_connectors`, and owner values.
2. Copy `claude-code/hooks/lib/deny.sh` to `~/.claude/hooks/lib/` and `claude-code/hooks/*.sh` to `~/.claude/hooks/` (CC); apply `codex/codex-egress.patch` to your Codex checkout.
3. Wire the hooks into `~/.claude/settings.json` per the `PreToolUse` entries in [docs/INSTALL.md](docs/INSTALL.md).
See [docs/INSTALL.md](docs/INSTALL.md) for the full runbook and [policy/mcp-gate-policy.example.json](policy/mcp-gate-policy.example.json) for the annotated starter policy.
## Adoption Kit
If you want the shortest receipt-producing path, start with [docs/ADOPTION-KIT.md](docs/ADOPTION-KIT.md). It summarizes the threat model, install path, default-deny examples, local verification commands, demo references, and provider-parity caveats. For the broader trust story across Egress Guard, OPERANT, MCPAudit, and mcpforge, see [docs/CONTROL-PLUS-CALIBRATION.md](docs/CONTROL-PLUS-CALIBRATION.md).
## What it does *not* do
It is **one layer, not a silver bullet.** It does not track output-side data flow, decode
encoded payloads, or detect injection carried in tool output, and an in-process hook shares
the agent's trust domain. Read [LIMITATIONS.md](LIMITATIONS.md) before relying on it, and
pair it with network-layer controls.
## Security
Found a bypass? Please report it privately; see [SECURITY.md](SECURITY.md). A bypass is a
vulnerability; don't open a public issue for one.
## Contributing
Contributions are welcome; see [CONTRIBUTING.md](CONTRIBUTING.md). The short version: egress
logic is additive (never weaken an existing deny), both agents stay in parity off the shared
policy, and every behavior change ships with a test.
## License
[Apache-2.0](LICENSE).