Volver a actualizaciones
Nuevo releaseJul 28, 2026

SecureAI-Scan v0.6.0

SecureAI-Scan es una herramienta CLI que escanea bases de código TypeScript y JavaScript en busca de problemas de seguridad específicos de aplicaciones impulsadas por IA — inyección de prompts, abuso de herramientas MCP, envenenamiento de datos RAG, violaciones de confianza de agentes, y más.

Compartir

SecureAI-Scan

npm version npm downloads CI CodeQL OpenSSF Scorecard license Node OWASP

El escáner de seguridad de IA que demuestra sus hallazgos.

SecureAI-Scan detecta vulnerabilidades de LLM, MCP, Agent Skill y RAG en TypeScript, JavaScript y Python — y te muestra la evidencia: la ruta exacta de origen → flujo → sumidero para cada hallazgo de flujo de datos, resuelta mediante importaciones reales, no mediante coincidencia de palabras clave.

Ofrece soporte desde la semana de lanzamiento para el OWASP Top 10 para Aplicaciones LLM 2026 oficial, junto con el Top 10 para Aplicaciones Agénticas (2026) y el MCP Top 10. Cada modelo de amenazas distingue la cobertura estática de las preocupaciones de tiempo de ejecución.

Comienza en 30 segundos```bash

npx --yes [email protected] scan .

No se requiere cuenta, carga en la nube, intérprete de Python ni configuración. TypeScript, JavaScript, Python, configuraciones MCP y paquetes de Agent Skill se detectan automáticamente.

**Candidato de versión `0.9.0` medido:** 136/136 pruebas · 88.08% de cobertura de sentencias · 12,676 archivos en 9 repositorios públicos · 0 huellas nuevas de nivel predeterminado frente a la línea base revisada. [Evidencia](https://github.com/akanthed/secureai-scan/blob/HEAD/docs/benchmarks/v0.9.0.json) · [metodología y límites](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.

¿Es esto para ti? SecureAI-Scan está deliberadamente enfocado en riesgos de LLM, MCP y RAG/agentes: inyección de prompts, envenenamiento de herramientas, manejo inseguro de salidas, control de acceso al almacén de vectores y envenenamiento de Agent Skills. No es un escáner SAST general ni de secretos, y no intenta serlo; un paquete conocido como malicioso sin una carga útil con forma de LLM (p. ej., una dirección de exfiltración hardcodeada en una llamada a una API de correo) lo detecta la lista de avisos offline (DEP003), no una regla de patrones. Si tu código se comunica con un LLM, un servidor MCP, un almacén de vectores o incluye Agent Skills, esto está hecho para ti.

Contenido

Por qué este escáner es diferente

  • Niveles de evidencia, no ruido. Cada hallazgo es proven (flujo de datos trazado o hecho de configuración analizado), likely (sumidero resuelto, un salto heurístico) o heuristic. Un escaneo predeterminado muestra solo proven + likely. Las heurísticas se activan de forma opcional con --paranoid.
  • Detección con resolución de importaciones. Una llamada solo es una "llamada LLM" si se resuelve a una importación real de SDK (openai, @anthropic-ai/sdk, ai, @google/genai, LangChain, Bedrock, …). Tu cliente de Google Maps nunca volverá a marcarse como LLM.
  • Limitado por precisión y comparado con repositorios reales. El conjunto de pruebas verifica que cada fixture vulnerable se active y que cada fixture seguro permanezca limpio: un falso positivo en el corpus seguro hace fallar la compilación. Además, npm run regression escanea repositorios públicos reales (SDK de OpenAI/Anthropic/Vercel AI, servidores MCP oficiales, LlamaIndex) contra una línea base confirmada y revisada manualmente, y falla ante cualquier hallazgo nuevo proven/likely. Consulta Pruebas y benchmarking para ver los números reales de antes/después, o Lo que encontramos al escanear repositorios reales para conocer la historia detrás de ellos: una tasa de detección de 6/6 en un corpus etiquetado de habilidades maliciosas, y por qué no llamamos "vulnerable" a llama_index por un hallazgo honesto a nivel de librería.
  • SARIF para el escaneo de código de GitHub. --output report.sarif coloca los hallazgos en línea en las pull requests y en la pestaña Security.
  • AI-BOM. secureai-scan bom . crea un inventario derivado de la sintaxis de SDK, IDs de modelos, almacenes de vectores, frameworks de agentes y servidores MCP, mapeado a las necesidades de documentación de OWASP LLM Top 10 / EU AI Act.
  • Escaneo de configuración MCP. Analiza .mcp.json, claude_desktop_config.json, .cursor/mcp.json: servidores npx -y sin fijar, secretos en línea, transporte HTTP en texto plano.
  • Detección de envenenamiento de herramientas MCP. Detecta el patrón detrás del rug-pull del MCP de WhatsApp y la puerta trasera de postmark-mcp: Unicode invisible, frases de inyección dirigidas a agentes y sombreado entre herramientas en nombres/descripciones, de forma estática, antes de que ejecutes el servidor.
  • Detección de inyección de comandos MCP. Marca los command/args del transporte stdio de MCP construidos a partir de datos de solicitud: el patrón detrás de la divulgación de RCE MCP STDIO de 2026.
  • Detección de envenenamiento de Agent Skills. Los mismos controles de Unicode invisible, frases de inyección y sombreado aplicados a los archivos SKILL.md: los Agent Skills se cargan completos en el contexto, así que una skill envenenada es una descripción de herramienta envenenada con otro nombre.
  • Escaneo de skills resistente a evasiones. Los paquetes de skills se escanean como directorios, no solo su SKILL.md, y cada verificación de contenido se ejecuta contra variantes desofuscadas del texto. Esto apunta a las técnicas publicadas — homóglifos, división con espacios de ancho cero, payloads preparados en .git/ o build/, exfiltración oculta en un archivo *.test.ts — que evadieron >90% de los nueve escáneres evaluados en Cloak and Detonate (arXiv:2607.02357). Consulta Resistencia a evasiones.
  • Avisos de paquetes vulnerables y maliciosos conocidos, sensibles a la versión. Verifica cada dependencia y cada paquete lanzado por MCP contra una instantánea de avisos incluida: una lista curada a mano de puertas traseras documentadas en la naturaleza, más avisos OSV HIGH/CRITICAL para una lista de vigilancia de paquetes LLM/MCP/RAG, regenerada por scripts/sync-advisories.js. Se ejecuta offline en cada escaneo, sin necesidad de ninguna bandera. Un CVE solo se activa cuando tu versión fijada está comprobablemente dentro del rango afectado; un paquete documentado como malicioso se activa incluso con un rango ambiguo, porque instalar una puerta trasera no tiene vuelta atrás.
  • Local-first. Nada sale de tu máquina.

Cómo se compara

SecureAI-Scan no es un reemplazo de una herramienta SAST general ni de un escáner de contenedores/IaC: ejecútalo junto a una, no en lugar de una. Está diseñado específicamente para la superficie de ataque LLM/MCP/RAG y enfatiza la evidencia de flujo de datos por encima de hallazgos planos por palabras clave.

SecureAI-ScanSemgrep (OSS rules)TrivyGitHub Advanced Security
Inyección de prompts (flujo fuente→sink trazado)✅ flujo de datos con resolución de importaciones⚠️ solo reglas de patrones, mantenidas por la comunidad⚠️ CodeQL puede, pero sin reglas específicas de IA
Envenenamiento de herramientas MCP / riesgo de configuración✅ MCP007–010, escáner de configuración
Envenenamiento de Agent Skills (SKILL.md)✅ resistente a evasiones, con conocimiento del bundle
RAG / mala configuración del almacén de vectores✅ VEC001–004
Avisos de paquetes de IA conocidos como maliciosos✅ DEP003, offline, sensible a la versión⚠️ feed general de CVE, no específico de IA⚠️ Dependabot, feed general de CVE
SAST general (SQLi, XSS, path traversal)❌ fuera de alcance por diseño
Escaneo de contenedores / IaC⚠️ vía CodeQL/Actions
Niveles de evidencia (proven/likely/heuristic)❌ los hallazgos son planos⚠️ CodeQL tiene algunos, no ajustados para IA
Salida SARIF (escaneo de código de GitHub)nativo
Funciona offline, sin cuenta✅ (reglas OSS)❌ requiere GitHub

Si ya ejecutas Semgrep o GHAS, consérvalos: añade SecureAI-Scan para la superficie de riesgo que no modelan en absoluto.

¿Prefieres hacer preguntas primero? Prueba el SecureAI-Scan AI Security Advisor en ChatGPT gratuito.

¿Estás a punto de ejecutar un servidor MCP que encontraste en GitHub o Twitter? Pega primero su descripción de herramientas en MCP X-Ray — lo revisa en busca de Unicode oculto, instrucciones inyectadas y paquetes maliciosos conocidos en tu navegador, sin instalar nada.

Míralo en acción

Flujo de datos de envenenamiento de herramientas MCPFlujo de datos de inyección de contexto RAG
Traza de ataque MCPTraza de envenenamiento RAG

Comandos

El que necesitas el 95% de las veces:```bash secureai-scan scan .

Todo lo demás está ahí cuando lo necesitas. `secureai-scan scan . --help` muestra todo esto en la terminal, agrupado de la misma manera:

**Todos los días**

| Bandera | Qué hace |
|------|---------------|
| *(ninguno)* | hallazgos `proven` + `likely` — el valor predeterminado, sin necesidad de banderas |
| `--paranoid` | también incluye hallazgos de nivel `heuristic` |
| `-s, --severity <level>` | solo muestra hallazgos en/por encima de `low`\|`medium`\|`high`\|`critical` |
| `--output <file>` | escribe un informe completo — `.sarif` (escaneo de código de GitHub), `.json`, `.md` o `.html` |

**Alcance: qué reglas se ejecutan**

| Bandera | Qué hace |
|------|---------------|
| `-r, --rules <list>` | ejecuta solo estos IDs de regla, p. ej. `AI001,MCP007` |
| `--only-ai` / `--only-mcp` / `--only-vec` / `--only-skl` | ejecuta solo una categoría de regla |
| `--check-dependencies` | también verifica `package.json`/`requirements.txt` contra el registro npm/PyPI para detectar errores tipográficos y paquetes alucinados (`DEP001`/`DEP002`). Se activa automáticamente si seleccionas esas reglas directamente mediante `-r` — nunca necesitas recordar pasar ambas. No es necesario para `DEP003` (paquetes maliciosos conocidos), que siempre se ejecuta sin conexión |

**CI / flujo de trabajo**

| Bandera | Qué hace |
|------|---------------|
| `--fail-on <severity>` | sale con código `1` si existen hallazgos en/por encima de esta severidad |
| `--baseline <file>` | rastrea solo problemas nuevos/cambiados contra una línea base guardada |
| `--policy <file>` | carga umbrales, rutas omitidas y reglas bloqueadas desde un `.secureai-policy.json` (se detecta automáticamente si está presente — `secureai-scan init` crea uno) |

**Avanzado**

| Bandera | Qué hace |
|------|---------------|
| `--min-confidence <0-1>` | más granular que `--paranoid`: oculta hallazgos por debajo de una puntuación de confianza exacta (`0.9` proven / `0.65` likely / `0.35` heuristic) |
| `--limit <n>` | número máximo de grupos de reglas mostrados en la terminal (por defecto `10`) — el detalle completo siempre va a `--output` |
| `--debug` | imprime cada archivo escaneado y qué reglas se ejecutaron |

**Escanea antes de instalar — sin clonar, sin configuración:**```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 y mcp descargan el objetivo y lo escanean, y luego eliminan la copia descargada (--keep para inspeccionarla en su lugar). Nada de lo descargado se ejecuta jamás: un objetivo npm se descarga con npm pack — solo el tarball, sin install, sin scripts de ciclo de vida — y un objetivo git es un simple git clone --depth 1. Este es el momento que más importa: antes de que una skill llegue a ~/.claude/skills/ o un servidor llegue a .mcp.json, no después.

Otros comandos:```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

Suprimir un hallazgo revisado en el código:```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

Los hallazgos aparecen como anotaciones en línea en el PR y en la pestaña Seguridad del repositorio. (`secureai-scan init` genera un flujo de trabajo equivalente usando la CLI directamente).

¿Escaneo limpio? Añade la insignia a tu propio README:```md
[![secureai-scan](https://img.shields.io/badge/secureai--scan-passing-brightgreen)](https://github.com/akanthed/SecureAI-Scan)

Reglas

39 reglas, asignadas al OWASP Top 10 oficial para aplicaciones LLM (2026) — más, cuando corresponde, el OWASP Top 10 para aplicaciones agénticas (2026, ASI), el OWASP MCP Top 10 (2025) y un artículo de la Ley de IA de la UE. Consulta la cobertura y límites versionados de 2026; threat-model renderiza la matriz para cada proyecto escaneado.

RuleWhat it provesOWASP
AI001La entrada del usuario fluye hacia un prompt de sistema/desarrollador (origen trazado → destino, incluso a través de los límites de función/archivo)LLM01
AI002Contenido del prompt o secretos escritos en logs (en archivos que usan un SDK de LLM)LLM02
AI003Llamada LLM en un manejador de peticiones sin comprobación de autenticación previaLLM06
AI004Objeto completo de usuario/sesión serializado en un prompt (la selección de campos no se marca)LLM02
AI005La salida del LLM llega a destinos eval/exec/SQL/HTMLLLM10
AI006Herramientas de alto impacto (delete, pay, deploy, ...) expuestas sin compuerta de aprobaciónLLM03
AI007Contenido RAG recuperado interpolado en prompts privilegiadosLLM01
AI008Secretos incrustados en el texto del prompt de sistemaLLM08
AI009Entrada de usuario sin límites / límites de tokens ausentesLLM06
AI010Contenido externo obtenido fluye hacia promptsLLM01
AI011Salida del agente elevada a rol de sistema en llamadas posterioresLLM03
AI012Salida del LLM analizada sin validación de esquemaLLM10
MCP001Los metadatos de la herramienta MCP llegan al prompt de sistema sin validaciónLLM01
MCP002URL del servidor MCP construida a partir de la entrada del usuarioLLM04
MCP003Resultados de herramientas MCP elevados a rol de sistemaLLM10
MCP004Servidor MCP lanzado como paquete npx -y sin fijarLLM04
MCP005Secreto incrustado en una configuración MCP confirmadaLLM02
MCP006Servidor MCP sobre HTTP en texto planoLLM04
MCP007Unicode invisible/bidi oculto en nombres o descripciones de herramientas MCPLLM01 · MCP03
MCP008Frases de inyección dirigidas a agentes en descripciones de herramientas MCPLLM01 · MCP03
MCP009Una descripción de herramienta que dirige llamadas a otra herramienta (shadowing)LLM01 · MCP03
MCP010Comando/argumentos del servidor MCP stdio construidos a partir de la entrada del usuario (RCE)LLM04 · MCP05
SKL001Unicode invisible/bidi en cualquier parte de un paquete de Agent SkillLLM01
SKL002Frases de inyección dirigidas a agentes en la descripción o el cuerpo de una skill (detectadas incluso con ofuscación)LLM01
SKL003El contenido de una skill dirige cuándo/cómo se usa otra skill (shadowing)LLM01
SKL004Payload por etapas/autoextraíble: blob opaco + instrucciones para decodificarlo y ejecutarloLLM04 · MCP04
SKL005Lectura de credenciales + egreso externo hardcodeado en un archivo complementario del paqueteLLM02 · MCP04
SKL006Ejecución de comandos en tiempo de carga mediante la sintaxis de inyección de contexto dinámico de Claude Code (!`cmd`/```!), antes de cualquier compuerta de permisos de herramientasLLM04 · MCP05
SKL007Concesión Bash sin ámbito en el frontmatter allowed-tools de una skillLLM03
SKL008La skill obtiene instrucciones de una URL externa y dirige al agente a seguirlas ("Circus of Skills")LLM04
SKL009La skill persiste una puerta trasera escribiendo en otro archivo de contexto (MEMORY.md/SOUL.md/AGENTS.md/CLAUDE.md)LLM05
SKL010Etiqueta de deserialización YAML/JSON insegura en el frontmatter de una skill o en un archivo de configuración incluido en el paqueteLLM04
VEC001Búsqueda vectorial sin filtro de tenant/usuarioLLM09
VEC002Límite de búsqueda sin acotar o controlado por el usuarioLLM06
VEC003Contenido de usuario ingerido en un almacén vectorial compartidoLLM05
VEC004Ingesta sin etiquetado de tenant/namespaceLLM09
DEP001Nombre de dependencia no encontrado en el registro (opt-in --check-dependencies)LLM04
DEP002Nombre de dependencia a una edición de distancia de un paquete popular (opt-in)LLM04
DEP003Dependencia con una versión maliciosa documentada o CVE crítico — comprobada sin conexión en cada escaneo, que tiene en cuenta el rango de versiones (postmark-mcp, mcp-remote CVE-2025-6514, ...)LLM04 · MCP04

secureai-scan explain <RULE_ID> proporciona el recorrido del exploit y un ejemplo de código antes/después para cualquier regla.

Arquitectura

Tres superficies de escaneo independientes alimentan una única lista de hallazgos combinada y deduplicada:``` ┌─────────────────────┐ *.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)

Cada regla AST solo considera una función como una "LLM call" si se resuelve a través de imports reales a un SDK conocido — nunca solo por coincidencia de nombres. Consulta [`docs/Architecture.md`](https://github.com/akanthed/secureai-scan/blob/HEAD/docs/Architecture.md) para el desglose completo de cada superficie, y [`docs/DetectionEngine.md`](https://github.com/akanthed/secureai-scan/blob/HEAD/docs/DetectionEngine.md) para saber cómo funciona el contrato de niveles de evidencia.

## Servidor MCP (úsalo desde Claude)

El paquete incluye un servidor MCP que expone `scan_repository`, `explain_rule`, `generate_bom` y `scan_untrusted_target` (obtiene y escanea un skill o servidor MCP antes de que Claude recomiende instalarlo — el mismo comportamiento de descarga sin ejecución que los comandos CLI `skill`/`mcp`):```json
{
  "mcpServers": {
    "secureai-scan": {
      "command": "node",
      "args": ["/path/to/secureai-scan/mcp-server/index.js"]
    }
  }
}

Claude Skill

Para usuarios de Claude Code / Claude.ai, skills/secureai-scan/SKILL.md enseña a Claude cuándo ejecutar un escaneo (al revisar código de IA/LLM, o al verificar un servidor MCP/Skill de Agente antes de instalarlo) y cómo leer los resultados — sin necesidad de un proceso separado, a diferencia del servidor MCP mencionado arriba. Copia el directorio skills/secureai-scan/ en tu .claude/skills/ para usarlo.

Resistencia a la evasión

En julio de 2026, dos investigaciones públicas demostraron que los escáneres de Skills de Agente se eluden trivialmente.

Cloak and Detonate (Ji, Xu, Li, Gao, Wei, Wang y Cheung — HKUST, arXiv:2607.02357) realizó un estudio adversarial contra nueve escáneres de skills publicados, usando 1.613 skills maliciosos reales. Dos familias de ataques, ambas preservando la carga útil exactamente:

  • Reify — reescribe los bytes que coinciden con una regla, mientras el agente vuelve a ensamblar el original en tiempo de ejecución. >80 % de evasión en la mayoría de los escáneres estáticos.
  • Disperse / empaquetado SFS — mueve la carga útil fuera del alcance de la regla: a un directorio que el escáner omite, detrás de una extensión renombrada, o archivada y codificada dentro de .git/, con un SKILL.md de cobertura benigno que la reconstruye en la primera ejecución. ≥90 % de evasión en todos los escáneres probados, ≥96 % en la mayoría. La tasa de detección de un escáner híbrido cayó de ~99 % a ~10 %.

Por separado, Gecko Security (VentureBeat) distribuyó una carga útil en un archivo *.test.ts. Todos los escáneres públicos la omitieron — pero Jest y Vitest autodetectan y ejecutan esos archivos en el siguiente npm test.

SecureAI-Scan maneja cada técnica publicada. Cada fila a continuación tiene una prueba correspondiente en test/deobfuscate.test.js o un fixture en test-fixtures/vulnerable/skills/:

Técnica (nombre del artículo)EjemploCómo se maneja
Inserción de ancho ceroIgn‍ore all pre‍vious…El texto se compara contra variantes sin caracteres invisibles
Glifos confundiblesсurl (с cirílico)Normalización de homoglifos a ASCII antes de la comparación
Cambio de mayúsculasEvAlTodos los patrones de contenido distinguen entre mayúsculas y minúsculas (case-insensitive)
Empalme de variables'cu' + 'rl'Literales de cadena adyacentes unidos antes de la comparación
División por líneaseval(x)Saltos de línea dentro de una palabra unidos como variante adicional
Extracción por archivocarga útil en un helper con origenSe escanea todo el paquete, no solo SKILL.md
Reubicación de directorioscripts/x.shbuild/x.shSin lista de exclusión de directorios dentro de un paquete
Renombrado de extensiónx.shx.txtLos archivos se escanean por contenido, no por extensión
Relleno de tamañorellenar más allá del límite del escánerLos archivos demasiado grandes se leen desde el inicio, nunca se omiten
Empaquetado SFScarga útil en .git/skillpack.datCualquier archivo no git bajo .git/ de un paquete es proven (SKL004)
Preparación de archivos de pruebacarga útil en *.test.tsEl escaneo de paquetes deliberadamente no degrada las rutas de prueba (SKL005)

Esto no debilita el contrato de precisión

La ofuscación suele ser un riesgo para la precisión: más coincidencias, más ruido. Aquí la lógica está invertida: una coincidencia que aparece solo después de la ofuscación se promueve a proven, no se degrada. La documentación ordinaria no contiene un jointe de ancho cero dentro de "ignore previous instructions", ni una с cirílica dentro de curl. El ocultamiento es en sí mismo evidencia afirmativa de intención.

La comparación se hace contra el conjunto de coincidencias en bruto, no meramente "si el texto en bruto coincidió en absoluto" — de lo contrario un atacante podría enmascarar la señal dejando una frase inocua en claro.

Las dos nuevas reglas de paquete se activan solo con conjunciones, nunca con una palabra clave:

  • SKL004 necesita un blob opaco y una directiva de desempaquetado que haga referencia a ese blob por nombre — un README que mencione tar -x junto a un activo binario no relacionado no es suficiente. Los archivos reales (gzip/zip/png/pdf/wasm — verificados por bytes mágicos, no por extensión) nunca son "opacos" en primer lugar, sin importar cómo estén comprimidos.
  • SKL005 necesita una señal concreta de credencial — una ruta (~/.aws/credentials, no la palabra "token") o enumeración masiva de variables de entorno (os.environ.items(), no os.environ["API_KEY"]) — y salida a un host codificado no local, dentro de 25 líneas entre sí en el mismo archivo, o una recuperación remota que se ejecuta después de ser reasignada a través de uno o más renombres. Un helper de publicación que lee ~/.npmrc en una función y llama a un registro cuarenta líneas después permanece limpio, y leer una variable de entorno nombrada para una llamada API nunca se marca — ambas formas están fijadas como fixtures seguros.

Validado contra dos corpus del mundo real, no solo fixtures que escribimos nosotros mismos: 0 hallazgos en los 18 paquetes de skills reales en anthropics/skills y los 14 en vercel/ai, y 6/6 correctos en el corpus de evaluación etiquetado de cisco-ai-defense/skill-scanner (20 skills, cada uno con un veredicto _expected.json) con cero falsos positivos en cualquier cosa etiquetada como segura. Ver Pruebas y evaluación comparativa.

Lo que esto no es

Limitación honesta: la conclusión del artículo es que la detonación en tiempo de ejecución supera al análisis estático, y eso es correcto. Un adversario adaptativo que conozca estas reglas puede escribir una transformación que no cubran. Lo que cambia aquí es el costo de la evasión — las técnicas publicadas y actualmente en circulación ya no funcionan, y la ofuscación necesaria para derrotarlas ahora eleva la gravedad del hallazgo. El escaneo estático es un filtro, no un límite de seguridad. Trata un skill no confiable como código no confiable, sin importar lo que diga cualquier escáner.

Garantía de confianza y publicación

  • CI se ejecuta en Linux, Windows y macOS en las versiones de Node compatibles.
  • CodeQL, auditoría de dependencias de producción, OpenSSF Scorecard, Dependabot y el propio escaneo bloqueante de este escáner proporcionan verificaciones independientes.
  • Cada publicación manual de npm invoca pruebas, suelos de cobertura, la compuerta de regresión de repositorios reales revisada y la inspección del tarball mediante prepublishOnly.
  • GitHub Actions no recibe contraseña ni token de npm y no puede publicar el paquete.
  • Garantía de publicación, gobernanza de un solo mantenedor, reporte de seguridad y evidencia de evaluación comparativa versionada son públicos.

Este es un proyecto de un solo mantenedor sin SLA contractual ni certificación independiente. Los controles anteriores reducen el riesgo; no convierten un escaneo estático en prueba de seguridad.

El contrato de precisión

Los falsos positivos matan a los escáneres. El motor de reglas de SecureAI-Scan sigue tres reglas estrictas:

  1. Los sinks se resuelven mediante imports. Si un identificador se resuelve a un módulo que no es un SDK de LLM, definitivamente no es una llamada a LLM — sin importar cómo se llame.
  2. La evidencia se etiqueta, nunca se mezcla. Un flujo de datos trazado y una coincidencia por proximidad de palabras no son lo mismo, por lo que nunca comparten un nivel.
  3. El corpus seguro controla cada lanzamiento. test-fixtures/safe/ contiene los patrones que solían causar falsos positivos (cargas útiles de PII redactadas, clientes de Google Maps, claves de API por variables de entorno junto a clientes de LLM, registro de respuestas ordinario, campos de metadatos de OAuth, chunks de respuestas de streaming, texto de prompt de ficción/narrativa). Cualquier hallazgo allí hace fallar la suite.

Pruebas y evaluación comparativa

Tres capas, porque una sola no es suficiente para confiar en las afirmaciones de un escáner — la precisión y el recuerdo son modos de fallo diferentes, y ambos se verifican.

1. Corpus de fixtures — precisión + recuerdo, se ejecuta en cada build.```bash npm test

[`test-fixtures/vulnerable/`](https://github.com/akanthed/secureai-scan/blob/HEAD/test-fixtures/vulnerable) y [`test-fixtures/safe/`](https://github.com/akanthed/secureai-scan/blob/HEAD/test-fixtures/safe) se escanean juntos: cada fixture vulnerable debe disparar su regla esperada con evidencia `proven`/`likely` (recall), cada fixture seguro debe producir **cero** hallazgos `proven`/`likely` (precisión). Rápido y determinista — pero solo demuestra que el escáner se comporta en código escrito específicamente para probarlo.

**2. Benchmark de regresión en el mundo real — contra repositorios públicos que no escribimos nosotros.**```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 conjunto curado y diverso de repos públicos reales (los SDK de IA de OpenAI/Anthropic/Vercel, los servidores MCP oficiales y el SDK de TypeScript, LlamaIndex, además de anthropics/skills y cisco-ai-defense/skill-scanner para cobertura de paquetes de skills — abarcando TS y Python, código de ejemplo de consumidores de SDK y código fuente de autores de SDK) y escanea cada uno con la CLI compilada.

Finaliza con código de salida distinto de cero ante cualquier hallazgo proven/likely que no esté ya en test/regression-baseline.json — un registro revisado manualmente de hallazgos ya verificados contra su línea de origen. Las huellas digitales son repo|rule|file, no números de línea, por lo que el vaivén ordinario de los repos upstream no genera ruido. Una huella nueva es una afirmación que el escáner debe justificar: si no es un problema genuino, es un error de regla, corregido en la causa raíz y fijado como un nuevo fixture de test-fixtures/safe/. Incluir en la línea base un hallazgo que no has leído anula todo el mecanismo.

La cobertura de paquetes de skills tiene su propia línea porque el corpus evals/ de cisco-ai-defense/skill-scanner está etiquetado — cada uno de sus 20 fixtures incluye un veredicto _expected.json y reside bajo un directorio literalmente llamado malicious/ o safe/, por lo que funciona también como comprobación de recall, no solo de precisión: 6/6 fixtures maliciosos dentro del alcance disparan, 0 hallazgos en cualquier cosa etiquetada como safe, y 0 hallazgos en los 18 paquetes reales de anthropics/skills y los 14 de vercel/ai. (Las categorías restantes de Cisco — inyección SQL, path traversal, agotamiento de recursos, eval() genérico de un argumento de función, un payload deliberadamente dividido en cuatro archivos — quedan fuera del alcance documentado de LLM/MCP/RAG o más allá del análisis de conjunción dentro del mismo archivo; consulta la entrada del changelog 0.6.0 para el razonamiento específico de cada una.)

Antes/después histórico de la ejecución que impulsó las correcciones de precisión originales (hallazgos en el nivel de evidencia predeterminado, sin --paranoid):

RepoAntesDespuésQué fallaba
vercel/ai7731examples/, los tests/ de nivel superior y los directorios con guiones tipo ecosystem-tests/ no se reconocían como rutas de menor confianza; chunks (una variable común de respuestas en streaming) se trataba como evidencia inequívoca de RAG
openai/openai-node470La misma brecha en la detección de rutas, aplicada a los examples//ecosystem-tests/ propios del SDK
anthropics/anthropic-sdk-typescript20La misma brecha en la detección de rutas en un directorio tests/ de nivel superior
modelcontextprotocol/typescript-sdk30Campos de metadatos OAuth tipo token_endpoint/tokenType marcados como secretos filtrados
run-llama/llama_index1815Una comprobación de Python marcaba cualquier campo description= que contuviera "system prompt" como envenenamiento de herramientas MCP proven, independientemente del contexto. Los 15 restantes son aciertos VEC001 sobre las definiciones genéricas de retrievers de la propia librería — se escanea el propio código fuente de un SDK de base de datos vectorial, no código de aplicación, por lo que no puede existir un filtro que comprobar; un límite honesto e inherente, no un error

Ejecución actual (2026-08-06) — la evidencia versionada está registrada en docs/benchmarks/v0.9.0.json:

RepoHallazgosReglasEstado
openai-node, anthropic-sdk-typescript, anthropic-sdk-python, modelcontextprotocol/typescript-sdk, modelcontextprotocol/servers0limpio
anthropics/skills (18 paquetes de skills reales)0limpio — comprobación de precisión pura para SKL001–005
vercel/ai (5,691 archivos)0eran 40 (AI001, AI003, AI005, AI010, MCP002) antes del triaje — cada uno revisado manualmente contra el código fuente y confirmado como falso positivo, rastreado hasta 3 bugs independientes de causa raíz (ver más abajo), corregidos y reconfirmados como limpios en un re-escaneo completo
run-llama/llama_index46VEC001límite inherente, no un error — las definiciones genéricas de retrievers de la propia librería, donde no puede existir ningún filtro de tenant
cisco-ai-defense/skill-scanner7SKL001, SKL002, SKL005todos sobre fixtures etiquetados como malicious/ — 6/6 dentro del alcance, 0 sobre cualquier cosa etiquetada como safe/

El triaje de vercel/ai encontró tres bugs reales con causa raíz identificada — ninguno específico de las reglas de skills de v0.6.0, todos en lógica compartida usada por muchas reglas:

  1. resolveLlmSink trataba cualquier llamada resuelta a un módulo de SDK de LLM como una invocación de modelo, sin importar el nombre del método — marcando isToolUIPart (un type guard que el paquete ai exporta justo al lado de generateText) como una llamada a LLM. Esto solo causó 3 de los 5 grupos de hallazgos (AI001, AI003, AI010).
  2. DANGEROUS_CALLEES en AI005 incluye "query" para sinks de tipo inyección SQL, pero "query" también es un verbo legítimo de invocación de LLM/agenteclaudeSdk.query({ prompt, options }), la llamada al modelo propia del Claude Agent SDK, se marcaba como "salida de LLM pasada a un sink peligroso" únicamente por el nombre compartido del método.
  3. REQUEST_SOURCES (duplicada idénticamente en MCP002, MCP010, VEC003) coincidía con un "params." desnudo — cualquier parámetro de función convencionalmente llamado params, no necesariamente datos de solicitud HTTP. Un validador de esquemas de URL (assertOpenLinkParams(params: unknown)) se marcó como "URL de servidor MCP proveniente de entrada de usuario".

Los tres se corrigieron en la causa raíz (no en el sitio de llamada específico) y quedaron fijados como fixtures permanentes bajo test-fixtures/. Detalles completos en CHANGELOG.md.

3. Validación vulnerable-vs-parcheado — demuestra recall, no solo precisión.

Las dos capas anteriores solo comprueban que el escáner permanezca en silencio sobre código seguro. Las comprobaciones de advisories de DEP003 se validan al revés: fija un paquete a una versión documentada como vulnerable y confirma que se marca; luego fíjalo a la versión parcheada y confirma que no se marca.```bash node --test test/dependency-guard.test.js

cubre: `[email protected]` (CVE-2025-6514, vulnerable) marcado / `[email protected]` (parcheado) limpio; `[email protected]` (antes de la puerta trasera) limpio / `[email protected]` (después — no existe un parche legítimo para un paquete malicioso) sigue marcado; `llama-cpp-python==0.2.71` (CVE-2024-34359, del conjunto generado por OSV) marcado / `==0.2.72` (parcheado) limpio, incluso con la normalización de nombres de PyPI (`llama_cpp_python`); y especificadores sin fijar al estilo `langchain>=0.1.0` que producen **cero** hallazgos en el informe predeterminado. Construir esta prueba reveló una laguna real: `DEP003` solía emparejar avisos solo por nombre de paquete, sin comparar nunca la versión declarada contra el rango afectado del aviso — corregido en [`src/scanner/semver.ts`](https://github.com/akanthed/secureai-scan/blob/HEAD/src/scanner/semver.ts).

La ambigüedad se resuelve deliberadamente de forma diferente según el tipo de aviso. Un paquete **malicioso** se dispara incluso cuando la versión declarada no se puede resolver — instalar una puerta trasera no se puede revertir, por lo que se decanta por marcarlo. Una **CVE** se dispara en `proven` solo cuando la versión declarada es un pin exacto demostrablemente dentro del rango afectado; la no fijada pero posiblemente afectada cae a `heuristic` (solo con `--paranoid`). Aplicar la regla del tipo malicioso a una instantánea de CVE de 162 entradas pondría un hallazgo crítico en cada repositorio que declare `langchain>=0.1.0` — ruido inaccionable a escala.

## Hoja de ruta

Consulta [`ROADMAP.md`](https://github.com/akanthed/secureai-scan/blob/HEAD/ROADMAP.md) para ver lo que ya se ha publicado y lo que está planificado. Ambos motores de lenguaje están basados en AST: ts-morph para TypeScript/JavaScript y Tree-sitter para Python. Las importaciones, llamadas, asignaciones, decoradores, ámbitos, argumentos de palabra clave, campos de diccionario y cadenas de Python son nodos de sintaxis; el código objetivo nunca se importa ni se ejecuta, y no se requiere ningún intérprete de Python. La laguna restante de Python es la profundidad de taint acotada entre funciones/archivos, no el parseo. El rendimiento del escaneo y los límites conocidos se documentan en [`docs/Performance.md`](https://github.com/akanthed/secureai-scan/blob/HEAD/docs/Performance.md).

## Contribuciones

Las contribuciones son bienvenidas — consulta [`CONTRIBUTING.md`](https://github.com/akanthed/secureai-scan/blob/HEAD/CONTRIBUTING.md) para conocer el flujo de trabajo, y [`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) para saber cómo añadir una regla de detección que alcance el listón de precisión anterior. Cada nueva regla necesita un fixture tanto en [`test-fixtures/vulnerable/`](https://github.com/akanthed/secureai-scan/blob/HEAD/test-fixtures/vulnerable) como en [`test-fixtures/safe/`](https://github.com/akanthed/secureai-scan/blob/HEAD/test-fixtures/safe), una entrada en `src/scanner/catalog.ts` y un caso en `test/corpus.test.js` — `npm test` exige los tres.

## Licencia

MIT © Akshay Kanthed

Categorías