Security-Audit Vault + Plugin (agentic-ventures)

Scope: /Users/marvinkuehlmann/source/agentic-ventures als Vault und Claude-Code-Plugin (Symlink skills/ -> intern/capabilities/skills/). Auditor-Methodik: intern/capabilities/skills/security-audit/SKILL.md, Phase 2 (Secrets), 7 (LLM/Prompt-Injection), 8 (Skill-Supply-Chain), 11 (DSGVO).

Repo-Visibility: PRIVATE (marvin-khl/agentic-ventures, GitHub) — bestaetigt via gh repo view. Senkt das Schadensbild der internen Inhalte deutlich (Voraussetzung: GH-Account-Hygiene + 2FA, ausserhalb dieses Audits).


Findings

F1 — CRIT: Plaintext-Secrets in ~/.claude.json mcpServers

  • Severity: CRIT
  • Phase: 2 (Secrets) / 8 (MCP-Supply-Chain)
  • Befund: ~/.claude.json (User-Home, NICHT im Vault-Repo) enthaelt zwei Live-Tokens im Klartext im env-Block:
    • github.env.GITHUB_PERSONAL_ACCESS_TOKEN — GH-PAT gho_... (write-scope vermutlich, da Plugin-Repos forken/pushen kann)
    • elevenlabs.env.ELEVENLABS_API_KEYsk_...-Schluessel
  • Risiko: Jeder Code-Execution-Bug in einem MCP, jedes neugierige Skill das cat ~/.claude.json ausfuehrt, jede Backup-/Sync-/iCloud-Indizierung leakt diese Tokens. Skill-Supply-Chain (siehe F4) macht das real — externe Plugin-Skills laufen mit Marvins User-Rechten und koennen die Datei lesen.
  • Vergleichsbeispiel: Andere MCPs in derselben Config (hetzner) machen es richtig: op run -- mcp-hetzner mit op://...-Referenz statt Plaintext.
  • Fix: Beide Tokens nach 1Password verschieben, Eintraege auf op run-Wrapper umstellen analog hetzner-MCP. Tokens nach Rotation in 1P. ElevenLabs-Key rotieren, da er hier im Audit-Output war (auch wenn ich ihn nicht zitiere — er stand in der Bash-Ausgabe).
  • Pfad: ~/.claude.json (User-File, kein Vault-Pfad)

F2 — HIGH: skipDangerousModePermissionPrompt: true in ~/.claude/settings.json

  • Severity: HIGH
  • Phase: 8 (Plugin-Sandbox)
  • Befund: Globale Claude-Code-Settings haben skipDangerousModePermissionPrompt: true. Bedeutet: der Bestaetigungsdialog vor Dangerous-Mode wird uebersprungen. In Kombination mit der breiten Bash-Allowlist (siehe F3) und externen Plugins (F4) verliert Marvin die letzte Bremse vor unkontrollierter Code-Ausfuehrung.
  • Fix: Auf false setzen oder ganz entfernen. Friction lohnt sich.
  • Pfad: ~/.claude/settings.json

F3 — HIGH: Project .claude/settings.local.json enthaelt Wildcard-Bash-Permissions

  • Severity: HIGH
  • Phase: 8
  • Befund: Die Project-Allowlist enthaelt mehrere stark generische Eintraege:
    • Bash(npm install *) — beliebige npm-Pakete ohne Prompt; Supply-Chain-Risiko (typosquatting, malicious post-install scripts).
    • Bash(aws s3 *), Bash(aws lambda *), Bash(aws cloudformation *), Bash(aws ecs *), Bash(aws logs *), Bash(aws configure *) — beliebige Mutations-Calls inkl. aws s3 rm, aws lambda delete-function, aws configure set .... Kein Profil-Gate.
    • Bash(op item *) — beliebige 1Password-Item-Calls ohne Prompt; faktisch Full-Read auf den Vault.
    • Bash(gh api *) — beliebige GitHub-API-Calls inkl. write (POST/DELETE auf Repos, Releases, Secrets).
    • Read(//tmp/**)//tmp ist eine ungewoehnliche Schreibweise; falls als Bypass interpretiert (Doppel-Slash → Root-Match), liest das ggf. mehr als gewuenscht. Pruefen.
    • Read(//Users/marvinkuehlmann/.aws/**) — gibt Lese-Zugriff auf AWS-Credentials/Config inkl. Session-Tokens.
  • Risiko: Ein bos-/buggy Skill (oder Prompt-Injection ueber externe Markdown/Email/Inbox-Files) kann diese Permissions ausnutzen ohne Marvin zu fragen. Read(//Users/.../.aws/**) plus Bash(aws s3 *) ist faktisch „delete any bucket”.
  • Fix: Wildcards aufbrechen. Konkrete Subcommands erlauben (aws s3 ls, aws s3 cp ja; aws s3 rm, aws s3 rb nein). op item * op item get * (read-only). npm install * raus, lieber pro Session prompten. Read(//Users/.../.aws/**) raus — .aws/credentials darf Claude nie sehen. gh api * gh api -X GET * whitelisten, write-Methoden raus.
  • Pfad: /Users/marvinkuehlmann/source/agentic-ventures/.claude/settings.local.json

F4 — MED: Externe Plugin-Marketplaces ohne Pinning, lange Trust-Chain

  • Severity: MED
  • Phase: 8
  • Befund: Aktive externe Marketplaces in ~/.claude/settings.json:
    • every-marketplace (compound-engineering) — kein Pin auf Commit/Tag.
    • coreyhaines31/marketingskills — privater Maintainer, kein Pin.
    • kepano/obsidian-skills — Privatperson.
    • anthropics/skills — offiziell, OK.
    • claude-plugins-official — offiziell, OK.
  • Jeder Skill aus diesen Marketplaces kann beim Update beliebig Bash-Blocks einfuegen. Skill-Inhalte sind Prompt-Code — sie werden vom Modell als Anweisungen interpretiert und koennen die Bash-Allowlist (F3) ausnutzen.
  • Risiko-Beispiel: every-marketplace versioniert die compound-engineering-Skills laufend; ein kompromittierter Commit dort kann z.B. ein git-commit-push-pr-Skill veraendern und in Marvins Workflow Secrets exfiltrieren.
  • Fix: (a) Marketplaces auf Commit-SHA pinnen sofern Plugin-System das unterstuetzt (siehe _meta/security.md falls dort bereits dokumentiert); (b) Mindestens coreyhaines31/marketingskills deaktivieren wenn nicht aktiv genutzt — laut _externe-skills.md ist Marketing-Disziplin „bewusst nicht eingebunden”, Plugin aber dennoch aktiv; (c) regelmaessiger git log der Marketplace-Forks vor Plugin-Reload.
  • Pfad: ~/.claude/settings.json enabledPlugins + extraKnownMarketplaces

F5 — MED: Sensible Vault-Inhalte committed (Kunden-Stammdaten, USt-IdNr, Telefonnummern, Geheimhaltungs-Context)

  • Severity: MED (mitigiert durch Private-Repo, eskaliert wenn Repo je public wird oder geforked)
  • Phase: 11 (DSGVO)
  • Befund: 260 .md-Files mit visibility: internal, davon 106 mit echten Kunden-Slugs (Becker, Vibe-Factory, Friseur-im-Sueden, Erlei, Ladezon, …).
    • intern/kunden/ladezon.md — HRB, Steuernummer, GF-Namen
    • intern/kunden/erlei.md — HRB, USt-IdNr DE363248648, Mitarbeitername
    • intern/kunden/vibe-factory.md — USt-IdNr DE453362151 in Frontmatter
    • intern/projekte/openwebui-vf/2026-05-14-test-plan-use-cases.md — Telefonnummern, Personenrollen
    • intern/runs/2026-05.md — Personennamen + Telefonnummer in Klartext
    • intern/runs/2026-05-13-vf-vault-analyse/... — vertraulicher Mandanten-Kontext
  • Risiko: Bei Repo-Visibility-Flip (Public-Klick versehentlich), Backup-Leak, Stolen-Laptop ohne FileVault, oder GitHub-Org-Access-Misconfig haetten Dritte Zugriff auf Auftragsdaten + Personendaten von Kunden. Bei Becker-NDA (Art. 1 (1), Frist 10 Jahre) ist die Existenz der Geschaeftsbeziehung selbst Vertraulich — die schiere Datei-Existenz intern/kunden/becker* ist NDA-relevant.
  • Mitigation aktiv: Repo ist privat, .gitignore blockt .env*/*--kontakt.md/assets/finanzen/. NDA-PDF (inbox/Geheimhaltungsvereinbarung_BAS_Agentic.pdf) ist ?? untracked — gut.
  • Fix: (a) Pre-commit-Hook der gh repo view --json visibility prueft und bei Wechsel auf Public hart abbricht; (b) Backup-Pruefung: laeuft iCloud-/Dropbox-Sync auf diesem Ordner? Wenn ja: ausschliessen (siehe _meta/security.md); (c) Disk-Verschluesselung (FileVault) verifizieren; (d) assets/firma/partner-materials/2026-04-22-claude-dsgvo-hosting-summary.pdf und assets/firma/brand-assets/agentic-vorschau.pdf sind die einzigen committeten PDFs — sind das wirklich nicht-vertrauliche Assets? Pruefen.

F6 — LOW: Becker-Attachments in inbox/becker-attachments/

  • Severity: LOW (untracked, aber riskant fuer kuenftiges Versehen)
  • Phase: 11 + 2
  • Befund: inbox/becker-attachments/ enthaelt 5 sensible PDFs/DOCX (Angebot, Datenschutzvertrag, Vertragsbedingungen, NDA-related). Aktuell nicht in Git getrackt — verifiziert via git ls-files inbox/becker-attachments/ → leer. Aber: inbox/ ist nur per .gitkeep plus Session-Prompts getrackt; PDFs koennen versehentlich mit git add inbox/ reinrutschen.
  • Fix: .gitignore-Eintrag inbox/*.pdf und inbox/*-attachments/ ergaenzen, damit Kundenanhaenge nie versehentlich committet werden. Geheimhaltungsvereinbarung_BAS_Agentic.pdf ist ebenfalls untracked → mitigiert mit demselben Pattern.

F7 — LOW: Eigene SKILL.md-Files mit Bash-Blocks — Prompt-Injection-Vektor

  • Severity: LOW (kein direkter ${ARGUMENTS}-Sink gefunden, aber Risiko bei kuenftigen Aenderungen)
  • Phase: 7 (LLM-Security)
  • Befund: Eigene Skills enthalten teils dichte Bash-Sequenzen:
    • security-audit/SKILL.md — 27 Bash-Bloecke (am dichtesten)
    • routine-anlegen/SKILL.md — 7
    • qa-staging/SKILL.md — 5
    • mcp-cloud-bereitstellung/SKILL.md — 4
    • mcp-eigenbau/SKILL.md — 3
  • Grep nach ${ARGUMENTS}/$1/direkter User-Input-Interpolation: 0 Treffer. Aktuell sicher.
  • Risiko (zukunftsorientiert): Wenn Marvin oder ein Skill-Creator-Lauf einen Bash-Block mit User-Input-Interpolation einfuegt (z.B. grep "$ISSUE_TITLE" ...), wird ein Issue-Title mit Backticks/Semikolons zur Shell-Injection. LLM-Skills sind Prompt-Code, der Bash-Befehle vorschlagen kann — alles was reinkommt sollte als untrusted gelten.
  • Fix: Konvention in _meta/security.md ergaenzen: „User-Input in SKILL-Bash-Bloecken NIE direkt interpolieren — immer ueber Argumente an ein Tool oder Heredoc-quoted”. Pre-commit-Hook der Skills auf \$\{?[A-Z_]+\}? in Bash-Bloecken scannt waere ideal.

F8 — LOW: External-Plugin Skills sind beim Update sichtbar in Skill-Tool-Liste — Verwechslungsgefahr

  • Severity: LOW
  • Phase: 7
  • Befund: Die Skill-Tool-Liste zeigt 200+ Skills inkl. Duplikaten (d230a6dd6eb1:doc-coauthoring etc. — Anthropic-Skills cached-id-mode). Marvin merkt sich Skill-Namen nicht (Regel 20). Risiko: Ein bos benannter externer Skill mit Trigger-Phrase email-schreiben wuerde vom Modell mit dem eigenen verwechselt — Trigger-Map-Override.
  • Fix: Skill-Auswahl im Routing strikt nach Plugin-Namespace: bei Konflikt zwischen agentic-ventures:email-schreiben und externem Skill immer agentic-ventures-Namespace bevorzugen. In CLAUDE.md Regel 20 dies explizit machen.

F9 — INFO: Keine echten Tokens in Vault-Files oder Git-History

  • Severity: INFO (positiv)
  • Phase: 2
  • Befund: Vollscan mit git log -p --all und Repo-Filescan auf sk-ant-, AKIA[0-9A-Z]{16}, gh[posu]_, xoxb-, EAA[...]60+, skc_: 0 Treffer in echten Daten (alle Hits in security-audit/SKILL.md Pattern-Beispielen oder QA-Checklisten).
  • inbox/Geheimhaltungsvereinbarung_BAS_Agentic.pdf und inbox/becker-attachments/* sind nicht getrackt.
  • .env.local ist gitignored und enthaelt 4 Zeilen (3 davon Kommentare).

F10 — INFO: .venv/ mit AWS-Botocore-Code committed

  • Severity: INFO (kein Secret, aber Bloat + Audit-Noise)
  • Befund: intern/projekte/telefon-assistent-aws/{webapp,livekit-agent,tools-mcp}/.venv/lib/.../{credentials.py,cacert.pem} etc. — Python-Venv-Inhalt wird tracked (nicht gitignored). Keine echten Credentials drin, nur Library-Source. Aber: blaeht Repo auf und macht Secret-Scans laut.
  • Fix: .venv/ zu .gitignore ergaenzen und einmalig git rm -r --cached intern/projekte/telefon-assistent-aws/*/.venv/.

Positive Befunde

  • Repo private bestaetigt.
  • .gitignore deckt Secrets-Pattern und PII-Pattern (*--kontakt.md) korrekt ab.
  • Hetzner-MCP benutzt op://-Reference statt Plaintext-Token (best-practice).
  • NDA-PDF + Becker-Attachments untracked.
  • Keine echten API-Keys/Tokens in .md-Files oder Git-History.

Top 3 Sofortmassnahmen

  1. F1 — ~/.claude.json Secrets in 1Password verschieben. ElevenLabs-Key + GitHub-PAT rotieren, auf op run-Wrapper umstellen analog Hetzner-MCP. Beide Tokens wurden im Bash-Output dieses Audits sichtbar — Rotation ist nicht optional. Aufwand: 20 Minuten.
  2. F3 — Project-Allowlist .claude/settings.local.json entschaerfen. Mindestens raus: Bash(aws s3 *), Bash(aws lambda *), Bash(aws configure *), Bash(op item *), Bash(gh api *), Bash(npm install *), Read(//Users/marvinkuehlmann/.aws/**). Wildcards durch konkrete read-only-Subcommands ersetzen. Plus F2: skipDangerousModePermissionPrompt: false. Aufwand: 15 Minuten.
  3. F5/F6 — Repo-Visibility-Guard + Inbox-Schutz. Pre-commit-Hook der gh repo view --json isPrivate prueft und bei false abbricht; .gitignore um inbox/*.pdf und inbox/*-attachments/ ergaenzen; FileVault + iCloud-Sync-Status verifizieren. Aufwand: 30 Minuten.