Torna agli aggiornamenti
New releaseJul 28, 2026

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.

Condividi

SecureAI-Scan

npm version npm downloads CI CodeQL OpenSSF Scorecard license Node OWASP

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

  • Livelli di evidenza, non rumore. Ogni riscontro è proven (dataflow tracciato o fatto di configurazione analizzato), likely (sink risolto, un salto euristico) o heuristic. 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 regression esegue 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 riscontro proven/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.sarif pubblica 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: server npx -y senza 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/args del 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/ o build/, 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-ScanSemgrep (OSS rules)TrivyGitHub 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 MCPDataflow della context injection RAG
MCP attack traceRAG poisoning trace

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
[![secureai-scan](https://img.shields.io/badge/secureai--scan-passing-brightgreen)](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.

RegolaCosa dimostraOWASP
AI001L'input utente fluisce in un prompt di sistema/sviluppatore (traccia sorgente → sink, anche attraverso i confini di funzione/file)LLM01
AI002Contenuto del prompt o segreti scritti nei log (in file che usano un SDK LLM)LLM02
AI003Chiamata LLM in un request handler senza controllo di autenticazione prima di essaLLM06
AI004Intero oggetto utente/sessione serializzato in un prompt (la selezione dei campi non viene segnalata)LLM02
AI005L'output LLM raggiunge sink eval/exec/SQL/HTMLLLM10
AI006Strumenti ad alto impatto (delete, pay, deploy, …) esposti senza un gate di approvazioneLLM03
AI007Contenuto RAG recuperato interpolato in prompt privilegiatiLLM01
AI008Segreti incorporati nel testo del prompt di sistemaLLM08
AI009Input utente senza limiti / limiti di token mancantiLLM06
AI010Contenuto esterno recuperato che fluisce nei promptLLM01
AI011Output dell'agente elevato a ruolo di sistema nelle chiamate a valleLLM03
AI012Output LLM analizzato senza validazione dello schemaLLM10
MCP001I metadati degli strumenti MCP raggiungono il prompt di sistema senza validazioneLLM01
MCP002URL del server MCP costruito dall'input utenteLLM04
MCP003Risultati degli strumenti MCP elevati al ruolo di sistemaLLM10
MCP004Server MCP avviato come pacchetto npx -y senza versione bloccataLLM04
MCP005Segreto incorporato in una configurazione MCP inclusa nel repositoryLLM02
MCP006Server MCP su HTTP in chiaroLLM04
MCP007Unicode invisibile/bidi nascosto nei nomi o nelle descrizioni degli strumenti MCPLLM01 · MCP03
MCP008Frasi di injection dirette all'agente nelle descrizioni degli strumenti MCPLLM01 · MCP03
MCP009Descrizione di uno strumento che indirizza le chiamate verso uno strumento diverso (shadowing)LLM01 · MCP03
MCP010Comando/argomenti del server stdio MCP costruiti dall'input utente (RCE)LLM04 · MCP05
SKL001Unicode invisibile/bidi in qualsiasi punto di un bundle di Agent SkillLLM01
SKL002Frasi di injection dirette all'agente nella descrizione o nel corpo di una skill (rilevate tramite offuscamento)LLM01
SKL003Il contenuto di una skill determina quando/come viene usata un'altra skill (shadowing)LLM01
SKL004Payload in più fasi/autoestraente: blob opaco + istruzioni per decodificarlo ed eseguirloLLM04 · MCP04
SKL005Lettura di credenziali + egress esterno hardcoded in un file associato al bundleLLM02 · MCP04
SKL006Esecuzione 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 strumentiLLM04 · MCP05
SKL007Concessione Bash senza scope nel frontmatter allowed-tools di una skillLLM03
SKL008La skill recupera istruzioni da un URL esterno e dirige l'agente a seguirle ("Circus of Skills")LLM04
SKL009La skill persiste un backdoor scrivendo in un altro file di contesto (MEMORY.md/SOUL.md/AGENTS.md/CLAUDE.md)LLM05
SKL010Tag di deserializzazione YAML/JSON non sicuro nel frontmatter di una skill o in un file di configurazione del bundleLLM04
VEC001Ricerca vettoriale senza filtro tenant/utenteLLM09
VEC002Limite di ricerca illimitato o controllato dall'utenteLLM06
VEC003Contenuti utente inseriti in un archivio vettoriale condivisoLLM05
VEC004Ingestione senza tagging tenant/namespaceLLM09
DEP001Nome della dipendenza non trovato nel registry (opt-in --check-dependencies)LLM04
DEP002Nome della dipendenza a una sola modifica da un pacchetto popolare (opt-in)LLM04
DEP003Dipendenza 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 innocuo SKILL.md di 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)EsempioCome viene gestita
Inserimento di caratteri a larghezza zeroIgn‍ore all pre‍vious…Il testo viene confrontato con varianti ripulite dai caratteri invisibili
Glifi confondibiliсurl (cirillico с)Normalizzazione degli omoglifi in ASCII prima del confronto
Inversione maiuscole/minuscoleEvAlTutti 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ù righeeval(x)Le interruzioni di riga interne alla parola vengono unite come variante aggiuntiva
Estrazione per-filepayload in un helper inclusoViene scansionato l'intero bundle, non solo SKILL.md
Spostamento di directoryscripts/x.shbuild/x.shNessuna lista di directory da saltare all'interno di un bundle
Rinomina dell'estensionex.shx.txtI file vengono scansionati per contenuto, non per estensione
Padding della dimensionepadding oltre il limite dello scannerI file sovradimensionati vengono letti dall'inizio, mai saltati
Impacchettamento SFSpayload in .git/skillpack.datQualsiasi file non git sotto la .git/ di un bundle è proven (SKL004)
Staging su file di testpayload in *.test.tsLa 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 -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 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(), non os.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 ~/.npmrc in 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:

  1. 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.
  2. 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.
  3. 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, chunks di 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):

RepoPrimaDopoCosa non andava
vercel/ai7731le 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-node470Lo stesso gap nel rilevamento dei percorsi, applicato a examples//ecosystem-tests/ dell'SDK stesso
anthropics/anthropic-sdk-typescript20Lo stesso gap nel rilevamento dei percorsi su una directory tests/ di primo livello
modelcontextprotocol/typescript-sdk30campi di metadati OAuth in stile token_endpoint/tokenType segnalati come segreti trapelati
run-llama/llama_index1815Un 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:

RepoSegnalazioniRegoleStato
openai-node, anthropic-sdk-typescript, anthropic-sdk-python, modelcontextprotocol/typescript-sdk, modelcontextprotocol/servers0pulito
anthropics/skills (18 bundle di skill reali)0pulito — puro controllo di precisione per SKL001–005
vercel/ai (5,691 file)0erano 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_index46VEC001limite 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-scanner7SKL001, SKL002, SKL005tutte 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:

  1. resolveLlmSink trattava qualsiasi chiamata risolta a un modulo di un SDK LLM come un'invocazione del modello, indipendentemente dal nome del metodo — segnalando isToolUIPart (una type guard che il pacchetto ai esporta proprio accanto a generateText) come chiamata LLM. Da sola, questa causava 3 dei 5 gruppi di segnalazioni (AI001, AI003, AI010).
  2. DANGEROUS_CALLEES in AI005 include "query" per i sink in stile SQL injection, ma "query" è anche un verbo di invocazione legittimo di LLM/agenticlaudeSdk.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.
  3. REQUEST_SOURCES (duplicato identicamente tra MCP002, MCP010, VEC003) corrispondeva a un semplice "params." — qualsiasi parametro di funzione denominato convenzionalmente params, 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

Categorie