
SecureAI-Scan v0.4.0
SecureAI-Scan ist ein CLI-Tool, das TypeScript- und JavaScript-Codebasen auf Sicherheitsprobleme spezifisch für KI-gestützte Anwendungen scannt — Prompt Injection, MCP Tool Abuse, RAG Data Poisoning, Agent Trust Violations und mehr.
SecureAI-Scan
Offline-CLI, das TypeScript, JavaScript und Python auf LLM-, MCP-, Agent-Skill- und RAG-Risiken scannt — importaufgelöste Datenfluss-Beweise, standardmäßig null falsch-positive Ergebnisse, zugeordnet zu OWASP LLM/ASI/MCP Top 10.
Die meisten Scanner in diesem Bereich gleichen ein Schlüsselwort ab und bezeichnen das als Befund. SecureAI-Scan verfolgt den tatsächlichen Pfad Quelle → Fluss → Senke durch echten, importaufgelösten Code — und ein Standard-Scan zeigt dir nur, was es beweisen kann. Kein Konto, kein Cloud-Upload, nichts verlässt deinen Rechner.
Abgedeckt werden die offiziellen OWASP Top 10 für LLM-Anwendungen 2026, Top 10 für agentische Anwendungen (2026) und die MCP Top 10 ab der Startwoche.
In 30 Sekunden starten```bash
npx --yes [email protected] scan .
Kein Konto, kein Cloud-Upload, kein Python-Interpreter und keine Konfiguration erforderlich. TypeScript, JavaScript, Python, MCP-Konfigurationen und Agent-Skill-Bündel werden automatisch erkannt.
**Gemessener `0.9.0`-Release-Kandidat:** 136/136 Tests · 88,08 % Anweisungsabdeckung · 12.676 Dateien in 9 öffentlichen Repositories · 0 neue Standard-Tier-Fingerabdrücke gegenüber der überprüften Baseline. [Nachweise](https://github.com/akanthed/secureai-scan/blob/main/docs/benchmarks/v0.9.0.json) · [Methodik und Grenzen](https://github.com/akanthed/secureai-scan/blob/main/docs/ReleaseAssurance.md)```
▌ HIGH AI001 Prompt injection via user input
PROVEN LLM01:2026 Prompt Injection
source src/chat.ts:8 request data `req.body.input`
flow src/chat.ts:13 passed as `systemPrompt`
sink src/chat.ts:10 openai.chat.completions.create — system role (OpenAI)
fix Keep system prompts static; pass user input as a user-role message.
Ist das etwas für dich? SecureAI-Scan ist bewusst auf LLM-, MCP- und RAG/Agent-Risiken ausgerichtet — Prompt-Injection, Tool-Poisoning, unsichere Ausgabebehandlung, Zugriffskontrolle für Vektor-Store, Agent-Skill-Poisoning. Es ist kein allgemeiner SAST- oder Secrets-Scanner und versucht auch nicht, einer zu sein; ein bekanntes bösartiges Paket ohne LLM-förmige Payload (z. B. eine hartcodierte Exfiltrationsadresse in einem E-Mail-API-Aufruf) wird von der Offline-Advisory-Liste (DEP003) erfasst, nicht von einer Pattern-Regel. Wenn deine Codebasis mit einem LLM, einem MCP-Server, einem Vektor-Store kommuniziert oder Agent Skills ausliefert, ist dieses Tool für dich gebaut.
Neu: statische Konfigurationsprüfung für LiteLLM Proxy (config.yaml) — hartcodierte Secrets, Klartext-Provider-Endpunkte, fehlende Guardrails. Siehe Regeln (LLC001–LLC003).
Inhalt
- Warum dieser Scanner anders ist
- Wie er sich vergleicht
- In 30 Sekunden loslegen
- In Aktion sehen
- Befehle
- GitHub Action
- Pre-commit-Hook
- Regeln
- Architektur
- MCP-Server (von Claude aus nutzen)
- Claude Skill
- Vertrauen und Release-Sicherheit
- Der Präzisionsvertrag
- Tests & Benchmarking
- Roadmap
- Mitwirken
Warum dieser Scanner anders ist
- Evidenzstufen, kein Rauschen. Jeder Befund ist
proven(nachverfolgter Datenfluss oder geparste Konfigurationsfakt),likely(aufgelöste Senke, ein heuristischer Sprung) oderheuristic. Ein Standard-Scan zeigt nur proven + likely. Heuristiken sind optional über--paranoidaktivierbar. - Import-aufgelöste Erkennung. Ein Aufruf ist nur dann ein „LLM-Aufruf", wenn er sich zu einem echten SDK-Import auflöst (
openai,@anthropic-ai/sdk,ai,@google/genai, LangChain, Bedrock, …). Dein Google-Maps-Client wird nie wieder als LLM markiert. - Präzisionsgesteuert und gegen echte Repos benchmarked. Die Testsuite stellt sicher, dass jede verwundbare Fixture auslöst und jede sichere Fixture sauber bleibt — ein False Positive im sicheren Korpus lässt den Build fehlschlagen. Darüber hinaus scannt
npm run regressionechte öffentliche Repos (OpenAI/Anthropic/Vercel AI SDKs, offizielle MCP-Server, LlamaIndex) gegen eine festgeschriebene, manuell geprüfte Baseline und schlägt bei jedem neuenproven/likely-Befund fehl. Siehe Tests & Benchmarking für die tatsächlichen Vorher/Nachher-Zahlen oder Was wir beim Scannen echter Repos gefunden haben für die Geschichte dahinter — eine 6/6-Erkennungsrate bei einem gelabelten bösartigen Skill-Korpus und warum wir llama_index nicht als „verwundbar" bezeichnen, trotz eines ehrlichen Befunds auf Bibliotheksebene. Diskussionsbeitrag → - SARIF für GitHub-Code-Scanning.
--output report.sarifplatziert Befunde inline in Pull Requests und im Security-Tab. - AI-BOM.
secureai-scan bom .erstellt ein syntaxbasiertes Inventar von SDKs, Modell-IDs, Vektor-Stores, Agent-Frameworks und MCP-Servern, zugeordnet zu den Dokumentationsanforderungen von OWASP LLM Top 10 / EU AI Act. - MCP-Konfigurationsscanning. Parst
.mcp.json,claude_desktop_config.json,.cursor/mcp.json: nicht gepinntenpx -y-Server, Inline-Secrets, Klartext-HTTP-Transports. - MCP-Tool-Poisoning-Erkennung. Erkennt das Muster hinter dem WhatsApp-MCP-Rug-Pull und dem postmark-mcp-Backdoor — unsichtbares Unicode, agentengerichtete Injektionsphrasen und Cross-Tool-Shadowing in Tool-Namen/Beschreibungen, statisch, bevor du den Server jemals ausführst.
- MCP-Befehlsinjektions-Erkennung. Markiert MCP-Stdio-Transport-
command/args, die aus Anfragedaten aufgebaut werden — das Muster hinter der MCP-STDIO-RCE-Offenlegung von 2026. - Agent-Skill-Poisoning-Erkennung. Dieselben Unsichtbares-Unicode-, Injektionsphrasen- und Shadowing-Prüfungen, angewendet auf
SKILL.md-Dateien — Agent Skills werden vollständig in den Kontext geladen, also ist ein vergifteter Skill eine vergiftete Tool-Beschreibung unter anderem Namen. - Evasion-resistentes Skill-Scanning. Skill-Bundles werden als Verzeichnisse gescannt, nicht nur ihre
SKILL.md, und jede Inhaltsprüfung läuft gegen deobfuskierte Varianten des Textes. Dies zielt auf die veröffentlichten Techniken — Homoglyphen, Zero-Width-Splitting, Payloads in.git/oderbuild/gestaged, Exfiltration versteckt in einer*.test.ts-Datei — die >90 % der neun Scanner umgingen, die in Cloak and Detonate (arXiv:2607.02357) untersucht wurden. Siehe Evasion-Resistenz. - Bekannt-verwundbare und bekannt-bösartige Paket-Advisories, versionsbewusst. Prüft jede Abhängigkeit und jedes MCP-gestartete Paket gegen einen gebündelten Advisory-Snapshot — eine handkuratierte Liste dokumentierter In-the-Wild-Backdoors, plus HIGH/CRITICAL-OSV-Advisories für eine LLM/MCP/RAG-Paket-Watchlist, neu generiert von
scripts/sync-advisories.js. Läuft offline bei jedem Scan, kein Flag erforderlich. Eine CVE feuert nur, wenn deine gepinnte Version nachweislich innerhalb des betroffenen Bereichs liegt; ein dokumentiert-bösartiges Paket feuert sogar bei einem mehrdeutigen Bereich, weil das Installieren einer Backdoor nicht rückgängig zu machen ist. - Local-first. Nichts verlässt deinen Rechner.
Wie er sich vergleicht
SecureAI-Scan ist kein Ersatz für ein allgemeines SAST-Tool oder einen Container/IaC-Scanner — führe es neben einem aus, nicht stattdessen. Es ist speziell für die LLM/MCP/RAG-Angriffsfläche gebaut und betont Datenfluss-Evidenz gegenüber flachen Keyword-Befunden.
| SecureAI-Scan | Semgrep (OSS-Regeln) | Trivy | GitHub Advanced Security | |
|---|---|---|---|---|
| Prompt-Injection (Quelle→Senke nachverfolgt) | ✅ import-aufgelöster Datenfluss | ⚠️ nur Pattern-Regeln, community-gepflegt | ❌ | ⚠️ CodeQL kann es, aber kein KI-spezifisches Regelset |
| MCP-Tool-Poisoning / Konfigurationsrisiko | ✅ MCP007–010, Konfigurationsscanner | ❌ | ❌ | ❌ |
Agent-Skill-Poisoning (SKILL.md) | ✅ evasion-resistent, bundle-bewusst | ❌ | ❌ | ❌ |
| RAG / Vektor-Store-Fehlkonfiguration | ✅ VEC001–004 | ❌ | ❌ | ❌ |
| Bekannt-bösartige KI-Paket-Advisories | ✅ DEP003, offline, versionsbewusst | ❌ | ⚠️ allgemeiner CVE-Feed, nicht KI-spezifisch | ⚠️ Dependabot, allgemeiner CVE-Feed |
| Allgemeines SAST (SQLi, XSS, Path Traversal) | ❌ bewusst außerhalb des Scopes | ✅ | ❌ | ✅ |
| Container / IaC-Scanning | ❌ | ❌ | ✅ | ⚠️ über CodeQL/Actions |
| Evidenzstufen (proven/likely/heuristic) | ✅ | ❌ Befunde sind flach | ❌ | ⚠️ CodeQL hat einige, nicht KI-abgestimmt |
| SARIF-Ausgabe (GitHub-Code-Scanning) | ✅ | ✅ | ✅ | nativ |
| Läuft offline, kein Konto | ✅ | ✅ (OSS-Regeln) | ✅ | ❌ erfordert GitHub |
Wenn du bereits Semgrep oder GHAS ausführst, behalte sie — füge SecureAI-Scan für die Risikofläche hinzu, die sie überhaupt nicht modellieren.
Lieber zuerst Fragen stellen? Probiere den kostenlosen SecureAI-Scan AI Security Advisor auf ChatGPT.
Du willst einen MCP-Server ausführen, den du auf GitHub oder Twitter gefunden hast? Füge zuerst seine Tool-Beschreibung in MCP X-Ray ein — prüft in deinem Browser auf verstecktes Unicode, injizierte Anweisungen und bekannt-bösartige Pakete, ohne Installation.
In Aktion sehen
secureai-scan scan . von Anfang bis Ende, echte Ausgabe gegen eine echte (kleine, absichtlich verwundbare) Datei — Quelle:
Angriffsformen, die der Scanner Ende-zu-Ende nachverfolgt:
| MCP-Tool-Poisoning-Datenfluss | RAG-Kontextinjektions-Datenfluss |
|---|---|
![]() | ![]() |
Befehle
Der, den du in 95 % der Fälle brauchst:```bash secureai-scan scan .
Alles andere ist da, wenn du es brauchst. `secureai-scan scan . --help` zeigt all das im Terminal an, gruppiert auf dieselbe Weise:
**Alltag**
| Flag | Was es tut |
|------|---------------|
| *(keine)* | `proven` + `likely` Befunde — die Standardeinstellung, keine Flags nötig |
| `--paranoid` | zusätzlich `heuristic`-Stufe-Befunde einbeziehen |
| `-s, --severity <level>` | nur Befunde ab `low`\|`medium`\|`high`\|`critical` anzeigen |
| `--output <file>` | einen vollständigen Bericht schreiben — `.sarif` (GitHub-Code-Scanning), `.json`, `.md` oder `.html` |
**Umfang, welche Regeln ausgeführt werden**
| Flag | Was es tut |
|------|---------------|
| `-r, --rules <list>` | nur diese Regel-IDs ausführen, z. B. `AI001,MCP007` |
| `--only-ai` / `--only-mcp` / `--only-vec` / `--only-skl` | nur eine Regelkategorie ausführen |
| `--check-dependencies` | zusätzlich `package.json`/`requirements.txt` gegen die npm/PyPI-Registry auf Tippfehler und halluzinierte Pakete prüfen (`DEP001`/`DEP002`). Automatisch aktiviert, wenn du diese Regeln direkt über `-r` auswählst — du musst nie daran denken, beide zu übergeben. Nicht nötig für `DEP003` (bekannt schädliche Pakete), das immer offline läuft |
**CI / Workflow**
| Flag | Was es tut |
|------|---------------|
| `--fail-on <severity>` | mit `1` beenden, wenn Befunde ab dieser Schwere vorhanden sind |
| `--baseline <file>` | nur neue/geänderte Probleme gegenüber einer gespeicherten Baseline verfolgen |
| `--policy <file>` | Schwellenwerte, übersprungene Pfade und blockierte Regeln aus einer `.secureai-policy.json` laden (automatisch erkannt, falls vorhanden — `secureai-scan init` erstellt eine) |
**Erweitert**
| Flag | Was es tut |
|------|---------------|
| `--min-confidence <0-1>` | feiner abgestuft als `--paranoid`: Befunde unterhalb eines exakten Konfidenzwerts ausblenden (`0.9` proven / `0.65` likely / `0.35` heuristic) |
| `--limit <n>` | maximale Anzahl angezeigter Regelgruppen im Terminal (Standard `10`) — vollständige Details gehen immer an `--output` |
| `--debug` | jede gescannte Datei und welche Regeln ausgeführt wurden, ausgeben |
**Vor der Installation scannen — kein Klon, keine Konfiguration:**```bash
secureai-scan skill anthropics/skills # a GitHub "owner/repo" shorthand
secureai-scan skill https://github.com/… # or a full git URL
secureai-scan skill ./some/local/skill-dir # or a local path
secureai-scan mcp some-mcp-server-package # a bare npm package name
secureai-scan mcp owner/mcp-server-repo # or git, same as `skill`
skill und mcp holen das Ziel und scannen es, dann löschen sie die heruntergeladene Kopie (--keep, um sie stattdessen zu inspizieren). Nichts, was geholt wird, wird jemals ausgeführt: Ein npm-Ziel wird mit npm pack heruntergeladen — nur das Tarball, kein install, keine Lifecycle-Skripte — und ein Git-Ziel ist ein einfaches git clone --depth 1. Dies ist der Moment, der am meisten zählt: bevor ein Skill in ~/.claude/skills/ landet oder ein Server in .mcp.json, nicht danach.
Andere Befehle:```bash secureai-scan bom . --output AI_BOM.md # AI Bill of Materials secureai-scan explain AI001 # why + exploit + fix example, for any rule secureai-scan threat-model . # THREAT_MODEL.md with the OWASP coverage matrix — example: docs/examples/THREAT_MODEL.example.md secureai-scan init # policy file + CI workflow, one-time setup
Unterdrücke einen geprüften Befund im Code:```ts
// secureai-ignore AI001: reviewed, input sanitized via allowlist
GitHub Action```yaml
name: SecureAI-Scan on: [pull_request] permissions: contents: read security-events: write jobs: scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: akanthed/[email protected] with: scanner-version: 0.10.0 fail-on: high
Befunde erscheinen als Inline-Anmerkungen im PR und im Security-Tab des Repositorys. (`secureai-scan init` generiert einen äquivalenten Workflow, der die CLI direkt verwendet.)
Scan sauber? Füge das Badge zu deiner eigenen README hinzu:```md
[](https://github.com/akanthed/SecureAI-Scan)
Pre-commit-Hook
Bevorzugst du es, Befunde zu erfassen, bevor sie gepusht werden? Füge dieses Repository stattdessen als pre-commit-Hook-Quelle hinzu, oder zusätzlich zur GitHub Action:```yaml repos:
- repo: https://github.com/akanthed/SecureAI-Scan
rev: v0.10.0
hooks:
- id: secureai-scan
Der Hook scannt bei jedem Commit das gesamte Projekt (nicht nur geänderte Dateien – eine Datenfluss-Ablaufverfolgung in Datei A kann von Datei B abhängen, die ein partieller Scan übersehen würde) und blockiert den Commit standardmäßig bei Befunden mit Schweregrad `high`+. Überschreibe den Schwellenwert in deiner eigenen Konfiguration:```yaml
- id: secureai-scan
args: ["--fail-on", "critical"]
Regeln
42 Regeln, zugeordnet zum offiziellen OWASP Top 10 für LLM-Anwendungen (2026) — zusätzlich, wo zutreffend, zum OWASP Top 10 für Agentic Applications (2026, ASI), zum OWASP MCP Top 10 (2025) und einem EU-AI-Act-Artikel. Siehe die versionierte 2026-Abdeckung und -Grenzen; threat-model rendert die Matrix für jedes gescannte Projekt.
| Regel | Was sie nachweist | OWASP |
|---|---|---|
| AI001 | Benutzereingaben fließen in einen System-/Entwickler-Prompt (verfolgte Quelle → Senke, einschließlich über Funktions-/Dateigrenzen hinweg) | LLM01 |
| AI002 | Prompt-Inhalte oder Geheimnisse werden in Logs geschrieben (in Dateien, die ein LLM-SDK verwenden) | LLM02 |
| AI003 | LLM-Aufruf in einem Request-Handler ohne vorherige Auth-Prüfung | LLM06 |
| AI004 | Vollständiges Benutzer-/Sitzungsobjekt wird in einen Prompt serialisiert (Feldauswahl wird nicht gemeldet) | LLM02 |
| AI005 | LLM-Ausgabe erreicht eval/exec/SQL/HTML-Senken | LLM10 |
| AI006 | Hochriskante Tools (delete, pay, deploy, …) ohne Genehmigungs-Gate freigegeben | LLM03 |
| AI007 | Abgerufene RAG-Inhalte werden in privilegierte Prompts interpoliert | LLM01 |
| AI008 | Geheimnisse im System-Prompt-Text eingebettet | LLM08 |
| AI009 | Unbegrenzte Benutzereingaben / fehlende Token-Limits | LLM06 |
| AI010 | Abgerufene externe Inhalte fließen in Prompts | LLM01 |
| AI011 | Agent-Ausgabe wird in nachgelagerten Aufrufen auf System-Rolle angehoben | LLM03 |
| AI012 | LLM-Ausgabe ohne Schema-Validierung geparst | LLM10 |
| MCP001 | MCP-Tool-Metadaten erreichen den System-Prompt ohne Validierung | LLM01 |
| MCP002 | MCP-Server-URL aus Benutzereingaben konstruiert | LLM04 |
| MCP003 | MCP-Tool-Ergebnisse auf System-Rolle angehoben | LLM10 |
| MCP004 | MCP-Server als nicht gepinntes npx -y-Paket gestartet | LLM04 |
| MCP005 | Geheimnis in einer committeten MCP-Konfiguration eingebettet | LLM02 |
| MCP006 | MCP-Server über Klartext-HTTP | LLM04 |
| MCP007 | Unsichtbare/Bidi-Unicode-Zeichen in MCP-Toolnamen oder -Beschreibungen versteckt | LLM01 · MCP03 |
| MCP008 | Agentengesteuerte Injektionsphrasen in MCP-Tool-Beschreibungen | LLM01 · MCP03 |
| MCP009 | Eine Tool-Beschreibung, die Aufrufe auf ein anderes Tool lenkt (Shadowing) | LLM01 · MCP03 |
| MCP010 | MCP-stdio-Server-Befehl/-Argumente aus Benutzereingaben konstruiert (RCE) | LLM04 · MCP05 |
| SKL001 | Unsichtbare/Bidi-Unicode-Zeichen irgendwo in einem Agent-Skill-Bundle | LLM01 |
| SKL002 | Agentengesteuerte Injektionsformulierung in der Beschreibung oder im Inhalt eines Skills (durch Obfuskation abgeglichen) | LLM01 |
| SKL003 | Der Inhalt eines Skills steuert, wann/wie ein anderer Skill verwendet wird (Shadowing) | LLM01 |
| SKL004 | Gestaffelte/selbstextrahierende Payload: undurchsichtiger Blob + Anweisungen zum Dekodieren und Ausführen | LLM04 · MCP04 |
| SKL005 | Anmeldedaten-Lesen + fest verdrahteter externer Egress in einer Begleitdatei des Bundles | LLM02 · MCP04 |
| SKL006 | Befehlsausführung zur Ladezeit über Claude Codes Syntax für dynamische Kontextinjektion (!`cmd`/```!), vor jedem Tool-Berechtigungs-Gate | LLM04 · MCP05 |
| SKL007 | Unbegrenzte Bash-Freigabe im allowed-tools-Frontmatter eines Skills | LLM03 |
| SKL008 | Skill ruft Anweisungen von einer externen URL ab und weist den Agenten an, ihnen zu folgen („Circus of Skills") | LLM04 |
| SKL009 | Skill persistiert eine Hintertür, indem er in eine andere Kontextdatei schreibt (MEMORY.md/SOUL.md/AGENTS.md/CLAUDE.md) | LLM05 |
| SKL010 | Unsicheres YAML/JSON-Deserialisierungs-Tag im Frontmatter eines Skills oder in einer gebündelten Konfigurationsdatei | LLM04 |
| VEC001 | Vektorsuche ohne Mandanten-/Benutzerfilter | LLM09 |
| VEC002 | Unbegrenztes oder benutzergesteuertes Suchlimit | LLM06 |
| VEC003 | Benutzerinhalte in einen gemeinsamen Vektor-Store aufgenommen | LLM05 |
| VEC004 | Aufnahme ohne Mandanten-/Namespace-Tagging | LLM09 |
| DEP001 | Abhängigkeitsname nicht in der Registry gefunden (optional --check-dependencies) | LLM04 |
| DEP002 | Abhängigkeitsname eine Bearbeitung von einem beliebten Paket entfernt (optional) | LLM04 |
| DEP003 | Abhängigkeit mit dokumentierter bösartiger Veröffentlichung oder kritischer CVE — bei jedem Scan offline geprüft, versionsbereichsbewusst (postmark-mcp, mcp-remote CVE-2025-6514, …) | LLM04 · MCP04 |
| LLC001 | Fest verdrahtetes Geheimnis in einer LiteLLM-Proxy-config.yaml | LLM02 |
| LLC002 | LiteLLM-Proxy-api_base über Klartext-HTTP erreichbar | LLM04 |
| LLC003 | LiteLLM-Proxy-Konfiguration hat keinen guardrails:-Abschnitt (heuristisch, nur --paranoid) | LLM03 |
secureai-scan explain <RULE_ID> liefert den Exploit-Walkthrough und ein Vorher/Nachher-Codebeispiel für jede Regel.
Architektur
Drei unabhängige Scan-Oberflächen speisen eine zusammengeführte, deduplizierte Befundliste:``` ┌─────────────────────┐ *.ts / *.js ───▶ │ ts-morph AST rules │───┐ │ (import-resolved │ │ │ sinks + dataflow) │ │ └─────────────────────┘ │ │ ┌─────────────────────┐ │ ┌──────────────┐ ┌─────────────────┐ *.py ───▶ │ tree-sitter AST + │───┼───▶ │ scan.ts │───▶ │ evidence filter │ │ local taint flow │ │ │ merge/dedupe│ │ → confidence │ └─────────────────────┘ │ │ + suppress │ │ → severity │ │ │ (// secure- │ │ → baseline diff │ .mcp.json, ┌─────────────────────┐ │ │ ai-ignore) │ │ → report │ SKILL.md ───▶ │ Config/bundle scan │──┘ └──────────────┘ └─────────────────┘ │ (off-disk, evasion- │ │ │ resistant) │ ▼ └─────────────────────┘ terminal · sarif · json · md · html
package.json, requirements.txt ─▶ dependency-guard.ts (advisories.ts, offline, version-aware)
Jede AST-Regel bezeichnet eine Funktion nur dann als „LLM-Aufruf“, wenn sie über echte Importe zu einem bekannten SDK aufgelöst wird – niemals allein durch Namensabgleich. Siehe [`docs/Architecture.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/Architecture.md) für die vollständige Aufschlüsselung jeder Oberfläche und [`docs/DetectionEngine.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/DetectionEngine.md) für die Funktionsweise des Vertrags der Evidenzstufen.
## MCP-Server (nutze ihn mit Claude)
Das Paket enthält einen MCP-Server, der `scan_repository`, `explain_rule`, `generate_bom` und `scan_untrusted_target` bereitstellt (eine Skill- oder MCP-Server-Ressource abrufen und scannen, bevor Claude deren Installation empfiehlt – dasselbe Abrufen-ohne-Ausführen-Verhalten wie die CLI-Befehle `skill`/`mcp`):```json
{
"mcpServers": {
"secureai-scan": {
"command": "node",
"args": ["/path/to/secureai-scan/mcp-server/index.js"]
}
}
}
Claude Skill
Für Claude Code / Claude.ai-Nutzer: skills/secureai-scan/SKILL.md bringt Claude bei, wann ein Scan ausgeführt werden soll (beim Überprüfen von KI/LLM-Code oder beim Prüfen eines MCP-Servers/Agent-Skills vor der Installation) und wie die Ergebnisse zu lesen sind – kein separater Prozess nötig, anders als beim MCP-Server oben. Kopieren Sie das Verzeichnis skills/secureai-scan/ in Ihr .claude/skills/, um es zu verwenden.
Umgehungsresistenz
Im Juli 2026 zeigten zwei öffentliche Forschungsarbeiten, dass Agent-Skill-Scanner trivial umgangen werden können.
Cloak and Detonate (Ji, Xu, Li, Gao, Wei, Wang & Cheung — HKUST, arXiv:2607.02357) führte eine adversariale Studie gegen neun veröffentlichte Skill-Scanner mit 1.613 in-the-wild bösartigen Skills durch. Zwei Angriffsfamilien, die beide die Payload exakt erhalten:
- Reify — schreibt die Bytes neu, die eine Regel matcht, während der Agent das Original zur Laufzeit wieder zusammensetzt. >80 % Umgehung bei den meisten statischen Scannern.
- Disperse / SFS-Packing — verschiebt die Payload außerhalb des Regelbereichs: in ein Verzeichnis, das der Scanner überspringt, hinter eine umbenannte Erweiterung, oder archiviert und kodiert in
.git/, mit einer harmlosen Tarn-SKILL.md, die sie beim ersten Lauf wiederherstellt. ≥90 % Umgehung bei jedem getesteten Scanner, ≥96 % bei den meisten. Die Erkennungsrate eines Hybrid-Scanners fiel von ~99 % auf ~10 %.
Separat lieferte Gecko Security (VentureBeat) eine Payload in einer *.test.ts-Datei aus. Jeder öffentliche Scanner übersprang sie – aber Jest und Vitest erkennen und führen diese Dateien beim nächsten npm test automatisch aus.
SecureAI-Scan behandelt jede veröffentlichte Technik. Jede Zeile unten hat einen entsprechenden Test in test/deobfuscate.test.js oder eine Fixture in test-fixtures/vulnerable/skills/:
| Technik (Name im Paper) | Beispiel | Wie sie behandelt wird |
|---|---|---|
| Zero-Width-Einfügung | Ignore all previous… | Text wird gegen unsichtbar-bereinigte Varianten gematcht |
| Verwechselbare Glyphen | сurl (kyrillisches с) | Homoglyph-Faltung zu ASCII vor dem Matching |
| Groß-/Kleinschreibung | EvAl | Alle Inhaltsmuster sind case-insensitiv |
| Variablen-Splicing | 'cu' + 'rl' | Benachbarte String-Literale werden vor dem Matching verbunden |
| Zeilenweise Aufteilung | ev⏎al(x) | Wortinterne Zeilenumbrüche werden als zusätzliche Variante verbunden |
| Dateiübergreifende Extraktion | Payload in einer eingebundenen Helper-Datei | Das gesamte Bundle wird gescannt, nicht nur SKILL.md |
| Verzeichnisverschiebung | scripts/x.sh → build/x.sh | Keine Verzeichnis-Skip-Liste innerhalb eines Bundles |
| Erweiterungsumbenennung | x.sh → x.txt | Dateien werden nach Inhalt gescannt, nicht nach Erweiterung |
| Größen-Padding | über das Scanner-Limit hinaus auffüllen | Überdimensionierte Dateien werden vom Anfang gelesen, nie übersprungen |
| SFS-Packing | Payload in .git/skillpack.dat | Jede Nicht-Git-Datei unter .git/ eines Bundles ist proven (SKL004) |
| Testdatei-Staging | Payload in *.test.ts | Bundle-Scanning stuft Testpfade bewusst nicht herab (SKL005) |
Das schwächt den Präzisionsvertrag nicht
Deobfuskation ist normalerweise ein Präzisionsrisiko – mehr Matches, mehr Rauschen. Hier ist die Logik umgekehrt: Ein Match, das erst nach der Deobfuskation erscheint, wird zu proven hochgestuft, nicht herabgestuft. Normale Dokumentation enthält weder ein Zero-Width-Joiner innerhalb von „ignore previous instructions" noch ein kyrillisches с innerhalb von curl. Die Verschleierung selbst ist ein affirmativer Beweis für die Absicht.
Der Vergleich erfolgt gegen die Menge der Roh-Matches, nicht nur gegen „hat der Rohtext überhaupt gematcht" – sonst könnte ein Angreifer das Signal maskieren, indem er einen harmlosen Ausdruck im Klartext lässt.
Die beiden neuen Bundle-Regeln feuern nur auf Konjunktionen, nie auf ein einzelnes Schlüsselwort:
- SKL004 benötigt einen opaken Blob und eine Entpack-Anweisung, die diesen Blob namentlich referenziert – eine README, die
tar -xneben einem unabhängigen Binär-Asset erwähnt, reicht nicht. Echte Archive (gzip/zip/png/pdf/wasm – per Magic Bytes geprüft, nicht per Erweiterung) sind von vornherein nie „opak", egal wie sie komprimiert sind. - SKL005 benötigt ein konkretes Credential-Signal – einen Pfad (
~/.aws/credentials, nicht das Wort „token") oder eine Massen-Aufzählung von Umgebungsvariablen (os.environ.items(), nichtos.environ["API_KEY"]) – und Egress zu einem hartkodierten Nicht-Local-Host, innerhalb von 25 Zeilen voneinander in derselben Datei, oder einen Remote-Fetch, der ausgeführt wird, nachdem er über eine oder mehrere Umbenennungen neu zugewiesen wurde. Ein Publish-Helper, der~/.npmrcin einer Funktion liest und vierzig Zeilen später ein Registry aufruft, bleibt sauber, und das Lesen einer benannten Env-Var für einen API-Aufruf wird nie geflaggt – beide Formen sind als sichere Fixtures festgelegt.
Validierung gegen zwei reale Korpora, nicht nur gegen selbst geschriebene Fixtures: 0 Befunde über alle 18 realen Skill-Bundles in anthropics/skills und alle 14 in vercel/ai, und 6/6 korrekt auf dem gelabelten Eval-Korpus von cisco-ai-defense/skill-scanner (20 Skills, jeweils mit einem _expected.json-Urteil) mit null False Positives auf allem, was als sicher gelabelt ist. Siehe Testing & Benchmarking.
Was das nicht ist
Ehrliche Einschränkung: Die Schlussfolgerung des Papers ist, dass Laufzeit-Detonation statische Analyse schlägt, und das ist korrekt. Ein adaptiver Angreifer, der diese Regeln kennt, kann eine Transformation schreiben, die sie nicht abdecken. Was sich hier ändert, sind die Kosten der Umgehung – die veröffentlichten, derzeit kursierenden Techniken funktionieren nicht mehr, und die zur Überwindung nötige Obfuskation erhöht nun selbst die Schwere des Befunds. Statisches Scannen ist ein Filter, keine Sicherheitsgrenze. Behandeln Sie einen nicht vertrauenswürdigen Skill als nicht vertrauenswürdigen Code, unabhängig davon, was ein Scanner sagt.
Vertrauen und Release-Absicherung
- CI läuft auf Linux, Windows und macOS über unterstützte Node-Versionen.
- CodeQL, Produktionsabhängigkeits-Audit, OpenSSF Scorecard, Dependabot und der eigene blockierende Selbst-Scan dieses Scanners bieten unabhängige Prüfungen.
- Jede manuelle npm-Veröffentlichung ruft Tests, Coverage-Untergrenzen, das geprüfte Real-Repository-Regressions-Gate und Tarball-Inspektion über
prepublishOnlyauf. - GitHub Actions erhält kein npm-Passwort und keinen Token und kann das Paket nicht veröffentlichen.
- Release-Absicherung, Single-Maintainer-Governance, Security-Reporting und versionierte Benchmark-Belege sind öffentlich.
Dies ist ein Single-Maintainer-Projekt ohne vertragliche SLA oder unabhängige Zertifizierung. Die oben genannten Kontrollen reduzieren Risiko; sie machen aus einem statischen Scan keinen Sicherheitsbeweis.
Der Präzisionsvertrag
False Positives töten Scanner. Die Regel-Engine von SecureAI-Scan folgt drei harten Regeln:
- Sinks werden über Imports aufgelöst. Wenn ein Bezeichner auf ein Modul auflöst, das kein LLM-SDK ist, ist es definitiv kein LLM-Aufruf – egal wie es heißt.
- Belege sind gelabelt, nie vermischt. Ein nachverfolgter Datenfluss und ein Wortnähe-Match sind nicht dasselbe, daher teilen sie nie eine Stufe.
- Der Safe-Korpus sperrt jedes Release.
test-fixtures/safe/enthält die Muster, die früher False Positives verursachten (redigierte PII-Payloads, Google-Maps-Clients, Env-Var-API-Keys neben LLM-Clients, gewöhnliches Response-Logging, OAuth-Metadatenfelder, Streaming-Response-chunks, Fiktions-/Erzähl-Prompt-Text). Jeder Befund dort lässt die Suite fehlschlagen.
Testing & Benchmarking
Drei Ebenen, weil eine allein nicht ausreicht, um den Behauptungen eines Scanners zu vertrauen – Präzision und Recall sind unterschiedliche Fehlermodi, und beide werden geprüft.
1. Fixture-Korpus – Präzision + Recall, läuft bei jedem Build.```bash npm test
[`test-fixtures/vulnerable/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/vulnerable) und [`test-fixtures/safe/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/safe) werden gemeinsam gescannt: Jede verwundbare Fixture muss ihre erwartete Regel mit `proven`/`likely`-Evidenz auslösen (Recall), jede sichere Fixture muss **null** `proven`/`likely`-Befunde erzeugen (Präzision). Schnell und deterministisch – aber es beweist nur, dass sich der Scanner bei Code, der speziell zum Testen geschrieben wurde, korrekt verhält.
**2. Realwelt-Regressions-Benchmark – gegen öffentliche Repos, die wir nicht geschrieben haben.**```bash
npm run regression # scan the full curated repo set
npm run regression -- --fresh # re-clone everything first
npm run regression -- openai-node # scan just one repo by name
npm run regression -- --update-baseline # accept the current findings
scripts/regression-scan.js klont eine kuratierte, vielfältige Auswahl realer öffentlicher Repos (OpenAI/Anthropic/Vercel AI SDKs, die offiziellen MCP-Server und das TypeScript-SDK, LlamaIndex, plus anthropics/skills und cisco-ai-defense/skill-scanner für Skill-Bundle-Abdeckung — über TS und Python, SDK-Konsumenten-Beispielcode und SDK-Autoren-Quellcode) und scannt jedes mit der gebauten CLI.
Es beendet sich mit einem Nicht-Null-Exitcode bei jedem proven/likely-Fund, der nicht bereits in test/regression-baseline.json steht — einem manuell geprüften Verzeichnis von Funden, die bereits gegen ihre Quellzeile gelesen wurden. Fingerprints sind repo|rule|file, nicht Zeilennummern, sodass gewöhnliches Upstream-Churn kein Rauschen erzeugt. Ein neuer Fingerprint ist ein Anspruch, den der Scanner rechtfertigen muss: Wenn es kein echtes Problem ist, ist es ein Regel-Bug, der an der Ursache behoben und als neues test-fixtures/safe/-Fixture festgeschrieben wird. Einen Fund zu baselinen, den man nicht gelesen hat, macht den gesamten Mechanismus zunichte.
Skill-Bundle-Abdeckung bekommt eine eigene Zeile, weil das evals/-Korpus von cisco-ai-defense/skill-scanner beschriftet ist — jedes seiner 20 Fixtures liefert ein _expected.json-Urteil und liegt unter einem Verzeichnis, das wörtlich malicious/ oder safe/ heißt, sodass es sich als Recall-Check verdoppelt, nicht nur als Precision-Check: 6/6 in-scope bösartige Fixtures feuern, 0 Funde auf allem, was als safe beschriftet ist, und 0 Funde über alle 18 echten Bundles in anthropics/skills und alle 14 in vercel/ai. (Die verbleibenden Cisco-Kategorien — SQL-Injection, Path Traversal, Ressourcenerschöpfung, generisches eval() eines Funktionsarguments, ein Payload, der absichtlich über vier Dateien verteilt ist — sind entweder außerhalb des dokumentierten LLM/MCP/RAG-Scopes oder jenseits der Analyse von Konjunktionen innerhalb derselben Datei; siehe den 0.6.0-Changelog-Eintrag für die spezifische Begründung zu jedem.)
Historisches Vorher/Nachher aus dem Lauf, der die ursprünglichen Precision-Fixes antrieb (Funde auf Standard-Evidenzstufe, ohne --paranoid):
| Repo | Vorher | Nachher | Was falsch war |
|---|---|---|---|
| vercel/ai | 773 | 1 | examples/, Top-Level-tests/ und Verzeichnisse im Stil von ecosystem-tests/ mit Bindestrich wurden nicht als Pfade mit geringerem Vertrauen erkannt; chunks (eine gängige Streaming-Response-Variable) wurde als eindeutiger RAG-Beweis behandelt |
| openai/openai-node | 47 | 0 | Dieselbe Pfad-Erkennungslücke, angewendet auf die eigenen examples//ecosystem-tests/ des SDKs |
| anthropics/anthropic-sdk-typescript | 2 | 0 | Dieselbe Pfad-Erkennungslücke bei einem Top-Level-tests/-Verzeichnis |
| modelcontextprotocol/typescript-sdk | 3 | 0 | OAuth-Metadatenfelder im Stil von token_endpoint/tokenType wurden als geleakte Secrets markiert |
| run-llama/llama_index | 18 | 15 | Ein Python-Check markierte jedes description=-Feld, das "system prompt" enthält, als proven MCP-Tool-Poisoning, unabhängig vom Kontext. Die verbleibenden 15 sind VEC001-Treffer auf den eigenen generischen Retriever-Definitionen der Bibliothek — das Scannen des Quellcodes eines Vektor-DB-SDKs selbst, nicht von Anwendungscode, sodass kein Filter existieren kann, den es zu prüfen gäbe; eine ehrliche, inhärente Grenze, kein Bug |
Aktueller Lauf (2026-08-06) — versionierte Beweise sind in docs/benchmarks/v0.9.0.json festgehalten:
| Repo | Funde | Regeln | Status |
|---|---|---|---|
| openai-node, anthropic-sdk-typescript, anthropic-sdk-python, modelcontextprotocol/typescript-sdk, modelcontextprotocol/servers | 0 | — | sauber |
| anthropics/skills (18 echte Skill-Bundles) | 0 | — | sauber — reiner Precision-Check für SKL001–005 |
| vercel/ai (5.691 Dateien) | 0 | — | war 40 (AI001, AI003, AI005, AI010, MCP002) vor dem Triage — jeder einzelne manuell gegen die Quelle geprüft und als False Positive bestätigt, zurückgeführt auf 3 unabhängige Root-Cause-Bugs (siehe unten), behoben und bei einem vollständigen Re-Scan erneut als sauber bestätigt |
| run-llama/llama_index | 46 | VEC001 | inhärente Grenze, kein Bug — die eigenen generischen Retriever-Definitionen der Bibliothek, wo kein Tenant-Filter existieren kann, den es zu finden gäbe |
| cisco-ai-defense/skill-scanner | 7 | SKL001, SKL002, SKL005 | alle auf Fixtures, die als malicious/ beschriftet sind — 6/6 in-scope, 0 auf allem, was als safe/ beschriftet ist |
Das vercel/ai-Triage fand drei echte, ursachenbehaftete Bugs — keiner spezifisch für die Skill-Regeln von v0.6.0, alle in gemeinsamer Logik, die von vielen Regeln verwendet wird:
resolveLlmSinkbehandelte jeden Aufruf, der zu einem LLM-SDK-Modul aufgelöst wurde, als Modell-Invokation, unabhängig vom Methodennamen — und markierteisToolUIPart(einen Type Guard, den dasai-Paket direkt nebengenerateTextexportiert) als LLM-Aufruf. Allein das verursachte 3 der 5 Fundgruppen (AI001, AI003, AI010).DANGEROUS_CALLEESin AI005 enthält"query"für Sinks im Stil von SQL-Injection, aber"query"ist auch ein legitimes LLM/Agent-Invokationsverb —claudeSdk.query({ prompt, options }), der eigene Modellaufruf des Claude-Agent-SDKs, wurde rein wegen des gemeinsamen Methodennamens als "LLM-Ausgabe an einen gefährlichen Sink übergeben" markiert.REQUEST_SOURCES(identisch dupliziert über MCP002, MCP010, VEC003) matchte ein nacktes"params."— jeden Funktionsparameter, der konventionellparamsheißt, nicht unbedingt HTTP-Request-Daten. Ein URL-Schema-Validator (assertOpenLinkParams(params: unknown)) wurde als "MCP-Server-URL aus Benutzereingabe" markiert.
Alle drei wurden an der Ursache behoben (nicht an der spezifischen Aufrufstelle) und als permanente Fixtures unter test-fixtures/ festgeschrieben. Vollständige Details in CHANGELOG.md.
3. Verwundbar-vs-gepatcht-Validierung — beweist Recall, nicht nur Precision.
Die beiden obigen Ebenen prüfen nur, dass der Scanner bei sicherem Code ruhig bleibt. Die Advisory-Checks von DEP003 werden in die andere Richtung validiert: Ein Paket auf eine dokumentiert-verwundbare Version pinnen und bestätigen, dass es markiert wird, dann auf die gepatchte Version pinnen und bestätigen, dass es nicht markiert wird.```bash
node --test test/dependency-guard.test.js
covers: `[email protected]` (CVE-2025-6514, verwundbar) markiert / `[email protected]` (gepatcht) sauber; `[email protected]` (vor der Hintertür) sauber / `[email protected]` (danach — es existiert kein legitimer Patch für ein bösartiges Paket) weiterhin markiert; `llama-cpp-python==0.2.71` (CVE-2024-34359, aus dem OSV-generierten Satz) markiert / `==0.2.72` (gepatcht) sauber, auch unter PyPI-Namensnormalisierung (`llama_cpp_python`); und `langchain>=0.1.0`-artige nicht gepinnte Spezifikatoren, die **null** Standard-Befunde erzeugen. Der Aufbau dieses Tests deckte eine echte Lücke auf: `DEP003` matchte Advisories bisher nur nach Paketname, ohne die deklarierte Version jemals mit dem betroffenen Bereich des Advisories zu vergleichen — behoben in [`src/scanner/semver.ts`](https://github.com/akanthed/secureai-scan/blob/main/src/scanner/semver.ts).
Mehrdeutigkeit wird bewusst je nach Advisory-Art unterschiedlich aufgelöst. Ein **bösartiges** Paket feuert selbst dann, wenn die deklarierte Version nicht aufgelöst werden kann — die Installation einer Hintertür ist nicht rückgängig zu machen, daher schlägt es in Richtung Markierung fehl. Eine **CVE** feuert bei `proven` nur, wenn die deklarierte Version ein exakter Pin ist, der nachweislich innerhalb des betroffenen Bereichs liegt; nicht gepinnt-aber-möglicherweise-betroffen fällt auf `heuristic` zurück (nur mit `--paranoid`). Die Anwendung der Regel für bösartige Pakete auf einen CVE-Snapshot mit 162 Einträgen würde in jedem Repo, das `langchain>=0.1.0` deklariert, einen kritischen Befund erzeugen — in großem Maßstab nicht umsetzbares Rauschen.
## Roadmap
Siehe [`ROADMAP.md`](https://github.com/akanthed/secureai-scan/blob/main/ROADMAP.md) für das, was ausgeliefert wurde und was geplant ist. Beide Sprach-Engines sind AST-basiert: ts-morph für TypeScript/JavaScript und Tree-sitter für Python. Python-Imports, Aufrufe, Zuweisungen, Dekoratoren, Scopes, Keyword-Argumente, Dictionary-Felder und Strings sind Syntaxknoten; Zielcode wird nie importiert oder ausgeführt, und es ist kein Python-Interpreter erforderlich. Die verbleibende Python-Lücke ist die begrenzte funktions- und dateiübergreifende Taint-Tiefe, nicht das Parsing. Scan-Leistung und bekannte Grenzen sind in [`docs/Performance.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/Performance.md) dokumentiert.
## Mitwirken
Beiträge sind willkommen — siehe [`CONTRIBUTING.md`](https://github.com/akanthed/secureai-scan/blob/main/CONTRIBUTING.md) für den Workflow und [`docs/WritingRules.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/WritingRules.md) / [`docs/RuleDevelopment.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/RuleDevelopment.md) für die Erstellung einer Erkennungsregel, die die obige Präzisionslatte erfüllt. Jede neue Regel benötigt eine Fixture sowohl in [`test-fixtures/vulnerable/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/vulnerable) als auch in [`test-fixtures/safe/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/safe), einen Eintrag in `src/scanner/catalog.ts` und einen Fall in `test/corpus.test.js` — `npm test` erzwingt alle drei.
## Lizenz
MIT © Akshay Kanthed

