MCP Security-Audit Findings — Cross-Repo-Tracker
Wachsende Liste. Bei jedem neuen security-audit-Lauf gegen einen unserer MCPs: eine Sektion hier anhaengen, Vergleichs-Tabelle erweitern, wiederkehrende Findings markieren. Ziel: Pattern-Wissen sammeln statt jedes neue MCP dieselben Fehler machen zu lassen.
Konvention: Findings-IDs sind pro Run lokal (F1/F2/F3 im jeweiligen Audit). Hier referenzieren wir sie als <mcp-slug>#F1. Severity-Skala identisch zum security-audit-Skill (CRIT/HIGH/MED/LOW + Konfidenz 1-10 + VERIFIED/UNVERIFIED/TENTATIVE).
Vergleichs-Matrix (Quick-Glance)
| Finding-Klasse | mcp-papierkram (2026-05-18) | mcp-replicate-hosted (2026-05-19) | Anti-Pattern |
|---|---|---|---|
| uv.lock fehlt + Range-Pinning | F1 MED 8/10 VERIFIED (offen) | clean (uv.lock im Initial-Commit getrackt) | gefixt in Skeleton |
| Raw-Escape-Hatch-Tools default an | F2 MED 8/10 VERIFIED (offen, raw_get/raw_post default exponiert) | F2 MED 9/10 VERIFIED GEFIXT d22c3f5 (Allow-Liste-Pattern, alle Admin/Raw-Tools serverseitig deaktiviert) | WIEDERKEHREND + schwerer im Wrapper-Pattern |
| HTTP-Bind 0.0.0.0 ohne Auth-Sanity-Warn | F3 MED 8/10 VERIFIED (offen) | F3 MED 8/10 VERIFIED GEFIXT d22c3f5 (Warn-Log via AWS_EXECUTION_ENV-Heuristik) | WIEDERKEHREND |
| Modell/Resource-Whitelist Bypass via alternative Args | n/a (kein API-Layer) | F1 MED 9/10 VERIFIED GEFIXT d22c3f5 (version=/deployment= hard-rejected) | neu, hosted-MCP-spezifisch |
Create/Update-Tools mit data: dict ohne Pydantic-Schema | T1 (TENTATIVE 6/10) | n/a (Layer-Tools haben typed Sigs) | nicht gefixt, beobachten |
| Size-Cap fuer PDF-Uploads / Prompt-Length | T2 (Phase-12-Hard-Exclusion) | T (Prompt-Length) | Hard-Exclusion-Pattern, nicht reporten |
Wiederkehrende Findings (Top-Pattern)
Diese Findings tauchen in 2+ MCPs auf — sie gehoeren ins Skeleton/Pattern, sonst produzieren wir sie immer wieder.
W1 — Bind-Sanity-Warn fehlt (default 0.0.0.0)
Trifft: mcp-papierkram#F3, mcp-replicate-hosted#F3.
Pattern. Python-MCP-Skeleton bindet defaultmaessig HOST=0.0.0.0 weil’s im Container so sein muss. Lokal-Debug erbt den Default und exponiert den Service ohne Warnung im LAN. Auth-Layer (Scalekit) schuetzt zwar, aber /health ist auth-frei und reveal-by-default.
Skeleton-Fix: in main.py jedes neuen MCPs Standard-Warn-Log einbauen wenn HOST=0.0.0.0 UND nicht in ECS (heuristik AWS_EXECUTION_ENV). Patch-Vorlage: runs/2026-05-19-security-audit-mcp-replicate-hosted/patches/F3-bind-sanity-warning.md.
Wo ergaenzen: mcp-best-practices § Security-Defaults — Bind-Sanity-Warn als Pflicht-Snippet aufnehmen.
W2 — Raw-Escape-Hatch-/Admin-Tools default zugaenglich
Trifft: mcp-papierkram#F2 (raw_get/raw_post default on), mcp-replicate-hosted#F2 (alle Admin-Tools via tools/call obwohl nicht in tools/list).
Pattern (zwei Auspraegungen).
- Lokaler MCP:
raw_get/raw_post-Notluken-Tools sind default exponiert. Modell halluziniert Pfade (Ankervorfall 2026-05-15). Fix: per-MCP-Env-Var-Toggle<MCP>_EXPOSE_RAW=falseals Default, in Hosted-Containertruenur wenn bewusst gewollt. - Hosted-Wrapper-MCP: Sub-MCP via Proxy gemounted → alle Sub-Tools sind via
tools/callerreichbar, auch wenntools/listvia ToolWhitelistMiddleware gefiltert wird. Fix: explizite Allow-Liste in derGuardMiddleware(nicht Block-Liste — neue Sub-Tools sollten default geblockt sein). Patch-Vorlage:runs/2026-05-19-security-audit-mcp-replicate-hosted/patches/F2-replicate-admin-tools-block.md.
Wo ergaenzen:
- mcp-best-practices § Tool-Design — Raw-Tools-Gating-Pflicht-Pattern dokumentieren
- mcp-hosting-fargate-tunnel § GuardMiddleware-Pflicht — Allow-Liste fuer Sub-MCP-Tools als Standard-Section
W3 — Resource-Whitelist nur auf ein Arg geprueft, alternative Args bypassen
Trifft (bisher): mcp-replicate-hosted#F1 (Modell-Whitelist greift nur auf model=, nicht version=/deployment=).
Pattern. Wenn das Sub-MCP-Tool mehrere alternative Wege bietet das zu schuetzende Resource zu adressieren (Replicate: model vs version vs deployment; aehnlich bei jedem MCP mit „Free-Form”-Pfaden), reicht eine Whitelist auf einen Arg nicht. Loesung: entweder alle alternativen Args pruefen oder die alternativen Pfade pauschal blocken und nur den kontrollierten Pfad zulassen. Hier: version/deployment blocken, nur model=<owner/name>-Pfad lassen.
Wo ergaenzen:
- mcp-best-practices § Tool-Design — pruefen pro Tool: gibt es alternative Adressierungspfade? Whitelist auf ALLE pruefen oder Alternativen blocken.
- mcp-hosting-fargate-tunnel § GuardMiddleware-Pflicht — “Alt-Arg-Bypass”-Sub-Check ergaenzen.
W4 — Lazy-Built Cache cached auch leere/partial-Ergebnisse bei Fehler
Trifft (bisher): mcp-replicate-hosted#CR-H1 (_TOOL_CATALOG cached [] bei Sub-MCP-Spawn-Fail → Discovery permanent kaputt bis ECS-Restart).
Pattern. Jeder lazy-built _get_X()-Helper mit Modul-Level-Singleton-Cache, der mehrere Sub-Sources iteriert, hat das Risiko: ein transienter Fehler beim ersten Aufruf wird gecacht und blockiert alle Folge-Aufrufe. ECS-Health bleibt 200 (Container lebt), niemand merkt’s bis externe Beobachtung greift.
Loesung: Cache nur committen wenn ALLE Sub-Sources erfolgreich. Partial-Result optional zurueckgeben aber nicht persistieren — naechster Call retried. Vorbild-Code: _get_tool_catalog in ~/source/mcps/mcp-replicate-hosted/src/mcp_replicate_hosted/main.py:431-475 (nach commit 50121f9).
Wo ergaenzen:
- mcp-best-practices § Operations — Cache-Pattern-Section.
- Retrofit in mcp-vf-hosted: das hat dieselbe Catalog-Logik mit demselben Bug.
W5 — Layer-Tool-Defaults driften aus Whitelist (Bypass per Design)
Trifft (bisher): mcp-replicate-hosted#CR-M1 (Layer-Tools bypassen GuardMiddleware per Design via _build_sub_mcp_client, ihre Defaults muessen explizit gegen Whitelist gepinned werden — sonst Cost-Schutz silent weg).
Pattern. Wenn Layer-Tools / High-Level-Wrapper interne Sub-Tool-Calls machen ohne durch die Auth-Middleware zu gehen (weil sie SELBST schon authorisiert sind), wird die Resource-Whitelist nicht angewandt. Wenn jemand spaeter die Whitelist enger macht ohne die Layer-Tool-Defaults nachzuziehen → Layer-Tool ruft ein Modell das nicht mehr erlaubt sein sollte, aber keiner sieht’s.
Loesung: 1-Zeilen-Assertion-Test: assert set(USE_CASE_DEFAULTS.values()) <= DEFAULT_MODEL_WHITELIST. Vorbild: test_all_use_case_defaults_in_whitelist in tests/test_model_whitelist.py.
Wo ergaenzen:
- mcp-best-practices § Tool-Design — bei Layer-Tool-Pattern Pflicht-Drift-Test.
W6 — CDK-PLACEHOLDER-Strings rutschen durch cdk synth
Trifft (bisher): mcp-replicate-hosted#CR-M4 (4 PLACEHOLDER-Werte im Stack, alle wuerden loud-fail, aber Mirror-Pattern-Risiko propagiert auf weitere hosted MCPs).
Pattern. Mirror-Stacks (von einem Vorbild kopiert) erben PLACEHOLDER-Werte fuer Secret-ARNs, Image-Digests, OAuth-Resource-IDs. Heute alle loud-fail, aber: wenn ein PLACEHOLDER-Wert mal versehentlich einen syntaktisch echten Wert hat (z.B. ein gueltiger ARN aber falsche Resource), gibt’s silent-deploy mit broken state.
Loesung: Pre-Synth-Guard requireNoPlaceholder(name, value) der bei value.includes('PLACEHOLDER') throwed. cdk synth bricht dann VOR jedem AWS-Call ab. Vorbild: ~/source/mcps/mcp-replicate-hosted/infra/lib/mcp-replicate-hosted-stack.ts (nach commit 50121f9).
Wo ergaenzen:
- mcp-hosting-fargate-tunnel § CDK-Skeleton — Guard-Helper als Pflicht-Pattern.
- Retrofit in
mcp-vf-hosted-stack.ts(hat dieselben Placeholders ohne Guard).
Pro-MCP-Findings
mcp-papierkram (Audit 2026-05-18)
Detail-Report: report. Pilot-Audit, Daily 8/10. 0 CRIT, 0 HIGH, 3 MED.
| ID | Sev | Conf | Status | Phase | Titel |
|---|---|---|---|---|---|
| F1 | MED | 8/10 | VERIFIED | 3 (Supply-Chain) | uv.lock fehlt + Dependencies range-pinned |
| F2 | MED | 8/10 | VERIFIED | 6 (Tool-Surface) | Raw-Escape-Hatch-Tools default aktiviert |
| F3 | MED | 8/10 | VERIFIED | 6 (Misconfiguration) | HTTP-Bind ohne Auth-Sanity-Check |
TENTATIVE (unter 8/10-Gate): T1 data: dict ohne Pydantic-Schema (Konf 6/10), T2 keine Size-Cap bei PDF-Uploads (Konf 5/10, Hard-Exclusion).
mcp-replicate-hosted (Audit 2026-05-19)
Detail-Report: report. Pre-Deploy-Audit, Daily 8/10. 0 CRIT, 0 HIGH, 3 MED.
| ID | Sev | Conf | Status | Phase | Titel |
|---|---|---|---|---|---|
| F1 | MED | 9/10 | VERIFIED + FIXED d22c3f5 | 6 (Tool-Surface) | Modell-Whitelist-Bypass via version/deployment Args |
| F2 | MED | 9/10 | VERIFIED + FIXED d22c3f5 | 6 (Tool-Surface) | Account-Admin- + Raw-Tools via tools/call ohne Whitelist erreichbar |
| F3 | MED | 8/10 | VERIFIED + FIXED d22c3f5 | 6 (Misconfiguration) | HTTP-Bind default 0.0.0.0 ohne Auth-Sanity-Check |
TENTATIVE (offen): T1 Brand-Lock-User-Prompt-Concat ohne <<<>>>-Wrapping (Konf 5/10), T2 keine CI/CD-Workflows (Konf 7/10), T3 _normalize_tool_result fail-tolerant ohne Audit-Spur (Konf 4/10).
Fix-Lauf (2026-05-19, gleiche Session wie Audit): commit d22c3f5 in mcp-replicate-hosted. F2 als Allow-Liste-Pattern (_ALLOWED_REPLICATE_TOOLS mit 5 Tools, alles andere geblockt via tool.startswith("replicate_") and tool not in allow). F1 zusaetzliche Schicht: bei replicate_create_prediction werden args.version/args.deployment hard-rejected (Layer-Tools nutzen ausschliesslich model=). F3 Bind-Sanity-Warn-Log via AWS_EXECUTION_ENV-Heuristik. search_tools-Discovery filtert geblockte Tools mit raus. 45 neue Test-Cases in test_guard_middleware_blocks.py, 84/84 pytest + ruff clean.
Patch-Skizzen (zum Nachvollziehen): F1, F2, F3.
Code-Review (Multi-Persona, 2026-05-19)
Anschluss-Lauf an den security-audit: 6 parallele compound-engineering-Reviewer (correctness, security, adversarial, kieran-python, testing, maintainability). Konvergente Findings (2+ Reviewer derselbe Befund) wurden priorisiert. Detail-Run: _index.
Phase-A (geshipped, commit 50121f9):
| ID | Severity | Konvergenz | Titel |
|---|---|---|---|
| CR-H1 | HIGH | correctness + adversarial | _TOOL_CATALOG-Vergiftung bei transientem Sub-MCP-Spawn-Fail — Discovery permanent kaputt bis Restart |
| CR-H2 | HIGH | testing + correctness | _extract_model_arg silent-bypass bei malformed model-Args |
| CR-M1 | MED | correctness | Drift-Test fuer USE_CASE_DEFAULTS vs DEFAULT_MODEL_WHITELIST |
| CR-M2 | MED | testing + adversarial | search_tools-Filter ohne Test (Defense-in-Depth-Invariante) |
| CR-M3 | MED | testing | test_register_prompts false-confidence (assertete app.name) |
| CR-M4 | MED | adversarial + security | CDK PLACEHOLDER-Pre-Synth-Guard |
| CR-M5 | MED | kieran + maintainability + testing (3-fach) | _normalize_tool_result fail-fast statt 3 Defensive-Wraps |
107/107 pytest gruen (vorher 84) + ruff clean. CDK-Guard verifiziert via npx cdk synth.
Phase-B-Backlog (separater PR nach AWS-Deploy): main.py-Split, Pydantic-Models, Integration-Tests, getattr-Ketten-Cleanup, Test-Builder statt MagicMock-Coupling.
Phase-C-Backlog (Hardening, irgendwann): Route53-Synthetic-Probe (gilt fuer alle hosted MCPs), Subject=None-Bucket-Sharing, Block-Audit-Log-Spam-Throttling, MAINT-1/2-Refactors, Audit-Klarnamen-Schutz, Brand-Lock-Idempotenz, Slash-Prompt-Empty-Handling, README-Doku-Drift.
Skeleton-Lessons (was wir aus den Audits ins Pattern uebernehmen)
Diese Aenderungen sollten in Pattern-Files und ggf. einem mcp-skeleton/-Template landen, damit zukuenftige MCPs sie nicht mehr produzieren.
| Lesson | Wo dokumentiert | Aufnahme-Status |
|---|---|---|
uv.lock im Initial-Commit committen | SKILL | gelernt aus papierkram#F1, bei replicate-hosted respektiert. Pflicht-Check in Skill. |
| Bind-Sanity-Warn-Log fuer lokal-Debug | mcp-best-practices § Security-Defaults | TODO: ergaenzen |
| Raw-Escape-Hatch-Tools default off via Env-Var | mcp-best-practices § Tool-Design (existiert teilweise, anchor zu W2) | TODO: anchor + Cross-Ref zu W2 |
| GuardMiddleware Allow-Liste statt Block-Liste fuer Sub-MCP-Tools | mcp-hosting-fargate-tunnel § GuardMiddleware-Pflicht (Section ggf. neu) | TODO: ergaenzen |
| Whitelist auch auf alternative Args pruefen (Resource-Adressierung) | mcp-best-practices § Tool-Design | TODO: ergaenzen |
| CI/CD vor Public-Repo: gitleaks + pytest + ruff GH-Action | mcp-best-practices § Operations | TODO: ergaenzen |
Naechster Schritt: wenn 3+ Skeleton-Lessons gesammelt sind und 2+ TODOs hier offen — ein zusammenhaengender PR auf mcp-best-practices + mcp-hosting-fargate-tunnel, der alle Pattern-Updates auf einmal landen laesst. Statt jedes Audit eine Mini-Edit zu produzieren.
Recurrence
Bei jedem neuen Lauf des security-audit-Skills:
- Run-Report ablegen unter
intern/runs/<datum>-security-audit-<scope>/. - Diese Datei updaten — neue Pro-MCP-Sektion, Vergleichs-Matrix-Spalte erweitern, ggf. W-Pattern (wiederkehrend) ergaenzen.
- Wenn ein Finding 2+ MCPs trifft: hoch in „Wiederkehrende Findings” als W-Eintrag, Skeleton-Lessons-Tabelle erweitern.
- Cross-Ref im Run-Report (Pointer hierher).
Cross-Ref
- Skill: SKILL
- Best-Practices: mcp-best-practices
- Hosting-Pattern: mcp-hosting-fargate-tunnel
- Run-Reports:
- Letzter Voll-Lauf (alle Scopes): report