Skip to main content

Servidor MCP para verificar dependencias npm y PyPI de forma deterministica.

Project description

Detector de Paquetes Fantasma

Servidor MCP que verifica dependencias npm y PyPI antes de instalarlas. Responde una sola pregunta con evidencia: ¿este paquete existe, es el que crees y trae algo conocido en su contra?

El veredicto es determinista. Dos ejecuciones con los mismos datos devuelven el mismo JSON, byte a byte: los pesos viven congelados en rules_v1, los identificadores de finding derivan de su contenido y no hay relojes ni UUID aleatorios en la respuesta.

Uso directo con uvx

uvx detector-paquetes-fantasma

El comando arranca el servidor sobre stdio. No necesita instalación previa, no descarga modelos y no toca la red hasta que una herramienta lo pide.

Para probar el paquete construido localmente:

python -m build --no-isolation
uvx --from dist/detector_paquetes_fantasma-0.1.0-py3-none-any.whl detector-paquetes-fantasma

Configuración en Kiro

Copia este bloque en .kiro/settings/mcp.json. Es el mismo contenido versionado en src/detector_paquetes_fantasma/resources/kiro/mcp.json, y scripts/release/check_rc.py falla si ambos divergen.

{
  "mcpServers": {
    "detector-paquetes-fantasma": {
      "command": "uvx",
      "args": ["detector-paquetes-fantasma"],
      "transportType": "stdio",
      "env": {},
      "disabled": false,
      "autoApprove": ["verify_package", "verify_manifest", "explain_risk"]
    }
  }
}

El hook PostFileSave versionado en .kiro/hooks/ verifica package.json y requirements.txt al guardarlos. Nunca cancela el guardado: si el servidor no está, tarda o responde algo inválido, avisa y sigue.

Herramientas

verify_package(ecosystem, name, version=None)

Resuelve la versión contra el registry oficial y devuelve un ScanResult.

{
  "ecosystem": "npm",
  "name": "aws-sdk-mcp-helper"
}
{
  "schema_version": 1,
  "request": {
    "ecosystem": "npm",
    "original_name": "aws-sdk-mcp-helper",
    "normalized_name": "aws-sdk-mcp-helper",
    "requested_specifier": null
  },
  "status": "COMPLETE",
  "resolved_version": null,
  "decision": {
    "rules_version": "rules_v1",
    "score": 70,
    "severity": "HIGH",
    "action": "BLOCK",
    "critical_flags": ["PACKAGE_NOT_FOUND"]
  },
  "findings": [
    {
      "type": "package_not_found",
      "source": "REGISTRY",
      "confidence": "CONFIRMED",
      "message": "el registry oficial afirma que el paquete no existe"
    }
  ]
}

version acepta una versión exacta, un rango o nada. Con rango se resuelve la mayor estable publicada y se avisa que un lockfile puede elegir otra. Un specifier que no se puede resolver (workspace:*, file:../x, referencias VCS) no se ignora: se reporta como unverifiable_entry sin llamar al registry.

verify_manifest(ecosystem, manifest_content, path=None)

Verifica todas las entradas de un package.json o requirements.txt. manifest_content se trata siempre como dato: no se interpola en ningún comando.

{
  "ecosystem": "npm",
  "manifest_content": "{\"dependencies\": {\"expres\": \"4.17.1\", \"lodash\": \"4.17.20\"}}",
  "path": "package.json"
}

La respuesta es un ManifestScanResult con un ScanResult por paquete, unverifiable_entries para lo que no se pudo verificar y un aggregate_decision que toma la acción más restrictiva, el score máximo y la unión estable de flags. Si falla una dependencia, las demás se conservan.

explain_risk(result)

Explica un ScanResult o ManifestScanResult ya calculado. La explicación jamás modifica score, severidad, acción ni flags: immutable_decision es una copia exacta de la decisión recibida. Devuelve la narrativa, el motor usado (deterministic_template, lexical_fallback, local_rag o bedrock) y los casos históricos relacionados del corpus versionado.

Decisión y score

Score Severidad Acción
cualquier flag crítico HIGH BLOCK
50–69 MEDIUM REVIEW
20–49 LOW CAUTION
0–19 MINIMAL ALLOW

Los cuatro flags críticos son PACKAGE_NOT_FOUND, OSV_ADVISORY_CONFIRMED, TYPOSQUAT_CONFIRMED y STATIC_COMPOSITE_CONFIRMED. No suman puntos: imponen un piso de 70, más 10 por cada tipo crítico distinto adicional. Sin flags críticos el score nunca pasa de 69, así que una acumulación de señales débiles no puede fabricar un BLOCK. Cada tipo aporta una sola vez: repetir el mismo mensaje no infla el resultado.

Configuración tipada

Todo se lee del entorno y se valida al arrancar. Un valor no interpretable produce un error con la variable responsable, no un default silencioso.

Variable Default Qué controla
DPF_OFFLINE false prohíbe cualquier salida de red
DPF_CACHE_PATH detector-paquetes-fantasma.sqlite3 ruta de la base SQLite
DPF_REGISTRY_TTL_HOURS 168 frescura de metadata de registry
DPF_OSV_TTL_HOURS 6 frescura de advisories
DPF_HTTP_TIMEOUT_SECONDS 10 timeout por request
DPF_DOWNLOAD_TIMEOUT_SECONDS 30 timeout de descarga de artefactos
DPF_MAX_CONCURRENCY 8 requests simultáneas al registry
DPF_MAX_DOWNLOAD_BYTES 25000000 tamaño máximo descargado
DPF_MAX_ARCHIVE_FILES 2000 entradas máximas por archivo
DPF_MAX_FILE_BYTES 5000000 tamaño máximo por archivo extraído
DPF_MAX_EXPANDED_BYTES 50000000 tamaño máximo expandido
DPF_RAG_MODEL_PATH sin valor ruta local del modelo de embeddings
DPF_BEDROCK_ENABLED false activa la redacción con Bedrock
AWS_REGION sin valor región requerida si Bedrock está activo

Las credenciales nunca se persisten ni aparecen en logs: la configuración se serializa redactada.

Cache y modo offline

La cache es SQLite en modo WAL, con TTL separados para registry y OSV, y negative caching solo cuando la ausencia es autoritativa (un 404 del registry). Un 5xx o un timeout nunca se cachean como "no existe".

Con DPF_OFFLINE=true no se crea ni el cliente HTTP. Las respuestas se sirven de cache declarando su edad en lenguaje llano (info de hace 6h) y bajando la cobertura a PARTIAL o UNVERIFIABLE. Reutilizar dato viejo puede elevar la cautela, y esa degradación se declara en los findings en lugar de fingir un veredicto completo.

Explicaciones: local por defecto, Bedrock opcional

El orden de fallback es Bedrock → RAG local → recuperación léxica → plantilla determinista. Sin configuración adicional se usa la recuperación léxica sobre el corpus de incidentes versionado, y toda explicación sin Bedrock se etiqueta como generada localmente, sin Bedrock.

  • RAG con embeddings: instala el extra rag y apunta DPF_RAG_MODEL_PATH a un modelo local. El servidor nunca descarga pesos por su cuenta y el import de sentence-transformers es lazy: arrancar MCP no lo carga.
  • Bedrock: instala el extra bedrock, define DPF_BEDROCK_ENABLED=true y AWS_REGION. El routing es determinista, Sonnet para scores 40–69 o señales contradictorias y Haiku para el resto. El payload lleva findings normalizados y procedencia, nunca código fuente completo, contenido de artefactos ni credenciales. Una respuesta que intente traer score, severidad, acción o flags se rechaza entera.

Límites del análisis estático

El análisis estático es una señal, no una sentencia. Es AST de Python y tree-sitter para JavaScript sobre el artefacto oficial, sin ejecutar nada y sin sandbox.

  • No hay ejecución: no detecta comportamiento que solo aparece en runtime.
  • El código ofuscado, minificado o generado dinámicamente puede evadirlo.
  • Un finding aislado (automatic_execution_isolated, sensitive_access_isolated, network_or_spawn_isolated) se marca solo detección estática, sin confirmación externa y nunca bloquea por sí solo. Solo el composite confirmado es crítico.
  • Si ningún parser puede analizar un archivo, se reporta parser_failure con el aviso de revisión manual; no se asume que sea limpio.

Scripts y reportes

python -m scripts.probes.cache_offline           # cache, TTL y offline sin sockets
python -m scripts.scenarios.hallucinated_package # paquete alucinado
python -m scripts.scenarios.vulnerable_typo_manifest
python -m scripts.scenarios.offline_cache_replay
python -m benchmarks.full_manifest               # manifest completo, cache y concurrencia
python -m scripts.reports.scan_secrets           # reportes sin credenciales ni rutas personales
python -m scripts.reports.acceptance             # agregado en reports/acceptance.json
python -m scripts.release.check_rc               # verificación del release candidate

Los tests usan providers mockeados. La red queda reservada a los tests marcados live, que solo corren con --live o RUN_LIVE_TESTS=1.

Roadmap

  • Sandboxing del análisis de artefactos.
  • MCP remoto en Lambda.
  • Redis como cache compartida.
  • OpenTelemetry para trazas y métricas.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

detector_paquetes_fantasma-0.1.0.tar.gz (236.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

detector_paquetes_fantasma-0.1.0-py3-none-any.whl (133.6 kB view details)

Uploaded Python 3

File details

Details for the file detector_paquetes_fantasma-0.1.0.tar.gz.

File metadata

  • Download URL: detector_paquetes_fantasma-0.1.0.tar.gz
  • Upload date:
  • Size: 236.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for detector_paquetes_fantasma-0.1.0.tar.gz
Algorithm Hash digest
SHA256 4315754e3bd3e813b07fe490ec26f407eb424b0f6cca399d9a364078da28fc7d
MD5 330046b33b45678e5805c67785d92c9c
BLAKE2b-256 76c6de4693204414c002243ebafab6963c6bebd7c96eb0ec65c14d5a2fda7280

See more details on using hashes here.

File details

Details for the file detector_paquetes_fantasma-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: detector_paquetes_fantasma-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 133.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for detector_paquetes_fantasma-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8a3652c0fe0d5eb8a8760de94b1a9e88546ae624cca4a676eef3f99e8e454ee9
MD5 6a2db1d6afe102e68610b12d141be4e7
BLAKE2b-256 53778877fd4330902fb2179fb5dee65d903983ef2f8e0e3acb509b69533de88c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page