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-Klassemcp-papierkram (2026-05-18)mcp-replicate-hosted (2026-05-19)Anti-Pattern
uv.lock fehlt + Range-PinningF1 MED 8/10 VERIFIED (offen)clean (uv.lock im Initial-Commit getrackt)gefixt in Skeleton
Raw-Escape-Hatch-Tools default anF2 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-WarnF3 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 Argsn/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-SchemaT1 (TENTATIVE 6/10)n/a (Layer-Tools haben typed Sigs)nicht gefixt, beobachten
Size-Cap fuer PDF-Uploads / Prompt-LengthT2 (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).

  1. 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=false als Default, in Hosted-Container true nur wenn bewusst gewollt.
  2. Hosted-Wrapper-MCP: Sub-MCP via Proxy gemounted → alle Sub-Tools sind via tools/call erreichbar, auch wenn tools/list via ToolWhitelistMiddleware gefiltert wird. Fix: explizite Allow-Liste in der GuardMiddleware (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:

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:

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:

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.

IDSevConfStatusPhaseTitel
F1MED8/10VERIFIED3 (Supply-Chain)uv.lock fehlt + Dependencies range-pinned
F2MED8/10VERIFIED6 (Tool-Surface)Raw-Escape-Hatch-Tools default aktiviert
F3MED8/10VERIFIED6 (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.

IDSevConfStatusPhaseTitel
F1MED9/10VERIFIED + FIXED d22c3f56 (Tool-Surface)Modell-Whitelist-Bypass via version/deployment Args
F2MED9/10VERIFIED + FIXED d22c3f56 (Tool-Surface)Account-Admin- + Raw-Tools via tools/call ohne Whitelist erreichbar
F3MED8/10VERIFIED + FIXED d22c3f56 (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):

IDSeverityKonvergenzTitel
CR-H1HIGHcorrectness + adversarial_TOOL_CATALOG-Vergiftung bei transientem Sub-MCP-Spawn-Fail — Discovery permanent kaputt bis Restart
CR-H2HIGHtesting + correctness_extract_model_arg silent-bypass bei malformed model-Args
CR-M1MEDcorrectnessDrift-Test fuer USE_CASE_DEFAULTS vs DEFAULT_MODEL_WHITELIST
CR-M2MEDtesting + adversarialsearch_tools-Filter ohne Test (Defense-in-Depth-Invariante)
CR-M3MEDtestingtest_register_prompts false-confidence (assertete app.name)
CR-M4MEDadversarial + securityCDK PLACEHOLDER-Pre-Synth-Guard
CR-M5MEDkieran + 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.

LessonWo dokumentiertAufnahme-Status
uv.lock im Initial-Commit committenSKILLgelernt aus papierkram#F1, bei replicate-hosted respektiert. Pflicht-Check in Skill.
Bind-Sanity-Warn-Log fuer lokal-Debugmcp-best-practices § Security-DefaultsTODO: ergaenzen
Raw-Escape-Hatch-Tools default off via Env-Varmcp-best-practices § Tool-Design (existiert teilweise, anchor zu W2)TODO: anchor + Cross-Ref zu W2
GuardMiddleware Allow-Liste statt Block-Liste fuer Sub-MCP-Toolsmcp-hosting-fargate-tunnel § GuardMiddleware-Pflicht (Section ggf. neu)TODO: ergaenzen
Whitelist auch auf alternative Args pruefen (Resource-Adressierung)mcp-best-practices § Tool-DesignTODO: ergaenzen
CI/CD vor Public-Repo: gitleaks + pytest + ruff GH-Actionmcp-best-practices § OperationsTODO: 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:

  1. Run-Report ablegen unter intern/runs/<datum>-security-audit-<scope>/.
  2. Diese Datei updaten — neue Pro-MCP-Sektion, Vergleichs-Matrix-Spalte erweitern, ggf. W-Pattern (wiederkehrend) ergaenzen.
  3. Wenn ein Finding 2+ MCPs trifft: hoch in „Wiederkehrende Findings” als W-Eintrag, Skeleton-Lessons-Tabelle erweitern.
  4. Cross-Ref im Run-Report (Pointer hierher).

Cross-Ref