1 puntos por GN⁺ 2 시간 전 | 1 comentarios | Compartir por WhatsApp
  • Las versiones 2.6.2 y 2.6.3 de lightning en PyPI, publicadas el 30 de abril de 2026, fueron utilizadas en un ataque a la cadena de suministro, y con solo ejecutar pip install lightning puede activarse un directorio oculto _runtime junto con una carga útil de JavaScript ofuscada
  • La carga maliciosa se ejecuta automáticamente al importar el módulo y roba credenciales, tokens de autenticación, variables de entorno y secretos de la nube, además de intentar contaminar repositorios de GitHub
  • Aunque el punto de entrada de este ataque es PyPI, la propagación tipo gusano ocurre a través de npm: si encuentra credenciales de publicación de npm, inyecta el dropper setup.mjs y router_runtime.js en los paquetes que pueda publicar y vuelve a publicarlos con un incremento de parche
  • La exfiltración de datos usa 4 canales paralelos: POST por HTTPS, dead-drop mediante búsqueda de commits en GitHub, repositorios públicos de GitHub controlados por el atacante y push directo a repositorios de las víctimas; deja como indicadores el prefijo de commit EveryBoiWeBuildIsAWormyBoi y la descripción de repositorio "A Mini Shai-Hulud has Appeared"
  • El malware instala un hook SessionStart en .claude/settings.json de Claude Code y una tarea runOn: folderOpen en .vscode/tasks.json de VS Code para ejecutar el dropper cada vez que se abre el repositorio; cualquier máquina que haya importado el paquete malicioso durante el periodo afectado debe considerarse totalmente comprometida

Paquetes afectados y procedimiento de verificación

  • lightning es un framework de deep learning que suele aparecer en el árbol de dependencias de equipos que construyen clasificadores de imágenes, hacen fine-tuning de LLM, ejecutan modelos de difusión o desarrollan pronosticadores de series temporales
  • Paquetes afectados

    • lightning versión 2.6.2
    • lightning versión 2.6.3
  • Procedimiento de verificación para clientes de Semgrep

    • Si no se ha ejecutado un escaneo reciente del proyecto, se debe lanzar uno nuevo
    • En la página de advisories se puede verificar si el proyecto instaló recientemente esas versiones del paquete
    • En el dependency filter se pueden revisar coincidencias; si aparece “No matching dependencies”, significa que el proyecto no está usando activamente la dependencia maliciosa
    • Si hay coincidencias, se deben auditar en el repositorio archivos inesperados dentro de los directorios .claude/ y .vscode/ indicados abajo en los indicadores de compromiso
    • Deben rotarse los tokens de GitHub, credenciales de nube y claves de API que pudieron estar presentes en los entornos afectados
    • Consejos generales sobre respuesta ante ataques a la cadena de suministro y periodos de espera se tratan en $foo compromised in $packagemanager y Attackers are Still Coming for Security Companies

Cómo se propaga de PyPI a npm

  • A diferencia de mini Shai-Hulud, que apuntaba directamente a npm, en este ataque el punto de entrada es PyPI
  • La carga maliciosa sigue siendo JavaScript, y la propagación tipo gusano ocurre por npm
  • Si el malware en ejecución encuentra credenciales de publicación de npm, inyecta el dropper setup.mjs y router_runtime.js en todos los paquetes que ese token pueda publicar
  • Luego configura scripts.preinstall para ejecutar el dropper, incrementa la versión de parche y vuelve a publicar
  • Los desarrolladores aguas abajo que instalen esos paquetes terminan ejecutando el malware completo en sus máquinas, lo que lleva al robo de tokens y a nuevas infecciones del gusano en paquetes

Métodos de exfiltración de datos

  • La funcionalidad de robo comparte mecanismos y diseño con la campaña previa de Mini Shai-Hulud, y usa 4 canales paralelos para asegurar la salida de datos incluso si alguna ruta individual es bloqueada
  • El ataque incluye una temática Shai-Hulud, como la creación del repositorio público EveryBoiWeBuildIsaWormBoi
  • La estructura de los indicadores del ataque coincide con la campaña previa de mini Shai-Hulud, y los mensajes de commit maliciosos usan el prefijo EveryBoiWeBuildIsAWormyBoi para diferenciarse del ataque original de Mini Shai-Hulud
  • Transmisión C2 vía HTTPS POST

    • Los datos robados se envían de inmediato por POST a un servidor controlado por el atacante a través del puerto 443
    • El dominio y la ruta están almacenados como cadenas cifradas dentro de la carga, lo que dificulta el análisis estático
  • Dead-drop mediante búsqueda de commits en GitHub

    • El malware consulta en bucle la API de búsqueda de commits de GitHub para encontrar mensajes con el prefijo EveryBoiWeBuildIsAWormyBoi
    • Los mensajes de commit transportan un token con doble codificación Base64 en el formato EveryBoiWeBuildIsAWormyBoi:<base64(base64(token))>
    • El token decodificado se usa luego para autenticar un cliente de Octokit para operaciones posteriores
  • Repositorio público de GitHub controlado por el atacante

    • Se crea un nuevo repositorio público con un nombre elegido aleatoriamente a partir de palabras de Dune y la descripción "A Mini Shai-Hulud has Appeared"
    • Esa descripción puede buscarse directamente en GitHub
    • Las credenciales robadas se confirman en results/results-<timestamp>-<n>.json; mediante la API van codificadas en Base64, pero internamente son JSON plano
    • Los archivos de más de 30 MB se dividen en fragmentos numerados
    • Los mensajes de commit usan chore: update dependencies como camuflaje
  • Push directo al repositorio de la víctima

    • Si el malware obtiene un token de servidor de GitHub ghs_, hace push directo de los datos robados a todas las ramas del GITHUB_REPOSITORY de la víctima

Objetivos del robo

  • El malware apunta a credenciales en archivos locales, entorno, pipelines de CI/CD y proveedores de nube
  • Sistema de archivos

    • Escanea más de 80 rutas de archivos de credenciales para buscar tokens ghp_, gho_ y npm_
    • Procesa hasta 5 MB por archivo
  • Shell y variables de entorno

    • Ejecuta gh auth token
    • Vuelca todas las variables de entorno de process.env
  • GitHub Actions

    • En runners Linux, vuelca la memoria del proceso Runner.Worker usando Python incorporado
    • Extrae todos los secretos marcados como "isSecret":true, además de GITHUB_REPOSITORY y GITHUB_WORKFLOW
  • Organizaciones de GitHub

    • Verifica los alcances del token repo y workflow
    • Recorre los secretos de organización de GitHub Actions
  • AWS

    • Intenta usar variables de entorno, perfiles ~/.aws/credentials, IMDSv2 169.254.169.254 y ECS 169.254.170.2 para invocar sts:GetCallerIdentity
    • Enumera y obtiene todos los valores de Secrets Manager y parámetros de SSM
  • Azure

    • Usa DefaultAzureCredential para enumerar suscripciones y acceder a secretos de Key Vault
  • GCP

    • Se autentica con GoogleAuth
    • Enumera y obtiene todos los secretos de Secret Manager
    • El alcance objetivo incluye entornos locales de desarrollo, runners de CI y los 3 principales proveedores de nube
    • Toda máquina que haya importado el paquete malicioso durante el periodo afectado debe considerarse totalmente comprometida

Persistencia a través de herramientas de desarrollo

  • Una vez dentro del repositorio, el malware apunta a herramientas de desarrollo de uso común como Claude Code y VS Code para instalar hooks de persistencia
  • Claude Code

    • El malware escribe un hook SessionStart con matcher: "*" en .claude/settings.json, el archivo de configuración de Claude Code del repositorio
    • El hook apunta a node .vscode/setup.mjs
    • Se ejecuta cada vez que un desarrollador abre Claude Code en el repositorio infectado, sin requerir uso de herramientas ni acciones del usuario más allá de iniciar la sesión
  • VS Code

    • Para usuarios de VS Code, instala una tarea runOn: folderOpen en .vscode/tasks.json
    • Cada vez que se abre la carpeta del proyecto, se ejecuta node .claude/setup.mjs
  • Dropper setup.mjs

    • Ambos hooks invocan setup.mjs, un bootstrapper autocontenido del runtime de Bun
    • Si Bun no está instalado, descarga silenciosamente bun-v1.3.13 desde GitHub releases
    • Soporta Linux x64, Linux arm64, Linux musl, macOS x64, macOS arm64, Windows x64 y Windows arm64
    • Después ejecuta la carga completa de 14.8 MB .claude/router_runtime.js y limpia /tmp
  • Workflow malicioso de GitHub Actions

    • Si el malware posee un token de GitHub con permisos de escritura, hace push a un workflow llamado Formatter en el repositorio de la víctima
    • En cada push, vuelca todos los secretos del repositorio mediante ${{ toJSON(secrets) }}
    • Sube el resultado como un artefacto descargable de Actions llamado format-results
    • Las Actions se fijan a un commit SHA específico para aparentar legitimidad
    • Los repositorios que hayan recibido el paquete lightning infectado en CI y cuenten con un token con permisos de escritura deben auditar esos archivos

Indicadores de compromiso

  • Indicadores buscables

    • Los mensajes de commit con el prefijo EveryBoiWeBuildIsAWormyBoi se usan como portadores de tokens del dead-drop y pueden encontrarse con la búsqueda de commits de GitHub
    • Los repositorios de GitHub con la descripción "A Mini Shai-Hulud has Appeared" son repositorios de exfiltración del atacante y pueden buscarse directamente
  • Paquetes

    • lightning@2.6.2
    • lightning@2.6.3
  • Archivos y artefactos del sistema

    • _runtime/start.py: cargador en Python que inicializa la carga al importar
    • _runtime/router_runtime.js: carga útil de JavaScript ofuscada y runtime de Bun de 14.8 MB
    • _runtime/: directorio añadido en las versiones maliciosas del paquete
    • .claude/router_runtime.js: copia del malware inyectada en el repositorio de la víctima
    • .claude/settings.json: configuración del hook de Claude Code inyectada en el repositorio de la víctima
    • .claude/setup.mjs: dropper inyectado en el repositorio de la víctima
    • .vscode/tasks.json: tarea de autoejecución de VS Code inyectada en el repositorio de la víctima
    • .vscode/setup.mjs: dropper inyectado en el repositorio de la víctima

1 comentarios

 
GN⁺ 2 시간 전
Comentarios de Hacker News
  • Puede que sea simplemente una ilusión de frecuencia, pero últimamente se ven bastantes ataques a la cadena de suministro muy sonados en paquetes importantes
    Incluso en las primeras páginas de HN ahora mismo hay varias publicaciones sobre casos distintos
    Al mirar atrás a left-pad hace 10 años, da la impresión de que hoy hay más ataques exitosos que antes, y probablemente sí sea así
    También debe haber aumentado claramente el valor de que un ataque tenga éxito, pero me pregunto si, como comunidad en general, realmente estamos mejorando en la capacidad de detectarlos antes del lanzamiento de paquetes
    Las empresas de software comercial deberían hacerlo mejor, pero todavía parece faltar una herramienta universal y sencilla para los casos en que algo empieza como código amateur o de hobby y termina siendo una dependencia de muchísimos proyectos
    Ya puse el mismo comentario en el hilo sobre el ataque a la cadena de suministro de SAP: https://news.ycombinator.com/item?id=47964003

    • Es un fenómeno real. A inicios de abril hubo 7 casos en los últimos 12 meses, y en los 20 años anteriores hubo 9: https://www.jefftk.com/p/more-and-more-extensive-supply-chai...
    • Como la gente mete código por todos lados en masa sin revisarlo bien, es natural que aumenten los ataques a la cadena de suministro
    • La razón es que las actualizaciones automáticas y las herramientas de CI ya alcanzaron masa crítica y todo el mundo las usa
      Antes era más común ejecutar npm install manualmente, y seguramente solo se hacía cuando se rompía el build o muy de vez en cuando
      Los ataques a la cadena de suministro dependen de que la gente, o más exactamente los pipelines, actualicen paquetes automáticamente en cuanto sale una nueva versión sin pensarlo mucho
    • Históricamente, el manejo de artefactos con verificaciones de seguridad adicionales era una opción enterprise de pago, y el lado menos seguro era por mucho la opción predeterminada menos engorrosa
      No sé si eso sea un buen modelo de negocio; probablemente no
    • left-pad no fue un ataque, fue un bug de NPM
      No debería haber sido posible borrar una versión de un paquete del que ya dependían otros paquetes públicos, y en cambio sí debería haber sido posible borrar una versión nueva específica de un paquete de la que nadie dependía
      Cuando el autor de left-pad intentó borrar todos sus datos con la intención de abandonar el servicio, NPM debió haber devuelto un código de error
      Según Wikipedia, cuando Koçulu se decepcionó de la decisión de npm, Inc. y dijo que no quería seguir siendo parte de la plataforma, Schlueter, creador de NPM, le proporcionó el comando para borrar los 273 módulos que había registrado
  • Lo raro es que se abrieron 4 issues de seguridad y en todos el bot pl-ghost comentó automáticamente y los cerró [1][2][3][4]
    Al final, solo [4] se atendió correctamente y todos los comentarios del bot fueron eliminados
    En otro reporte [5] se puede ver el comentario del bot, y da más información que la publicación original
    [1] https://github.com/Lightning-AI/pytorch-lightning/issues/216...
    [2] https://github.com/Lightning-AI/pytorch-lightning/issues/216...
    [3] https://github.com/Lightning-AI/pytorch-lightning/issues/216...
    [4] https://github.com/Lightning-AI/pytorch-lightning/issues/216...
    [5] https://socket.dev/blog/lightning-pypi-package-compromised

    • Soy Andy de Lightning. Sí, las credenciales de PyPI fueron robadas a través de la cuenta comprometida del bot pl-ghost
      El atacante creó un nuevo workflow de Actions con esa cuenta y, desde ese workflow ejecutado, extrajo y se llevó los secretos de PyPI
      Después de publicar el paquete, usó esa misma cuenta para comentar y burlarse un poco de nosotros
  • Ojalá llegue pronto el día en que no haya dependencias en absoluto
    Como ejemplo extremo, ahora cuando hago apps educativas interactivas para mi hija le pido a Opus que use solo JavaScript y HTML puros
    Desde péndulos dobles hasta simulaciones de fluidos, todo funciona de una sola vez, y antes eso implicaba cientos de dependencias
    Si el código tiene licencia MIT, puedo pedirle a Opus que extraiga exactamente las partes necesarias, las adapte a mi caso de uso y las incluya
    En proyectos de hobby me ha funcionado bien hasta ahora, y ojalá en el futuro también el software de producción pueda quedarse sin dependencias

    • Si haces eso, tendrás que gestionar tú mismo todos los cambios y una enorme cantidad de casos especiales
      Si Chrome cambia la forma de alguna API, tú mismo tendrás que encontrarlo y corregirlo; y si Marruecos cambia la fecha de inicio del horario de verano, también tendrás que actualizar por tu cuenta el código de fechas y horas
      Esas son cosas que las librerías nos resolvían y que dábamos por sentadas
      No es gran cosa para un simulador de péndulo doble del que tu hija perderá interés la próxima semana, pero sí es un problema para una empresa que construye algo que debe seguir funcionando indefinidamente
    • Ahora en realidad quedaste expuesto a la dependencia de verdad: el navegador
    • Claro, ¿vas a revisar con el mismo nivel de cuidado cada línea del código que generó Opus que el que esperas de un mantenedor de open source? ¿Verdad?
      Debería publicar algún código de acceso remoto con licencia MIT para que entre en los datos de entrenamiento de Opus
    • Me gusta más Rust que Go, así que me deja pensando. Incluso desde la perspectiva de los LLM, Rust sale mejor, pero la filosofía de dependencias de Rust es básicamente un agujero negro de seguridad, y Go es mucho mejor en eso
    • ¿Tienes algún repositorio o forge público que puedas compartir? Tengo un juego para deletrear animales de granja y me gustaría expandir mi librería y sacar más ideas
  • Cuando tomé el curso de deep learning de Fast.AI, me sorprendió la cantidad de dependencias de Python que arrastra un proyecto de machine learning
    Siempre pensé que los proyectos de frontend web tenían muchas dependencias de terceros, pero a mí el ecosistema de machine learning me parece mucho más enredado
    Además, el desarrollo web siempre se ha considerado sensible a la seguridad, así que ha acumulado mucha sabiduría y prácticas al respecto, mientras que el desarrollo de ML parece mucho más improvisado y como que ni siquiera aplica muchas prácticas generales de ingeniería de software
    Por ejemplo, en ese momento una de las formas de desplegar modelos de ML era con Python pickle, que es básicamente un objeto ejecutable sin restricciones por defecto
    Un modelo en ese formato podía hacer cualquier cosa en la computadora que lo importara, y ese ecosistema tipo lejano oeste de los inicios puede facilitar mucho más las intrusiones de seguridad y los ataques a la cadena de suministro

    • En ese ecosistema hay mucha gente que originalmente no es ingeniera de software
      Algunos aprendieron algo de programación sobre la marcha, algunos son matemáticos, y otros son como desarrolladores intoxicados con la IA
      También existe la mentalidad de “el código ya no importa, mientras funcione”
      Para mucha gente, una gestión de dependencias adecuada no es más que trabajo tedioso del que no quiere ocuparse
      En muchos proyectos de machine learning se combinan todos esos factores, cuando en realidad los proyectos de ML deberían ser de los que más se enfocan en la reproducibilidad
  • Busqué en repositorios y me salieron 2.2 mil repositorios creados en el último día que contienen el texto "A Mini Shai-Hulud has Appeared": https://github.com/search?q=A%20Mini%20Shai-Hulud%20has%20Ap...

    • Los nombres de los repositorios parecen ser todos dos términos/palabras de Dune, como harkonen, mentat, ornithoptor, con números añadidos
      Eso parece indicar que se usaron para crear repositorios después de comprometer cuentas, probablemente tokens de autenticación de GitHub/Actions
    • Esta cuenta https://github.com/tinin46 parece tener muchísimas claves guardadas, aunque no me queda claro para qué
    • No entiendo por qué GitHub no responde de inmediato y bloquea los repositorios cuyo README coincida con la expresión regular
      Ya había pasado algo parecido antes, así que pensé que habrían aprendido la lección
      Este malware no se esforzó mucho, y Microsoft tampoco parece estar esforzándose mucho
    • ¿Qué demonios está pasando aquí?
  • Para ser claro, nunca he usado pytorch y tampoco sé mucho de prácticas de seguridad de software
    Pero no se me ocurren muchos escenarios en los que pytorch necesite acceso a red
    Parece incorrecto que desde cualquier parte del código base se pueda importar cualquier módulo y usar esa API
    Da la impresión de que hacen falta restricciones adicionales de importación o análisis estático
    El lenguaje no parece tener la abstracción adecuada para tratar estos problemas
    En comparación, me gusta que en Rust puedas ver mutabilidad y lifetimes solo con la firma de una función, sin entender el código interno
    Siento que hace falta algo parecido para las dependencias
    Los desarrolladores deberían poder auditar fácilmente todas las dependencias sin meterse al código inferior y ver “ah, esta dependencia usa eval()” o “tiene acceso a red”
    Las apps móviles obligan a pedir permisos; uno pensaría que los desarrolladores también deberían poder poner en lista blanca solo ciertas capacidades, en lugar de tragarse bloques enormes de funcionalidad

    • El ecosistema de Python jamás permitiría algo así, pero ojalá este tema se entendiera y se aceptara mejor dentro de ese mundo
      No me gusta generalizar, pero parece que la comunidad de desarrollo de IA en particular prioriza la comodidad por encima de cualquier otra consideración
      Por ejemplo, ya casi es estándar que un proyecto descargue automáticamente un modelo grande en la primera ejecución
      Normalmente se puede desactivar, pero encontrar el parámetro correcto es un dolor terrible por las capas profundas de clases y código repartidas entre varias librerías
      Está bien que sea muy fácil empezar con cosas complejas, por lo general casi juguetes, pero este ambiente permisivo es bastante incómodo
      Siento que el primer paso para resolver cualquier problema siempre es “pip install …”, y algunos entornos, como MacOS, ni siquiera virtualizan bien el acceso a GPU
    • Hay casos en los que se entrena un modelo a través de varios nodos de cómputo. Ese es un gran ejemplo de algo que sí necesita acceso a red
  • Esta semana me estaba preguntando si era buena idea usar uv para gestionar versiones de Python
    El sitio web [1] dice: “Como Python no ofrece binarios oficiales para distribución, uv usa distribuciones del proyecto Astral python-build-standalone”
    Apunta a este repositorio de GitHub https://github.com/astral-sh/python-build-standalone, y ahí luego menciona https://gregoryszorc.com/docs/python-build-standalone/main/r...
    Si lo entendí bien, parece que no toman directamente de python.org el código fuente para compilar Python, y no estoy seguro de qué tan seguro sea eso
    Tengo la misma preocupación con asdf [2], aunque asdf usa pyenv [3] y eso me suena más cercano a lo oficial
    ¿Alguien puede explicar cuál herramienta es mejor y más segura entre uv y asdf para instalar Python?
    [1] https://docs.astral.sh/uv/guides/install-python/
    [2] https://github.com/asdf-community/asdf-python
    [3] https://github.com/pyenv/pyenv/tree/master/plugins/python-bu...

    • python-build-standalone sí obtiene el código fuente de CPython directamente de python.org[1]
      De hecho, ¿de dónde más lo sacaría?
      [1]: https://github.com/astral-sh/python-build-standalone/blob/a2...
    • uv y cpython no me preocupan demasiado. El proceso es sólido, responden rápido y ahora además tienen bastante financiamiento
      Lo que sí me preocupa es, por ejemplo, un formateador como mdformat, muy usado pero mantenido principalmente por una sola persona en su tiempo libre, o una dependencia súper específica que lleva años sin actualizarse y está tres niveles abajo en el árbol de dependencias
      No quiero fijar y aprobar manualmente todas las actualizaciones en una app desarrollada activamente, pero para una app seria eso ya empieza a parecer obligatorio
      Mientras tanto voy a sacar las claves API de archivos .env sin cifrar
      Si te pasa en una gran webapp de consumo da vergüenza, pero se entiende; perder cientos o miles de dólares por una dependencia indirecta de un repositorio demo de juguete que casualmente está en el mismo host y sistema duele muchísimo
      ¿Alguien sabe si OAI o Anthropic reembolsan cuando te roban claves de esta manera, o se considera error del usuario?
    • Al final, uv en sí ya es un binario que ejecutas en tu computadora para gestionar binarios de Python, paquetes, binarios dentro de esos paquetes y herramientas globales del sistema
      No sé cuánto cambia el riesgo si ellos compilan los binarios de Python o si los compila alguien más
  • Últimamente la mayoría de mis pip install vienen de sugerencias de Claude Code, y yo solo aprieto Enter
    El modelo fue entrenado con datos de hace unos meses, así que no tiene forma de saber qué se comprometió esta semana
    Básicamente armé el peor filtro posible para decidir si “este paquete es seguro en este momento”

    • No deberías culpar al LLM por la flojera y la falta de diligencia
    • ¿A qué filtro te refieres?
      Dijiste que dejas que Claude Code recomiende software para instalar desde internet, y luego lo instalas tal cual
      Nunca he oído a nadie sugerir que Claude Code, ni ningún LLM, sea un filtro para decidir “si este paquete es seguro en este momento”, y por las razones que mencionaste parece una heurística pésima
    • Que los datos de entrenamiento estén desactualizados influye un poco, pero incluso un modelo al día no puede saber qué va a ejecutar setup.py en tu máquina
      Eso es porque no hay una inspección real del paquete antes de ejecutarlo
      Lo que hace falta es una herramienta que obtenga metadatos antes de ejecutar nada y verifique qué hooks existen
    • Se evita fácilmente si no presionas Enter
    • ¿Con “el peor filtro” te refieres a apretar Enter a todo lo que te diga Claude?
  • Claude Code se actualiza casi todos los días, a veces varias veces por día
    Si algún día comprometen Anthropic, a todos nos va a ir muy mal

    • ¿Quiénes son “todos nosotros”?
    • No si lo ejecutas en una VM/contenedor sin privilegios y con acceso restringido a la red
      Pero hoy en día todo es YOLO
    • Seguro eso ya está descontado en el precio de Polymarket
  • Vi este mensaje en GitHub del 20 de abril y me dejó algo confundido
    "deependujha hi @thebaptiste, thanks for inquiring. Release of 2.6.2 is blocked due to some internal reasons. Will notify once release is made."
    Si sabían del problema desde entonces y no advirtieron hasta ahora, eso sí me parecería muy mal
    Ojalá alguien que sepa más pueda aclararlo bien
    https://github.com/Lightning-AI/pytorch-lightning/issues/216...

    • Soy Andy de Lightning. El paquete malicioso se publicó en PyPI hoy a las 12:45 PM UTC
      Antes de eso no había distribuciones afectadas y no sabíamos de la filtración
      La release original en GitHub no tenía problema, pero la bajamos para evitar confusiones
    • Para quienes usan uv: https://docs.astral.sh/uv/reference/settings/#exclude-newer