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

CLI offline che esegue la scansione di TypeScript, JavaScript e Python per rischi LLM, MCP, Agent Skill e RAG — evidenze di dataflow risolte tramite import, zero falsi positivi di default, mappate su OWASP LLM/ASI/MCP Top 10.

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.

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.

**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).

Contenuti

Perché questo scanner è diverso

  • Livelli di evidenza, non rumore. Ogni riscontro è 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.
  • Rilevamento risolto tramite import. Una chiamata è una "chiamata LLM" solo se risolve a un import SDK reale (openai, @anthropic-ai/sdk, ai, @google/genai, LangChain, Bedrock, …). Il tuo client di Google Maps non verrà mai più segnalato come LLM.
  • Gated sulla precisione e benchmarkato su repository reali. La suite di test asserisce 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 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 →
  • SARIF per GitHub code scanning. --output report.sarif mette i riscontri inline sulle pull request e nella scheda Security.
  • AI-BOM. 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.
  • Scansione configurazione MCP. Analizza .mcp.json, claude_desktop_config.json, .cursor/mcp.json: server npx -y non pinnati, segreti inline, trasporti HTTP in chiaro.
  • Rilevamento avvelenamento strumenti MCP. Cattura il pattern dietro il rug-pull di WhatsApp MCP e il backdoor di postmark-mcp — Unicode invisibile, frasi di injection dirette all'agente e shadowing tra strumenti in nomi/descrizioni, staticamente, prima ancora di eseguire il server.
  • Rilevamento command-injection MCP. Segnala i command/args del trasporto stdio MCP costruiti da dati di richiesta — il pattern dietro la divulgazione RCE MCP STDIO del 2026.
  • Rilevamento avvelenamento Agent Skill. Gli stessi controlli di Unicode invisibile, frasi di injection e shadowing applicati ai file SKILL.md — le Agent Skills vengono caricate nel contesto per intero, quindi una skill avvelenata è una descrizione di strumento avvelenata con un altro nome.
  • Scansione skill resistente all'evasione. I bundle di skill vengono scansionati come directory, non solo il loro 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.
  • Advisory di pacchetti notoriamente vulnerabili e dannosi, sensibili alla versione. Controlla ogni dipendenza e ogni pacchetto avviato da MCP contro uno snapshot advisory incluso — un elenco curato manualmente di backdoor documentati in-the-wild, più advisory OSV HIGH/CRITICAL per una watchlist di pacchetti LLM/MCP/RAG, rigenerato da 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.
  • Local-first. Nulla lascia la tua macchina.

Come si confronta

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-ScanSemgrep (regole OSS)TrivyGitHub 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.

Vedilo in azione

secureai-scan scan . end to end, output reale su un file reale (piccolo, deliberatamente vulnerabile) — sorgente:

Registrazione terminale di secureai-scan scan . che trova una vulnerabilità di injection di prompt tracciata

Forme di attacco che lo scanner traccia end to end:

Dataflow avvelenamento strumenti MCPDataflow injection contesto RAG
Traccia attacco MCPTraccia avvelenamento RAG

Comandi

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

GitHub Action```yaml

name: SecureAI-Scan on: [pull_request] permissions: contents: read security-events: write jobs: scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: akanthed/[email protected] with: scanner-version: 0.10.0 fail-on: high

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

Pre-commit hook

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"]

Regole

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.

RegolaCosa dimostraOWASP
AI001L'input utente fluisce in un prompt di sistema/sviluppatore (sorgente tracciata → 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 gestore di richieste senza controllo di autenticazione primaLLM06
AI004Intero oggetto utente/sessione serializzato in un prompt (la selezione di 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 illimitato / 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 da input utenteLLM04
MCP003Risultati degli strumenti MCP elevati a ruolo di sistemaLLM10
MCP004Server MCP avviato come pacchetto npx -y non bloccatoLLM04
MCP005Segreto inline in una configurazione MCP committataLLM02
MCP006Server MCP su HTTP in chiaroLLM04
MCP007Unicode invisibile/bidi nascosto in nomi o descrizioni di strumenti MCPLLM01 · MCP03
MCP008Frasi di iniezione dirette all'agente nelle descrizioni degli strumenti MCPLLM01 · MCP03
MCP009Una descrizione di strumento che indirizza le chiamate a uno strumento diverso (shadowing)LLM01 · MCP03
MCP010Comando/argomenti del server MCP stdio costruiti da input utente (RCE)LLM04 · MCP05
SKL001Unicode invisibile/bidi ovunque in un bundle di Agent SkillLLM01
SKL002Frasi di iniezione 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 una skill diversa (shadowing)LLM01
SKL004Payload in più fasi/autoestraente: blob opaco + istruzioni per decodificarlo ed eseguirloLLM04 · MCP04
SKL005Lettura di credenziali + egress esterno hardcoded in un file di supporto del bundleLLM02 · MCP04
SKL006Esecuzione di comandi al 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 ambito 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 una 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 inclusoLLM04
VEC001Ricerca vettoriale senza filtro tenant/utenteLLM09
VEC002Limite di ricerca illimitato o controllato dall'utenteLLM06
VEC003Contenuto utente inserito in uno store vettoriale condivisoLLM05
VEC004Inserimento senza tagging tenant/namespaceLLM09
DEP001Nome della dipendenza non trovato nel registro (opt-in --check-dependencies)LLM04
DEP002Nome della dipendenza a una modifica di distanza da un pacchetto popolare (opt-in)LLM04
DEP003Dipendenza 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
LLC001Segreto hardcoded in un config.yaml del proxy LiteLLMLLM02
LLC002api_base del proxy LiteLLM raggiungibile su HTTP in chiaroLLM04
LLC003La configurazione del proxy LiteLLM non ha una sezione guardrails: (euristica, solo --paranoid)LLM03

secureai-scan explain <RULE_ID> fornisce il walkthrough dello sfruttamento 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 "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"]
    }
  }
}

Claude Skill

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.

Resistenza all'evasione

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:

  • Reify — riscrive i byte che una regola intercetta, mentre l'agente ricompone comunque l'originale a runtime. >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 in .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)EsempioCome viene gestita
Inserimento di larghezza zeroIgn‍ore all pre‍vious…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/minuscoleEvAlTutti i pattern di contenuto sono case-insensitive
Splicing di variabili'cu' + 'rl'Le stringhe letterali adiacenti vengono unite prima del confronto
Suddivisione per rigaeval(x)Le interruzioni di riga intra-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 skip-list di directory all'interno di un bundle
Rinomina dell'estensionex.shx.txtI file vengono scansionati per contenuto, non per estensione
Padding di dimensioneriempimento oltre il limite dello scannerI file sovradimensionati vengono letti dall'inizio, mai saltati
Impacchettamento SFSpayload in .git/skillpack.datQualsiasi file non-git sotto .git/ di un bundle è proven (SKL004)
Staging di file di testpayload in *.test.tsLa scansione dei 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 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:

  • SKL004 richiede un blob opaco e una direttiva di decompressione che faccia riferimento a quel blob per nome — un 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 vengano 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 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.

Cosa non è questo

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.

Garanzia di fiducia e rilascio

  • La CI viene eseguita su Linux, Windows e macOS su tutte le versioni Node supportate.
  • CodeQL, audit delle dipendenze di produzione, OpenSSF Scorecard, Dependabot e l'auto-scansione bloccante di questo stesso scanner forniscono controlli indipendenti.
  • Ogni pubblicazione npm manuale invoca test, soglie di copertura, il gate di regressione sul repository reale revisionato 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 sicurezza e prove di benchmark versionate sono pubbliche.

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.

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 è definitivamente una chiamata LLM — qualunque sia il suo nome.
  2. Le prove sono etichettate, mai mescolate. 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 blocca ogni rilascio. 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.

Testing & benchmarking

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):

RepoPrimaDopoCosa non andava
vercel/ai7731examples/, 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-node470Stessa lacuna nel rilevamento dei percorsi, applicata a examples//ecosystem-tests/ dello SDK stesso
anthropics/anthropic-sdk-typescript20Stessa lacuna 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 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:

RepoRiscontriRegoleStato
openai-node, anthropic-sdk-typescript, anthropic-sdk-python, modelcontextprotocol/typescript-sdk, modelcontextprotocol/servers0pulito
anthropics/skills (18 bundle di skill reali)0pulito — controllo di precisione puro per SKL001–005
vercel/ai (5.691 file)0era 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_index46VEC001limite intrinseco, non un bug — le definizioni generiche di retriever della libreria stessa, dove nessun filtro tenant può esistere da trovare
cisco-ai-defense/skill-scanner7SKL001, SKL002, SKL005tutti 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:

  1. 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).
  2. DANGEROUS_CALLEES in AI005 include "query" per sink in stile SQL injection, ma "query" è anche un verbo legittimo di invocazione LLM/agenteclaudeSdk.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.
  3. 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

Categorie