
SecureAI-Scan v0.6.0
SecureAI-Scan è uno strumento CLI che analizza codebase TypeScript e JavaScript per problemi di sicurezza specifici delle app basate su AI — prompt injection, abuso di strumenti MCP, avvelenamento dei dati RAG, violazioni della fiducia degli agenti e altro.
SecureAI-Scan
Lo scanner di sicurezza per l'AI che dimostra i suoi risultati.
SecureAI-Scan trova vulnerabilità LLM, MCP, Agent Skill e RAG in TypeScript, JavaScript e Python — e ti mostra le prove: il percorso esatto source → flow → sink per ogni rilevamento del dataflow, ricostruito tramite import reali, non con corrispondenze di parole chiave.
Offre supporto fin dalla settimana del lancio per l'ufficiale OWASP Top 10 for LLM Applications 2026, insieme al Top 10 for Agentic Applications (2026) e al MCP Top 10. Ogni modello di minaccia distingue la copertura statica dagli aspetti di runtime.
Inizia in 30 secondi```bash
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.
**Misurata la release candidate `0.9.0`:** 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/HEAD/docs/benchmarks/v0.9.0.json) · [metodologia e limiti](https://github.com/akanthed/secureai-scan/blob/HEAD/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.
Fa per te? SecureAI-Scan è deliberatamente limitato ai rischi LLM, MCP e RAG/agenti — prompt injection, tool poisoning, gestione non sicura dell'output, controllo degli accessi al vector store, avvelenamento delle skill degli agenti. Non è uno scanner SAST generico né uno scanner di segreti, e non cerca di esserlo; un pacchetto notoriamente dannoso senza payload in stile LLM (ad esempio un indirizzo di esfiltrazione hardcoded in una chiamata API email) viene intercettato dalla lista 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, è fatto per te.
Indice
- Perché questo scanner è diverso
- Come si confronta
- Inizia in 30 secondi
- Vedilo in azione
- Comandi
- GitHub Action
- Regole
- Architettura
- Server MCP (usalo da Claude)
- Claude Skill
- Garanzia di fiducia e di rilascio
- Il contratto di precisione
- Test e benchmark
- Roadmap
- Contribuire
Perché questo scanner è diverso
- Livelli di evidenza, non rumore. Ogni riscontro è
proven(dataflow tracciato o fatto di configurazione analizzato),likely(sink risolto, un salto euristico) oheuristic. Una scansione predefinita mostra solo proven + likely. Le euristiche sono attivabili tramite--paranoid. - Rilevamento basato sulla risoluzione degli import. Una chiamata è una "LLM call" solo se risolve a un import SDK reale (
openai,@anthropic-ai/sdk,ai,@google/genai, LangChain, Bedrock, …). Il tuo client Google Maps non verrà mai più segnalato come LLM. - Gated sulla precisione e testato su repository reali. La suite di test verifica che ogni fixture vulnerabile scatti e che ogni fixture sicura resti pulita — un falso positivo sul corpus sicuro fa fallire la build. Inoltre,
npm run regressionesegue la scansione di repository pubblici reali (OpenAI/Anthropic/Vercel AI SDKs, server MCP ufficiali, LlamaIndex) confrontandoli con una baseline committata e revisionata manualmente e fallisce su qualsiasi nuovo riscontroproven/likely. Vedi Test e benchmark per i numeri prima/dopo effettivi, oppure Cosa abbiamo trovato scansionando repository reali per la storia che c'è dietro — un tasso di rilevamento di 6/6 su un corpus etichettato di skill dannose, e perché non stiamo definendo llama_index "vulnerabile" per un riscontro onesto a livello di libreria. - SARIF per il code scanning di GitHub.
--output report.sarifpubblica i riscontri direttamente sulle pull request e nella scheda Sicurezza. - AI-BOM.
secureai-scan bom .costruisce un inventario derivato dalla sintassi di SDK, ID modello, vector store, framework per agenti e server MCP, mappato sulle esigenze di documentazione OWASP LLM Top 10 / EU AI Act. - Scansione della configurazione MCP. Analizza
.mcp.json,claude_desktop_config.json,.cursor/mcp.json: servernpx -ysenza pin, segreti inline, transport HTTP in chiaro. - Rilevamento del tool-poisoning MCP. Individua il pattern dietro il rug-pull del WhatsApp MCP e il backdoor di postmark-mcp — Unicode invisibile, frasi di injection indirizzate all'agente e shadowing incrociato tra tool nei nomi/descrizioni dei tool, in modo statico, prima ancora di eseguire il server.
- Rilevamento della command injection MCP. Segnala i
command/argsdel transport stdio MCP costruiti dai dati delle richieste — il pattern dietro la divulgazione della RCE MCP STDIO del 2026. - Rilevamento dell'avvelenamento delle Agent Skill. Gli stessi controlli su Unicode invisibile, frasi di injection e shadowing applicati ai file
SKILL.md— le Agent Skill vengono caricate nel contesto in blocco, quindi una skill avvelenata è una descrizione di tool avvelenata con un altro nome. - Scansione delle skill resistente all'evasione. I bundle di skill vengono scansionati come directory, non solo come
SKILL.md, e ogni controllo dei contenuti viene eseguito su varianti deoffuscate del testo. Questo mira alle tecniche pubblicate — omoglifi, suddivisione a larghezza zero, payload collocati in.git/obuild/, esfiltrazione nascosta in un file*.test.ts— che hanno bypassato oltre il 90% dei nove scanner presi in esame in Cloak and Detonate (arXiv:2607.02357). Vedi Resistenza all'evasione. - Advisory version-aware per pacchetti notoriamente vulnerabili e dannosi. Controlla ogni dipendenza e ogni pacchetto avviato da MCP contro uno snapshot advisory incluso — un elenco curato a mano di backdoor documentate in the wild, più gli advisory OSV HIGH/CRITICAL per una watchlist di pacchetti LLM/MCP/RAG, rigenerato da
scripts/sync-advisories.js. Viene eseguito offline a ogni scansione, senza bisogno di flag. Una CVE scatta solo quando la tua versione bloccata è provabilmente all'interno dell'intervallo interessato; un pacchetto documentato come dannoso scatta anche su un intervallo ambiguo, perché installare una backdoor è irreversibile. - Local-first. Nulla esce dalla tua macchina.
Come si confronta
SecureAI-Scan non è un sostituto di uno strumento SAST generico né di uno scanner per container/IaC — eseguilo insieme a uno di questi, non al posto di uno. È progettato appositamente per la superficie d'attacco LLM/MCP/RAG e privilegia l'evidenza basata sul dataflow rispetto ai semplici riscontri per parole chiave.
| SecureAI-Scan | Semgrep (OSS rules) | Trivy | GitHub Advanced Security | |
|---|---|---|---|---|
| Prompt injection (source→sink tracciato) | ✅ dataflow risolto tramite import | ⚠️ solo regole pattern, mantenute dalla community | ❌ | ⚠️ CodeQL può, ma nessun ruleset specifico per l'IA |
| Tool-poisoning MCP / rischio di configurazione | ✅ MCP007–010, scanner di configurazione | ❌ | ❌ | ❌ |
Avvelenamento delle Agent Skill (SKILL.md) | ✅ resistente all'evasione, consapevole dei bundle | ❌ | ❌ | ❌ |
| Misconfigurazione RAG / vector-store | ✅ VEC001–004 | ❌ | ❌ | ❌ |
| Advisory per pacchetti AI notoriamente dannosi | ✅ DEP003, offline, version-aware | ❌ | ⚠️ feed CVE generico, non specifico per l'IA | ⚠️ 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) | ✅ | ❌ riscontri piatti | ❌ | ⚠️ CodeQL ne ha alcuni, non ottimizzati per l'IA |
| Output SARIF (GitHub code scanning) | ✅ | ✅ | ✅ | nativo |
| Funziona offline, senza account | ✅ | ✅ (OSS rules) | ✅ | ❌ 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 gratuitamente il SecureAI-Scan AI Security Advisor su ChatGPT.
Stai per eseguire un server MCP trovato su GitHub o Twitter? Incolla prima la sua descrizione dei tool in MCP X-Ray — la controlla per Unicode nascosto, istruzioni iniettate e pacchetti notoriamente dannosi nel tuo browser, senza installazione.
Vedilo in azione
Forme di attacco che lo scanner traccia dall'inizio alla fine:
| Dataflow del tool-poisoning MCP | Dataflow della context injection RAG |
|---|---|
![]() | ![]() |
Comandi
Quello che ti serve il 95% delle volte:```bash secureai-scan scan .
Everything else is there when you need it. `secureai-scan scan . --help` shows all of this in the terminal, grouped the same way:
**Uso quotidiano**
| Flag | Cosa fa |
|------|---------------|
| *(nessuno)* | segnalazioni `proven` + `likely` — l'impostazione predefinita, nessun flag necessario |
| `--paranoid` | include anche le segnalazioni di livello `heuristic` |
| `-s, --severity <level>` | mostra solo le segnalazioni con gravità pari o superiore a `low`\|`medium`\|`high`\|`critical` |
| `--output <file>` | scrive un report completo — `.sarif` (scansione del codice di GitHub), `.json`, `.md` o `.html` |
**Seleziona quali regole 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` rispetto al registry npm/PyPI per errori di battitura e pacchetti allucinati (`DEP001`/`DEP002`). Si abilita 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 / flusso di lavoro**
| Flag | Cosa fa |
|------|---------------|
| `--fail-on <severity>` | esce con stato `1` se esistono segnalazioni con gravità pari o superiore a questa |
| `--baseline <file>` | tiene traccia solo dei problemi nuovi/modificati rispetto a una baseline salvata |
| `--policy <file>` | carica soglie, percorsi esclusi e regole bloccate da un `.secureai-policy.json` (rilevato automaticamente se presente — `secureai-scan init` ne crea uno) |
**Avanzate**
| Flag | Cosa fa |
|------|---------------|
| `--min-confidence <0-1>` | più granulare di `--paranoid`: nasconde le segnalazioni 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`) — il dettaglio completo va sempre in `--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 scaricato viene mai eseguito: un target npm viene scaricato con npm pack — solo il tarball, senza install, senza script di lifecycle — 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 secureai-scan init # policy file + CI workflow, one-time setup
Sopprimere una segnalazione revisionata nel codice:```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.9.0 fail-on: high
I risultati appaiono come annotazioni inline sulla PR e nella scheda Sicurezza del repository. (`secureai-scan init` genera un workflow equivalente usando direttamente la CLI.)
La scansione è pulita? Aggiungi il badge al tuo README:```md
[](https://github.com/akanthed/SecureAI-Scan)
Regole
39 regole, mappate sull'OWASP Top 10 ufficiale per applicazioni LLM (2026) — più, dove applicabile, l'OWASP Top 10 per Agentic Applications (2026, ASI), l'OWASP MCP Top 10 (2025) e un articolo dell'EU AI Act. Vedi la copertura e i limiti della versione 2026; threat-model genera la matrice per ogni progetto scansionato.
| Regola | Cosa dimostra | OWASP |
|---|---|---|
| AI001 | L'input utente fluisce in un prompt di sistema/sviluppatore (traccia sorgente → 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 request handler senza controllo di autenticazione prima di essa | LLM06 |
| AI004 | Intero oggetto utente/sessione serializzato in un prompt (la selezione dei 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 senza limiti / 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 dall'input utente | LLM04 |
| MCP003 | Risultati degli strumenti MCP elevati al ruolo di sistema | LLM10 |
| MCP004 | Server MCP avviato come pacchetto npx -y senza versione bloccata | LLM04 |
| MCP005 | Segreto incorporato in una configurazione MCP inclusa nel repository | LLM02 |
| MCP006 | Server MCP su HTTP in chiaro | LLM04 |
| MCP007 | Unicode invisibile/bidi nascosto nei nomi o nelle descrizioni degli strumenti MCP | LLM01 · MCP03 |
| MCP008 | Frasi di injection dirette all'agente nelle descrizioni degli strumenti MCP | LLM01 · MCP03 |
| MCP009 | Descrizione di uno strumento che indirizza le chiamate verso uno strumento diverso (shadowing) | LLM01 · MCP03 |
| MCP010 | Comando/argomenti del server stdio MCP costruiti dall'input utente (RCE) | LLM04 · MCP05 |
| SKL001 | Unicode invisibile/bidi in qualsiasi punto di un bundle di Agent Skill | LLM01 |
| SKL002 | Frasi di injection 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 un'altra skill (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 associato al bundle | LLM02 · MCP04 |
| SKL006 | Esecuzione di comandi al momento del 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 scope 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 un 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 del bundle | LLM04 |
| VEC001 | Ricerca vettoriale senza filtro tenant/utente | LLM09 |
| VEC002 | Limite di ricerca illimitato o controllato dall'utente | LLM06 |
| VEC003 | Contenuti utente inseriti in un archivio vettoriale condiviso | LLM05 |
| VEC004 | Ingestione senza tagging tenant/namespace | LLM09 |
| DEP001 | Nome della dipendenza non trovato nel registry (opt-in --check-dependencies) | LLM04 |
| DEP002 | Nome della dipendenza a una sola modifica da un pacchetto popolare (opt-in) | LLM04 |
| DEP003 | Dipendenza con una release dannosa documentata o CVE critico — controllata offline a ogni scansione, consapevole degli intervalli di versione (postmark-mcp, mcp-remote CVE-2025-6514, …) | LLM04 · MCP04 |
secureai-scan explain <RULE_ID> fornisce il walkthrough dell'exploit e un esempio di codice prima/dopo per qualsiasi regola.
Architettura
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 "chiamata LLM" solo se risolve tramite import reali a un SDK noto — mai basandosi solo sulla corrispondenza dei nomi. Vedi [`docs/Architecture.md`](https://github.com/akanthed/secureai-scan/blob/HEAD/docs/Architecture.md) per la ripartizione completa di ogni superficie, e [`docs/DetectionEngine.md`](https://github.com/akanthed/secureai-scan/blob/HEAD/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 scansiona una skill o un server MCP prima che Claude ne raccomandi 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"]
}
}
}
Claude Skill
Per gli utenti di Claude Code / Claude.ai, skills/secureai-scan/SKILL.md insegna a Claude quando eseguire una scansione (rivedendo codice AI/LLM, oppure controllando un server MCP/Agent Skill prima di installarlo) e come interpretare i risultati — nessun processo separato da eseguire, a differenza del server MCP qui sopra. Copia la directory skills/secureai-scan/ nella tua .claude/skills/ per usarla.
Resistenza all'evasione
A luglio 2026, due ricerche pubbliche hanno mostrato che gli scanner di Agent Skill sono banalmente aggirabili.
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, usando 1.613 skill dannose reperite in natura. Due famiglie di attacco, entrambe preservano il payload esattamente:
- Reify — riscrive i byte che corrispondono a una regola, mentre l'agente riassembla comunque l'originale a runtime. Oltre 80% di bypass sulla maggior parte degli scanner statici.
- Disperse / impacchettamento SFS — sposta il payload fuori dallo scope della regola: in una directory che lo scanner salta, dietro un'estensione rinominata, oppure archiviato e codificato dentro
.git/, con un innocuoSKILL.mddi copertura che lo ricostruisce alla prima esecuzione. ≥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 scoprono ed eseguono automaticamente quei file al successivo npm test.
SecureAI-Scan gestisce ogni tecnica pubblicata. Ogni riga qui 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 caratteri a larghezza zero | Ignore all previous… | Il testo viene confrontato con varianti ripulite dai caratteri invisibili |
| Glifi confondibili | сurl (cirillico с) | Normalizzazione degli omoglifi in ASCII prima del confronto |
| Inversione maiuscole/minuscole | EvAl | Tutti i pattern di contenuto non distinguono tra maiuscole e minuscole |
| Splicing di variabili | 'cu' + 'rl' | I literal stringa adiacenti vengono uniti prima del confronto |
| Suddivisione su più righe | ev⏎al(x) | Le interruzioni di riga interne alla 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 lista di directory da saltare all'interno di un bundle |
| Rinomina dell'estensione | x.sh → x.txt | I file vengono scansionati per contenuto, non per estensione |
| Padding della dimensione | padding 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 la .git/ di un bundle è proven (SKL004) |
| Staging su file di test | payload in *.test.ts | La scansione del bundle deliberatamente non declassa i percorsi di test (SKL005) |
Questo non indebolisce il contratto di precisione
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 carattere unificatore a larghezza zero dentro "ignore previous instructions", né una с cirillica dentro curl. Il nascondimento è di per sé una prova affermativa dell'intento.
Il confronto viene effettuato rispetto all'insieme delle corrispondenze grezze, non semplicemente "il testo grezzo corrispondeva o no" — altrimenti un attaccante potrebbe mascherare il segnale lasciando una frase innocua in chiaro.
Le due nuove regole per i bundle scattano solo su congiunzioni, mai su una singola parola chiave:
- SKL004 richiede un blob opaco e una direttiva di unpack che fa riferimento a quel blob per nome — una README che menziona
tar -xaccanto 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 siano compressi. - SKL005 richiede un segnale concreto di credenziali — un percorso (
~/.aws/credentials, non la parola "token") o un'enumerazione massiva di variabili d'ambiente (os.environ.items(), nonos.environ["API_KEY"]) — e un egress 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 attraverso una o più rinomine. Un helper di pubblicazione che legge~/.npmrcin una funzione e chiama un registro quaranta righe dopo rimane 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 corpus del mondo reale, non solo su fixture scritte da noi: 0 rilevamenti su tutti i 18 bundle di skill reali in anthropics/skills e su tutti i 14 in vercel/ai, e 6/6 corretto sul corpus di valutazione etichettato di cisco-ai-defense/skill-scanner (20 skill, ciascuna con un verdetto _expected.json) con zero falsi positivi su qualsiasi cosa etichettata come sicura. Vedi Test e benchmark.
Cosa non è
Limitazione onesta: la conclusione dell'articolo è che la detonazione a runtime batte l'analisi statica, ed è corretto. Un avversario adattivo che conosce queste regole può scrivere una trasformazione che queste regole 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 aumenta di per sé 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 dica uno scanner.
Fiducia e garanzie di rilascio
- La CI viene eseguita su Linux, Windows e macOS su tutte le versioni supportate di Node.
- CodeQL, l'audit delle dipendenze di produzione, OpenSSF Scorecard, Dependabot e la self-scan bloccante di questo stesso scanner forniscono controlli indipendenti.
- Ogni pubblicazione npm manuale invoca test, soglie di copertura, il gate di regressione revisionato su repository reali e l'ispezione del tarball tramite
prepublishOnly. - GitHub Actions non riceve alcuna password o token npm e non può pubblicare il pacchetto.
- Garanzia di rilascio, governance a manutentore singolo, segnalazione di vulnerabilità e prove di benchmark versionate sono pubbliche.
Questo è un progetto a manutentore singolo senza SLA contrattuale né certificazione indipendente. I controlli sopra riducono il rischio; non trasformano una scansione statica in una prova di sicurezza.
Il contratto di precisione
I falsi positivi uccidono gli scanner. Il motore di regole di SecureAI-Scan segue tre regole ferree:
- I sink vengono risolti tramite gli import. Se un identificatore risolve a un modulo che non è un SDK LLM, non è in alcun modo una chiamata LLM — qualunque sia il suo nome.
- L'evidenza è etichettata, mai mescolata. Un dataflow tracciato e una corrispondenza per prossimità di parole non sono la stessa cosa, quindi non condividono mai un livello.
- Il corpus sicuro fa da gate a ogni rilascio.
test-fixtures/safe/contiene i pattern che in passato causavano falsi positivi (payload PII oscurati, client Google Maps, chiavi API da variabili d'ambiente accanto a client LLM, logging ordinario delle risposte, campi metadata OAuth,chunksdi risposta in streaming, testo di prompt di finzione/narrativa). Qualsiasi rilevamento lì fa fallire la suite.
Test e benchmark
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/HEAD/test-fixtures/vulnerable) e [`test-fixtures/safe/`](https://github.com/akanthed/secureai-scan/blob/HEAD/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 insieme curato e diversificato di repository pubbliche reali (SDK AI di OpenAI/Anthropic/Vercel, i server MCP ufficiali e l'SDK TypeScript, LlamaIndex, oltre a anthropics/skills e cisco-ai-defense/skill-scanner per la copertura dei bundle di skill — coprendo TS e Python, codice di esempio dei consumer di SDK e sorgente degli autori di SDK) e scansiona ciascuna con la CLI compilata.
Esce con un codice di uscita non zero per qualsiasi segnalazione proven/likely non già presente in test/regression-baseline.json — un registro revisionato a mano delle segnalazioni già lette rispetto alla loro riga di origine. Le fingerprint sono repo|rule|file, non numeri di riga, quindi il normale churn a monte non genera rumore. Una nuova fingerprint è un'affermazione che lo scanner deve giustificare: se non è un problema reale, è un bug della regola, corretto alla causa principale e fissato come nuova fixture in test-fixtures/safe/. Inserire in baseline una segnalazione che non hai letto vanifica l'intero meccanismo.
La copertura dei bundle di skill ha una sezione dedicata perché il corpus evals/ di cisco-ai-defense/skill-scanner è etichettato — ciascuna delle sue 20 fixture include un verdetto _expected.json e si trova in una directory letteralmente chiamata malicious/ o safe/, quindi funge anche da controllo di recall, non solo di precisione: 6/6 fixture dannose in scope scattano, 0 segnalazioni su qualsiasi elemento etichettato come sicuro, e 0 segnalazioni in tutti i 18 bundle reali in anthropics/skills e in tutti i 14 in vercel/ai. (Le restanti categorie Cisco — SQL injection, path traversal, esaurimento delle risorse, eval() generico di un argomento di funzione, un payload deliberatamente suddiviso in quattro file — sono o fuori dallo scope documentato LLM/MCP/RAG o oltre l'analisi di congiunzione dello stesso file; vedere la voce del changelog 0.6.0 per il ragionamento specifico su ciascuna.)
Storico prima/dopo dell'esecuzione che ha guidato le correzioni di precisione originali (segnalazioni al livello di evidenza predefinito, senza --paranoid):
| Repo | Prima | Dopo | Cosa non andava |
|---|---|---|---|
| vercel/ai | 773 | 1 | le directory examples/, tests/ di primo livello e quelle in stile ecosystem-tests/ con trattino non venivano riconosciute come percorsi a fiducia inferiore; chunks (una variabile comune delle risposte in streaming) veniva trattata come evidenza RAG inequivocabile |
| openai/openai-node | 47 | 0 | Lo stesso gap nel rilevamento dei percorsi, applicato a examples//ecosystem-tests/ dell'SDK stesso |
| anthropics/anthropic-sdk-typescript | 2 | 0 | Lo stesso gap 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 dello strumento MCP proven, indipendentemente dal contesto. I restanti 15 sono hit VEC001 sulle definizioni generiche di retriever della libreria stessa — si scansiona il sorgente dell'SDK di un database vettoriale, non codice applicativo, quindi non può esistere un filtro 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 | Segnalazioni | 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 — puro controllo di precisione per SKL001–005 |
| vercel/ai (5,691 file) | 0 | — | erano 40 (AI001, AI003, AI005, AI010, MCP002) prima del triage — ognuna revisionata a mano contro il sorgente e confermata come falso positivo, ricondotta a 3 bug indipendenti alla causa principale (vedi sotto), corretti; una nuova scansione completa ha riconfermato il risultato pulito |
| run-llama/llama_index | 46 | VEC001 | limite intrinseco, non un bug — le definizioni generiche di retriever della libreria stessa, dove non può esistere alcun filtro tenant da individuare |
| cisco-ai-defense/skill-scanner | 7 | SKL001, SKL002, SKL005 | tutte su fixture etichettate malicious/ — 6/6 in scope, 0 su qualsiasi elemento etichettato 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:
resolveLlmSinktrattava qualsiasi chiamata risolta a un modulo di un SDK LLM come un'invocazione del modello, indipendentemente dal nome del metodo — segnalandoisToolUIPart(una type guard che il pacchettoaiesporta proprio accanto agenerateText) come chiamata LLM. Da sola, questa causava 3 dei 5 gruppi di segnalazioni (AI001, AI003, AI010).DANGEROUS_CALLEESin AI005 include"query"per i sink in stile SQL injection, ma"query"è anche un verbo di invocazione legittimo di LLM/agenti —claudeSdk.query({ prompt, options }), la chiamata al modello dell'SDK Claude Agent stesso, veniva segnalata come "output LLM passato a un sink pericoloso" esclusivamente a causa del nome del metodo condiviso.REQUEST_SOURCES(duplicato identicamente tra MCP002, MCP010, VEC003) corrispondeva a un semplice"params."— qualsiasi parametro di funzione denominato convenzionalmenteparams, non necessariamente dati di richiesta HTTP. Un validatore di schemi URL (assertOpenLinkParams(params: unknown)) veniva segnalato come "URL del server MCP da input utente."
Tutti e tre corretti alla causa principale (non al call site specifico) e bloccati come fixture permanenti in test-fixtures/. Dettagli completi in CHANGELOG.md.
3. Validazione vulnerabile-vs-corretto — dimostra il recall, non solo la precisione.
I due livelli precedenti verificano solo che lo scanner rimanga in silenzio sul codice sicuro. I controlli degli advisory di DEP003 sono validati nella direzione opposta: fissa un pacchetto a una versione documentata come vulnerabile e conferma che venga segnalato, poi fissalo alla versione corretta e conferma che non lo sia.```bash
node --test test/dependency-guard.test.js
copre: `[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 maligno) ancora segnalato; `llama-cpp-python==0.2.71` (CVE-2024-34359, dal set generato da OSV) segnalato / `==0.2.72` (corretto) pulito, inclusa la normalizzazione dei nomi PyPI (`llama_cpp_python`); e specificatori non pinnati del tipo `langchain>=0.1.0` che producono **zero** segnalazioni nel report predefinito. Costruire questo test ha evidenziato una lacuna reale: `DEP003` associava gli advisory solo in base al nome del pacchetto, senza mai confrontare effettivamente la versione dichiarata con l'intervallo di versioni interessate dell'advisory — corretto in [`src/scanner/semver.ts`](https://github.com/akanthed/secureai-scan/blob/HEAD/src/scanner/semver.ts).
L'ambiguità viene risolta in modo diverso a seconda del tipo di advisory, volutamente. Un pacchetto **maligno** scatta anche quando la versione dichiarata non può essere risolta — installare una backdoor è irreversibile, quindi in caso di incertezza si preferisce segnalare. Una **CVE** scatta a `proven` solo quando la versione dichiarata è un pin esatto dimostrabilmente all'interno dell'intervallo interessato; i casi non pinnati ma potenzialmente interessati scendono a `heuristic` (solo con `--paranoid`). Applicare la regola del tipo maligno a uno snapshot CVE di 162 voci metterebbe una segnalazione critica in ogni repo che dichiara `langchain>=0.1.0` — rumore inutilizzabile su larga scala.
## Roadmap
Vedi [`ROADMAP.md`](https://github.com/akanthed/secureai-scan/blob/HEAD/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 keyword, campi di dizionario e stringhe Python sono nodi di sintassi; il codice target non viene mai importato né eseguito e non è richiesto alcun interprete Python. La lacuna Python rimanente è rappresentata dalla profondità limitata del taint tra funzioni e file, non dal parsing. Le prestazioni di scansione e i limiti noti sono documentati in [`docs/Performance.md`](https://github.com/akanthed/secureai-scan/blob/HEAD/docs/Performance.md).
## Contributi
I contributi sono benvenuti — vedi [`CONTRIBUTING.md`](https://github.com/akanthed/secureai-scan/blob/HEAD/CONTRIBUTING.md) per il flusso di lavoro e [`docs/WritingRules.md`](https://github.com/akanthed/secureai-scan/blob/HEAD/docs/WritingRules.md) / [`docs/RuleDevelopment.md`](https://github.com/akanthed/secureai-scan/blob/HEAD/docs/RuleDevelopment.md) per come aggiungere una regola di rilevamento che soddisfi lo standard di precisione sopra descritto. Ogni nuova regola richiede una fixture sia in [`test-fixtures/vulnerable/`](https://github.com/akanthed/secureai-scan/blob/HEAD/test-fixtures/vulnerable) sia in [`test-fixtures/safe/`](https://github.com/akanthed/secureai-scan/blob/HEAD/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

