Security-Audit — agents-platform
Scope: /Users/marvinkuehlmann/source/agents-platform
Methodik: SKILL.md Phasen 1–5 + 9 (Attack-Surface, Secrets, Dependencies, CI/CD, Infra/CDK, OWASP-Mini), Modus tief.
Stand: clean Working-Tree, branch main, letzter commit 0930503 feat: DashboardStack + GitHub-Aktivitaet-Modul deployed.
Gesamtbewertung: 8/10 — solides Security-Setup. Eine HIGH (transitive npm CVE), zwei niedrigprioritaere Code-Hygiene-Themen, sonst Lehrbuch.
Top-3 Findings
1. HIGH — fast-uri transitive Dependency mit zwei CVEs (CVSS 7.5)
Location: node_modules/aws-cdk-lib/node_modules/fast-uri@<=3.1.1
Refs: GHSA-q3j6-qgpj-74h6 (path traversal via percent-encoded dot segments), GHSA-v39h-62p7-jpjc (host confusion via percent-encoded authority delimiters)
npm audit meldet beide HIGH-Findings ueber aws-cdk-lib. Build-time only — fast-uri laeuft nicht in der Lambda-Runtime, sondern nur beim cdk synth/cdk deploy. Realer Angriffsvektor: minimal (lokale Dev-Maschine + CI), aber die Fix-Verfuegbarkeit ist trivial.
Fix: npm audit fix (haengt automatisch aws-cdk-lib Minor-Bump dran). Danach npm run synth ausfuehren um sicherzustellen dass kein Drift in den Templates entstand.
2. MEDIUM — Pre-Sign-Up-Lambda leakt Whitelist-Mechanismus via Fehlermeldung
Location: lambdas/dashboard-presignup/main.py:28
raise Exception(f"Diese Email ist nicht autorisiert.")Cognito propagiert die exception-message zurueck zum Client (Hosted UI). Ein Angreifer der versucht sich mit einer fremden Google-Identitaet einzuloggen sieht direkt dass es eine Whitelist gibt — Information-Leak ueber das Auth-Modell. Nicht kritisch (Username-Enumeration ist via Cognito-Hosted-UI ohnehin nicht moeglich dank preventUserExistenceErrors), aber unnoetig informativ.
Zusaetzlich: Wenn ALLOWED_EMAIL env-var leer ist, wird ebenfalls eine sprechende Fehlermeldung geworfen ("Dashboard ist temporaer geschlossen (Konfigurationsfehler)") — gibt Hinweis auf Misconfiguration.
Fix:
raise Exception("Sign-up disabled.")Generisch halten. Im CloudWatch-Log (intern) bleibt die detaillierte Info via log.warning(...) erhalten.
3. LOW — Bedrock Invoke-Permission mit Wildcard-Region
Location: infra/lib/agent-construct.ts:184-187
resources: [
'arn:aws:bedrock:eu-*::foundation-model/anthropic.claude-haiku-4-5-*',
`arn:aws:bedrock:eu-*:${this.role.env.account}:inference-profile/eu.anthropic.claude-haiku-4-5-*`,
],eu-* matcht aktuell eu-central-1, eu-west-*, eu-north-*, eu-south-*. Bedrock-EU-Inference-Profile laufen aktuell nur ueber eu-central-1 (siehe Vault intern/wissen/entscheidungen/bedrock-eu-image-gen-limitation.md). Wildcard ist Future-Proofing, expandiert aber den Permission-Scope.
Fix: eu-* durch eu-central-1 ersetzen. Wenn spaeter andere EU-Regions dazukommen, explizit erweitern.
Phasen-Detail
Phase 1 — Attack-Surface
- Eingang: EventBridge-Crons (3 Stacks, kein User-Input), Cognito Pre-Sign-Up Trigger (Google IdP), Lambda Direct-Invoke (manuell durch Marvin via AWS-CLI).
- Ausgang: Bedrock (Haiku 4.5), Telegram Bot API, Gmail API, Calendar API, GitHub REST (PAT), Papierkram MCP (
https://mcp-vf.agenticventures.de/mcp), Secrets Manager, S3 (av-finanzen, av-dashboard-frontend, av-dashboard-data), STS AssumeRole (mgmt + mk-privat). - Cognito-Surface: Hosted UI mit Google IdP + Pre-Sign-Up-Whitelist (single email). Sauber.
- Kein API-Gateway (Sprint 1.5), kein Public-Endpoint ausser CloudFront-Distribution (statische Dashboard-Files + JSON-Daten, OAC, alles ueber HTTPS-Redirect).
- Bewertung: Schmal, sauber definiert.
Phase 2 — Secrets
- Git history:
git ls-fileszeigt nursecrets.py(Wrapper-Code, kein Secret).git log -pueber*.env*/*secret*zeigt nur die Wrapper-Datei. Keine accidentally committeten Tokens. .gitignore: deckt.env,.env.*,__pycache__,.venv. OK.- In-Code: alle Secrets via
secrets.get("agent-platform/<name>")aus Secrets Manager. Kein hardcodetes Token, kein Plaintext-Env-Var fuer Secrets. - Telegram chat_id als Env-Var (
8257793678) — nicht sensibel (Code-Kommentar). OK. unsafeUnwrap()Google client_id indashboard-stack.ts:442— wird in CFN-Template gebakt. Acceptable (client_id ist per OAuth-Spec public).- Bewertung: clean.
Phase 3 — Dependencies / Supply Chain
package-lock.json: vorhanden, committed. Reproduzierbare Builds.npm audit: 1 HIGH (siehe Finding #1), 0 critical/medium/low/moderate.- Python-Layer: keine
requirements.txt—agentic_commonnutzt nur stdlib +boto3+urllib3(beides Lambda-preinstalled). Kein pip-bundle = kein Supply-Chain-Risiko via PyPI. uvx/pipSmoke-Test-Code in den Modulen ist__main__-guarded, laeuft nicht in Lambda.- Bewertung: minimal Surface, ein bekannter HIGH-CVE der trivial fixbar ist.
Phase 4 — CI/CD
- Keine
.github/workflows/vorhanden. Keinpull_request_target, keine unpinned third-party Actions. CI = nicht existent (Deploy nur lokal vianpm run deploy). cdk deploy --all --require-approval neverinpackage.jsonScript — bewusst, fuer Marvins Solo-Workflow OK, aber Vorsicht: wenn der Befehl irgendwann in Automation laeuft, sollte--require-approval broadeningrein (warnt bei IAM-/Security-Group-Changes).- Bewertung: kein Angriff-Vektor weil nicht existent.
Phase 5 — Infra / CDK
- IAM-Wildcards:
grep -E '"\*"' infra/lib/*.ts→ keine Treffer fueractions:["*"]oderresources:["*"]. Alle Policies sind least-privilege:- Bedrock: scoped auf Haiku 4.5 model+inference-profile (Wildcard nur in Region, siehe Finding #3).
- S3: scoped auf
av-finanzen-eu-central-1Bucket +/*(read+write fuer dieses eine Bucket). - KMS: scoped auf den av-finanzen Key-ARN.
- SecretsManager: scoped auf die 3 spezifischen Secret-ARNs.
- STS:AssumeRole: scoped auf die 2 spezifischen Cross-Account-Role-ARNs.
- S3-Buckets (alle 3):
blockPublicAccess: BLOCK_ALL,enforceSSL: true,encryption: S3_MANAGED,removalPolicy: RETAIN.dataBucketzusaetzlichversioned: truemit 30-Tage-noncurrent-Lifecycle. Lehrbuch. - CloudFront: OAC (Origin Access Control, neu, nicht legacy OAI),
REDIRECT_TO_HTTPS,PRICE_CLASS_100. SPA-Fallback errorResponses (404+403 → index.html) auf Distribution-Level — minor Schoenheitsfehler (Finding #4 unten als LOW gelistet), nicht security-kritisch. - Cognito:
passwordPolicy12+ Zeichen, alle Klassen required, MFA optional,preventUserExistenceErrors: true,generateSecret: false(public client), nurauthorizationCodeGrant,implicitCodeGrant: false. Pre-Sign-Up-Lambda als Defense-in-Depth zur Whitelist. Solide. - Lambda-Konfig: alle ARM64 (Graviton, billiger + getrennt von x86-CVEs),
PYTHON_3_12(Support bis 2028), Log-Retention 30 Tage, Timeouts knapp (3–10 Min), Memory 128–1024 MB. Kein VPC (kein VPC-noetig — alle Calls gehen ueber Internet zu Google/Telegram/Bedrock). - EventBridge-Rules: Schedule-only, kein offener Pattern-Listener — keine Lambda kann von beliebigen AWS-Events getriggert werden.
- Bewertung: sehr gut.
Phase 9 — OWASP / Code-Patterns
- Input-Validation: EventBridge-Events sind AWS-trusted, Cognito-Events sind Cognito-trusted. Kein User-Input aus HTTP-Requests (kein API-Gateway).
event.get("force_run") is Truein daily-briefing ist sicher — Lambda-Direct-Invoke ist auf IAM beschraenkt. - SSRF: Lambda macht Outbound-Requests an fixe URLs (Telegram API, Gmail API, Calendar API, GitHub API, Papierkram MCP). Keine User-supplied URLs.
- Command-Injection: keine
subprocess/os.system/shell=Truein Lambda-Code. Bedrock-Prompt-Injection waere theoretisch moeglich ueber Mail-Subjects/Snippets (briefing.py:75), aber Output ist via JSON-Schema-Tool-Use erzwungen — kein freier Text der dann irgendwo executed wird. Worst-Case: Briefing-Output unpassend formatiert. Acceptable. - Path-Traversal: GitHub-Client baut Pfade aus
ACTION_ITEMS_PATH/BRIEFING_ARCHIVE_DIREnv-Vars (operator-controlled). OK. - Token-Logging:
traceback.format_exc()[:500]inbeleg-pipeline/main.py:99koennte theoretisch Stack-Frames mit Secrets enthalten, aber die Secrets werden nicht als lokale Variablen mit erkennbarem Namen gehalten —bot_token = secrets.get(...)ist lokal und kommt nicht in Stack-Traces vor solange kein Exception innerhalb des Telegram-Calls geworfen wird. Akzeptabel. - Bewertung: sauber.
Weitere LOW / INFO Findings
-
LOW — CloudFront errorResponses 403/404 → index.html auch fuer
/api/data/*.dashboard-stack.ts:379-383setzt errorResponses Distribution-weit. Eine 404 auf/api/data/nonexistent.jsonliefertindex.htmlmit HTTP 200 zurueck. Frontend muss damit umgehen. Nicht security-kritisch, aber confused-deputy-Potenzial. Fix: per-behavior errorResponses oder CloudFront Function. -
INFO —
unsafeUnwrap()of Google client_id (dashboard-stack.ts:442) — bewusst per Code-Kommentar. OAuth-client_id ist per Spec public, OK. -
INFO — Cross-Account-Trust-Policies nicht im Repo verifizierbar. Code setzt korrekt die Source-Role als Principal beim AssumeRole-Call. Die Trust-Policies auf der empfangenden Seite (mk-privat, mgmt) muessen separat verifiziert werden (Repo
agentic-ventures/intern/capabilities/aws/). Siehedocs/migration-cloudflare-zu-lambda.mdfuer den Cross-Account-Setup. -
INFO — GitHub-PAT mit Vault-Write-Permission.
agent-platform/github-patschreibt inmarvin-khl/agentic-ventures. Bei Kompromittierung: vollstaendiger Vault-Write moeglich. Mitigation: Fine-Grained-PAT (contents:write+pull_requests:readnur fuer dieses eine Repo). Status-Check ob PAT bereits fine-grained ist: nicht aus dem Code erkennbar. Empfehlung: dokumentieren inintern/capabilities/agents-platform.md. -
INFO —
cdk deploy --all --require-approval neverinpackage.json:scripts.deploy. Bei Solo-CLI-Usage OK; bei kuenftiger CI-Integration aufbroadeningumstellen damit IAM-/SG-Changes Bestaetigung verlangen. -
INFO —
MGMT_ACCOUNT_IDKonstante im Code (infra/bin/app.ts:16). Account-ID ist nicht sensitiv (kommt sowieso in jeden ARN), aber wenn das Repo public wird oder geleakt: Account-IDs sind ein Recon-Vorteil fuer Angreifer (gezieltes IAM-Probing, Cross-Account-CVE-Targeting). Aktuell privates Repo — OK. Bei Open-Source: in Env-Var auslagern. -
INFO — Telegram-Token + GitHub-PAT in derselben Lambda-Role. Bei Lambda-Compromise (z.B. via maliciously crafted Bedrock-Response, theoretisch) kann der Angreifer beide Secrets ziehen. Inhaerent zur Architektur — Mitigation waere Permission-Split pro Funktion, aber bei Marvins Solo-Setup overkill. Acceptable.
Action-Items (priorisiert)
- Sofort (5 Min):
npm audit fixausfuehren,npm run synthvalidieren, committen. → Schliesst HIGH-CVE. - Naechste Edit-Session (5 Min):
dashboard-presignup/main.py:28Fehlermeldung generisch machen. Bedrock-Region-Wildcardeu-*→eu-central-1inagent-construct.ts:184-187. - Bei naechstem Commit am Dashboard-Stack: errorResponses 403/404 auf default-behavior beschraenken (per-behavior config).
- Dokumentation: in
intern/capabilities/agents-platform.mdNote dass GitHub-PAT fine-grained sein muss (single repo, contents:write + pull_requests:read).
Quellen / Methodik
- Skill: SKILL Phasen 1–5 + 9
- Tools:
npm audit,git grep,git log -p, manuelle CDK-Code-Review - Out-of-scope (nicht hier auditiert): Trust-Policies auf mgmt/mk-privat Accounts, MCP
mcp-vf.agenticventures.de(eigener Audit), hostende Cloudflare-/Fargate-Setup.