CodeQL en GitHub Advanced Security: qué cubre de verdad

Octocat de GitHub, marca bajo la cual se ofrece CodeQL como parte de Advanced Security

CodeQL es el motor de análisis semántico de código de GitHub, heredero de Semmle, y desde hace tres años el corazón de la oferta GitHub Advanced Security. Con el soporte para Rust que llegó en vista previa a finales de 2024 y la mejora continua de las bibliotecas de queries, CodeQL cubre hoy casi todos los lenguajes que usa una empresa media. La pregunta que merece respuesta honesta es qué detecta de verdad, qué no detecta y cómo hay que integrarlo para que el equipo lo aproveche en vez de ignorarlo.

Este texto es un balance desde el uso en varias bases de código: dónde funciona bien, dónde he visto falsos positivos repetidos, qué tipo de vulnerabilidades se escapan y qué hábitos de revisión lo hacen útil.

Cómo funciona por debajo

CodeQL trata el código como un dato consultable. El compilador de CodeQL extrae una base de datos que representa el código fuente: tablas de funciones, llamadas, tipos, flujo de control y flujo de datos. Sobre esa base se ejecutan queries escritas en un lenguaje declarativo propio que se parece a SQL con lógica recursiva, donde uno describe patrones como «busca un flujo de datos desde una entrada HTTP hasta una llamada a exec sin pasar por ninguna sanitización conocida».

Esta arquitectura es la diferencia esencial con los SAST clásicos basados en regex o AST simple. Un regex grep detecta «aquí llaman a eval»; CodeQL detecta «este eval consume un valor que originalmente venía del parámetro username del endpoint POST, sin haber pasado por la función sanitize que el proyecto considera segura». La segunda detección es mucho más útil porque refleja riesgo real, no riesgo potencial genérico.

El proceso de análisis tiene dos costes obvios. La extracción de la base de datos requiere compilar el proyecto entero, lo cual puede tardar desde minutos hasta una hora en repositorios grandes. La ejecución de las queries es relativamente barata una vez construida la base. En GitHub Actions, el flujo habitual es hacer la extracción y las queries en la misma corrida, y el coste de minutos de CI suele ser el que se nota.

Los lenguajes y qué tan maduros son

La lista actual de lenguajes con soporte de CodeQL incluye C, C++, C#, Go, Java, JavaScript (y TypeScript), Python, Ruby, Swift y ahora Rust. No todos tienen la misma madurez. Java, C# y JavaScript son los más pulidos, con bibliotecas de queries extensas y detección fina de vulnerabilidades típicas de esos ecosistemas. Python y Go están sólidos. Ruby y Swift tienen cobertura buena pero con menos queries específicas para frameworks populares. Rust está en vista previa y su catálogo de queries es todavía reducido.

Lo que he notado es que la calidad de la detección depende tanto del lenguaje como del framework usado. CodeQL detecta inyección SQL muy bien en un proyecto Java Spring o JavaScript con Express; la detecta peor en un proyecto que usa un ORM propio o una capa de acceso a datos poco común, porque la biblioteca no tiene modelado ese flujo. Esto es un patrón general: CodeQL conoce bien lo que conoce, y los frameworks menos populares quedan en zona gris.

Hay también lenguajes que CodeQL no cubre y que en 2025 siguen siendo habituales: Kotlin (aunque hay soporte alfa comunitario), Scala, PHP, Elixir, Dart. Si tu stack pasa por estos, CodeQL no va a ser la pieza principal de tu cobertura y habrá que complementar con herramientas específicas del lenguaje.

Qué detecta bien

Las categorías donde CodeQL destaca son varias. Inyecciones (SQL, comandos, LDAP, NoSQL, XPath) donde hay un origen claro y un sumidero claro, y el flujo entre ambos no es excesivamente convoluto. XSS reflejado y almacenado en aplicaciones web con framework reconocido. Deserialización insegura en Java y C#, especialmente cuando hay caminos conocidos de gadgets. Vulnerabilidades de desbordamiento y uso después de liberación en C y C++, aunque aquí la tasa de falsos positivos es más alta que en lenguajes manejados.

También detecta bien patrones de mala configuración de criptografía: uso de algoritmos débiles, claves hardcodeadas, generación insegura de valores aleatorios. Y un conjunto creciente de patrones específicos de contenedores y nube (exposición de secretos en entornos, permisos excesivos) que GitHub ha ido añadiendo como queries auxiliares.

Un punto fuerte que se aprecia con el tiempo es la extensibilidad. CodeQL permite escribir queries propias en el mismo lenguaje declarativo. Si tu empresa tiene una función auditLog propia que debería invocarse en todos los endpoints que modifican datos, se puede escribir una query que detecte endpoints modificadores sin esa llamada. Esta capacidad es donde CodeQL se diferencia más de herramientas que solo ofrecen reglas predefinidas: con esfuerzo moderado, se codifican las convenciones internas como parte del análisis automático.

Qué se le escapa

No todo es detectable con análisis estático, y CodeQL no es mágico. Lo que se le escapa con más frecuencia son problemas de lógica de negocio: si tu función de autorización olvida comprobar el rol antes de devolver un recurso, CodeQL no sabe que ese código debería comprobarlo, porque la lógica esperada no está codificada como query. Este tipo de fallos (IDOR, rupturas de autorización, errores en el modelo de amenazas) siguen siendo trabajo humano o de análisis dinámico.

Tampoco detecta bien vulnerabilidades que dependen del tiempo de ejecución: condiciones de carrera reales en concurrencia compleja, problemas que solo emergen bajo carga, fallos provocados por interacción con servicios externos específicos. Y las cadenas de dependencias siguen fuera de su alcance: una librería vulnerable en tu package.json la detecta Dependabot (también parte de GHAS), no CodeQL.

Un tercer tipo de fallo que he visto con frecuencia es el de frameworks que CodeQL no modela con profundidad. Si usas Next.js con app router o SvelteKit, algunos flujos entre server components y client components no están bien representados y los análisis de flujo de datos se interrumpen. La biblioteca va avanzando, pero siempre va por detrás de las novedades de los frameworks principales.

Cómo integrarlo para que sirva

Lo fácil es activar CodeQL con el workflow que GitHub genera por defecto y olvidarse. Lo útil es incorporarlo de forma que el equipo de desarrollo lo aprecie en vez de ignorarlo. Hay tres decisiones que marcan la diferencia.

Primera, afinar el conjunto de queries. Por defecto se ejecutan las queries de seguridad y de calidad. En una base de código sucia, las queries de calidad generan tantas alertas que el ruido tapa las de seguridad. Mi recomendación es arrancar solo con security-extended (o security-and-quality solo si se va a actuar sobre ello), arreglar el flujo para que aparezcan alertas limpias y después añadir calidad como segunda capa.

Segunda, integrarlo con la revisión de pull requests. CodeQL puede ejecutarse tanto en pushes a la rama principal como en PRs, comentando inline los hallazgos nuevos que introduce un cambio. Esa es la integración útil: el desarrollador ve el aviso en el contexto de su cambio, no en una consola de seguridad que nadie visita. Los hallazgos en la rama principal que no se encuentran en ninguna PR son la deuda histórica que hay que tratar por separado.

Tercera, invertir en queries propias para las cosas que importan en tu contexto. Las queries por defecto cubren vulnerabilidades comunes, pero las convenciones internas (uso obligatorio de cierta función de logging, pases por cierta capa de autorización, patrones prohibidos del framework elegido) son específicas de cada empresa. Dedicar un día cada trimestre a escribir una o dos queries propias termina produciendo una batería que convierte CodeQL en parte del estilo del equipo.

Costes operativos

GitHub Advanced Security se factura por committer activo en repositorios privados que lo tienen activado. A precios de lista esto puede acumularse rápidamente en organizaciones medianas, y conviene modelarlo antes de activarlo de forma general. Para repositorios públicos, CodeQL es gratuito, lo cual explica su adopción masiva en proyectos open source.

El coste menos visible es el de los minutos de CI. En un repositorio grande con varios lenguajes, la extracción de bases de datos puede consumir decenas de minutos por corrida. Esto se gestiona bien con cachés, con ejecución solo en ciertos eventos (no en cada push de cada rama) y con dividir los análisis por lenguaje para paralelizar. He visto organizaciones donde el coste de CI de CodeQL superaba al del software en sí, por configuración descuidada.

El último coste es humano: alguien tiene que mirar las alertas, filtrar falsos positivos, mantener las queries propias actualizadas y decidir cuándo rechazar un PR por razones de seguridad. Si no hay esa figura (puede ser un rol parcial de un desarrollador senior, no necesariamente un especialista a tiempo completo), la herramienta genera deuda en vez de valor.

Cuándo compensa

CodeQL dentro de GHAS compensa cuando se cumplen varias condiciones. Tu stack está entre los bien soportados. Tienes equipo con capacidad de atender las alertas generadas. Tu modelo de amenazas incluye vulnerabilidades estáticamente detectables como riesgo relevante (cualquier aplicación web seria lo cumple). Y estás dispuesto a invertir en la parte de queries propias para que la herramienta se adapte a tus convenciones, no al revés.

Si solo puedes permitirte una herramienta de seguridad en tu pipeline y trabajas sobre GitHub, CodeQL es muy probablemente la mejor primera inversión, por su integración y su cobertura. Si ya tienes un SAST maduro de otro proveedor, la decisión se complica: conviene pilotar CodeQL en paralelo durante un trimestre y comparar sobre el mismo código antes de decidir. Lo que he visto es que, más allá del motor, lo que distingue una integración útil de una inútil es el hábito de revisión: sin ese hábito, cualquier SAST es solo ruido con presupuesto.

Entradas relacionadas