
SecureAI-Scan è uno strumento CLI che analizza le codebase TypeScript e JavaScript alla ricerca di problemi di sicurezza specifici delle app basate sull'IA — iniezione di prompt, abuso degli strumenti MCP, avvelenamento dei dati RAG, violazioni della fiducia degli agenti e altro ancora.
La maggior parte degli scanner in questo ambito confronta una parola chiave e la definisce un riscontro. SecureAI-Scan traccia il percorso effettivo sorgente → flusso → sink attraverso codice reale, risolto tramite import — e una scansione di default mostra solo ciò che può dimostrare. Nessun account, nessun caricamento sul cloud, nulla lascia la tua macchina.
Copre l'OWASP Top 10 ufficiale per applicazioni LLM 2026, il Top 10 per applicazioni agentiche (2026) e il MCP Top 10 dalla settimana di lancio.
npx --yes [email protected] scan .
Nessun account, caricamento su cloud, interprete Python o configurazione richiesti. TypeScript, JavaScript, Python, configurazioni MCP e bundle di Agent Skill vengono rilevati automaticamente.
**Release candidate `0.9.0` misurata:** 136/136 test · 88,08% di copertura delle istruzioni · 12.676 file in 9 repository pubblici · 0 nuove impronte digitali di livello predefinito rispetto alla baseline revisionata. [Evidenza](https://github.com/akanthed/secureai-scan/blob/main/docs/benchmarks/v0.9.0.json) · [metodologia e limiti](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.
È per te? SecureAI-Scan è deliberatamente limitato ai rischi LLM, MCP e RAG/agent — injection di prompt, avvelenamento degli strumenti, gestione non sicura dell'output, controllo degli accessi al vector store, avvelenamento delle skill degli agenti. Non è uno scanner SAST generico né un cercatore di segreti, e non cerca di esserlo; un pacchetto notoriamente dannoso senza payload in stile LLM (ad es. un indirizzo di esfiltrazione hardcoded in una chiamata API email) viene rilevato dall'elenco advisory offline (DEP003), non da una regola pattern. Se il tuo codebase parla con un LLM, un server MCP, un vector store o distribuisce Agent Skills, questo strumento è fatto per te.
Novità: scansione statica della configurazione per LiteLLM Proxy (config.yaml) — segreti hardcoded, endpoint provider in chiaro, guardrail mancanti. Vedi Regole (LLC001–LLC003).
proven (dataflow tracciato o fatto di configurazione parsato), likely (sink risolto, un salto euristico) o heuristic. Una scansione predefinita mostra solo proven + likely. Le euristiche sono opzionali tramite --paranoid.openai, @anthropic-ai/sdk, ai, @google/genai, LangChain, Bedrock, …). Il tuo client di Google Maps non verrà mai più segnalato come LLM.npm run regression scansiona repository pubblici reali (OpenAI/Anthropic/Vercel AI SDKs, server MCP ufficiali, LlamaIndex) contro una baseline committata e revisionata manualmente e fallisce su qualsiasi nuovo riscontro proven/likely. Vedi Test e benchmark per i numeri reali prima/dopo, oppure Cosa abbiamo trovato scansionando repository reali per la storia dietro quei numeri — un tasso di rilevamento 6/6 su un corpus di skill dannose etichettate, e perché non stiamo definendo llama_index "vulnerabile" per un riscontro onesto a livello di libreria. Write-up della discussione →--output report.sarif mette i riscontri inline sulle pull request e nella scheda Security.secureai-scan bom . costruisce un inventario derivato dalla sintassi di SDK, ID modello, vector store, framework agent e server MCP, mappato alle esigenze documentali di OWASP LLM Top 10 / EU AI Act..mcp.json, claude_desktop_config.json, .cursor/mcp.json: server npx -y non pinnati, segreti inline, trasporti HTTP in chiaro.command/args del trasporto stdio MCP costruiti da dati di richiesta — il pattern dietro la divulgazione RCE MCP STDIO del 2026.SKILL.md — le Agent Skills vengono caricate nel contesto per intero, quindi una skill avvelenata è una descrizione di strumento avvelenata con un altro nome.SKILL.md, e ogni controllo di contenuto viene eseguito su varianti deoffuscate del testo. Questo mira alle tecniche pubblicate — omoglifi, suddivisione a larghezza zero, payload collocati in .git/ o build/, esfiltrazione nascosta in un file *.test.ts — che hanno bypassato >90% dei nove scanner esaminati in Cloak and Detonate (arXiv:2607.02357). Vedi Resistenza all'evasione.scripts/sync-advisories.js. Funziona offline su ogni scansione, senza bisogno di flag. Una CVE scatta solo quando la tua versione pinnata è provabilmente all'interno dell'intervallo interessato; un pacchetto documentato come dannoso scatta anche su un intervallo ambiguo, perché installare un backdoor è irreversibile.SecureAI-Scan non sostituisce uno strumento SAST generico o uno scanner container/IaC — eseguilo insieme a uno di questi, non al posto di uno. È costruito appositamente per la superficie d'attacco LLM/MCP/RAG e enfatizza l'evidenza del dataflow rispetto a riscontri piatti basati su parole chiave.
| SecureAI-Scan | Semgrep (regole OSS) | Trivy | GitHub Advanced Security | |
|---|---|---|---|---|
| Injection di prompt (source→sink tracciato) | ✅ dataflow risolto tramite import | ⚠️ solo regole pattern, mantenute dalla community | ❌ | ⚠️ CodeQL può, ma nessun ruleset specifico per AI |
| Avvelenamento strumenti MCP / rischio configurazione | ✅ MCP007–010, scanner configurazione | ❌ | ❌ | ❌ |
Avvelenamento Agent Skill (SKILL.md) | ✅ resistente all'evasione, consapevole dei bundle | ❌ | ❌ | ❌ |
| Misconfigurazione RAG / vector-store | ✅ VEC001–004 | ❌ | ❌ | ❌ |
| Advisory pacchetti AI notoriamente dannosi | ✅ DEP003, offline, sensibile alla versione | ❌ | ⚠️ feed CVE generico, non specifico per AI | ⚠️ Dependabot, feed CVE generico |
| SAST generico (SQLi, XSS, path traversal) | ❌ fuori scope per design | ✅ | ❌ | ✅ |
| Scansione container / IaC | ❌ | ❌ | ✅ | ⚠️ tramite CodeQL/Actions |
| Livelli di evidenza (proven/likely/heuristic) | ✅ | ❌ i riscontri sono piatti | ❌ | ⚠️ CodeQL ne ha alcuni, non ottimizzati per AI |
| Output SARIF (GitHub code scanning) | ✅ | ✅ | ✅ | nativo |
| Funziona offline, senza account | ✅ | ✅ (regole OSS) | ✅ | ❌ richiede GitHub |
Se usi già Semgrep o GHAS, tienili — aggiungi SecureAI-Scan per la superficie di rischio che non modellano affatto.
Preferisci fare domande prima? Prova il SecureAI-Scan AI Security Advisor su ChatGPT gratuito.
Stai per eseguire un server MCP trovato su GitHub o Twitter? Incolla prima la sua descrizione dello strumento in MCP X-Ray — lo controlla per Unicode nascosto, istruzioni iniettate e pacchetti notoriamente dannosi nel tuo browser, senza installazione.
secureai-scan scan . end to end, output reale su un file reale (piccolo, deliberatamente vulnerabile) — sorgente:

Forme di attacco che lo scanner traccia end to end:
| Dataflow avvelenamento strumenti MCP | Dataflow injection contesto RAG |
|---|---|
![]() | ![]() |
Quello che ti serve il 95% delle volte:```bash secureai-scan scan .
Tutto il resto è disponibile quando ti serve. `secureai-scan scan . --help` mostra tutto questo nel terminale, raggruppato allo stesso modo:
**Uso quotidiano**
| Flag | Cosa fa |
|------|---------------|
| *(nessuno)* | risultati `proven` + `likely` — l'impostazione predefinita, nessun flag necessario |
| `--paranoid` | include anche i risultati di livello `heuristic` |
| `-s, --severity <level>` | mostra solo i risultati a/oltre `low`\|`medium`\|`high`\|`critical` |
| `--output <file>` | scrive un report completo — `.sarif` (code scanning di GitHub), `.json`, `.md` o `.html` |
**Ambito delle regole da eseguire**
| Flag | Cosa fa |
|------|---------------|
| `-r, --rules <list>` | esegue solo questi ID di regola, es. `AI001,MCP007` |
| `--only-ai` / `--only-mcp` / `--only-vec` / `--only-skl` | esegue solo una categoria di regole |
| `--check-dependencies` | controlla anche `package.json`/`requirements.txt` contro il registro npm/PyPI per refusi e pacchetti allucinati (`DEP001`/`DEP002`). Si attiva automaticamente se selezioni quelle regole direttamente con `-r` — non devi mai ricordarti di passarli entrambi. Non necessario per `DEP003` (pacchetti noti come dannosi), che viene sempre eseguito offline |
**CI / workflow**
| Flag | Cosa fa |
|------|---------------|
| `--fail-on <severity>` | esce con `1` se esistono risultati a/oltre questa gravità |
| `--baseline <file>` | tiene traccia solo dei problemi nuovi/modificati rispetto a una baseline salvata |
| `--policy <file>` | carica soglie, percorsi saltati e regole bloccate da un `.secureai-policy.json` (rilevato automaticamente se presente — `secureai-scan init` ne crea uno) |
**Avanzato**
| Flag | Cosa fa |
|------|---------------|
| `--min-confidence <0-1>` | più granulare di `--paranoid`: nasconde i risultati al di sotto di un punteggio di confidenza esatto (`0.9` proven / `0.65` likely / `0.35` heuristic) |
| `--limit <n>` | numero massimo di gruppi di regole mostrati nel terminale (predefinito `10`) — i dettagli completi vanno sempre a `--output` |
| `--debug` | stampa ogni file scansionato e quali regole sono state eseguite |
**Scansiona prima di installare — nessun clone, nessuna configurazione:**```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 e mcp recuperano il target e lo scansionano, quindi eliminano la copia scaricata (--keep per ispezionarla invece). Nulla di ciò che viene recuperato viene mai eseguito: un target npm viene scaricato con npm pack — solo il tarball, nessun install, nessuno script di ciclo di vita — e un target git è un semplice git clone --depth 1. Questo è il momento che conta di più: prima che una skill finisca in ~/.claude/skills/ o un server finisca in .mcp.json, non dopo.
Altri comandi:```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
Sopprimi un risultato revisionato nel codice:```ts
// secureai-ignore AI001: reviewed, input sanitized via allowlist
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
Findings appear as inline annotations on the PR and in the repository's Security tab. (`secureai-scan init` generates an equivalent workflow using the CLI directly.)
Scanning clean? Add the badge to your own README:```md
[](https://github.com/akanthed/SecureAI-Scan)
Preferisci individuare i risultati prima che vengano pubblicati? Aggiungi questo repository come sorgente di hook pre-commit invece di, o insieme a, la GitHub Action:```yaml repos:
The hook scans the whole project on every commit (not just changed files — a dataflow trace into file A can depend on file B, which a partial scan would miss) and blocks the commit on `high`+ severity findings by default. Override the threshold in your own config:```yaml
- id: secureai-scan
args: ["--fail-on", "critical"]
42 regole, mappate sulla OWASP Top 10 ufficiale per applicazioni LLM (2026) — più, dove applicabile, la OWASP Top 10 per applicazioni agentiche (2026, ASI), la OWASP MCP Top 10 (2025) e un articolo dell'EU AI Act. Vedi la copertura e i limiti 2026 versionati; threat-model genera la matrice per ogni progetto scansionato.
| Regola | Cosa dimostra | OWASP |
|---|---|---|
| AI001 | L'input utente fluisce in un prompt di sistema/sviluppatore (sorgente tracciata → sink, anche attraverso i confini di funzione/file) | LLM01 |
| AI002 | Contenuto del prompt o segreti scritti nei log (in file che usano un SDK LLM) | LLM02 |
| AI003 | Chiamata LLM in un gestore di richieste senza controllo di autenticazione prima | LLM06 |
| AI004 | Intero oggetto utente/sessione serializzato in un prompt (la selezione di campi non viene segnalata) | LLM02 |
| AI005 | L'output LLM raggiunge sink eval/exec/SQL/HTML | LLM10 |
| AI006 | Strumenti ad alto impatto (delete, pay, deploy, …) esposti senza un gate di approvazione | LLM03 |
| AI007 | Contenuto RAG recuperato interpolato in prompt privilegiati | LLM01 |
| AI008 | Segreti incorporati nel testo del prompt di sistema | LLM08 |
| AI009 | Input utente illimitato / limiti di token mancanti | LLM06 |
| AI010 | Contenuto esterno recuperato che fluisce nei prompt | LLM01 |
| AI011 | Output dell'agente elevato a ruolo di sistema nelle chiamate a valle | LLM03 |
| AI012 | Output LLM analizzato senza validazione dello schema | LLM10 |
| MCP001 | I metadati degli strumenti MCP raggiungono il prompt di sistema senza validazione | LLM01 |
| MCP002 | URL del server MCP costruito da input utente | LLM04 |
| MCP003 | Risultati degli strumenti MCP elevati a ruolo di sistema | LLM10 |
| MCP004 | Server MCP avviato come pacchetto npx -y non bloccato | LLM04 |
| MCP005 | Segreto inline in una configurazione MCP committata | LLM02 |
| MCP006 | Server MCP su HTTP in chiaro | LLM04 |
| MCP007 | Unicode invisibile/bidi nascosto in nomi o descrizioni di strumenti MCP | LLM01 · MCP03 |
| MCP008 | Frasi di iniezione dirette all'agente nelle descrizioni degli strumenti MCP | LLM01 · MCP03 |
| MCP009 | Una descrizione di strumento che indirizza le chiamate a uno strumento diverso (shadowing) |
secureai-scan explain <RULE_ID> fornisce il walkthrough dello sfruttamento e un esempio di codice prima/dopo per qualsiasi regola.
Tre superfici di scansione indipendenti alimentano un'unica lista di risultati unita e deduplicata:``` ┌─────────────────────┐ *.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)
Ogni regola AST chiama una funzione "LLM call" solo se questa risolve attraverso import reali a un SDK noto — mai solo tramite corrispondenza del nome. Vedi [`docs/Architecture.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/Architecture.md) per la ripartizione completa di ogni superficie, e [`docs/DetectionEngine.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/DetectionEngine.md) per come funziona il contratto dei livelli di evidenza.
## Server MCP (usalo da Claude)
Il pacchetto include un server MCP che espone `scan_repository`, `explain_rule`, `generate_bom` e `scan_untrusted_target` (recupera e analizza una skill o un server MCP prima che Claude ne consigli l'installazione — stesso comportamento di fetch-senza-esecuzione dei comandi CLI `skill`/`mcp`):```json
{
"mcpServers": {
"secureai-scan": {
"command": "node",
"args": ["/path/to/secureai-scan/mcp-server/index.js"]
}
}
}
Per gli utenti di Claude Code / Claude.ai, skills/secureai-scan/SKILL.md insegna a Claude quando eseguire una scansione (durante la revisione di codice AI/LLM, o quando si verifica un server MCP/Skill di un Agente prima di installarlo) e come interpretare i risultati — nessun processo separato da eseguire, a differenza del server MCP sopra. Copia la directory skills/secureai-scan/ nella tua .claude/skills/ per usarla.
A luglio 2026, due ricerche pubbliche hanno dimostrato che gli scanner di Skill per Agenti vengono aggirati banalmente.
Cloak and Detonate (Ji, Xu, Li, Gao, Wei, Wang & Cheung — HKUST, arXiv:2607.02357) ha condotto uno studio avversariale contro nove scanner di skill pubblicati utilizzando 1.613 skill dannose reali. Due famiglie di attacco, entrambe in grado di preservare esattamente il payload:
.git/, con un innocuo SKILL.md di copertura che lo ricostruisce al primo avvio. ≥90% di bypass su ogni scanner testato, ≥96% sulla maggior parte. Il tasso di rilevamento di uno scanner ibrido è crollato da ~99% a ~10%.Separatamente, Gecko Security (VentureBeat) ha distribuito un payload in un file *.test.ts. Ogni scanner pubblico lo ha saltato — ma Jest e Vitest rilevano ed eseguono automaticamente quei file al successivo npm test.
SecureAI-Scan gestisce ogni tecnica pubblicata. Ogni riga sotto ha un test corrispondente in test/deobfuscate.test.js o una fixture in test-fixtures/vulnerable/skills/:
| Tecnica (nome del paper) | Esempio | Come viene gestita |
|---|---|---|
| Inserimento di larghezza zero | Ignore all previous… | Il testo viene confrontato con varianti prive di caratteri invisibili |
| Glifi confondibili | сurl (с cirillica) | Piegatura degli omoglifi in ASCII prima del confronto |
| Inversione di maiuscole/minuscole | EvAl | Tutti i pattern di contenuto sono case-insensitive |
| Splicing di variabili | 'cu' + 'rl' | Le stringhe letterali adiacenti vengono unite prima del confronto |
| Suddivisione per riga | ev⏎al(x) | Le interruzioni di riga intra-parola vengono unite come variante aggiuntiva |
| Estrazione per file | payload in un helper incluso | Viene scansionato l'intero bundle, non solo SKILL.md |
| Spostamento di directory | scripts/x.sh → build/x.sh | Nessuna skip-list di directory all'interno di un bundle |
| Rinomina dell'estensione | x.sh → x.txt | I file vengono scansionati per contenuto, non per estensione |
| Padding di dimensione | riempimento oltre il limite dello scanner | I file sovradimensionati vengono letti dall'inizio, mai saltati |
| Impacchettamento SFS | payload in .git/skillpack.dat | Qualsiasi file non-git sotto .git/ di un bundle è proven (SKL004) |
| Staging di file di test | payload in *.test.ts | La scansione dei bundle deliberatamente non declassa i percorsi di test (SKL005) |
La deoffuscazione è di solito un rischio per la precisione — più corrispondenze, più rumore. Qui la logica è invertita: una corrispondenza che appare solo dopo la deoffuscazione viene promossa a proven, non declassata. La documentazione ordinaria non contiene un joiner a larghezza zero dentro "ignore previous instructions", né una с cirillica dentro curl. L'occultamento è di per sé una prova affermativa dell'intento.
Il confronto avviene rispetto all'insieme delle corrispondenze grezze, non semplicemente "il testo grezzo corrispondeva o no" — altrimenti un attaccante potrebbe mascherare il segnale lasciando in chiaro una frase innocua.
Le due nuove regole per i bundle scattano solo su congiunzioni, mai su una singola parola chiave:
tar -x accanto a un asset binario non correlato non basta. Gli archivi reali (gzip/zip/png/pdf/wasm — verificati tramite magic bytes, non estensione) non sono mai "opachi" in primo luogo, comunque vengano compressi.~/.aws/credentials, non la parola "token") o un'enumerazione massiva di variabili d'ambiente (os.environ.items(), non os.environ["API_KEY"]) — e un egresso verso un host non locale hardcoded, entro 25 righe l'uno dall'altro nello stesso file, oppure un fetch remoto che viene eseguito dopo essere stato riassegnato tramite una o più rinomine. Un helper di pubblicazione che legge ~/.npmrc in una funzione e chiama un registry quaranta righe dopo resta pulito, e la lettura di una singola variabile d'ambiente nominata per una chiamata API non viene mai segnalata — entrambe le forme sono fissate come fixture sicure.Validato su due corpora del mondo reale, non solo su fixture scritte da noi: 0 rilevamenti su tutti i 18 bundle di skill reali in anthropics/skills e tutti i 14 in vercel/ai, e 6/6 corretto su cisco-ai-defense/skill-scanner's corpus di valutazione etichettato (20 skill, ciascuna con un verdetto _expected.json) con zero falsi positivi su qualsiasi cosa etichettata come sicura. Vedi Testing & benchmarking.
Limitazione onesta: la conclusione del paper è che la detonazione a runtime batte l'analisi statica, ed è corretto. Un avversario adattivo che conosce queste regole può scrivere una trasformazione che non coprono. Ciò che cambia qui è il costo dell'evasione — le tecniche pubblicate e attualmente in circolazione non funzionano più, e l'offuscamento necessario per sconfiggerle ora di per sé aumenta la gravità del rilevamento. La scansione statica è un filtro, non un confine di sicurezza. Tratta una skill non fidata come codice non fidato, indipendentemente da ciò che dice qualsiasi scanner.
prepublishOnly.Questo è un progetto a manutentore singolo senza SLA contrattuale o certificazione indipendente. I controlli sopra riducono il rischio; non trasformano una scansione statica in una prova di sicurezza.
I falsi positivi uccidono gli scanner. Il motore di regole di SecureAI-Scan segue tre regole ferree:
test-fixtures/safe/ contiene i pattern che in passato causavano falsi positivi (payload PII redatti, client Google Maps, chiavi API da variabili d'ambiente accanto a client LLM, logging ordinario delle risposte, campi di metadati OAuth, chunks di risposte in streaming, testo di prompt di narrativa/finzione). Qualsiasi rilevamento lì fa fallire la suite.Tre livelli, perché uno solo non basta per fidarsi delle affermazioni di uno scanner — precisione e recall sono modalità di errore diverse, ed entrambe vengono verificate.
1. Corpus di fixture — precisione + recall, eseguito a ogni build.```bash npm test
[`test-fixtures/vulnerable/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/vulnerable) e [`test-fixtures/safe/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/safe) vengono scansionati insieme: ogni fixture vulnerabile deve attivare la regola prevista con evidenza `proven`/`likely` (recall), ogni fixture sicura deve produrre **zero** risultati `proven`/`likely` (precision). Veloce e deterministico — ma dimostra solo che lo scanner si comporta correttamente su codice scritto appositamente per testarlo.
**2. Benchmark di regressione sul mondo reale — contro repository pubblici che non abbiamo scritto noi.**```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 clona un set curato e diversificato di repository pubbliche reali (OpenAI/Anthropic/Vercel AI SDK, i server MCP ufficiali e TypeScript SDK, LlamaIndex, oltre a anthropics/skills e cisco-ai-defense/skill-scanner per la copertura dei bundle di skill — spaziando tra TS e Python, codice di esempio consumer di SDK e sorgente autore di SDK) e scansiona ciascuna con la CLI compilata.
Esce con codice non-zero su qualsiasi riscontro proven/likely non già presente in test/regression-baseline.json — un registro revisionato manualmente di riscontri già letti rispetto alla loro riga sorgente. Le impronte digitali sono repo|rule|file, non numeri di riga, quindi il normale churn a monte non produce rumore. Una nuova impronta digitale è un'affermazione che lo scanner deve giustificare: se non è un problema reale è un bug della regola, corretto alla causa principale e bloccato come nuova fixture test-fixtures/safe/. Mettere in baseline un riscontro che non hai letto vanifica l'intero meccanismo.
La copertura dei bundle di skill ha una propria riga perché il corpus evals/ di cisco-ai-defense/skill-scanner è etichettato — ciascuna delle sue 20 fixture include un verdetto _expected.json e si trova sotto una directory letteralmente chiamata malicious/ o safe/, quindi funge anche da controllo di recall, non solo di precisione: 6/6 fixture malicious in scope scattano, 0 riscontri su qualsiasi cosa etichettata safe, e 0 riscontri su tutti i 18 bundle reali in anthropics/skills e tutti i 14 in vercel/ai. (Le restanti categorie Cisco — SQL injection, path traversal, resource exhaustion, eval() generico di un argomento di funzione, un payload deliberatamente suddiviso su quattro file — sono fuori dallo scope documentato LLM/MCP/RAG o oltre l'analisi di congiunzione nello stesso file; vedi la voce del changelog 0.6.0 per il ragionamento specifico su ciascuna.)
Prima/dopo storico dell'esecuzione che ha guidato le correzioni di precisione originali (riscontri al livello di evidenza predefinito, senza --paranoid):
| Repo | Prima | Dopo | Cosa non andava |
|---|---|---|---|
| vercel/ai | 773 | 1 | examples/, tests/ di primo livello e directory in stile ecosystem-tests/ con trattino non erano riconosciute come percorsi a fiducia inferiore; chunks (una variabile comune di risposta in streaming) era trattata come evidenza RAG inequivocabile |
| openai/openai-node | 47 | 0 | Stessa lacuna nel rilevamento dei percorsi, applicata a examples//ecosystem-tests/ dello SDK stesso |
| anthropics/anthropic-sdk-typescript | 2 | 0 | Stessa lacuna nel rilevamento dei percorsi su una directory tests/ di primo livello |
| modelcontextprotocol/typescript-sdk | 3 | 0 | Campi di metadati OAuth in stile token_endpoint/tokenType segnalati come segreti trapelati |
| run-llama/llama_index | 18 | 15 | Un controllo Python segnalava qualsiasi campo description= contenente "system prompt" come avvelenamento di tool MCP proven, indipendentemente dal contesto. I restanti 15 sono hit VEC001 sulle definizioni generiche di retriever della libreria stessa — si scansiona il sorgente di uno SDK di vector-DB, non codice applicativo, quindi un filtro non può esistere da verificare; un limite onesto e intrinseco, non un bug |
Esecuzione corrente (2026-08-06) — l'evidenza versionata è registrata in docs/benchmarks/v0.9.0.json:
| Repo | Riscontri | Regole | Stato |
|---|---|---|---|
| openai-node, anthropic-sdk-typescript, anthropic-sdk-python, modelcontextprotocol/typescript-sdk, modelcontextprotocol/servers | 0 | — | pulito |
| anthropics/skills (18 bundle di skill reali) | 0 | — | pulito — controllo di precisione puro per SKL001–005 |
| vercel/ai (5.691 file) | 0 | — | era 40 (AI001, AI003, AI005, AI010, MCP002) prima del triage — ognuno revisionato manualmente rispetto al sorgente e confermato falso positivo, ricondotto a 3 bug indipendenti alla causa principale (vedi sotto), corretto e riconfermato pulito con una nuova scansione completa |
| run-llama/llama_index | 46 | VEC001 | limite intrinseco, non un bug — le definizioni generiche di retriever della libreria stessa, dove nessun filtro tenant può esistere da trovare |
| cisco-ai-defense/skill-scanner | 7 | SKL001, SKL002, SKL005 | tutti su fixture etichettate malicious/ — 6/6 in scope, 0 su qualsiasi cosa etichettata safe/ |
Il triage di vercel/ai ha trovato tre bug reali, con causa principale individuata — nessuno specifico delle regole skill v0.6.0, tutti nella logica condivisa usata da molte regole:
resolveLlmSink trattava qualsiasi chiamata risolta a un modulo SDK LLM come un'invocazione di modello, indipendentemente dal nome del metodo — segnalando isToolUIPart (un type guard che il pacchetto ai esporta proprio accanto a generateText) come chiamata LLM. Da solo, questo ha causato 3 dei 5 gruppi di riscontri (AI001, AI003, AI010).DANGEROUS_CALLEES in AI005 include "query" per sink in stile SQL injection, ma "query" è anche un verbo legittimo di invocazione LLM/agente — claudeSdk.query({ prompt, options }), la chiamata di modello del Claude Agent SDK stesso, veniva segnalata come "output LLM passato a un sink pericoloso" solo per il nome di metodo condiviso.REQUEST_SOURCES (duplicato identicamente in MCP002, MCP010, VEC003) corrispondeva a un semplice "params." — qualsiasi parametro di funzione convenzionalmente chiamato params, non necessariamente dati di richiesta HTTP. Un validatore di schema URL (assertOpenLinkParams(params: unknown)) veniva segnalato come "URL del server MCP da input utente."Tutti e tre corretti alla causa principale (non al sito di chiamata specifico) e bloccati come fixture permanenti sotto test-fixtures/. Dettagli completi in CHANGELOG.md.
3. Validazione vulnerabile-vs-patched — dimostra il recall, non solo la precisione.
I due livelli precedenti verificano solo che lo scanner resti silenzioso su codice sicuro. I controlli advisory di DEP003 sono validati nell'altro modo: blocca un pacchetto a una versione documentata come vulnerabile e conferma che venga segnalato, poi bloccalo alla versione patched e conferma che non lo sia.```bash
node --test test/dependency-guard.test.js
covers: `[email protected]` (CVE-2025-6514, vulnerabile) segnalato / `[email protected]` (corretto) pulito; `[email protected]` (prima della backdoor) pulito / `[email protected]` (dopo — non esiste alcuna patch legittima per un pacchetto dannoso) ancora segnalato; `llama-cpp-python==0.2.71` (CVE-2024-34359, dal set generato da OSV) segnalato / `==0.2.72` (corretto) pulito, anche con la normalizzazione dei nomi PyPI (`llama_cpp_python`); e specificatori non pinnati tipo `langchain>=0.1.0` che producono **zero** risultati nel report predefinito. Costruire questo test ha colto una lacuna reale: `DEP003` prima confrontava gli advisory solo per nome del pacchetto, senza mai confrontare effettivamente la versione dichiarata con l'intervallo interessato dell'advisory — corretto in [`src/scanner/semver.ts`](https://github.com/akanthed/secureai-scan/blob/main/src/scanner/semver.ts).
L'ambiguità viene risolta in modo diverso per tipo di advisory, deliberatamente. Un pacchetto **dannoso** scatta anche quando la versione dichiarata non può essere risolta — installare una backdoor è irreversibile, quindi fallisce verso la segnalazione. Una **CVE** scatta a `proven` solo quando la versione dichiarata è un pin esatto dimostrabilmente all'interno dell'intervallo interessato; le versioni non pinnate ma potenzialmente interessate scendono a `heuristic` (solo con `--paranoid`). Applicare la regola del tipo dannoso a uno snapshot CVE di 162 voci metterebbe un risultato critico su ogni repository che dichiara `langchain>=0.1.0` — rumore inutilizzabile su larga scala.
## Roadmap
Vedi [`ROADMAP.md`](https://github.com/akanthed/secureai-scan/blob/main/ROADMAP.md) per ciò che è stato rilasciato e ciò che è pianificato. Entrambi i motori linguistici sono basati su AST: ts-morph per TypeScript/JavaScript e Tree-sitter per Python. Import, chiamate, assegnazioni, decoratori, scope, argomenti con parola chiave, campi di dizionario e stringhe Python sono nodi di sintassi; il codice target non viene mai importato o eseguito, e non è richiesto alcun interprete Python. Il gap Python rimanente è la profondità di taint limitata tra funzioni/file, non il parsing. Le prestazioni di scansione e i limiti noti sono documentati in [`docs/Performance.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/Performance.md).
## Contribuire
I contributi sono benvenuti — vedi [`CONTRIBUTING.md`](https://github.com/akanthed/secureai-scan/blob/main/CONTRIBUTING.md) per il flusso di lavoro, e [`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) per come aggiungere una regola di rilevamento che soddisfi lo standard di precisione sopra. Ogni nuova regola richiede un fixture sia in [`test-fixtures/vulnerable/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/vulnerable) che in [`test-fixtures/safe/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/safe), una voce in `src/scanner/catalog.ts`, e un caso in `test/corpus.test.js` — `npm test` impone tutti e tre.
## Licenza
MIT © Akshay Kanthed
| LLM01 · MCP03 |
| MCP010 | Comando/argomenti del server MCP stdio costruiti da input utente (RCE) | LLM04 · MCP05 |
| SKL001 | Unicode invisibile/bidi ovunque in un bundle di Agent Skill | LLM01 |
| SKL002 | Frasi di iniezione dirette all'agente nella descrizione o nel corpo di una skill (rilevate tramite offuscamento) | LLM01 |
| SKL003 | Il contenuto di una skill determina quando/come viene usata una skill diversa (shadowing) | LLM01 |
| SKL004 | Payload in più fasi/autoestraente: blob opaco + istruzioni per decodificarlo ed eseguirlo | LLM04 · MCP04 |
| SKL005 | Lettura di credenziali + egress esterno hardcoded in un file di supporto del bundle | LLM02 · MCP04 |
| SKL006 | Esecuzione di comandi al caricamento tramite la sintassi di iniezione dinamica del contesto di Claude Code (!`cmd`/```!), prima di qualsiasi gate di autorizzazione degli strumenti | LLM04 · MCP05 |
| SKL007 | Concessione Bash senza ambito nel frontmatter allowed-tools di una skill | LLM03 |
| SKL008 | La skill recupera istruzioni da un URL esterno e dirige l'agente a seguirle ("Circus of Skills") | LLM04 |
| SKL009 | La skill persiste una backdoor scrivendo in un altro file di contesto (MEMORY.md/SOUL.md/AGENTS.md/CLAUDE.md) | LLM05 |
| SKL010 | Tag di deserializzazione YAML/JSON non sicuro nel frontmatter di una skill o in un file di configurazione incluso | LLM04 |
| VEC001 | Ricerca vettoriale senza filtro tenant/utente | LLM09 |
| VEC002 | Limite di ricerca illimitato o controllato dall'utente | LLM06 |
| VEC003 | Contenuto utente inserito in uno store vettoriale condiviso | LLM05 |
| VEC004 | Inserimento senza tagging tenant/namespace | LLM09 |
| DEP001 | Nome della dipendenza non trovato nel registro (opt-in --check-dependencies) | LLM04 |
| DEP002 | Nome della dipendenza a una modifica di distanza da un pacchetto popolare (opt-in) | LLM04 |
| DEP003 | Dipendenza con una release dannosa documentata o una CVE critica — verificata offline a ogni scansione, consapevole dell'intervallo di versioni (postmark-mcp, mcp-remote CVE-2025-6514, …) | LLM04 · MCP04 |
| LLC001 | Segreto hardcoded in un config.yaml del proxy LiteLLM | LLM02 |
| LLC002 | api_base del proxy LiteLLM raggiungibile su HTTP in chiaro | LLM04 |
| LLC003 | La configurazione del proxy LiteLLM non ha una sezione guardrails: (euristica, solo --paranoid) | LLM03 |