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

CLI offline que escanea TypeScript, JavaScript y Python en busca de riesgos de LLM, MCP, Agent Skill y RAG: evidencia de flujo de datos resuelta por imports, cero falsos positivos por defecto, mapeado a OWASP LLM/ASI/MCP Top 10.

La mayoría de los escáneres en este ámbito buscan una palabra clave y la llaman hallazgo. SecureAI-Scan rastrea la ruta real de origen → flujo → sumidero a través de código real resuelto por imports, y un escaneo por defecto solo te muestra aquello que puede demostrar. Sin cuenta, sin carga en la nube, nada sale de tu máquina.

Cubre el OWASP Top 10 oficial para aplicaciones LLM 2026, el Top 10 para aplicaciones agénticas (2026) y el MCP Top 10 desde la semana de lanzamiento.

Empieza 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 de MCP y paquetes de Agent Skill se detectan automáticamente.

**Candidato de lanzamiento `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/main/docs/benchmarks/v0.9.0.json) · [metodología y límites](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.

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

Nuevo: escaneo estático de configuración para LiteLLM Proxy (config.yaml): secretos hardcodeados, endpoints de proveedor en texto plano, guardarraíles ausentes. Consulta Reglas (LLC001–LLC003).

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 son opcionales mediante --paranoid.
  • Detección resuelta por importaciones. Una llamada solo es una "llamada a LLM" si se resuelve a una importación real del 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 evaluado contra repositorios reales. El conjunto de pruebas afirma que cada fixture vulnerable se dispara y que cada fixture seguro permanece limpio: un falso positivo en el corpus seguro hace fallar la compilación. Más allá de eso, npm run regression escanea repositorios públicos reales (OpenAI/Anthropic/Vercel AI SDKs, servidores MCP oficiales, LlamaIndex) contra una línea base confirmada y revisada manualmente, y falla ante cualquier hallazgo nuevo proven/likely. Consulta Pruebas y evaluación comparativa para ver los números reales de antes/después, o Lo que encontramos escaneando 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 a llama_index "vulnerable" por un hallazgo honesto a nivel de biblioteca. Escrito de discusión →
  • SARIF para code scanning 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 . construye un inventario derivado de la sintaxis de SDKs, IDs de modelos, almacenes vectoriales, 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 versión, secretos en línea, transportes HTTP en texto plano.
  • Detección de envenenamiento de herramientas MCP. Detecta el patrón detrás del rug-pull de WhatsApp MCP y del backdoor de postmark-mcp: Unicode invisible, frases de inyección dirigidas al agente y sombreado entre herramientas en nombres/descripciones, de forma estática, antes de que ejecutes el servidor.
  • Detección de inyección de comandos en 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 en MCP STDIO de 2026.
  • Detección de envenenamiento de Agent Skills. Las mismas comprobaciones de Unicode invisible, frases de inyección y sombreado aplicadas a archivos SKILL.md: las Agent Skills se cargan en el contexto por completo, así que una skill envenenada es una descripción de herramienta envenenada con otro nombre.
  • Escaneo de skills resistente a evasión. Los paquetes de skills se escanean como directorios, no solo su SKILL.md, y cada comprobación de contenido se ejecuta contra variantes desofuscadas del texto. Esto apunta a las técnicas publicadas — homoglifos, división con cero ancho, cargas útiles almacenadas en .git/ o build/, exfiltración oculta en un archivo *.test.ts — que eludieron a >90% de los nueve escáneres encuestados en Cloak and Detonate (arXiv:2607.02357). Consulta Resistencia a evasión.
  • Avisos de paquetes conocidos como vulnerables y maliciosos, conscientes de la versión. Comprueba cada dependencia y cada paquete lanzado por MCP contra una instantánea de avisos incluida: una lista curada a mano de backdoors documentados 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 bandera. Un CVE solo se dispara cuando tu versión fijada está demostrablemente dentro del rango afectado; un paquete documentado como malicioso se dispara incluso con un rango ambiguo, porque instalar un backdoor es irrecuperable.
  • Local primero. 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á construido a propósito para la superficie de ataque LLM/MCP/RAG y enfatiza la evidencia de flujo de datos sobre hallazgos planos de palabras clave.

SecureAI-ScanSemgrep (reglas OSS)TrivyGitHub Advanced Security
Inyección de prompts (origen→sumidero trazado)✅ flujo de datos resuelto por importaciones⚠️ solo reglas de patrón, mantenidas por la comunidad⚠️ CodeQL puede, pero sin conjunto de reglas específico 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 evasión, consciente de paquetes
Mala configuración de RAG / almacén vectorial✅ VEC001–004
Avisos de paquetes de IA conocidos como maliciosos✅ DEP003, offline, consciente de versión⚠️ feed CVE general, no específico de IA⚠️ Dependabot, feed CVE general
SAST general (SQLi, XSS, path traversal)❌ fuera de alcance por diseño
Escaneo de contenedores / IaC⚠️ mediante CodeQL/Actions
Niveles de evidencia (proven/likely/heuristic)❌ los hallazgos son planos⚠️ CodeQL tiene algunos, no ajustados a IA
Salida SARIF (code scanning de GitHub)nativa
Se ejecuta 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 Asesor de Seguridad de IA de SecureAI-Scan en ChatGPT gratis.

¿A punto de ejecutar un servidor MCP que encontraste en GitHub o Twitter? Pega primero su descripción de herramientas en MCP X-Ray — comprueba Unicode oculto, instrucciones inyectadas y paquetes conocidos como maliciosos en tu navegador, sin instalación.

Míralo funcionar

secureai-scan scan . de principio a fin, salida real contra un archivo real (pequeño, deliberadamente vulnerable) — fuente:

Grabación de terminal de secureai-scan scan . encontrando una vulnerabilidad de inyección de prompts trazada

Formas de ataque que el escáner traza de principio a fin:

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 necesites. `secureai-scan scan . --help` muestra todo esto en la terminal, agrupado de la misma manera:

**Uso diario**

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

**Alcance de qué reglas se ejecutan**

| Indicador | Qué hace |
|------|---------------|
| `-r, --rules <lista>` | ejecuta solo estos ID de regla, p. ej. `AI001,MCP007` |
| `--only-ai` / `--only-mcp` / `--only-vec` / `--only-skl` | ejecuta solo una categoría de reglas |
| `--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 con `-r` — nunca necesitas recordar pasar ambos. No es necesario para `DEP003` (paquetes maliciosos conocidos), que siempre se ejecuta sin conexión |

**CI / flujo de trabajo**

| Indicador | Qué hace |
|------|---------------|
| `--fail-on <severidad>` | sale con `1` si existen hallazgos en/por encima de esta severidad |
| `--baseline <archivo>` | rastrea solo problemas nuevos/cambiados contra una línea base guardada |
| `--policy <archivo>` | 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**

| Indicador | 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>` | máximo de grupos de reglas mostrados en la terminal (predeterminado `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 configurar:**```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 obtienen el objetivo y lo escanean, y luego eliminan la copia descargada (--keep para inspeccionarla en su lugar). Nada de lo obtenido 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 — example: docs/examples/THREAT_MODEL.example.md secureai-scan init # policy file + CI workflow, one-time setup

Suprime un hallazgo revisado en el código:```ts
// secureai-ignore AI001: reviewed, input sanitized via allowlist

Acción de GitHub```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

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

¿El escaneo está 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)

Gancho de pre-commit

¿Prefieres detectar hallazgos antes de que se suban? Añade este repositorio como fuente de gancho de pre-commit en lugar de, o junto a, la GitHub Action:```yaml repos:

El hook escanea todo el proyecto en cada commit (no solo los archivos modificados — un rastreo de flujo de datos hacia el archivo A puede depender del archivo B, que un escaneo parcial pasaría por alto) y bloquea el commit en hallazgos de severidad `high`+ por defecto. Anula el umbral en tu propia configuración:```yaml
      - id: secureai-scan
        args: ["--fail-on", "critical"]

Reglas

42 reglas, mapeadas al OWASP Top 10 oficial para Aplicaciones LLM (2026) — además, cuando corresponde, al OWASP Top 10 para Aplicaciones Agénticas (2026, ASI), al OWASP MCP Top 10 (2025) y a un artículo de la Ley de IA de la UE. Consulta la cobertura y límites de la versión 2026; threat-model genera la matriz para cada proyecto escaneado.

ReglaQué demuestraOWASP
AI001La entrada del usuario fluye hacia un prompt de sistema/desarrollador (fuente rastreada → sumidero, incluso a través de límites de función/archivo)LLM01
AI002El contenido del prompt o los secretos se escriben en registros (en archivos que usan un SDK de LLM)LLM02
AI003Llamada a LLM en un manejador de solicitudes sin verificación de autenticación antesLLM06
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 sumideros de eval/exec/SQL/HTMLLLM10
AI006Herramientas de alto impacto (eliminar, pagar, desplegar, …) expuestas sin una puerta 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 faltantesLLM06
AI010Contenido externo obtenido fluye hacia los promptsLLM01
AI011Salida del agente elevada a rol de sistema en llamadas posterioresLLM03
AI012Salida del LLM analizada sin validación de esquemaLLM10
MCP001Los metadatos de herramientas 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 fijar versiónLLM04
MCP005Secreto insertado 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 por el agente en descripciones de herramientas MCPLLM01 · MCP03
MCP009Una descripción de herramienta que desvía llamadas a una herramienta diferente (sombreado)LLM01 · MCP03
MCP010Comando/argumentos del servidor MCP stdio construidos a partir de la entrada del usuario (RCE)LLM04 · MCP05
SKL001Unicode invisible/bidi en cualquier lugar de un paquete de habilidades de agenteLLM01
SKL002Fraseo de inyección dirigido por el agente en la descripción o el cuerpo de una habilidad (coincidencia mediante ofuscación)LLM01
SKL003El contenido de una habilidad dirige cuándo/cómo se usa una habilidad diferente (sombreado)LLM01
SKL004Carga útil por etapas/autoextraíble: blob opaco + instrucciones para decodificarlo y ejecutarloLLM04 · MCP04
SKL005Lectura de credenciales + salida externa codificada 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 puerta de permisos de herramientasLLM04 · MCP05
SKL007Concesión de Bash sin ámbito en el frontmatter de allowed-tools de una habilidadLLM03
SKL008La habilidad obtiene instrucciones de una URL externa y dirige al agente a seguirlas ("Circus of Skills")LLM04
SKL009La habilidad persiste una puerta trasera escribiendo en otro archivo de contexto (MEMORY.md/SOUL.md/AGENTS.md/CLAUDE.md)LLM05
SKL010Etiqueta insegura de deserialización YAML/JSON en el frontmatter de una habilidad o en un archivo de configuración incluidoLLM04
VEC001Búsqueda vectorial sin filtro de inquilino/usuarioLLM09
VEC002Límite de búsqueda sin límites o controlado por el usuarioLLM06
VEC003Contenido del usuario ingerido en un almacén vectorial compartidoLLM05
VEC004Ingestión sin etiquetado de inquilino/espacio de nombresLLM09
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 — verificado sin conexión en cada escaneo, consciente del rango de versiones (postmark-mcp, mcp-remote CVE-2025-6514, …)LLM04 · MCP04
LLC001Secreto codificado en un config.yaml del proxy LiteLLMLLM02
LLC002api_base del proxy LiteLLM accesible sobre HTTP en texto planoLLM04
LLC003La configuración del proxy LiteLLM no tiene sección guardrails: (heurístico, solo --paranoid)LLM03

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 fusionada 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 llama a una función "llamada LLM" si se resuelve mediante importaciones reales a un SDK conocido — nunca solo por coincidencia de nombres. Consulta [`docs/Architecture.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/Architecture.md) para el desglose completo de cada superficie, y [`docs/DetectionEngine.md`](https://github.com/akanthed/secureai-scan/blob/main/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 una skill o servidor MCP antes de que Claude recomiende instalarlo — mismo comportamiento de obtener-sin-ejecutar 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 comprobar 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 anterior. 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 & Cheung — HKUST, arXiv:2607.02357) realizó un estudio adversarial contra nueve escáneres de skills publicados usando 1.613 skills maliciosas reales. Dos familias de ataques, ambas preservando el payload exactamente:

  • Reify — reescribe los bytes que coinciden con una regla, mientras el agente sigue reensamblando 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 el payload fuera del alcance de la regla: a un directorio que el escáner omite, detrás de una extensión renombrada, o archivado y codificado en .git/, con un SKILL.md de cobertura benigno que lo 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ó un payload en un archivo *.test.ts. Todos los escáneres públicos lo omitieron — pero Jest y Vitest auto-descubren 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 paper)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)Plegado de homoglifos a ASCII antes de la comparación
Cambio de mayúsculasEvAlTodos los patrones de contenido distinguen mayúsculas de minúsculas
Empalme de variables'cu' + 'rl'Literales de cadena adyacentes se unen antes de la comparación
División por líneaseval(x)Saltos de línea dentro de una palabra se unen como variante adicional
Extracción por archivopayload en un helper importadoSe 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 sobredimensionados se leen por cabecera, nunca se omiten
Empaquetado SFSpayload en .git/skillpack.datCualquier archivo no git bajo el .git/ de un paquete se marca como proven (SKL004)
Preparación de archivos de pruebapayload 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 de precisión — más coincidencias, más ruido. Aquí la lógica está invertida: una coincidencia que aparece solo tras la ofuscación se promueve a proven, no se degrada. La documentación ordinaria no contiene un conector 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 brutas, no meramente "si el texto 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 paquetes se activan solo con conjunciones, nunca con una palabra clave:

  • SKL004 requiere un blob opaco y una directiva de desempaquetado que referencie 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 requiere 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 egreso a un host no local codificado, dentro de 25 líneas entre sí en el mismo archivo, o una descarga remota que se ejecuta tras ser reasignada mediante uno o más renombrados. 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 escritos por nosotros: 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 una con un veredicto _expected.json) con cero falsos positivos en cualquier cosa etiquetada como segura. Ver Pruebas y benchmarking.

Qué no es esto

Limitación honesta: la conclusión del paper 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 coste 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 por sí misma la gravedad del hallazgo. El escaneo estático es un filtro, no un límite de seguridad. Trata una skill no confiable como código no confiable, sin importar lo que diga cualquier escáner.

Confianza y garantía de 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 auto-escaneo bloqueante de este propio escáner proporcionan verificaciones independientes.
  • Cada publicación manual de npm invoca pruebas, mínimos de cobertura, la puerta de regresión revisada del repositorio real 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 mantenedor único, reporte de seguridad y evidencia de benchmark versionada son públicos.

Este es un proyecto de mantenedor único 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 rastreado y una coincidencia por proximidad de palabras no son lo mismo, por lo que nunca comparten nivel.
  3. El corpus seguro controla cada publicación. test-fixtures/safe/ contiene los patrones que solían causar falsos positivos (payloads de PII redactados, clientes de Google Maps, claves API de variables de entorno junto a clientes LLM, registro de respuestas ordinario, campos de metadatos OAuth, chunks de respuestas en streaming, texto de prompts de ficción/narrativa). Cualquier hallazgo allí hace fallar la suite.

Pruebas y benchmarking

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

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

[`test-fixtures/vulnerable/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/vulnerable) y [`test-fixtures/safe/`](https://github.com/akanthed/secureai-scan/blob/main/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 correctamente en código escrito específicamente para probarlo.

**2. Benchmark de regresión con código 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 repositorios públicos reales (OpenAI/Anthropic/Vercel AI SDKs, 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 habilidades — 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 el CLI compilado.

Sale con código distinto de cero ante cualquier hallazgo proven/likely que no esté ya en test/regression-baseline.json — un registro revisado manualmente de hallazgos ya contrastados con su línea de origen. Las huellas son repo|rule|file, no números de línea, por lo que el cambio ordinario en los repositorios ascendentes 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 habilidades 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 se encuentra 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 en 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 — están 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 originales de precisión (hallazgos en el nivel de evidencia predeterminado, sin --paranoid):

RepoAntesDespuésQué estaba mal
vercel/ai7731Los directorios examples/, tests/ de nivel superior y los de estilo ecosystem-tests/ con guiones no se reconocían como rutas de menor confianza; chunks (una variable común de respuesta en streaming) se trataba como evidencia inequívoca de RAG
openai/openai-node470La misma brecha de detección de rutas, aplicada a los propios examples//ecosystem-tests/ del SDK
anthropics/anthropic-sdk-typescript20La misma brecha de detección de rutas en un directorio tests/ de nivel superior
modelcontextprotocol/typescript-sdk30Campos de metadatos OAuth de estilo 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 proven de herramientas MCP, independientemente del contexto. Los 15 restantes son aciertos VEC001 en las definiciones genéricas de retriever de la propia biblioteca — escanear el código fuente del propio 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 inherente y honesto, 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 habilidades reales)0limpio — comprobación de precisión pura para SKL001–005
vercel/ai (5.691 archivos)0era 40 (AI001, AI003, AI005, AI010, MCP002) antes del triaje — cada uno revisado manualmente contra el origen y confirmado como falso positivo, rastreado a 3 errores independientes de causa raíz (ver más abajo), corregido y reconfirmado limpio en un re-escaneo completo
run-llama/llama_index46VEC001límite inherente, no un error — las definiciones genéricas de retriever de la propia biblioteca, donde no puede existir un filtro de tenant que encontrar
cisco-ai-defense/skill-scanner7SKL001, SKL002, SKL005todos en fixtures etiquetados como malicious/ — 6/6 dentro del alcance, 0 en cualquier cosa etiquetada como safe/

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

  1. resolveLlmSink trataba cualquier llamada resuelta a un módulo de SDK de LLM como una invocación de modelo, independientemente del nombre del método — marcando isToolUIPart (una guarda de tipo que el paquete ai exporta justo junto a generateText) como una llamada de LLM. Esto solo causó 3 de los 5 grupos de hallazgos (AI001, AI003, AI010).
  2. DANGEROUS_CALLEES en AI005 incluye "query" para sinks de estilo inyección SQL, pero "query" también es un verbo legítimo de invocación de LLM/agenteclaudeSdk.query({ prompt, options }), la llamada de modelo propia del Claude Agent SDK, se marcaba como "salida de LLM pasada a un sink peligroso" únicamente por el nombre de método compartido.
  3. REQUEST_SOURCES (duplicado 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 esquema de URL (assertOpenLinkParams(params: unknown)) se marcaba como "URL de servidor MCP desde entrada de usuario".

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

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

Las dos capas anteriores solo comprueban que el escáner permanezca en silencio ante código seguro. Las comprobaciones de asesoramiento de DEP003 se validan en la dirección opuesta: 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

covers: `[email protected]` (CVE-2025-6514, vulnerable) marcado / `[email protected]` (parcheado) limpio; `[email protected]` (antes del backdoor) 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 bajo la normalización de nombres de PyPI (`llama_cpp_python`); y especificadores sin fijar de versión estilo `langchain>=0.1.0` que producen **cero** hallazgos en el informe por defecto. Construir esta prueba detectó una brecha real: `DEP003` solía coincidir con avisos solo por nombre de paquete, sin comparar realmente la versión declarada contra el rango afectado del aviso — corregido en [`src/scanner/semver.ts`](https://github.com/akanthed/secureai-scan/blob/main/src/scanner/semver.ts).

La ambigüedad se resuelve de forma diferente según el tipo de aviso, deliberadamente. Un paquete **malicioso** se dispara incluso cuando la versión declarada no puede resolverse — instalar un backdoor es irrecuperable, por lo que falla hacia el marcado. Un **CVE** se dispara en `proven` solo cuando la versión declarada es una fijación exacta demostrablemente dentro del rango afectado; sin fijar pero posiblemente afectado baja a `heuristic` (solo con `--paranoid`). Aplicar la regla de 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 inaplicable a escala.

## Roadmap

Consulta [`ROADMAP.md`](https://github.com/akanthed/secureai-scan/blob/main/ROADMAP.md) para ver lo que 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 brecha restante de Python es la profundidad de flujo de datos (taint) limitada entre funciones/archivos, no el análisis sintáctico. El rendimiento del escaneo y las limitaciones conocidas están documentados en [`docs/Performance.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/Performance.md).

## Contribuciones

Las contribuciones son bienvenidas — consulta [`CONTRIBUTING.md`](https://github.com/akanthed/secureai-scan/blob/main/CONTRIBUTING.md) para el flujo de trabajo, y [`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) para saber cómo añadir una regla de detección que cumpla con el estándar de precisión anterior. Cada regla nueva necesita un fixture tanto en [`test-fixtures/vulnerable/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/vulnerable) como en [`test-fixtures/safe/`](https://github.com/akanthed/secureai-scan/blob/main/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