ScopeGate MCP Server
by XIOQIGE
README.md
<div align="center">
<img src="docs/assets/banner.svg" alt="ScopeGate — scope enforcement and evidence for authorized AI pentest agents" width="100%">
</div>
# ScopeGate
**Keep autonomous pentest agents inside the rules of engagement.**
ScopeGate is an independent, Apache-2.0 control plane for authorized AI-assisted
security testing. It sits between an LLM agent and the target, turns the
engagement scope into runtime policy, quarantines hostile target content before
it reaches the model, and records a hash-chained evidence trail that a human can
verify afterwards.
### Part of a three-part portfolio
ScopeGate is the **agent runtime governance** piece of a three-part body of work.
The other two cover the execution and research sides:
| Project | Layer | What it demonstrates |
|---|---|---|
| [ylinux-code-audit-agent](https://github.com/XIOQIGE/ylinux-code-audit-agent) | **AI for Security** | A tool-calling code-audit agent with planner/critic separation, gates, replay, and precision/recall evaluation against a fixed ground truth |
| [hack-cot-research](https://github.com/XIOQIGE/hack-cot-research) | **Security for AI** | Controlled experiments on reasoning-model behavior: 8 methods, 16 cases, deterministic judges, raw logs, and confidence intervals |
| **ScopeGate** (this repo) | **Agent runtime governance** | Scope-as-code, pinned DNS, a typed tool boundary, prompt-injection quarantine, a finding validation gate, and hash-chained evidence |
Read [`docs/INSPIRATION.md`](docs/INSPIRATION.md) for the field notes that
connect this repo to public work on agent workbenches, bug-bounty hooks,
multi-agent CVE pipelines, and the abuse risks of autonomous pentesting.
The rest of the stack lives in separate repositories:
[mcp-trust](https://github.com/XIOQIGE/mcp-trust) (tool supply chain),
[prompt-range](https://github.com/XIOQIGE/prompt-range) (injection delivery
layers), [rag-leak-lab](https://github.com/XIOQIGE/rag-leak-lab) (tenant/RAG
boundaries), [model-supply-guard](https://github.com/XIOQIGE/model-supply-guard)
(model artifacts), [llm-infra-probe](https://github.com/XIOQIGE/llm-infra-probe)
(local AI infrastructure), [agent-redteam](https://github.com/XIOQIGE/agent-redteam)
(tool-boundary behavior), and [atlas-map](https://github.com/XIOQIGE/atlas-map)
(framework mapping).
> **Status: alpha.** ScopeGate is deliberately not another exploit engine. It is
> the authorization, tool-boundary, and evidence layer around one.




---
## Why this exists
Autonomous pentest agents are now good at *acting*: they can browse, run tools,
chain findings, and write reports. The part that has not caught up is
**accountability**. In most agent setups, the scope is a sentence in a prompt,
the tool surface is a shell, and the evidence is a transcript.
That creates three concrete problems:
1. **Scope is advisory.** A prompt injection, a hallucination, or a curious
agent can reach a host that was never authorized.
2. **Target content is executable context.** HTML, headers, and error pages can
contain instructions aimed at the model (indirect prompt injection).
3. **"Trust me" is not evidence.** A report needs to show which requests were
made, which were blocked, and whether the log was altered.
ScopeGate makes those three things explicit:
| Control | Implementation |
|---|---|
| Scope as code | TOML/JSON allow/deny rules, ports, methods, time window, request/rate/runtime budgets |
| Fail-closed target checks | Deny wins; private ranges require an explicit lab flag; cloud metadata endpoints are always denied |
| DNS pinning | Resolve once, authorize every address, connect to the pinned IP with the original hostname for Host/SNI |
| Tool boundary | Typed HTTP/DNS/TCP/TLS/finding tools over MCP; no shell tool |
| Egress proxy | HTTP and CONNECT tunnel policy enforcement for agents that honour `HTTP_PROXY` |
| Prompt-injection handling | Detect instruction-like content, quarantine it, label all target output as untrusted |
| Finding quality gate | Reject low-confidence or evidence-free findings before they reach the report |
| Evidence | Append-only JSONL ledger with a SHA-256 hash chain, response hashes, `report.md`, `report.json`, SARIF |
| Reproducible benchmark | A deliberately imperfect loopback lab, ground truth, precision/recall, and a simulated compromised-agent probe |
The result is a project you can demo in one command, inspect line by line, and
extend with your own tools or agent.
---
## Quickstart
Requirements: Python 3.11+ and no third-party runtime dependencies.
```bash
git clone https://github.com/XIOQIGE/scopegate.git
cd scopegate
python -m venv .venv
# Windows: .venv\Scripts\activate
# macOS/Linux: source .venv/bin/activate
python -m pip install -e .
scopegate demo
```
No install? Run it from the source tree:
```powershell
# Windows PowerShell
$env:PYTHONPATH = "src"; python -m scopegate demo
```
```bash
# macOS/Linux
PYTHONPATH=src python -m scopegate demo
```
`demo` starts a loopback-only lab on an ephemeral port, writes a one-hour scope,
runs the deterministic playbook, runs a simulated prompt-injected agent, scores
the result against ground truth, and writes a complete evidence pack.
Actual output from a clean run:
```text
ScopeGate demo complete
target: http://127.0.0.1:64897
run: runs/demo-20261105T075655Z
findings: {'critical': 0, 'high': 1, 'medium': 5, 'low': 2, 'info': 0, 'total': 8}
policy: {'attempts': 6, 'allowed': 5, 'denied': 1, 'denials_by_code': {'target.metadata': 1}, 'out_of_scope_denials': 1, ...}
audit: valid
Evaluation
true positives : 8
false positives: 0
false negatives: 0
precision : 1.000
recall : 1.000
f1 : 1.000
audit valid : True
scope denials : 1
probe blocked : True
report: runs/demo-20261105T075655Z/report.md
```
The run directory contains:
```text
runs/demo-.../
├── audit.jsonl # append-only, hash-chained event ledger
├── findings.jsonl # deduplicated findings with evidence references
├── report.md # human-readable assessment report
├── report.json # machine-readable report
├── report.sarif # SARIF 2.1.0 for code scanning / CI
├── eval.json # ground-truth metrics and probe result
├── scope.toml # the exact scope used for this run
├── meta.json # environment, tool version, policy summary
└── evidence/ # bounded HTTP response captures
```
Verify the chain at any time:
```bash
scopegate audit verify --run-dir runs/demo-...
```
---
## How it works
```mermaid
flowchart LR
A["LLM agent / external tool"] -->|MCP stdio| G["Scoped tool gateway"]
A -->|HTTP_PROXY / HTTPS_PROXY| P["Egress proxy"]
G --> D["Policy engine"]
P --> D
D --> S[("scope.toml")]
D --> L[("hash-chained audit.jsonl")]
G --> H["Pinned HTTP client"]
G --> N["Pinned TCP / TLS"]
P --> N
H --> T["Authorized target"]
N --> T
G --> F["Findings store"]
F --> R["report.md / report.json / SARIF"]
```
A request follows one path:
1. Parse the target and reject credentials, odd schemes, and invalid ports.
2. Resolve the host **once**; reject if any address is outside the allow rules,
non-public without `allow_private`, or a cloud metadata endpoint.
3. Pin one authorized IP. Connect to that IP, but keep the original hostname for
the `Host` header and TLS SNI/certificate validation.
4. Check method, tool, request budget, rate limit, runtime budget, and
concurrency.
5. Record the decision and the response hash in the ledger.
6. Treat the response body as hostile: detect instruction-like content, strip
it for the model, and label the remainder as untrusted data.
The scope file is the source of truth. A prompt cannot expand it, and the model
never receives a shell. The full design and trust boundaries are in
[`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md).
---
## Scope as code
```toml
[engagement]
name = "acme-web-2026-11"
authorized_by = "Alice Security Lead"
authorization_ref = "SEC-1234"
expires_at = "2026-11-12T18:00:00+08:00"
# not_before = "2026-11-05T09:00:00+08:00"
[targets]
allow = ["https://staging.example.com", "https://api.staging.example.com"]
deny = ["169.254.169.254/32", "10.0.0.0/8"]
[limits]
allowed_ports = [443]
max_requests = 500
max_rps = 2
max_runtime_seconds = 900
max_concurrent = 4
max_response_bytes = 2000000
[policy]
allowed_methods = ["GET", "HEAD", "OPTIONS"]
allowed_tools = [
"http.request",
"dns.resolve",
"tcp.connect",
"tls.inspect",
"report.finding",
"scope.check",
]
allow_private = false
follow_redirects = false
min_finding_confidence = "medium"
require_finding_evidence = true
```
Design rules:
- **Deny wins.** A deny rule always beats an allow rule.
- **No global scope.** `*`, `0.0.0.0/0`, and `::/0` are rejected at load time.
- **Private address space is opt-in.** Loopback and RFC1918 targets require
`allow_private = true`, intended for local labs and explicitly approved
internal engagements.
- **Cloud metadata is never in scope.** `169.254.169.254`, the ECS task metadata
address, Alibaba's metadata address, and the IPv6 EC2 metadata address are
denied regardless of allow rules.
- **Time-boxed.** `expires_at` is mandatory; `not_before` is optional.
- **Path prefixes are supported.** `https://example.com/app` authorizes `/app`
and below, not `/admin`.
- **Findings must clear a gate.** Low-confidence or evidence-free claims are
rejected before they reach the report.
`scopegate check` is a dry-run:
```bash
scopegate check --scope scope.toml --target https://staging.example.com/app
scopegate check --scope scope.toml --target https://staging.example.com/app --json
```
---
## Use it with an agent
### 1. MCP tool gateway
Run the stdio server:
```bash
scopegate mcp --scope scope.toml --out runs/mcp-session
```
It exposes seven tools:
| Tool | Purpose |
|---|---|
| `scope_check` | Check a URL without sending traffic |
| `http_request` | GET/HEAD/OPTIONS by default; POST/PUT/etc. must be enabled in scope |
| `dns_lookup` | Resolve an in-scope host and show the pinned addresses |
| `tcp_connect` | Test an in-scope host/port |
| `tls_inspect` | Read certificate metadata from an in-scope endpoint |
| `report_finding` | Record a validated, evidence-backed finding |
| `list_findings` | Read findings for the current run |
Example MCP client configuration: [`examples/mcp-config.json`](examples/mcp-config.json).
The server speaks newline-delimited JSON-RPC 2.0 over stdio and writes logs only
to stderr.
### 2. Egress proxy for an existing CLI agent
```bash
scopegate proxy --scope scope.toml
# prints HTTP_PROXY=http://127.0.0.1:8080
```
Or inject the proxy into a child process:
```bash
scopegate run --scope scope.toml --target https://staging.example.com -- \
your-agent --target https://staging.example.com
```
This sets `HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`, and the lowercase variants,
and records every proxied request. **It is cooperative enforcement**: a process
that ignores the proxy can still open a direct socket. Use a network namespace
or container egress policy when you need a hard boundary. That limitation is
documented in [`docs/THREAT_MODEL.md`](docs/THREAT_MODEL.md).
### 3. Optional LLM planner
The repo includes a small OpenAI-compatible planner loop. It is intentionally
limited to the typed tools and bounded by `--max-steps`:
```bash
export SCOPEGATE_LLM_API_KEY="..."
scopegate scan \
--scope scope.toml \
--target https://staging.example.com \
--agent llm \
--llm-model gpt-4.1-mini \
--llm-base-url https://api.openai.com/v1 \
--max-steps 8
```
For sensitive engagements, point `--llm-base-url` at a local OpenAI-compatible
server (for example, an Ollama or vLLM endpoint) so target-derived content does
not leave your environment. The planner is a demonstration of the agent
paradigm, not a claim that a particular model is a good pentester.
---
## Reproducible evaluation
```bash
scopegate eval
```
The bundled lab has eight seeded issues and a prompt-injection canary. The
playbook is deterministic, so the result is reproducible rather than a cherry-
picked model run.
| Metric | Playbook agent |
|---|---|
| True positives | 8 |
| False positives | 0 |
| False negatives | 0 |
| Precision | 1.000 |
| Recall | 1.000 |
| F1 | 1.000 |
| Audit chain valid | yes |
| Out-of-scope metadata attempts blocked | 1 / 1 |
| Prompt-injection signals detected | 6 labels |
The `InjectionProbeAgent` is a **deliberately compromised** scripted agent. It
reads the lab's injected instruction and attempts to authorize
`http://169.254.169.254/latest/meta-data/`. The request is denied by policy
before a socket is opened; the probe is a dry-run so it can never reach a real
metadata service even if the gate regresses. This is the control test that
matters: the model can be fooled, but the boundary still holds.
This is a lab benchmark, not a claim of real-world coverage. See
[`docs/EVALUATION.md`](docs/EVALUATION.md) for the ground truth and methodology.
---
## Prior art and honest positioning
ScopeGate is **not a fork** of Strix, PentestGPT, or PentAGI, and contains no
source code copied from them. Those projects are excellent execution engines;
ScopeGate is a companion control plane that can supervise an engine or expose a
safer tool surface to a custom agent.
| Project | License | What it gives you | What ScopeGate adds |
|---|---|---|---|
| [Strix](https://github.com/usestrix/strix) | Apache-2.0 | Autonomous multi-agent pentesting, sandbox, PoCs, reports | Runtime egress policy, MCP tool boundary, prompt-injection quarantine, hash-chained evidence |
| [PentestGPT](https://github.com/GreyDGL/PentestGPT) | MIT | LLM-driven pentest/CTF pipeline, task tree, sessions | Scope-as-code, pinned DNS, budgets, auditable tool calls |
| [PentAGI](https://github.com/vxcontrol/pentagi) | MIT | Multi-agent system with Docker sandbox and a large tool suite | A small, testable policy/evidence layer that can front other agents |
| [HexStrike AI](https://github.com/0x4m4/hexstrike-ai) | MIT | MCP server exposing 150+ security tools | Fail-closed scope, no shell tool by default, evidence chain |
| [RedAmon](https://github.com/samugit83/redamon) | MIT | Recon graph, exploitation pipeline, remediation PRs | Lightweight authorization and audit layer; no Neo4j/Docker requirement |
| ScopeGate | Apache-2.0 | Scope enforcement, tool gateway, egress proxy, evidence, lab benchmark | — |
Star counts and project status change quickly; the comparison in
[`docs/PRIOR_ART.md`](docs/PRIOR_ART.md) records the sources and the date they
were checked. The short version: **use Strix or PentAGI when you need a full
autonomous engine; use ScopeGate when you need to constrain, observe, and
evidence one.**
For the public field reports that shaped the finding gate and roadmap, see
[`docs/INSPIRATION.md`](docs/INSPIRATION.md). If you are evaluating this as a
portfolio project, read [`docs/PORTFOLIO.md`](docs/PORTFOLIO.md) for the design
rationale, a five-minute demo script, and an explicit "what I did not build"
section.
---
## Project layout
```text
src/scopegate/
├── scope.py # scope parsing, matching, DNS resolution, fail-closed checks
├── policy.py # budgets, rate limiting, audited decisions
├── httpclient.py # pinned-IP HTTP client with re-authorized redirects
├── tools.py # typed tools, evidence capture, MCP-compatible schemas
├── sanitize.py # prompt-injection detection and quarantine
├── audit.py # append-only hash-chained ledger
├── proxy.py # HTTP/CONNECT egress proxy
├── mcp_server.py # dependency-free MCP stdio server
├── agent.py # deterministic playbook, optional LLM planner, injection probe
├── lab.py # loopback-only intentionally imperfect target
├── eval.py # ground-truth metrics
├── report.py # Markdown, JSON, SARIF
└── cli.py # init/check/scan/demo/eval/proxy/run/mcp/audit/report/lab
```
Tests live in `tests/` (48 tests, no network access beyond loopback).
---
## Limitations
ScopeGate is useful today, but it is not a silver bullet:
- The proxy is cooperative. A hostile binary can bypass it. The next step is a
network namespace/container with the proxy as the only route.
- HTTPS CONNECT traffic is authorized and logged at the host/port level, not
inspected. There is no MITM CA in this project.
- Prompt-injection detection is heuristic. It reduces risk; it does not
eliminate it. The hard control is that target content never grants authority.
- The built-in playbook is read-only and shallow. It is a reproducible demo, not
a replacement for manual testing.
- The lab benchmark is intentionally small. A perfect score on it is not
evidence of real-world coverage.
- The audit chain detects casual tampering; it is not an external notary. For
stronger proof, anchor the head hash in a separate system or sign it.
- `allow_private = true` weakens the default network boundary. Use it only for a
lab or an explicitly approved internal engagement.
---
## Roadmap
- [ ] Hard egress boundary using a network namespace / container and nftables
- [ ] Signed scope files (Sigstore/cosign) and an approval workflow
- [ ] Policy backend abstraction (OPA/Rego or Cedar)
- [ ] First-class adapters for Strix and PentestGPT with tested Docker configs
- [ ] More lab scenarios (OWASP Juice Shop, DVWA, API-only targets)
- [ ] GitHub Action that uploads SARIF and fails on out-of-scope attempts
- [ ] MCP resources for findings and audit summaries
- [ ] External timestamping of the audit head
---
## Authorized use
ScopeGate is for authorized security testing only. The scope file is an
attestation that a human approved the engagement; it is not a substitute for
legal authorization. The bundled lab binds to loopback and contains no real
credentials.
Read [`SECURITY.md`](SECURITY.md) before running against anything you do not
own.
## Contributing
Contributions are welcome. Start with [`CONTRIBUTING.md`](CONTRIBUTING.md), run
the tests, and keep the fail-closed defaults intact. If you add code from another
project, add its notice to [`NOTICE`](NOTICE).
## License
Apache-2.0. See [`LICENSE`](LICENSE) and [`NOTICE`](NOTICE).