Retour aux mises à jour
New releaseJul 21, 2026

SecureAI-Scan v0.4.0

SecureAI-Scan est un outil CLI qui analyse les bases de code TypeScript et JavaScript pour les problèmes de sécurité spécifiques aux applications basées sur l'IA — prompt injection, MCP tool abuse, RAG data poisoning, agent trust violations, et plus encore.

Partager

SecureAI-Scan

npm version npm downloads CI CodeQL OpenSSF Scorecard license Node OWASP

CLI hors ligne qui analyse TypeScript, JavaScript et Python pour détecter les risques LLM, MCP, Agent Skill et RAG — preuves de flux de données résolues par import, zéro faux positif par défaut, mappées sur l'OWASP LLM/ASI/MCP Top 10.

La plupart des scanners dans ce domaine font correspondre un mot-clé et l'appellent une découverte. SecureAI-Scan trace le chemin réel source → flux → puits à travers un code réel, résolu par import — et une analyse par défaut ne vous montre que ce qu'il peut prouver. Pas de compte, pas d'upload cloud, rien ne quitte votre machine.

Couvre l'officiel OWASP Top 10 pour les applications LLM 2026, le Top 10 pour les applications agentiques (2026), et le MCP Top 10 dès la semaine de lancement.

Commencez en 30 secondes```bash

npx --yes [email protected] scan .

Aucun compte, téléversement cloud, interpréteur Python ou configuration requis. Les bundles TypeScript, JavaScript, Python, configurations MCP et compétences d'agent sont détectés automatiquement.

**Candidat de version `0.9.0` mesuré :** 136/136 tests · 88,08 % de couverture des instructions · 12 676 fichiers sur 9 dépôts publics · 0 nouvelle empreinte de niveau par défaut par rapport à la base de référence examinée. [Preuves](https://github.com/akanthed/secureai-scan/blob/main/docs/benchmarks/v0.9.0.json) · [méthodologie et limites](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.

Est-ce fait pour vous ? SecureAI-Scan est délibérément ciblé sur les risques LLM, MCP et RAG/agents — injection de prompts, empoisonnement d'outils, gestion non sécurisée des sorties, contrôle d'accès aux magasins de vecteurs, empoisonnement des compétences d'agents. Ce n'est pas un scanner SAST généraliste ni un scanner de secrets, et il ne cherche pas à l'être ; un paquet connu comme malveillant sans charge utile de type LLM (par exemple, une adresse d'exfiltration codée en dur dans un appel d'API e-mail) est détecté par la liste d'avis hors ligne (DEP003), et non par une règle de motif. Si votre codebase communique avec un LLM, un serveur MCP, un magasin de vecteurs, ou embarque des Agent Skills, cet outil est fait pour vous.

Nouveau : analyse statique de configuration pour LiteLLM Proxy (config.yaml) — secrets codés en dur, points de terminaison de fournisseurs en texte clair, garde-fous manquants. Voir Règles (LLC001–LLC003).

Sommaire

Pourquoi ce scanner est différent

  • Niveaux de preuve, pas de bruit. Chaque résultat est proven (flux de données tracé ou fait de configuration analysé), likely (cible résolue, un saut heuristique), ou heuristic. Une analyse par défaut n'affiche que proven + likely. Les heuristiques sont activables via --paranoid.
  • Détection résolue par import. Un appel n'est un « appel LLM » que s'il se résout vers un import SDK réel (openai, @anthropic-ai/sdk, ai, @google/genai, LangChain, Bedrock, …). Votre client Google Maps ne sera plus jamais signalé comme un LLM.
  • Contrôlé par la précision, et benchmarké sur de vrais dépôts. La suite de tests vérifie que chaque fixture vulnérable déclenche une alerte et que chaque fixture sûre reste propre — un faux positif sur le corpus sûr fait échouer la build. Au-delà, npm run regression analyse de vrais dépôts publics (OpenAI/Anthropic/Vercel AI SDKs, serveurs MCP officiels, LlamaIndex) par rapport à une base de référence validée manuellement et échoue sur tout nouveau résultat proven/likely. Voir Tests et benchmarks pour les chiffres avant/après réels, ou Ce que nous avons trouvé en analysant de vrais dépôts pour l'histoire qui les sous-tend — un taux de détection de 6/6 sur un corpus de compétences malveillantes étiquetées, et pourquoi nous ne qualifions pas llama_index de « vulnérable » pour un résultat honnête au niveau de la bibliothèque. Compte-rendu de discussion →
  • SARIF pour le code scanning GitHub. --output report.sarif place les résultats en ligne sur les pull requests et dans l'onglet Security.
  • AI-BOM. secureai-scan bom . construit un inventaire dérivé de la syntaxe des SDK, identifiants de modèles, magasins de vecteurs, frameworks d'agents et serveurs MCP, mappé aux besoins de documentation OWASP LLM Top 10 / EU AI Act.
  • Analyse de configuration MCP. Analyse .mcp.json, claude_desktop_config.json, .cursor/mcp.json : serveurs npx -y non épinglés, secrets en ligne, transports HTTP en texte clair.
  • Détection d'empoisonnement d'outils MCP. Détecte le motif derrière le rug-pull WhatsApp MCP et la backdoor postmark-mcp — Unicode invisible, phrases d'injection dirigées vers l'agent, et shadowing croisé d'outils dans les noms/descriptions d'outils, statiquement, avant même d'exécuter le serveur.
  • Détection d'injection de commandes MCP. Signale les command/args de transport stdio MCP construits à partir de données de requête — le motif derrière la divulgation RCE MCP STDIO 2026.
  • Détection d'empoisonnement d'Agent Skills. Les mêmes vérifications d'Unicode invisible, de phrases d'injection et de shadowing appliquées aux fichiers SKILL.md — les Agent Skills se chargent en bloc dans le contexte, donc une compétence empoisonnée est une description d'outil empoisonnée sous un autre nom.
  • Analyse de compétences résistante à l'évasion. Les bundles de compétences sont analysés comme des répertoires, pas seulement leur SKILL.md, et chaque vérification de contenu s'exécute sur des variantes désobfusquées du texte. Cela cible les techniques publiées — homoglyphes, découpage en largeur nulle, charges utiles placées dans .git/ ou build/, exfiltration cachée dans un fichier *.test.ts — qui ont contourné >90 % des neuf scanners étudiés dans Cloak and Detonate (arXiv:2607.02357). Voir Résistance à l'évasion.
  • Avis de paquets connus comme vulnérables et malveillants, sensibles à la version. Vérifie chaque dépendance et chaque paquet lancé par MCP par rapport à un instantané d'avis intégré — une liste triée à la main de backdoors documentées en circulation, plus les avis OSV HIGH/CRITICAL pour une liste de surveillance de paquets LLM/MCP/RAG, régénérée par scripts/sync-advisories.js. S'exécute hors ligne à chaque analyse, sans drapeau requis. Un CVE ne se déclenche que lorsque votre version épinglée est prouvablement dans la plage affectée ; un paquet documenté comme malveillant se déclenche même sur une plage ambiguë, car installer une backdoor est irrécupérable.
  • Local d'abord. Rien ne quitte votre machine.

Comment il se compare

SecureAI-Scan n'est pas un remplacement d'un outil SAST généraliste ni d'un scanner de conteneurs/IaC — exécutez-le en parallèle, pas à la place. Il est conçu sur mesure pour la surface d'attaque LLM/MCP/RAG et privilégie la preuve par flux de données plutôt que des résultats plats par mots-clés.

SecureAI-ScanSemgrep (règles OSS)TrivyGitHub Advanced Security
Injection de prompts (source→cible tracée)✅ flux de données résolu par import⚠️ règles de motifs uniquement, maintenues par la communauté⚠️ CodeQL le peut, mais aucun ensemble de règles spécifique à l'IA
Empoisonnement d'outils / risque de configuration MCP✅ MCP007–010, scanner de configuration
Empoisonnement d'Agent Skills (SKILL.md)✅ résistant à l'évasion, conscient des bundles
Mauvaise configuration RAG / magasin de vecteurs✅ VEC001–004
Avis de paquets IA connus comme malveillants✅ DEP003, hors ligne, sensible à la version⚠️ flux CVE général, non spécifique à l'IA⚠️ Dependabot, flux CVE général
SAST généraliste (SQLi, XSS, traversée de chemin)❌ hors périmètre par conception
Analyse de conteneurs / IaC⚠️ via CodeQL/Actions
Niveaux de preuve (proven/likely/heuristic)❌ résultats plats⚠️ CodeQL en a certains, non adaptés à l'IA
Sortie SARIF (code scanning GitHub)natif
Fonctionne hors ligne, sans compte✅ (règles OSS)❌ nécessite GitHub

Si vous exécutez déjà Semgrep ou GHAS, gardez-les — ajoutez SecureAI-Scan pour la surface de risque qu'ils ne modélisent pas du tout.

Vous préférez poser des questions d'abord ? Essayez le SecureAI-Scan AI Security Advisor gratuit sur ChatGPT.

Sur le point d'exécuter un serveur MCP trouvé sur GitHub ou Twitter ? Collez d'abord sa description d'outil dans MCP X-Ray — il vérifie l'Unicode caché, les instructions injectées et les paquets connus comme malveillants dans votre navigateur, sans installation.

Voyez-le en action

secureai-scan scan . de bout en bout, sortie réelle contre un vrai fichier (petit, délibérément vulnérable) — source :

Enregistrement de terminal de secureai-scan scan . trouvant une vulnérabilité d'injection de prompts tracée

Formes d'attaque que le scanner trace de bout en bout :

Flux de données d'empoisonnement d'outils MCPFlux de données d'injection de contexte RAG
Trace d'attaque MCPTrace d'empoisonnement RAG

Commandes

Celle dont vous avez besoin 95 % du temps :```bash secureai-scan scan .

Tout le reste est là quand vous en avez besoin. `secureai-scan scan . --help` affiche tout cela dans le terminal, regroupé de la même manière :

**Usage quotidien**

| Indicateur | Effet |
|------|---------------|
| *(aucun)* | résultats `proven` + `likely` — le comportement par défaut, aucun indicateur requis |
| `--paranoid` | inclut également les résultats de niveau `heuristic` |
| `-s, --severity <niveau>` | n'affiche que les résultats à/au-dessus de `low`\|`medium`\|`high`\|`critical` |
| `--output <fichier>` | écrit un rapport complet — `.sarif` (analyse de code GitHub), `.json`, `.md` ou `.html` |

**Portée des règles exécutées**

| Indicateur | Effet |
|------|---------------|
| `-r, --rules <liste>` | exécute uniquement ces identifiants de règles, p. ex. `AI001,MCP007` |
| `--only-ai` / `--only-mcp` / `--only-vec` / `--only-skl` | exécute uniquement une catégorie de règles |
| `--check-dependencies` | vérifie également `package.json`/`requirements.txt` par rapport au registre npm/PyPI pour détecter les fautes de frappe et les paquets hallucinés (`DEP001`/`DEP002`). Activé automatiquement si vous sélectionnez ces règles directement via `-r` — vous n'avez jamais besoin de penser à passer les deux. Non requis pour `DEP003` (paquets connus comme malveillants), qui s'exécute toujours hors ligne |

**CI / flux de travail**

| Indicateur | Effet |
|------|---------------|
| `--fail-on <sévérité>` | sort avec le code `1` si des résultats à/au-dessus de cette sévérité existent |
| `--baseline <fichier>` | ne suit que les problèmes nouveaux/modifiés par rapport à une référence enregistrée |
| `--policy <fichier>` | charge les seuils, les chemins ignorés et les règles bloquées depuis un `.secureai-policy.json` (détecté automatiquement s'il est présent — `secureai-scan init` en crée un) |

**Avancé**

| Indicateur | Effet |
|------|---------------|
| `--min-confidence <0-1>` | plus précis que `--paranoid` : masque les résultats en dessous d'un score de confiance exact (`0.9` proven / `0.65` likely / `0.35` heuristic) |
| `--limit <n>` | nombre maximal de groupes de règles affichés dans le terminal (par défaut `10`) — le détail complet va toujours dans `--output` |
| `--debug` | affiche chaque fichier analysé et les règles exécutées |

**Analysez avant d'installer — sans clone, sans configuration :**```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 et mcp récupèrent la cible et l'analysent, puis suppriment la copie téléchargée (--keep pour l'inspecter à la place). Rien de ce qui est récupéré n'est jamais exécuté : une cible npm est téléchargée avec npm pack — uniquement l'archive, pas d'install, pas de scripts de cycle de vie — et une cible git est un simple git clone --depth 1. C'est le moment le plus important : avant qu'une compétence n'atterrisse dans ~/.claude/skills/ ou qu'un serveur n'atterrisse dans .mcp.json, pas après.

Autres commandes :```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

Supprimer un résultat examiné dans le 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

Les résultats apparaissent sous forme d'annotations en ligne sur la PR et dans l'onglet Sécurité du dépôt. (`secureai-scan init` génère un workflow équivalent utilisant directement la CLI.)

Analyse propre ? Ajoutez le badge à votre propre README :```md
[![secureai-scan](https://img.shields.io/badge/secureai--scan-passing-brightgreen)](https://github.com/akanthed/SecureAI-Scan)

Pre-commit hook

Vous préférez détecter les résultats avant qu'ils ne soient poussés ? Ajoutez ce dépôt comme source de hook pre-commit au lieu de, ou en plus de, l'action GitHub :```yaml repos:

Le hook analyse l’ensemble du projet à chaque commit (pas seulement les fichiers modifiés — une trace de flux de données dans le fichier A peut dépendre du fichier B, qu’une analyse partielle manquerait) et bloque le commit sur les résultats de sévérité `high`+ par défaut. Remplacez le seuil dans votre propre configuration :```yaml
      - id: secureai-scan
        args: ["--fail-on", "critical"]

Règles

42 règles, mappées sur l'OWASP Top 10 officiel pour les applications LLM (2026) — plus, le cas échéant, l'OWASP Top 10 pour les applications agentiques (2026, ASI), l'OWASP MCP Top 10 (2025) et un article de l'AI Act européen. Voir la couverture et les limites de la version 2026 ; threat-model génère la matrice pour chaque projet analysé.

RègleCe qu'elle prouveOWASP
AI001Les entrées utilisateur circulent dans un prompt système/développeur (source tracée → puits, y compris à travers les frontières de fonctions/fichiers)LLM01
AI002Le contenu du prompt ou des secrets sont écrits dans les journaux (dans les fichiers qui utilisent un SDK LLM)LLM02
AI003Appel LLM dans un gestionnaire de requêtes sans vérification d'authentification avantLLM06
AI004Objet utilisateur/session entier sérialisé dans un prompt (la sélection de champs n'est pas signalée)LLM02
AI005La sortie LLM atteint des puits eval/exec/SQL/HTMLLLM10
AI006Outils à fort impact (suppression, paiement, déploiement, …) exposés sans passerelle d'approbationLLM03
AI007Contenu RAG récupéré interpolé dans des prompts privilégiésLLM01
AI008Secrets intégrés dans le texte du prompt systèmeLLM08
AI009Entrée utilisateur illimitée / limites de jetons manquantesLLM06
AI010Contenu externe récupéré circulant dans les promptsLLM01
AI011Sortie d'agent élevée au rôle système dans les appels en avalLLM03
AI012Sortie LLM analysée sans validation de schémaLLM10
MCP001Les métadonnées d'outil MCP atteignent le prompt système sans validationLLM01
MCP002URL de serveur MCP construite à partir d'entrées utilisateurLLM04
MCP003Résultats d'outil MCP élevés au rôle systèmeLLM10
MCP004Serveur MCP lancé comme package npx -y non épingléLLM04
MCP005Secret intégré dans une configuration MCP validéeLLM02
MCP006Serveur MCP sur HTTP en clairLLM04
MCP007Unicode invisible/bidi caché dans les noms ou descriptions d'outils MCPLLM01 · MCP03
MCP008Phrases d'injection dirigées par l'agent dans les descriptions d'outils MCPLLM01 · MCP03
MCP009Description d'outil qui oriente les appels vers un autre outil (shadowing)LLM01 · MCP03
MCP010Commande/arguments de serveur MCP stdio construits à partir d'entrées utilisateur (RCE)LLM04 · MCP05
SKL001Unicode invisible/bidi n'importe où dans un bundle de compétences d'agentLLM01
SKL002Formulation d'injection dirigée par l'agent dans la description ou le corps d'une compétence (correspondance via obfuscation)LLM01
SKL003Le contenu d'une compétence oriente quand/comment une autre compétence est utilisée (shadowing)LLM01
SKL004Payload échelonné/auto-extractible : blob opaque + instructions pour le décoder et l'exécuterLLM04 · MCP04
SKL005Lecture d'identifiants + sortie externe codée en dur dans un fichier compagnon du bundleLLM02 · MCP04
SKL006Exécution de commande au moment du chargement via la syntaxe d'injection de contexte dynamique de Claude Code (!`cmd`/```!), avant toute passerelle de permission d'outilLLM04 · MCP05
SKL007Octroi Bash sans portée dans le frontmatter allowed-tools d'une compétenceLLM03
SKL008La compétence récupère des instructions depuis une URL externe et dirige l'agent pour les suivre (« Circus of Skills »)LLM04
SKL009La compétence persiste une porte dérobée en écrivant dans un autre fichier de contexte (MEMORY.md/SOUL.md/AGENTS.md/CLAUDE.md)LLM05
SKL010Balise de désérialisation YAML/JSON non sécurisée dans le frontmatter d'une compétence ou un fichier de configuration groupéLLM04
VEC001Recherche vectorielle sans filtre de locataire/utilisateurLLM09
VEC002Limite de recherche illimitée ou contrôlée par l'utilisateurLLM06
VEC003Contenu utilisateur ingéré dans un magasin vectoriel partagéLLM05
VEC004Ingestion sans étiquetage de locataire/espace de nomsLLM09
DEP001Nom de dépendance introuvable dans le registre (opt-in --check-dependencies)LLM04
DEP002Nom de dépendance à une modification près d'un package populaire (opt-in)LLM04
DEP003Dépendance avec une version malveillante documentée ou une CVE critique — vérifiée hors ligne à chaque analyse, sensible à la plage de versions (postmark-mcp, mcp-remote CVE-2025-6514, …)LLM04 · MCP04
LLC001Secret codé en dur dans un config.yaml de proxy LiteLLMLLM02
LLC002api_base de proxy LiteLLM accessible sur HTTP en clairLLM04
LLC003La configuration du proxy LiteLLM n'a pas de section guardrails: (heuristique, --paranoid uniquement)LLM03

secureai-scan explain <RULE_ID> fournit la procédure pas à pas d'exploitation et un exemple de code avant/après pour chaque règle.

Architecture

Trois surfaces d'analyse indépendantes alimentent une liste de résultats fusionnée et dédupliquée :``` ┌─────────────────────┐ *.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)

Chaque règle AST ne qualifie une fonction d'« appel LLM » que si elle résout, via de véritables imports, vers un SDK connu — jamais par simple correspondance de nom. Voir [`docs/Architecture.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/Architecture.md) pour la répartition complète de chaque surface, et [`docs/DetectionEngine.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/DetectionEngine.md) pour le fonctionnement du contrat de niveau de preuve.

## Serveur MCP (utilisez-le depuis Claude)

Le paquet fournit un serveur MCP exposant `scan_repository`, `explain_rule`, `generate_bom` et `scan_untrusted_target` (récupère et analyse une compétence ou un serveur MCP avant que Claude ne recommande son installation — même comportement de récupération sans exécution que les commandes CLI `skill`/`mcp`) :```json
{
  "mcpServers": {
    "secureai-scan": {
      "command": "node",
      "args": ["/path/to/secureai-scan/mcp-server/index.js"]
    }
  }
}

Compétence Claude

Pour les utilisateurs de Claude Code / Claude.ai, skills/secureai-scan/SKILL.md apprend à Claude quand lancer une analyse (lors de la revue de code IA/LLM, ou avant d'installer un serveur MCP / une compétence d'agent) et comment interpréter les résultats — aucun processus séparé à exécuter, contrairement au serveur MCP ci-dessus. Copiez le répertoire skills/secureai-scan/ dans votre .claude/skills/ pour l'utiliser.

Résistance à l'évasion

En juillet 2026, deux travaux de recherche publics ont montré que les analyseurs de compétences d'agent sont trivialement contournables.

Cloak and Detonate (Ji, Xu, Li, Gao, Wei, Wang & Cheung — HKUST, arXiv:2607.02357) a mené une étude contradictoire contre neuf analyseurs de compétences publiés, en utilisant 1 613 compétences malveillantes réelles. Deux familles d'attaques, qui préservent toutes deux la charge utile à l'identique :

  • Reify — réécrit les octets qu'une règle détecte, tandis que l'agent réassemble toujours l'original à l'exécution. >80 % de contournement sur la plupart des analyseurs statiques.
  • Disperse / empaquetage SFS — déplace la charge utile hors de portée de la règle : dans un répertoire ignoré par l'analyseur, derrière une extension renommée, ou archivée et encodée dans .git/, avec un SKILL.md de couverture bénin qui la reconstruit au premier lancement. ≥90 % de contournement sur chaque analyseur testé, ≥96 % sur la plupart. Le taux de détection d'un analyseur hybride est passé d'environ 99 % à environ 10 %.

Par ailleurs, Gecko Security (VentureBeat) a livré une charge utile dans un fichier *.test.ts. Chaque analyseur public l'a ignorée — mais Jest et Vitest découvrent et exécutent automatiquement ces fichiers lors du prochain npm test.

SecureAI-Scan traite chaque technique publiée. Chaque ligne ci-dessous possède un test correspondant dans test/deobfuscate.test.js ou un fixture dans test-fixtures/vulnerable/skills/ :

Technique (nom de l'article)ExempleComment elle est traitée
Insertion de caractères de largeur nulleIgn‍ore all pre‍vious…Le texte est comparé aux variantes sans caractères invisibles
Glyphes confondablesсurl (с cyrillique)Réduction des homoglyphes en ASCII avant la comparaison
Inversion de casseEvAlTous les motifs de contenu sont insensibles à la casse
Éclatement de variables'cu' + 'rl'Les littéraux de chaîne adjacents sont concaténés avant la comparaison
Découpage par ligneeval(x)Les sauts de ligne intra-mot sont joints comme variante supplémentaire
Extraction par fichiercharge utile dans un helper importéL'ensemble du bundle est analysé, pas seulement SKILL.md
Relocalisation de répertoirescripts/x.shbuild/x.shAucune liste d'exclusion de répertoire dans un bundle
Renommage d'extensionx.shx.txtLes fichiers sont analysés par contenu, pas par extension
Remplissage de taillerembourrage au-delà de la limite de l'analyseurLes fichiers surdimensionnés sont lus en tête, jamais ignorés
Empaquetage SFScharge utile dans .git/skillpack.datTout fichier non-git sous le .git/ d'un bundle est proven (SKL004)
Mise en scène de fichier de testcharge utile dans *.test.tsL'analyse de bundle ne déclasse délibérément pas les chemins de test (SKL005)

Cela n'affaiblit pas le contrat de précision

La désobfuscation est généralement un risque de précision — plus de correspondances, plus de bruit. Ici, la logique est inversée : une correspondance qui n'apparaît qu'après désobfuscation est promue en proven, pas déclassée. Une documentation ordinaire ne contient pas de joncteur de largeur nulle dans « ignore previous instructions », ni un с cyrillique dans curl. La dissimulation est en soi une preuve affirmative d'intention.

La comparaison se fait sur l'ensemble des correspondances brutes, pas seulement sur « le texte brut correspondait-il ou non » — sinon un attaquant pourrait masquer le signal en laissant une phrase anodine en clair.

Les deux nouvelles règles de bundle ne se déclenchent que sur des conjonctions, jamais sur un mot-clé seul :

  • SKL004 nécessite un blob opaque et une directive de dépaquetage qui référence ce blob par son nom — un README mentionnant tar -x à côté d'un actif binaire sans rapport ne suffit pas. Les archives réelles (gzip/zip/png/pdf/wasm — vérifiées par octets magiques, pas par extension) ne sont jamais « opaques » en premier lieu, quelle que soit leur compression.
  • SKL005 nécessite un signal d'identifiants concret — un chemin (~/.aws/credentials, pas le mot « token ») ou une énumération massive de variables d'environnement (os.environ.items(), pas os.environ["API_KEY"]) — et une exfiltration vers un hôte non local codé en dur, à moins de 25 lignes l'un de l'autre dans le même fichier, ou une récupération distante exécutée après avoir été réassignée via un ou plusieurs renommages. Un helper de publication qui lit ~/.npmrc dans une fonction et appelle un registre quarante lignes plus loin reste propre, et la lecture d'une variable d'environnement nommée pour un appel API n'est jamais signalée — les deux formes sont figées comme fixtures sûres.

Validé sur deux corpus du monde réel, pas seulement sur des fixtures que nous avons écrites nous-mêmes : 0 résultat sur les 18 bundles de compétences réels dans anthropics/skills et les 14 dans vercel/ai, et 6/6 corrects sur le corpus d'évaluation étiqueté de cisco-ai-defense/skill-scanner (20 compétences, chacune avec un verdict _expected.json) avec zéro faux positif sur tout ce qui est étiqueté sûr. Voir Tests et benchmarks.

Ce que cela n'est pas

Limite honnête : la conclusion de l'article est que la détonation à l'exécution bat l'analyse statique, et c'est exact. Un adversaire adaptatif qui connaît ces règles peut écrire une transformation qu'elles ne couvrent pas. Ce qui change ici, c'est le coût de l'évasion — les techniques publiées et actuellement en circulation ne fonctionnent plus, et l'obfuscation nécessaire pour les vaincre élève désormais elle-même la gravité du résultat. L'analyse statique est un filtre, pas une frontière de sécurité. Traitez une compétence non fiable comme un code non fiable, quel que soit ce que dit un analyseur.

Garantie de confiance et de publication

  • La CI s'exécute sur Linux, Windows et macOS sur les versions de Node prises en charge.
  • CodeQL, l'audit des dépendances de production, OpenSSF Scorecard, Dependabot et l'auto-analyse bloquante de cet analyseur fournissent des contrôles indépendants.
  • Chaque publication npm manuelle invoque les tests, les seuils de couverture, la porte de régression sur le référentiel réel examiné et l'inspection du tarball via prepublishOnly.
  • GitHub Actions ne reçoit aucun mot de passe ni jeton npm et ne peut pas publier le paquet.
  • Garantie de publication, gouvernance à mainteneur unique, signalement de sécurité et preuves de benchmark versionnées sont publics.

C'est un projet à mainteneur unique, sans SLA contractuel ni certification indépendante. Les contrôles ci-dessus réduisent le risque ; ils ne transforment pas une analyse statique en preuve de sécurité.

Le contrat de précision

Les faux positifs tuent les analyseurs. Le moteur de règles de SecureAI-Scan suit trois règles strictes :

  1. Les sinks sont résolus via les imports. Si un identifiant se résout vers un module qui n'est pas un SDK LLM, ce n'est définitivement pas un appel LLM — quel que soit son nom.
  2. Les preuves sont étiquetées, jamais mélangées. Un flux de données tracé et une correspondance par proximité de mots ne sont pas la même chose, ils ne partagent donc jamais un niveau.
  3. Le corpus sûr verrouille chaque publication. test-fixtures/safe/ contient les motifs qui causaient auparavant des faux positifs (charges utiles PII expurgées, clients Google Maps, clés API par variables d'environnement à côté de clients LLM, journalisation ordinaire des réponses, champs de métadonnées OAuth, chunks de réponses en streaming, texte de prompt de fiction/narration). Tout résultat là-bas fait échouer la suite.

Tests et benchmarks

Trois couches, car une seule ne suffit pas pour faire confiance aux affirmations d'un analyseur — la précision et le rappel sont des modes de défaillance différents, et les deux sont vérifiés.

1. Corpus de fixtures — précision + rappel, exécuté à chaque build.```bash npm test

[`test-fixtures/vulnerable/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/vulnerable) et [`test-fixtures/safe/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/safe) sont analysés ensemble : chaque fixture vulnérable doit déclencher sa règle attendue avec une preuve `proven`/`likely` (rappel), chaque fixture sûre doit produire **zéro** résultat `proven`/`likely` (précision). Rapide et déterministe — mais cela prouve uniquement que le scanner se comporte correctement sur du code écrit spécifiquement pour le tester.

**2. Benchmark de régression sur le monde réel — contre des dépôts publics que nous n'avons pas écrits.**```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 clone un ensemble organisé et diversifié de dépôts publics réels (OpenAI/Anthropic/Vercel AI SDKs, les serveurs MCP officiels et le SDK TypeScript, LlamaIndex, plus anthropics/skills et cisco-ai-defense/skill-scanner pour la couverture des bundles de compétences — couvrant TS et Python, du code d'exemple consommateur de SDK et du code source d'auteur de SDK) et analyse chacun avec le CLI compilé.

Il se termine avec un code non nul sur toute constatation proven/likely déjà présente dans test/regression-baseline.json — un enregistrement relu manuellement des constatations déjà vérifiées par rapport à leur ligne source. Les empreintes sont repo|rule|file, pas des numéros de ligne, donc le remaniement ordinaire en amont ne produit pas de bruit. Une nouvelle empreinte est une affirmation que le scanner doit justifier : si ce n'est pas un problème réel, c'est un bug de règle, corrigé à la cause racine et verrouillé comme nouveau fixture test-fixtures/safe/. Établir une référence de base sur une constatation que vous n'avez pas lue anéantit tout le mécanisme.

La couverture des bundles de compétences a sa propre ligne car le corpus evals/ de cisco-ai-defense/skill-scanner est étiqueté — chacun de ses 20 fixtures est livré avec un verdict _expected.json et se trouve sous un répertoire littéralement nommé malicious/ ou safe/, donc il sert aussi de contrôle de rappel, pas seulement de précision : 6/6 fixtures malveillants dans le périmètre déclenchent, 0 constatation sur tout ce qui est étiqueté safe, et 0 constatation sur les 18 bundles réels de anthropics/skills et les 14 de vercel/ai. (Les catégories Cisco restantes — injection SQL, traversée de chemin, épuisement des ressources, eval() générique d'un argument de fonction, une charge utile délibérément répartie sur quatre fichiers — sont soit hors du périmètre documenté LLM/MCP/RAG, soit au-delà de l'analyse de conjonction dans un même fichier ; voir l'entrée du changelog 0.6.0 pour le raisonnement spécifique sur chacune.)

Avant/après historique de l'exécution qui a conduit aux corrections de précision d'origine (constatations au niveau de preuve par défaut, sans --paranoid) :

DépôtAvantAprèsCe qui n'allait pas
vercel/ai7731examples/, tests/ de niveau supérieur, et les répertoires de style ecosystem-tests/ avec trait d'union n'étaient pas reconnus comme chemins de confiance inférieure ; chunks (une variable courante de réponse en streaming) était traité comme preuve RAG sans ambiguïté
openai/openai-node470Même lacune de détection de chemin, appliquée aux propres examples//ecosystem-tests/ du SDK
anthropics/anthropic-sdk-typescript20Même lacune de détection de chemin sur un répertoire tests/ de niveau supérieur
modelcontextprotocol/typescript-sdk30Champs de métadonnées OAuth de style token_endpoint/tokenType signalés comme secrets divulgués
run-llama/llama_index1815Un contrôle Python signalait tout champ description= contenant « system prompt » comme empoisonnement d'outil MCP proven, quel que soit le contexte. Les 15 restants sont des hits VEC001 sur les définitions génériques de récupérateur de la bibliothèque elle-même — analyser le code source d'un SDK de base de données vectorielle, pas du code applicatif, donc un filtre ne peut pas exister à vérifier ; une limite honnête et inhérente, pas un bug

Exécution actuelle (2026-08-06) — les preuves versionnées sont enregistrées dans docs/benchmarks/v0.9.0.json :

DépôtConstatationsRèglesStatut
openai-node, anthropic-sdk-typescript, anthropic-sdk-python, modelcontextprotocol/typescript-sdk, modelcontextprotocol/servers0propre
anthropics/skills (18 bundles de compétences réels)0propre — contrôle de précision pur pour SKL001–005
vercel/ai (5 691 fichiers)0était de 40 (AI001, AI003, AI005, AI010, MCP002) avant le triage — chacune relue manuellement par rapport à la source et confirmée comme faux positif, tracée à 3 bugs indépendants de cause racine (voir ci-dessous), corrigée, et re-confirmée propre sur une re-analyse complète
run-llama/llama_index46VEC001limite inhérente, pas un bug — les définitions génériques de récupérateur de la bibliothèque elle-même, où aucun filtre de locataire ne peut exister à trouver
cisco-ai-defense/skill-scanner7SKL001, SKL002, SKL005toutes sur des fixtures étiquetés malicious/ — 6/6 dans le périmètre, 0 sur tout ce qui est étiqueté safe/

Le triage vercel/ai a trouvé trois bugs réels, corrigés à la cause racine — aucun spécifique aux règles de compétences v0.6.0, tous dans la logique partagée utilisée par de nombreuses règles :

  1. resolveLlmSink traitait tout appel résolu vers un module SDK LLM comme une invocation de modèle, quel que soit le nom de méthode — signalant isToolUIPart (un garde de type que le package ai exporte juste à côté de generateText) comme un appel LLM. Cela seul a causé 3 des 5 groupes de constatations (AI001, AI003, AI010).
  2. DANGEROUS_CALLEES dans AI005 inclut "query" pour les sinks de style injection SQL, mais "query" est aussi un verbe d'invocation légitime LLM/agentclaudeSdk.query({ prompt, options }), l'appel de modèle propre du SDK Claude Agent, était signalé comme « sortie LLM passée à un sink dangereux » uniquement à cause du nom de méthode partagé.
  3. REQUEST_SOURCES (dupliqué à l'identique dans MCP002, MCP010, VEC003) correspondait à un "params." nu — tout paramètre de fonction conventionnellement nommé params, pas nécessairement des données de requête HTTP. Un validateur de schéma d'URL (assertOpenLinkParams(params: unknown)) était signalé comme « URL de serveur MCP provenant d'une entrée utilisateur. »

Tous les trois corrigés à la cause racine (pas au site d'appel spécifique) et épinglés comme fixtures permanents sous test-fixtures/. Détails complets dans CHANGELOG.md.

3. Validation vulnérable-vs-corrigé — prouve le rappel, pas seulement la précision.

Les deux couches ci-dessus ne vérifient que le scanner reste silencieux sur du code sûr. Les contrôles d'avis de DEP003 sont validés dans l'autre sens : épingler un package à une version documentée comme vulnérable et confirmer qu'il est signalé, puis l'épingler à la version corrigée et confirmer qu'il ne l'est pas.```bash node --test test/dependency-guard.test.js

covers : `[email protected]` (CVE-2025-6514, vulnérable) signalé / `[email protected]` (corrigé) clair ; `[email protected]` (avant la porte dérobée) clair / `[email protected]` (après — aucun correctif légitime n'existe pour un paquet malveillant) toujours signalé ; `llama-cpp-python==0.2.71` (CVE-2024-34359, issu de l'ensemble généré par OSV) signalé / `==0.2.72` (corrigé) clair, y compris sous la normalisation du nom PyPI (`llama_cpp_python`) ; et les spécificateurs non épinglés de type `langchain>=0.1.0` produisant **zéro** résultat dans le rapport par défaut. La construction de ce test a révélé une lacune réelle : `DEP003` faisait correspondre les avis uniquement par nom de paquet, sans jamais comparer réellement la version déclarée à la plage affectée de l'avis — corrigé dans [`src/scanner/semver.ts`](https://github.com/akanthed/secureai-scan/blob/main/src/scanner/semver.ts).

L'ambiguïté est résolue différemment selon le type d'avis, délibérément. Un paquet **malveillant** se déclenche même lorsque la version déclarée ne peut pas être résolue — l'installation d'une porte dérobée est irrécupérable, donc il échoue vers le signalement. Une **CVE** se déclenche à `proven` uniquement lorsque la version déclarée est un épinglage exact prouvablement dans la plage affectée ; les versions non épinglées mais potentiellement affectées passent à `heuristic` (`--paranoid` uniquement). Appliquer la règle du type malveillant à un instantané CVE de 162 entrées placerait un résultat critique sur chaque dépôt déclarant `langchain>=0.1.0` — un bruit inexploitable à grande échelle.

## Feuille de route

Voir [`ROADMAP.md`](https://github.com/akanthed/secureai-scan/blob/main/ROADMAP.md) pour ce qui est livré et ce qui est prévu. Les deux moteurs linguistiques sont basés sur l'AST : ts-morph pour TypeScript/JavaScript et Tree-sitter pour Python. Les imports, appels, affectations, décorateurs, portées, arguments de mots-clés, champs de dictionnaires et chaînes Python sont des nœuds de syntaxe ; le code cible n'est jamais importé ni exécuté, et aucun interpréteur Python n'est requis. La lacune Python restante est une profondeur de taint inter-fonctions/inter-fichiers bornée, et non l'analyse syntaxique. Les performances d'analyse et les limites connues sont documentées dans [`docs/Performance.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/Performance.md).

## Contribution

Les contributions sont les bienvenues — voir [`CONTRIBUTING.md`](https://github.com/akanthed/secureai-scan/blob/main/CONTRIBUTING.md) pour le flux de travail, et [`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) pour savoir comment ajouter une règle de détection qui respecte le niveau de précision ci-dessus. Chaque nouvelle règle nécessite un fixture dans [`test-fixtures/vulnerable/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/vulnerable) et [`test-fixtures/safe/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/safe), une entrée dans `src/scanner/catalog.ts`, et un cas dans `test/corpus.test.js` — `npm test` impose ces trois éléments.

## Licence

MIT © Akshay Kanthed

Catégories