
Construye y consulta una representación de base de datos de grafos del código fuente.
Analiza el código fuente en grafos consultables de funciones, clases, llamadas y anotaciones semánticas para el análisis de seguridad.
Trailmark utiliza tree-sitter para el análisis de AST independiente del lenguaje y rustworkx para el recorrido de grafos de alto rendimiento. La visión a largo plazo es combinar este grafo con pruebas de mutación y fuzzing guiado por cobertura para identificar brechas entre las suposiciones y la cobertura de pruebas que sean alcanzables desde la entrada del usuario.
Trailmark opera en tres fases: parse, index y query.
flowchart TD
A["Source Files"] --> B["tree-sitter Parser"]
B --> C["CodeGraph (nodes + edges)"]
C --> D["rustworkx GraphStore"]
D --> E["QueryEngine"]
E --> F["JSON / Summary / Hotspots"]
classDef src fill:#007bff26,stroke:#007bff,color:#007bff
classDef parse fill:#28a74526,stroke:#28a745,color:#28a745
classDef data fill:#6f42c126,stroke:#6f42c1,color:#6f42c1
classDef query fill:#ffc10726,stroke:#e6a817,color:#e6a817
class A src
class B parse
class C,D data
class E,F query
Un analizador específico del lenguaje recorre el directorio, analiza cada archivo en un AST de tree-sitter y extrae:
| Lenguaje | Extensiones | Construcciones clave |
|---|---|---|
| Python | .py | funciones, clases, métodos |
| JavaScript | .js, .jsx, .mjs, .cjs | funciones, clases, funciones flecha |
| TypeScript | .ts, .tsx | funciones, clases, interfaces, enums |
| PHP | .php | funciones, clases, interfaces, traits |
| Ruby | .rb | métodos, clases, módulos |
| C | .c, .h | funciones, structs, enums |
| C++ | .cpp, .hpp, .cc, .hh, .cxx, .hxx | funciones, clases, structs, espacios de nombres |
| C# | .cs | métodos, clases, interfaces, structs, enums, espacios de nombres |
| Java | .java | métodos, clases, interfaces, enums |
| Go | .go | funciones, métodos, structs, interfaces |
| Rust | .rs | funciones, structs, traits, enums, bloques impl, bloques verus! |
| Solidity | .sol | contratos, interfaces, librerías, funciones, modificadores, structs, enums |
| Cairo | .cairo | funciones, traits, structs, enums, bloques impl, contratos StarkNet |
| Circom | .circom | plantillas, funciones, señales, componentes |
| Haskell | .hs | funciones, tipos de datos, clases de tipos, instancias |
| Erlang | .erl | funciones, registros, comportamientos, módulos |
| Miden Assembly | .masm | procedimientos, puntos de entrada, constantes, invocaciones |
| Swift | .swift | funciones, clases, structs, enums, protocolos, extensiones |
| Objective-C | .m, .mm, .h | funciones C, clases, métodos (nombres basados en selectores) |
| Kotlin | .kt, .kts | funciones, clases, interfaces, clases de datos, objetos, métodos |
| Dart | .dart | funciones, clases, clases abstractas, métodos, constructores |
| Move | .move | módulos, funciones, importaciones, llamadas directas |
| Tact | .tact | contratos, structs, receptores, funciones |
| Func | .fc, .func | funciones, inclusiones, llamadas directas |
| Sway | .sw | interfaces ABI, structs, métodos impl, funciones |
| Rego | .rego | paquetes, importaciones, reglas de política, llamadas a reglas |
| Proto | .proto | servicios, RPCs, mensajes, campos, enums |
| Thrift | .thrift | servicios, funciones, structs, campos, enums |
| GraphQL | .graphql, .gql | tipos de objeto, operaciones raíz, campos, enums |
| SQL | .sql | esquemas, tablas, vistas, funciones, procedimientos |
El analizador de Rust extrae funciones ordinarias, spec y proof con cuerpos
de bloques verus! { ... }. La gramática de Verus actualmente solo admite
bloques delimitados por llaves; verus!(...) y verus![...] se omiten con una advertencia.
Los módulos en línea conservan IDs cualificados (por ejemplo, file.inner:function), y
los métodos conservan su propietario impl o trait. Cada bloque se analiza de forma independiente;
los errores de sintaxis producen una advertencia con la ubicación del archivo y del bloque.
También se admiten invocaciones cualificadas como vstd::prelude::verus!.
Las funciones auxiliares locales conservan su función contenedora en sus IDs (por ejemplo,
file:outer.inner). Las llamadas a estas funciones auxiliares y las importaciones relativas como
use super::* se resuelven a declaraciones presentes en el archivo.
Los nombres locales repetidos en distintos bloques reciben un sufijo de desplazamiento de bytes para mantener
sus nodos distintos, y las llamadas respetan el ámbito del bloque.
Los structs, enums, traits y módulos en línea locales también conservan la función contenedora:
por ejemplo, file:outer.Guard.drop y file:outer.local.helper.
Sus cuerpos tienen llamadas y complejidad separadas. Las llamadas explícitas a tipos y módulos
locales conocidos se resuelven dentro del archivo. Las llamadas sin cualificar buscan en los ámbitos de funciones y módulos;
los elementos asociados usan rutas como Type::method. Las llamadas con punto se resuelven
solo cuando el receptor nombra un struct o enum conocido. Otros receptores de valor,
incluido self.method(), permanecen sin resolver. Las llamadas implícitas a destructores y la inferencia
de tipos del receptor no se modelan. Estas reglas se aplican tanto al Rust ordinario como a los
bloques de Verus. Editar código antes de una declaración con un sufijo de desplazamiento cambia
su ID, por lo que trailmark diff puede informar esa declaración como eliminada y añadida.
Trailmark no expande macros arbitrarias. Las invocaciones literales de Verus dentro de
macros opacas o cuerpos de funciones se omiten con una advertencia. Las definiciones de
macro_rules! no invocadas no se expanden. Las firmas de funciones sin cuerpos
y las llamadas en cláusulas requires/ensures no se extraen.
flowchart TD
subgraph "Per-File Parsing"
F["Source file"] --> TS["tree-sitter AST"]
TS --> EX["Extract nodes"]
TS --> EC["Extract call edges"]
TS --> EB["Count branches"]
TS --> ET["Resolve types"]
end
EX --> CG["CodeGraph"]
EC --> CG
EB --> CG
ET --> CG
classDef src fill:#007bff26,stroke:#007bff,color:#007bff
classDef parse fill:#28a74526,stroke:#28a745,color:#28a745
classDef extract fill:#ffc10726,stroke:#e6a817,color:#e6a817
classDef data fill:#6f42c126,stroke:#6f42c1,color:#6f42c1
class F src
class TS parse
class EX,EC,EB,ET extract
class CG data
Los ID de nodo siguen el esquema module:function, module:Class o module:Class.method para una búsqueda inequívoca. El análisis de directorios resuelve las llamadas entre archivos sin calificar cuando existe una definición única; las llamadas entre archivos ambiguas se dejan en su objetivo original de mejor esfuerzo y se marcan como uncertain. La confianza de las aristas se etiqueta como certain (llamadas directas, self.method()), inferred (acceso a atributos en objetos que no son self) o uncertain (despacho dinámico o resolución ambigua).