Media

Extractor de Fortran de Graphify: endurecimiento incompleto de cpp #include permite la lectura/exfiltración arbitraria de archivos del host al indexar un repositorio no confiable

  • Publicada: 19.10.2026
  • Actualizado: 20.10.2026 09:53

Resumen

El paso del preprocesador de C del extractor de Fortran (graphify/extractors/fortran.py, _cpp_preprocess) aplica un endurecimiento incompleto (#include). Los flags de -nostdinc -I /dev/null en los que confía (comentario "Security (F-007)") no impiden que un archivo fuente de Fortran con F mayúscula incluya rutas absolutas (#include "/etc/passwd") o realice un salto de directorio fuera del corpus (#include "../../../../home/victim/.ssh/id_rsa"). cpp siempre busca en el directorio del archivo que se está preprocesando en el caso de inclusiones entre comillas y siempre respeta las rutas de inclusión absolutas, independientemente de -nostdinc/-I. Como consecuencia, indexar un repositorio no confiable que contenga un archivo .F/.F90/.F95/.F03/.F08 lee archivos locales arbitrarios del host e inserta su contenido en el grafo de conocimiento, el cual luego se envía al LLM/agente y se escribe en la salida persistente y compartible en disco (graph.json, GRAPH_REPORT.md, la wiki de Obsidian, exportaciones HTML/SVG).

Este es exactamente el "vector de exfiltración de archivos del lado del corpus" contra el cual el comentario F-007 afirma estar defendiéndose; la mitigación es eludible.

Severidad

Alta. Lectura arbitraria de archivos locales en el host, activada por la acción principal de graphify (indexar una carpeta no confiable: "turn any folder of code ... into a queryable knowledge graph"), evadiendo un control de seguridad documentado. Se ejecuta en la ruta de AST predeterminada, sin conexión y sin clave de API (graphify update . / graphify extract), sin necesidad de credenciales, red ni llamadas a LLM. CWE-73 (control externo de nombre/ruta de archivo) / CWE-22 (salto de directorio) / CWE-200 (exposición de información).

Ubicación

graphify/extractors/fortran.py, _cpp_preprocess():

result = subprocess.run(
    ["cpp", "-w", "-P", "-nostdinc", "-I", "/dev/null", str(path.resolve())],
    capture_output=True, timeout=30,
)

Se alcanza desde extract_fortran() (mismo archivo) para cualquier sufijo en _FORTRAN_CPP_EXTS = {".F", ".F90", ".F95", ".F03", ".F08"}, los cuales se recolectan por defecto (detect.CODE_EXTENSIONS) y se envían mediante extract._DISPATCH.

Por qué la mitigación no funciona

-nostdinc solo elimina los directorios de inclusión estándar del sistema, y -I /dev/null solo establece la ruta de búsqueda adicional. Ninguno afecta a:

  1. Inclusiones absolutas — #include "/etc/passwd" es abierto directamente por cpp.
  2. Inclusiones entre comillas relativas al archivo actual — cpp siempre busca en el directorio que contiene el archivo preprocesado para #include "...", por lo que #include "../../../../etc/shadow" escapa del corpus.

Prueba de concepto (verificada)

Reproducido contra el código publicado con el comando exacto que construye _cpp_preprocess (cpp -w -P -nostdinc -I /dev/null <file>), y directamente contra extract_fortran().

Secreto en disco (/tmp/secret.txt): TOP_SECRET_...

(a) Inclusión absoluta — evil_abs.F90:

program evil
#include "/absolute/path/to/secret.txt"
end program evil

(b) Salto relativo — evil_rel.F90:

program evil
#include "../secret.txt"
end program evil

Ambos producen una salida preprocesada que contiene el contenido completo del archivo secreto ("SECRET IN OUTPUT: True" para ambos).

Aparición en el grafo — leak.F90 coloca la inclusión en una posición de identificador mediante una continuación libre, de modo que el valor filtrado se convierte en un nodo/arista:

module m
contains
subroutine s
use &
#include "/path/to/token.secret"
end subroutine s
end module m

extract_fortran() sobre este archivo emite un nodo cuya etiqueta es el contenido de una sola línea de token.secret y una arista imports hacia él. Ese nodo se escribe en graph.json / GRAPH_REPORT.md / la wiki y es retornado por las herramientas MCP query_graph / get_node / get_neighbors, es decir, exfiltrado al contexto del agente y a los artefactos compartibles persistidos.

Nota de fidelidad: Fortran no distingue entre mayúsculas y minúsculas, por lo que los identificadores capturados se convierten a minúsculas. Los secretos no sensibles a mayúsculas y minúsculas (rutas de archivo, nombres de host, nombres de usuario, tokens hexadecimales, fragmentos de cadenas de conexión y la mera existencia/contenido parcial de un archivo legible) se filtran de forma íntegra; los bloques de alta entropía sensibles a mayúsculas y minúsculas (base64/JWT) se corrompen parcialmente por la conversión a minúsculas, pero aún confirman la legibilidad y estructura. Los archivos de secretos con un único token por línea (archivos de claves de API, archivos de credenciales/tokens de una sola línea) filtran dicho token completo (en minúsculas). Requiere cpp/gcc en el PATH (prácticamente omnipresente en los entornos dev/CI a los que apunta graphify); retrocede limpiamente a bytes en bruto cuando está ausente.

Corrección sugerida

Reflejar el enfoque de contención ya utilizado en otras partes del código base (_project_xml_is_safe, security.validate_graph_path): antes de invocar cpp, escanear el código fuente en busca de directivas #include y rechazar o neutralizar cualquier inclusión cuyo objetivo sea absoluto o se resuelva fuera de la raíz del corpus/repositorio. Concretamente, en _cpp_preprocess:

  • Analizar directivas #include "..." / #include <...>.
  • Resolver cada objetivo respecto al directorio del archivo; si es absoluto o su .resolve() escapa de la raíz de escaneo, eliminar esa línea (o recurrir a path.read_bytes() sin preprocesamiento).
  • Permitir únicamente inclusiones que se resuelvan en una ruta dentro de la raíz del corpus.

Una alternativa más drástica pero segura es eliminar todas las líneas #include antes del preprocesamiento (las macros se siguen expandiendo; las inclusiones entre archivos pierden fidelidad, pero no se pueden leer archivos del host). Ejecutar cpp dentro de un entorno aislado de sistema de archivos (bwrap/unshare con solo el corpus montado) también resolvería el problema, aunque resulta más pesado.

Detectado por

Escáner de vulnerabilidades Aeon. Semgrep alertó sobre la ruta del subproceso Fortran; la revisión manual de la mitigación F-007 junto con una reproducción empírica con cpp confirmaron la evasión.

Qué hacer

Actualice a la versión 0.9.70 o posterior.

Software afectado

Haz clic en una tecnología para ver todas sus vulnerabilidades.

PlataformaPaqueteVersiones vulnerablesCorregida en
Python (PyPI) graphifyy >= 0.7.2, < 0.9.70 0.9.70

Referencias

Puntuación CVSS -
Vector CVSS
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Debilidad (CWE)
CWE-22, CWE-73
Plataforma
Python (PyPI)
Paquete
graphifyy
Publicada
19.10.2026
Actualizado
20.10.2026 09:53

Fuentes y licencias · Editado o traducido por DockRay; se mantienen los derechos y las licencias de las fuentes.

OSV / GitHub Advisory Database - GHSA-pcc4-rvhr-2pr8 - CC BY 4.0

Historial de vulnerabilidades: graphifyy

Todas las vulnerabilidades publicadas de este paquete (Python (PyPI)), de la más reciente a la más antigua. Muestra si es un fallo aislado o un paquete que hay que vigilar.

Esta es la única vulnerabilidad conocida de este paquete.

Identifica las vulnerabilidades de tu aplicación

Con la integración de DockRay y el inventario de software activado, DockRay compara las versiones notificadas de los componentes con las vulnerabilidades conocidas y avisa de las coincidencias confirmadas. La cobertura depende de la tecnología compatible y de los avisos disponibles.

Conectar tu aplicación
Newsletter DockRay

La seguridad de tus aplicaciones en tu correo

Vulnerabilidades importantes, recomendaciones concretas y consejos prácticos para tus aplicaciones. Elige tus tecnologías y sigue lo que importa para tu proyecto.

  • Un resumen diario de nuevas vulnerabilidades, también las críticas.
  • Avisos sobre actualizaciones importantes.
  • Consejos prácticos sobre seguridad y monitorización de aplicaciones.

Déjalo vacío para recibir avisos de todas las tecnologías.

Suscripción gratuita. Date de baja con un clic en cualquier correo.