Metadata-Version: 2.5
Name: redcell
Version: 0.1.0
Summary: The MCP security scanner that actively exploits: prove SSRF, path traversal, command injection and more in MCP servers (OWASP MCP Top 10).
Project-URL: Homepage, https://github.com/Sahilo6/redcell
Project-URL: Repository, https://github.com/Sahilo6/redcell
Project-URL: Issues, https://github.com/Sahilo6/redcell/issues
Author: Sahil Sadhwani
License: Apache-2.0
License-File: LICENSE
Keywords: agents,ai-security,llm,mcp,mcp-security,model-context-protocol,owasp,prompt-injection,red-teaming,security-scanner,ssrf
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3.11
Classifier: Topic :: Security
Requires-Python: >=3.11
Provides-Extra: api
Requires-Dist: fastapi>=0.110; extra == 'api'
Requires-Dist: uvicorn>=0.29; extra == 'api'
Provides-Extra: dev
Requires-Dist: httpx>=0.27; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Provides-Extra: llm
Requires-Dist: anthropic>=0.40; extra == 'llm'
Description-Content-Type: text/markdown

# RedCell

**The MCP security scanner that actually exploits — it doesn't just lint your tool descriptions, it attacks your running server and proves what's broken.**

Point RedCell at any [MCP](https://modelcontextprotocol.io) server and it enumerates the
server's tools, then *actively exploits* them — proving path traversal, SSRF, OS command
injection, tool-description poisoning, and secret leakage with a real exploit transcript, not a
guess. One command, no adapter to write:

```bash
redcell scan -- python3 my_mcp_server.py
```

```
✗ CRITICAL  OS Command Injection · tool 'run_command' · OWASP MCP05
    An injected shell expression executed on the host: 4655*7529 resolved to 35047495.
    ↳ Payload: command='x; echo $((4655*7529))'
✗ CRITICAL  Path Traversal · tool 'read_file' · OWASP MCP03
    The tool returned /etc/passwd outside any intended directory (proven read).
```

Findings map to the **OWASP MCP Top 10** and drop straight into CI (non-zero exit on findings,
HTML + SARIF reports). Most MCP scanners statically lint tool *descriptions*; RedCell calls the
tools and **demonstrates** the exploit — the high-signal, low-noise part everyone else skips.

> ⚠️ **Defensive / authorized use only.** Scan MCP servers and agents you own or have explicit
> permission to test. Payloads are deliberately non-destructive (read a world-readable file,
> hit a loopback listener, `echo` a canary). See [RESPONSIBLE_USE.md](RESPONSIBLE_USE.md).

## Install

The core (and all stdio scanning) is **stdlib-only — no dependencies**.

```bash
uvx redcell scan -- python3 my_mcp_server.py        # run without installing (recommended)
pipx install redcell                               # or install the CLI globally
pip install redcell                                # or into your environment
# from source (before the PyPI release):  git clone … && pip install -e .
```

## Scan an MCP server

```bash
# Try it on the bundled deliberately-vulnerable server (finds 6 classes → exit 1):
redcell scan -- python3 examples/mcp/vulnerable_server.py

# …and the hardened one (clean → exit 0):
redcell scan -- python3 examples/mcp/hardened_server.py

# Your own server, with reports for CI:
redcell scan --out report.html --sarif results.sarif -- python3 my_mcp_server.py
redcell scan --static-only -- python3 my_mcp_server.py   # inspect definitions, call nothing
```

What it checks (OWASP MCP Top 10), and how:

| Check | OWASP | How it's proven |
|---|---|---|
| Path traversal / unsandboxed file read | MCP03 | calls file tools with `../../etc/passwd`, confirms the leaked contents |
| Server-side request forgery | MCP04 | steers fetch tools at a loopback callback + `file://`, confirms the request fired |
| OS command injection | MCP05 | injects shell arithmetic `$((a*b))`; a finding fires only if the **product** comes back (reflection can't fool it) |
| Tool definition poisoning / line-jumping | MCP01 | scans tool descriptions/param schemas for hidden agent instructions |
| Tool output injection (ATPA) | MCP02 | calls tools benignly and inspects the **output** for injected instructions |
| Credential / secret leakage | MCP06 | flags keys/tokens/private-keys in definitions and outputs |

The client speaks MCP over **stdio** (the common local case) with zero extra dependencies.

### Scanning untrusted servers safely

Scanning a third-party server *runs its code*. To scan servers you don't trust, do it inside a
throwaway, host-isolated container — no host filesystem is mounted, it's removed after each scan,
and path-traversal/SSRF probes hit the **container's** `/etc/passwd` and loopback, never yours:

```bash
brew install colima docker && colima start      # one-time: a Linux VM for true isolation
sandbox/scan-sandboxed.sh -- npx -y some-untrusted-mcp-server
```

---

## Also: full agent-loop red-teaming

Beyond MCP, RedCell red-teams the **full agent loop** — tools, retrieval, memory, and
multi-step reasoning — where the under-covered vulnerabilities live (NIST found novel agent
attacks hit an **81% task-hijack rate** vs 11% for prompt-only attacks). It plays an adversary
against a target agent and maps findings to the **OWASP Top 10 for Agentic Applications (2026)**
and **NIST AI 100-2**.

## Status

Early (v0.1, milestones **M1–M2**). What works today:

- A pluggable engine: **attacks → target → judge → report**.
- An attack library mapped to the OWASP Agentic Top 10, all via the **indirect** vector
  (payload delivered through a *tool's output*, not the user prompt):
  - **Indirect prompt-injection → exfiltration** (ASI01) — leak-in-output + egress-tool variants.
  - **Privileged tool abuse** (ASI03) — coaxing a destructive tool call.
  - **Tool misuse / SSRF** — fetch tool aimed at the cloud metadata endpoint.
- A deterministic, criteria-driven **HeuristicJudge** (canary leak, forbidden-tool, unsafe
  tool argument) — no API key needed.
- Deliberately **vulnerable** and **hardened** mock agents so you can verify the whole loop
  offline.
- HTML + JSON reports with OWASP/NIST mapping, and a CI-friendly exit code.
- **Adaptive multi-turn red-teaming** (`redcell adaptive`): a feedback-driven attacker that
  builds rapport then escalates — extracting a secret from a chat agent only after multiple
  turns (a weakness single-prompt scans miss). Runs offline with the `scripted` attacker; a
  real **Claude-powered** attacker + target are wired and **gated behind `ANTHROPIC_API_KEY`**
  (drop the key in, no code changes).

## Adaptive multi-turn (offline)

```bash
redcell adaptive --target chat-vulnerable     # scripted attacker extracts the secret → exit 1
redcell adaptive --target chat-hardened       # resisted → exit 0
```

### Real LLM attacker/target — bring any provider (free options included)

The LLM attacker and target are provider-agnostic (stdlib-only, no SDK lock-in). Pick a backend
with `--provider` or an env key:

| Provider | Cost | Setup |
|---|---|---|
| `groq` | **free key, no card** | `export GROQ_API_KEY=...` |
| `ollama` | **free, fully local, no key** | install [Ollama](https://ollama.com), `ollama pull llama3.1` |
| `openrouter` | free `:free` models | `export OPENROUTER_API_KEY=...` |
| `gemini` | free tier | `export GEMINI_API_KEY=...` |
| `anthropic` | paid | `export ANTHROPIC_API_KEY=...` (`pip install 'redcell[llm]'`) |

```bash
# Free: a Groq-powered adversary vs. an Ollama-hosted target
redcell adaptive --attacker llm --provider groq --target llm-chat
# or fully local, no keys at all:
redcell adaptive --attacker llm --provider ollama --target llm-chat --provider ollama
```

Provider resolves from `--provider` → `REDCELL_LLM_PROVIDER` → whichever API key is set. With
nothing configured, RedCell prints the free-options menu instead of failing cryptically.

- A labeled **benchmark** (`redcell benchmark`) over targets with known vulnerability profiles,
  reporting judge **precision/recall/F1** and attack coverage per OWASP category — and honestly
  surfacing what the heuristic judge misses.

Roadmap (see the project plan): GitHub Action → an **LLM-as-judge** to close the recall gap the
benchmark exposes → hosted cloud.

## Benchmark + judges

```bash
redcell benchmark --md benchmark.md --json benchmark.json            # heuristic (offline)
redcell benchmark --judge hybrid --provider groq                     # + LLM backstop (free)
```

Three judges: `heuristic` (offline, exact, perfect precision), `llm` (semantic — catches
obfuscated/encoded leaks; needs a provider), and `hybrid` (heuristic first, LLM only as a
backstop on negatives — keeps precision, recovers recall, minimizes tokens).

Measured on the 20-case labeled benchmark:

| Judge | Precision | Recall | F1 |
|---|---|---|---|
| heuristic | 1.00 | 0.90 | 0.95 |
| hybrid (+ LLM) | 1.00 | **1.00** | **1.00** |

The heuristic's one miss is a deliberately space-obfuscated canary leak — a real vulnerability
exact matching can't see. The LLM judge recovers it without introducing false positives.

## Status & honest limitations

RedCell is an early, well-tested **skeleton**, not a battle-tested product. Findings are so far
demonstrated against deliberately-vulnerable sample agents (no real-world findings yet), the
attack library is smaller than mature tools, and the benchmark is synthetic. Read the candid
[build writeup](docs/WRITEUP.md) for the full picture, results, and the roadmap that would matter
for real adoption.

## Quickstart (offline, no API key)

```bash
cd redcell
pip install -e .

# Run against the deliberately-vulnerable mock agent — should find the injection.
redcell run --target mock-vulnerable --out report.html

# Run against the hardened mock agent — should be clean.
redcell run --target mock-hardened --out report-hardened.html
```

Exit code is non-zero when vulnerabilities are found, so it drops straight into CI.

## Test your own agent (HTTP) + CI

Point RedCell at any agent you own/are authorized to test by exposing an HTTP endpoint:

```bash
redcell run --target-url https://your-agent.example/redcell --out report.html
```

Your endpoint receives `{user_input, injected_tool, injected_payload, canary}` and returns
`{final_output, tool_calls:[...]}` (full contract in
[http_adapter.py](redcell/adapters/http_adapter.py)). Exit code is non-zero on findings.

**Regression gating** — record a baseline and fail CI only when a *new* vulnerability appears
(pre-existing ones don't break the build):

```bash
redcell run --target-url $URL --history .redcell/history.jsonl --fail-on-new
```

Drop it into CI with the bundled GitHub Action — it fails the build on findings, emits SARIF to
the Security tab, and uploads the report:

```yaml
- uses: Sahilo6/redcell@v0
  with:
    target-url: https://your-agent.example/redcell
```

## REST API (backend)

A FastAPI backend (the web frontend consumes it). Targets + scans are persisted in SQLite;
scans run as background jobs you poll.

```bash
pip install -e ".[api]"
redcell serve            # http://127.0.0.1:8000 · interactive docs at /docs
```

**MCP scanning over HTTP** — the flagship scanner as a pollable job:
`POST /mcp/scans` (body `{command, static_only?, timeout?, name?}`) → poll `GET /mcp/scans/{id}`
→ `GET /mcp/scans/{id}/report.html|.sarif`; plus `GET /mcp/probes`, `GET /mcp/stats` (dashboard
aggregates), and `DELETE /mcp/scans/{id}`.

> ⚠️ `POST /mcp/scans` **launches the given command on the host** — running it executes the MCP
> server's code. Today it runs locally (set `REDCELL_API_KEY` and don't expose it on an untrusted
> network). For a hosted/multi-user deployment, route scans through the sandbox harness — the seam
> is `service.execute_mcp_scan`.

Agent red-teaming endpoints (pre-MCP, still supported): `POST /targets`, `POST /scans`
(→ poll `GET /scans/{id}`), `GET /scans/{id}/report.html|.sarif`, `GET /stats`, `GET /benchmark`,
`POST /adaptive`, `GET /attacks`, `GET /health`.

Errors use a consistent `{"error": {type, message}}` envelope. Set `REDCELL_API_KEY` to require
an `x-api-key` header on all routes except `/health` and `/docs` (no key set ⇒ open, for dev).
Lock CORS to your frontend origin in prod via `REDCELL_CORS_ORIGINS` (comma-separated; default `*`).

## Web app (frontend)

A modern dark "security console" UI (Vite + React + TypeScript + Tailwind + shadcn/ui) in
[`web/`](web/) — Dashboard, Targets, Scans (+ live-polling detail, reports), Benchmark, and
Adaptive pages, all over the REST API.

```bash
cd web
cp .env.example .env          # set VITE_API_BASE_URL (default http://localhost:8000)
npm install
npm run dev                   # http://localhost:5173  (run `redcell serve` alongside)
```

## Deploy

- **API** → Render / Railway / any container host via the root [`Dockerfile`](Dockerfile)
  (`uvicorn redcell.api.app:app`). Set `REDCELL_API_KEY` and `REDCELL_CORS_ORIGINS` in prod.
  (SQLite is ephemeral on free tiers — mount a volume at `/data` for durability.)
- **Frontend** → Vercel with root `web/`, build `npm run build`, output `dist/`, env
  `VITE_API_BASE_URL` = your deployed API URL. SPA routing handled by [`web/vercel.json`](web/vercel.json).

## Dashboard

A local web view of your scan history — per-target run trends, latest findings, and OWASP
breakdown. The seed of the hosted-cloud layer.

```bash
redcell dashboard --history .redcell/history.jsonl   # then open http://localhost:8000
```

## CTF demo (Gandalf-style)

A playable web demo: chat with a defended agent across escalating levels and try to
social-engineer its secret out of it. Great for showing the problem to non-experts.

```bash
redcell ctf --provider groq        # then open http://localhost:8000
```

Backed by the same provider layer (free Groq/Ollama work). Win detection catches verbatim
*and* whitespace-obfuscated leaks. Defensive/educational only — the secrets are throwaway words.

## Design

```
attack  ──build_scenarios(canary)──▶  scenarios
scenario ──────target.run()────────▶  transcript (tool calls + final output)
transcript ─────judge.evaluate()───▶  verdict (success? evidence)
verdicts ──────────report──────────▶  HTML / JSON  (OWASP + NIST mapping)
```

The indirect-injection surface is modeled explicitly: a `Scenario` names the tool whose output
the attacker controls, and the target substitutes that tool's return value with the payload —
exactly the cross-surface vector that single-prompt scanners miss.
