Ejecución de CodeQL en una base de datos
Con el código extraído en una base de datos, ahora puede analizarlo mediante consultas CodeQL. Los expertos de GitHub, los investigadores de seguridad y los colaboradores de la comunidad escriben y mantienen las consultas de CodeQL predeterminadas. También puede escribir sus propias consultas.
Puede usar consultas codeQL en análisis de código para encontrar problemas en el código fuente y encontrar posibles vulnerabilidades de seguridad. También puede escribir consultas personalizadas para identificar los problemas de cada idioma que use en el código fuente.
Hay dos tipos importantes de consultas:
- Las consultas de alerta resaltan problemas en ubicaciones específicas del código.
- Las consultas de ruta describen el flujo de información entre un origen y un destino en tu código.
Consulta simple de CodeQL
La estructura básica de consulta CodeQL tiene la extensión .ql de archivo y contiene una select cláusula . Esta es una estructura de consulta de ejemplo:
/**
*
* Query metadata
*
*/
import /* ... CodeQL libraries or modules ... */
/* ... Optional, define CodeQL classes and predicates ... */
from /* ... variable declarations ... /
where / ... logical formula ... /
select / ... expressions ... */
Metadatos de consulta
El uso de CodeQL con el análisis de código convierte los resultados de una manera que resalta los posibles problemas que las consultas están diseñadas para buscar. Las consultas contienen propiedades de metadatos que indican cómo se deben interpretar los resultados. Use metadatos de consulta para:
- Identifique las consultas personalizadas al agregarlas al repositorio de GitHub.
- Proporcione información sobre el propósito de la consulta.
La información de metadatos puede incluir una descripción de la consulta, un identificador único y el tipo de problema que es (alerta o ruta de acceso). Los metadatos también especifican cómo interpretar y mostrar los resultados de la consulta.
GitHub tiene una guía de estilo recomendada para los metadatos de consulta. Puede encontrarlo en la documentación de CodeQL.
En este ejemplo se muestran los metadatos de una de las consultas de Java estándar:
CodeQL no interpreta las consultas que no tienen metadatos. Muestra esos resultados como una tabla y no los muestra en el código fuente.
Sintaxis de QL
QL es un lenguaje de consulta declarativo orientado a objetos. Está optimizado para permitir un análisis eficaz de estructuras de datos jerárquicas y, en particular, bases de datos que representan artefactos de software.
La sintaxis de QL es similar a SQL, pero la semántica de QL se basa en Datalog. Datalog es un lenguaje de programación lógica declarativo, que a menudo se usa como lenguaje de consulta. Dado que QL es principalmente un lenguaje lógico, todas las operaciones de QL son operaciones lógicas. QL también hereda predicados recursivos de Datalog. QL agrega compatibilidad con agregados para que incluso consultas complejas sean concisas y sencillas.
El lenguaje QL consta de fórmulas lógicas. Usa conectivos lógicos comunes, como and, ory not, junto con cuantificadores como forall y exists. Dado que QL hereda predicados recursivos, también puede escribir consultas recursivas complejas mediante la sintaxis básica de QL y agregados como count, sumy average.
Para obtener más información sobre el lenguaje QL, consulte la documentación de CodeQL.
Consultas de ruta de acceso
La forma en que fluye la información a través de un programa es importante. Los datos que parecen benignos pueden fluir de maneras inesperadas que permiten su uso malintencionado.
La creación de consultas de ruta de acceso puede ayudarle a visualizar el flujo de información a través de un código base. Una consulta puede realizar un seguimiento de la ruta que los datos toman desde sus posibles puntos de partida (source) hasta sus posibles puntos finales (sink). Para modelar rutas de acceso, la consulta debe proporcionar información sobre el origen, el receptor y los pasos de flujo de datos que los vinculan.
La manera más fácil de empezar a escribir tu propia consulta de ruta es usar una de las consultas existentes como plantilla. Para obtener estas consultas para los idiomas admitidos, consulte la documentación de CodeQL.
La consulta de ruta de acceso necesitará determinados metadatos, predicados de consulta y estructuras de instrucciones select. Muchas de las consultas de ruta de acceso integradas en CodeQL siguen una estructura básica. La estructura depende de cómo CodeQL modele el lenguaje que está analizando.
Aquí tienes una plantilla de ejemplo para una consulta de ruta:
/**
* ...
* @kind path-problem
* ...
*/
import <language>
// For some languages (Java/C++/Python/Swift), you need to explicitly import the data-flow library, such as
// import semmle.code.java.dataflow.DataFlow or import codeql.swift.dataflow.DataFlow
...
module Flow = DataFlow::Global<MyConfiguration>;
import Flow::PathGraph
from Flow::PathNode source, Flow::PathNode sink
where Flow::flowPath(source, sink)
select sink.getNode(), source, sink, "<message>"
En esa plantilla:
MyConfigurationes un módulo que contiene los predicados que definen cómo fluyen los datos entresourceysink.Flowes el resultado del cálculo de flujo de datos basado enMyConfiguration.Flow::Pathgraphes el módulo resultante del grafo de flujo de datos que debe importar para incluir las explicaciones de ruta en la consulta.sourceysinkson nodos del gráfico tal como se define en la configuración yFlow::PathNodees su tipo.DataFlow::Global<..>es una invocación del flujo de datos. Puede usarTaintTracking::Global<..>en su lugar para incluir un conjunto predeterminado de pasos taint.
Cómo redactar una consulta de ruta
La consulta debe calcular un grafo de camino para generar explicaciones de la ruta. Para ello, defina un predicado de consulta denominado edges. Un predicado de consulta es un predicado no miembro con una query anotación. La anotación de consulta devuelve todas las tuplas que evalúa el predicado.
El edges predicado define las relaciones perimetrales del grafo que está calculando. Se usa para calcular las rutas de acceso relacionadas con cada resultado que genera la consulta. También puede importar un predicado predefinido edges desde un módulo de grafo de caminos en una de las bibliotecas de flujo de datos estándar.
Las bibliotecas de flujo de datos, además del módulo de grafo de caminos, contienen las otras clases, predicados y módulos que se usan habitualmente en el análisis de flujo de datos. Las bibliotecas de flujo de datos de CodeQL funcionan mediante el modelado del grafo de flujo de datos o implementando el análisis de flujo de datos. Las bibliotecas de flujo de datos normales se usan para analizar el flujo de información en el que se conservan los valores de datos en cada paso.
Esta es una instrucción de ejemplo que importa el pathgraph módulo desde la biblioteca de flujo de datos (DataFlow.qll), en la que edges se define:
import DataFlow::PathGraph
Puede importar muchas otras bibliotecas incluidas con CodeQL. También puede importar bibliotecas diseñadas específicamente para implementar el análisis de flujo de datos en varios marcos y entornos comunes.
La clase PathNode está diseñada para implementar el análisis de flujo de datos. Se Node aumenta con un contexto de llamada (excepto los receptores), una ruta de acceso y una configuración. Solo se generan valores PathNode que son alcanzables desde un origen.
Este es un ejemplo de la ruta de importación:
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl
Opcionalmente, puede definir un nodes predicado de consulta, que especifica los nodos del gráfico de rutas de acceso para todos los lenguajes. Al definir nodes, los nodos seleccionados definen solo bordes con puntos de conexión. Cuando no define nodes, debe seleccionar todos los puntos de conexión posibles de edges.
Análisis de base de datos
Al usar consultas para analizar una base de datos CodeQL, recibirá resultados significativos en el contexto del código fuente. Los resultados tienen un estilo de alertas o rutas de acceso en SARIF u otro formato interpretado.
Este es un ejemplo de un comando de base de datos CodeQL que analiza la base de datos ejecutando consultas seleccionadas en ella e interpretando los resultados:
codeql database analyze --format=<format> ---output=<output> [--threads=<num>] [--ram=<MB>] <options>... -- <database> <query|dir|suite>...
Este comando combina el efecto de los comandos de fontanería codeql database run-queries y codeql database interpret-results .
Como alternativa, puede ejecutar consultas que no cumplan los requisitos para interpretarse como alertas de código fuente. Para ello, use codeql-database run-queries o codeql query run. A continuación, use codeql bqrs decode para convertir los resultados sin procesar en una notación legible.
Puede obtener una lista completa de los comandos de la CLI de CodeQL disponibles en el manual de la CLI de CodeQL.
Uso de un archivo SARIF con categorías
CodeQL admite SARIF para compartir resultados de análisis estáticos. SARIF está diseñado para representar la salida de una amplia gama de herramientas de análisis estáticos.
Debe especificar una categoría al usar la salida SARIF para el análisis de CodeQL. Las categorías pueden distinguir varios análisis realizados en el mismo repositorio de confirmación y en distintos lenguajes o partes diferentes del código. Sin embargo, los archivos SARIF con la misma categoría se sobrescriben entre sí.
Puede examinar cada archivo de salida SARIF mediante CodeQL para analizar diferentes idiomas dentro de la misma base de código cuando el valor de categoría es coherente entre las ejecuciones de análisis. Se recomienda usar el idioma que se examina como identificador de la categoría.
Este es un ejemplo. Aparece el valor de categoría (con una barra diagonal final anexada si aún no está presente) como la <run>.automationId propiedad en SARIF v1, la <run>.automationLogicalId propiedad de SARIF v2 y la <run>.automationDetails.id propiedad en SARIF v2.1.0.
Publicación de los resultados de SARIF en GitHub
Una vez lista la base de datos, puede consultarla de forma interactiva. También puede ejecutar un conjunto de consultas para generar un conjunto de resultados en formato SARIF y cargar los resultados en un repositorio de destino en GitHub.com:
codeql github upload-results --sarif=<file> [--github-auth-stdin] [--github-url=<url>] [--repository=<repository-name>] [--ref=<ref>] [--commit=<commit>] [--checkout-path=<path>] <options>...
Para cargar los resultados en GitHub, asegúrese de que cada servidor de integración continua (CI) tenga una aplicación de GitHub o un token de acceso personal para que la CLI de CodeQL la use. Debe usar un token de acceso o una aplicación de GitHub con el security_events permiso de escritura.
Podría permitir que la CLI de CodeQL use el mismo token si los servidores de CI ya usan un token con este ámbito para consultar repositorios de GitHub. De lo contrario, cree un token con el permiso de escritura security_events y agréguelo al almacén de secretos del sistema de CI. Un procedimiento recomendado para la seguridad es establecer la --github-auth-stdin marca y pasar el token al comando a través de la entrada estándar.
Carga de resultados de SARIF
Para que el análisis de código muestre los resultados de una herramienta de análisis estático que no es de Microsoft en el repositorio de GitHub, los resultados deben almacenarse en un archivo SARIF que admita un subconjunto específico del esquema JSON SARIF 2.1.0. Puede cargar los resultados mediante la API de análisis de código o la CLI de CodeQL.
Cada vez que cargue los resultados de un nuevo examen de código, CodeQL procesa los resultados y agrega alertas al repositorio. Para evitar alertas duplicadas para el mismo problema, el examen de código usa la propiedad SARIF partialFingerprints para que coincidan con los resultados en varias ejecuciones para que aparezcan solo una vez en la última ejecución de la rama seleccionada. La eliminación de duplicados permite hacer coincidir las alertas con la línea de código correcta cuando se editan los archivos.
El identificador de regla de un resultado debe ser el mismo en todos los análisis. Los datos de huellas digitales se incluyen automáticamente en los archivos SARIF creados mediante el flujo de trabajo de análisis de CodeQL o el ejecutor de CodeQL.
Las especificaciones SARIF usan el nombre de propiedad JSON partialFingerprints, un diccionario de tipos de huellas digitales con nombre para la huella digital. Esta propiedad contiene, como mínimo, un valor para primaryLocationLineHash, que proporciona una huella digital basada en el contexto de la ubicación principal.
GitHub intenta rellenar el partialFingerprints campo de los archivos de origen si carga un archivo SARIF mediante la upload-sarif acción y faltan estos datos. Además, si carga un archivo SARIF sin datos de huellas digitales mediante el punto de conexión de la API /code-scanning/sarifs, es posible que los usuarios vean alertas duplicadas cuando se procesan y muestran alertas de análisis de código.
Para evitar ver alertas duplicadas mientras trabaja con herramientas de análisis estáticos, calcule los datos de huellas digitales y rellene la partialFingerprints propiedad antes de cargar el archivo SARIF. Un punto de partida útil es usar el mismo script que la upload-sarif acción.