Skip to content
KitploitKITPLOIT
StrumentiExploitsBlog
Log in
Invia
StrumentiExploitsBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

FeedContattoPrivacy© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
trailmark — Crea e interroga una rappresentazione a database grafico del codice sorgente. | Kitploit
Strumenti/GitHubGitHub/trailofbits/trailmark
Analisi StaticaAnalisi delle VulnerabilitàAnalisi del CodiceFuzzingAnalisi di BinariApprendimento e Formazione
GitHubtrailofbits/trailmark

trailmark

Crea e interroga una rappresentazione a database grafico del codice sorgente.

Vedi Repository
46329253 giorni faRevisionato da Kitploit

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi
# Trailmark

[![CI](https://github.com/trailofbits/trailmark/actions/workflows/ci.yml/badge.svg)](https://github.com/trailofbits/trailmark/actions/workflows/ci.yml)
[![Mutation Testing](https://github.com/trailofbits/trailmark/actions/workflows/mutation.yml/badge.svg)](https://github.com/trailofbits/trailmark/actions/workflows/mutation.yml)

Analizza il codice sorgente in grafi interrogabili di funzioni, classi, chiamate e annotazioni semantiche per l'analisi di sicurezza.

Trailmark utilizza [tree-sitter](https://tree-sitter.github.io/) per il parsing dell'AST indipendente dal linguaggio e [rustworkx](https://www.rustworkx.org/) per l'attraversamento dei grafi ad alte prestazioni. La visione a lungo termine è quella di combinare questo grafo con il mutation testing e il fuzzing guidato dalla copertura per identificare lacune tra le assunzioni e la copertura dei test che sono raggiungibili dall'input dell'utente.

## Come funziona

Trailmark opera in tre fasi: **parse**, **index** e **query**.

```mermaid
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
```

### 1. Analisi

Un parser specifico per linguaggio attraversa la directory, analizza ogni file in un AST di tree-sitter ed estrae:

- **Nodi** — funzioni, metodi, classi, struct, interfacce, trait, enum, moduli, namespace
- **Archi** — chiamate, ereditarietà, implementazione, contenimento, import
- **Metadati** — annotazioni di tipo, complessità ciclomatica, rami, docstring, tipi di eccezione

### Linguaggi supportati

| Linguaggio | Estensioni | Costrutti principali |
| --- | --- | --- |
| Python | `.py` | funzioni, classi, metodi |
| JavaScript | `.js`, `.jsx`, `.mjs`, `.cjs` | funzioni, classi, arrow function |
| TypeScript | `.ts`, `.tsx` | funzioni, classi, interfacce, enum |
| PHP | `.php` | funzioni, classi, interfacce, trait |
| Ruby | `.rb` | metodi, classi, moduli |
| C | `.c`, `.h` | funzioni, struct, enum |
| C++ | `.cpp`, `.hpp`, `.cc`, `.hh`, `.cxx`, `.hxx` | funzioni, classi, struct, namespace |
| C# | `.cs` | metodi, classi, interfacce, struct, enum, namespace |
| Java | `.java` | metodi, classi, interfacce, enum |
| Go | `.go` | funzioni, metodi, struct, interfacce |
| Rust | `.rs` | funzioni, struct, trait, enum, blocchi impl, blocchi `verus!` |
| Solidity | `.sol` | contratti, interfacce, librerie, funzioni, modifier, struct, enum |
| Cairo | `.cairo` | funzioni, trait, struct, enum, blocchi impl, contratti StarkNet |
| Circom | `.circom` | template, funzioni, segnali, componenti |
| Haskell | `.hs` | funzioni, tipi di dati, classi di tipo, istanze |
| Erlang | `.erl` | funzioni, record, behaviour, moduli |
| Miden Assembly | `.masm` | procedure, entrypoint, costanti, invocazioni |
| Swift | `.swift` | funzioni, classi, struct, enum, protocolli, estensioni |
| Objective-C | `.m`, `.mm`, `.h` | funzioni C, classi, metodi (denominazione basata su selettori) |
| Kotlin | `.kt`, `.kts` | funzioni, classi, interfacce, data class, oggetti, metodi |
| Dart | `.dart` | funzioni, classi, classi astratte, metodi, costruttori |
| Move | `.move` | moduli, funzioni, import, chiamate dirette |
| Tact | `.tact` | contratti, struct, receiver, funzioni |
| Func | `.fc`, `.func` | funzioni, include, chiamate dirette |
| Sway | `.sw` | interfacce ABI, struct, metodi impl, funzioni |
| Rego | `.rego` | pacchetti, import, regole di policy, chiamate a regole |
| Proto | `.proto` | servizi, RPC, messaggi, campi, enum |
| Thrift | `.thrift` | servizi, funzioni, struct, campi, enum |
| GraphQL | `.graphql`, `.gql` | tipi di oggetto, operazioni root, campi, enum |
| SQL | `.sql` | schemi, tabelle, viste, funzioni, procedure |

Il parser Rust estrae le funzioni ordinarie, `spec` e `proof` con i relativi corpi
dai blocchi `verus! { ... }`. La grammatica Verus attualmente supporta solo
blocchi delimitati da parentesi graffe; `verus!(...)` e `verus![...]` vengono saltati con un avviso.
I moduli inline mantengono gli ID qualificati (ad esempio, `file.inner:function`), e
i metodi mantengono il loro impl o trait di appartenenza. Ogni blocco viene analizzato in modo indipendente;
gli errori di sintassi producono un avviso con il file e la posizione del blocco.
Sono supportate anche le invocazioni qualificate come `vstd::prelude::verus!`.
Le funzioni di supporto locali mantengono la funzione che le racchiude nei loro ID (ad esempio,
`file:outer.inner`). Le chiamate a queste funzioni di supporto e gli import relativi come
`use super::*` vengono risolti verso le dichiarazioni presenti nel file.
I nomi locali ripetuti in blocchi diversi ricevono un suffisso di offset in byte per mantenere
i loro nodi distinti, e le chiamate rispettano l'ambito del blocco.
Anche le struct, enum, trait e i moduli inline locali mantengono la funzione che li racchiude:
ad esempio, `file:outer.Guard.drop` e `file:outer.local.helper`.
I loro corpi hanno chiamate e complessità separate. Le chiamate esplicite a tipi e moduli
locali noti vengono risolte all'interno del file. Le chiamate semplici cercano negli ambiti delle funzioni e dei moduli;
gli elementi associati usano percorsi come `Type::method`. Le chiamate con punto vengono risolte
solo quando il ricevente indica una struct o enum nota. Gli altri riceventi di valore,
incluso `self.method()`, rimangono non risolti. Le chiamate implicite ai distruttori e l'inferenza
del tipo del ricevente non sono modellate. Queste regole si applicano anche al Rust ordinario oltre che
ai blocchi Verus. Modificare il codice prima di una dichiarazione con un suffisso di offset cambia
il suo ID, quindi `trailmark diff` può segnalare quella dichiarazione come rimossa e aggiunta.

Trailmark non espande macro arbitrarie. Le invocazioni Verus letterali all'interno di
macro opache o corpi di funzione vengono saltate con un avviso. Le definizioni `macro_rules!`
non invocate non vengono espanse. Le firme di funzione senza corpi
e le chiamate nelle clausole `requires`/`ensures` non vengono estratte.

```mermaid
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
```

Gli ID dei nodi seguono lo schema `module:function`, `module:Class` o `module:Class.method` per una ricerca non ambigua. L'analisi delle directory risolve le chiamate semplici tra file quando esiste una definizione univoca; le chiamate tra file ambigue vengono lasciate al loro target originale best-effort e contrassegnate come `uncertain`. L'affidabilità degli archi è etichettata come `certain` (chiamate dirette, `self.method()`), `inferred` (accesso ad attributi su oggetti non-self) o `uncertain` (dispatch dinamico o risoluzione ambigua).

### 2. Indice

Il `GraphStore` carica il `CodeGraph` in un `PyDiGraph` di rustworkx e costruisce mappature bidirezionali ID/indice per un attraversamento rapido.

### 3. Query

Il `QueryEngine` fornisce un'API di alto livello sul grafo indicizzato:

| Metodo | Descrizione |
| --- | --- |
| `callers_of(name)` | Chiamanti diretti del target indicato |
| `callees_of(name)` | Chiamati diretti della sorgente indicata |
| `ancestors_of(name)` | Ogni funzione che può raggiungere transitivamente il target (slice verso l'alto) |
| `reachable_from(name)` | Ogni funzione raggiungibile transitivamente dalla sorgente |
| `paths_between(src, dst)` | Tutti i percorsi di chiamata semplici tra due nodi |
| `connect_subgraphs(source, target)` | Percorsi che collegano due sottografi indicati |
| `entrypoint_paths_to(name)` | Percorsi da qualsiasi entrypoint rilevato verso il target |
| `attack_surface()` | Entrypoint etichettati con livello di fiducia, valore dell'asset e attributi del parser quando presenti |
| `complexity_hotspots(n)` | Funzioni con complessità ciclomatica ≥ n |
| `functions_that_raise(exc)` | Funzioni la cui lista di eccezioni rilevata dal parser include `exc` |
| `generic_parameters(name)` | Parametri di tipo generico dichiarati da un nodo |
| `type_references(name)` | Riferimenti di tipo per parametri, valori di ritorno, eccezioni e bound generici |
| `annotate(name, kind, description, source)` | Aggiunge un'annotazione semantica a un nodo |
| `annotations_of(name, kind=None)` | Ottiene le annotazioni di un nodo, opzionalmente filtrate per tipo |
| `nodes_with_annotation(kind)` | Ogni nodo etichettato con il tipo di annotazione specificato |
| `clear_annotations(name, kind=None)` | Rimuove le annotazioni da un nodo |
| `diff_against(other)` | Diff strutturale del grafo di questo engine rispetto a un altro |
| `preanalysis()` | Esegue le passate di pre-analisi integrate e memorizza annotazioni/sottografi |
| `augment_sarif(path)` | Unisce i risultati SARIF nel grafo |
| `augment_weaudit(path)` | Unisce i risultati weAudit nel grafo |
| `augment_binary(path)` | Unisce un file JSON esterno di grafo di analisi binaria |
| `findings(kind=None)` | Restituisce i nodi che portano annotazioni di tipo finding |
| `subgraph(name)` | Restituisce i nodi in un sottografo indicato |
| `subgraph_edges(name)` | Restituisce gli archi indotti all'interno di un sottografo indicato |
| `subgraph_names()` | Elenca ogni sottografo indicato attualmente presente nel grafo |
| `summary()` | Conteggi dei nodi, conteggi degli archi, dipendenze |
| `to_json()` | Esportazione completa del grafo |

### Modello dei dati

```mermaid
classDiagram
    class CodeGraph {
        language: str
        root_path: str
        nodes: dict[str, CodeUnit]
        edges: list[CodeEdge]
        annotations: dict[str, list[Annotation]]
        entrypoints: dict[str, EntrypointTag]
        dependencies: list[str]
        add_annotation(node_id, annotation)
        clear_annotations(node_id, kind=None)
        merge(other)
    }

    class CodeUnit {
        id: str
        name: str
        kind: NodeKind
        location: SourceLocation
        parameters: tuple[Parameter]
        return_type: TypeRef
        exception_types: tuple[TypeRef]
        cyclomatic_complexity: int
        branches: tuple[BranchInfo]
        docstring: str
    }

    class CodeEdge {
        source_id: str
        target_id: str
        kind: EdgeKind
        confidence: EdgeConfidence
    }

    class Annotation {
        kind: AnnotationKind
        description: str
        source: str
    }

    class EntrypointTag {
        kind: EntrypointKind
        trust_level: TrustLevel
        description: str
        asset_value: AssetValue
    }

    CodeGraph "1" *-- "*" CodeUnit
    CodeGraph "1" *-- "*" CodeEdge
    CodeGraph "1" *-- "*" Annotation
    CodeGraph "1" *-- "*" EntrypointTag
```

**Tipi di nodo:** `function`, `method`, `class`, `module`, `struct`, `interface`, `trait`, `enum`, `namespace`, `contract`, `library`, `template`, `proxy`

**Origini dei nodi:** `source`, `proxy`, `binary`, `synthetic`

**Tipi di arco:** `calls`, `inherits`, `implements`, `contains`, `imports`, `resolves_to`, `type_uses`, `specializes`, `corresponds_to`

**Affidabilità dell'arco:** `certain`, `inferred`, `uncertain`

Le chiamate non risolte vengono materializzate come nodi proxy quali
`proxy.unresolved:<raw-symbol>` in modo che i risultati dell'attraversamento possano mostrare dove
l'analisi del codice sorgente ha perso la risoluzione invece di scartare silenziosamente quell'arco. Le analisi
di directory, incluse le analisi a singolo linguaggio, applicano anche i collegamenti del repository e
materializzano gli archi `TYPE_USES`. Il supporto all'analisi binaria importa grafi di chiamate JSON
esterni; Trailmark non disassembla gli eseguibili da solo.

### Grafo di esempio

Dato questo codice Python:

```python
class Auth:
    def verify(self, token: str) -> bool:
        return self._check_sig(token)

    def _check_sig(self, token: str) -> bool:
        ...

def handle_request(req: Request) -> Response:
    auth = Auth()
    if auth.verify(req.token):
        return process(req)
    return deny()
```

Trailmark produce un grafo come:

```mermaid
graph TD
    HR["handle_request"] -->|calls| AV["Auth.verify"]
    HR -->|calls| P["process"]
    HR -->|calls| D["deny"]
    AV -->|calls| CS["Auth._check_sig"]
    A["Auth"] -->|contains| AV
    A -->|contains| CS

    classDef fn fill:#007bff26,stroke:#007bff,color:#007bff
    classDef cls fill:#6f42c126,stroke:#6f42c1,color:#6f42c1

    class HR,P,D fn
    class A,AV,CS cls
```

## Installazione

Gli esempi seguenti fanno riferimento al branch di sviluppo corrente. Per l'ultimo
pacchetto pubblicato, installa da PyPI. Per l'esatto set di funzionalità descritto
qui, installa da un checkout ed esegui i comandi tramite `uv run`.

```bash
# Latest published release
uv pip install trailmark

# Current checkout / development branch
uv sync --all-groups
```

Richiede Python &ge; 3.12.

Trailmark richiede `tree-sitter-language-pack` 1.22.1 o versione successiva per la maggior parte delle grammatiche.
Le cache delle grammatiche di proprietà di root sono supportate quando non sono scrivibili da gruppo o da altri utenti.
Le release attuali utilizzano l'archivio dei certificati della piattaforma per il download delle grammatiche. In
ambienti con ispezione TLS o offline, pre-popolare la cache dei pacchetti con
`python -c "import tree_sitter_language_pack as p; p.download_all()"` su una
piattaforma corrispondente, quindi copiare la directory della cache `tree-sitter-language-pack` risultante
sulla macchina di destinazione. È inoltre supportata la variabile `HTTPS_PROXY`. La grammatica SQL
viene distribuita come dipendenza del wheel `tree-sitter-sql` e non utilizza quella cache.

Le grammatiche incluse Circom, Miden Assembly e Verus vengono compilate al primo utilizzo.
Ciò richiede un compilatore C (`cc`), gli header di sviluppo di Python e l'accesso in scrittura
alle directory delle grammatiche installate. I file Rust senza blocchi Verus non
attivano la compilazione. Se la grammatica Verus non può essere compilata o caricata, Trailmark
registra un avviso, salta i blocchi Verus e continua ad analizzare il normale Rust.
La cache dei binding Verus include i contenuti del codice sorgente compilato, l'ABI di Python,
la piattaforma e le opzioni del compilatore. Input modificati selezionano un nuovo binding; le build
completate vengono pubblicate in modo atomico. I fallimenti del compilatore includono lo stato di uscita e
stderr nell'avviso.

I checkout Git mantengono la definizione della grammatica Verus e generano il relativo parser C durante
le build con Tree-sitter CLI 0.27.1. Prima di `uv sync` o `uv build` in un checkout,
eseguire `npm ci` con gli script di installazione di npm abilitati; Node.js è necessario per la generazione.
Il parser e gli header generati sono esclusi da git. I wheel e le distribuzioni sorgente
includono questi sorgenti generati, quindi l'installazione dei pacchetti pubblicati non richiede né
Node.js né la Tree-sitter CLI.
Le installazioni modificabili con `uv` rieseguono l'hook di build per ripristinare i file generati mancanti;
gli input invariati riutilizzano l'output esistente.

## Utilizzo

```bash
# Report the installed version
trailmark --version     # or: trailmark -V
trailmark version       # subcommand form

# Full JSON graph (Python, the default)
trailmark analyze path/to/project

# Analyze a different language
trailmark analyze --language rust path/to/project
trailmark analyze --language javascript path/to/project

# Polyglot: auto-detect and merge every supported language found in the
# tree, or pass an explicit comma-separated list.
trailmark analyze --language auto path/to/project
trailmark analyze --language python,rust,solidity path/to/project

# Summary statistics
trailmark analyze --summary path/to/project

# Complexity hotspots (threshold >= 10)
trailmark analyze --complexity 10 path/to/project

# Augment the graph with external findings (SARIF from static analyzers,
# weAudit findings from the VS Code extension). Each --sarif / --weaudit
# flag is repeatable. Add --json to print the augmented graph.
trailmark augment --sarif results.sarif path/to/project
trailmark augment --weaudit findings.json path/to/project
trailmark augment --sarif a.sarif --sarif b.sarif --json path/to/project

# List detected entrypoints (attack surface). Uses heuristic detection
# (main() functions, pyproject.toml [project.scripts]) plus an optional
# override file at .trailmark/entrypoints.toml (see below).
trailmark entrypoints path/to/project
trailmark entrypoints --json path/to/project

# Structural diff between two code graphs. Accepts directory paths or
# git refs (branches, tags, commits). Surfaces added/removed nodes,
# call-edge changes, and — most usefully — attack-surface changes.
trailmark diff before/ after/
trailmark diff --repo . main HEAD          # compare git refs
trailmark diff --json before/ after/        # machine-readable output

# Generate a Mermaid diagram from the code graph. --type is required; the
# choices are call-graph, class-hierarchy, module-deps, containment,
# complexity, and data-flow. Use --focus to scope large graphs.
trailmark diagram --target path/to/project --type call-graph
trailmark diagram -t path/to/project -T call-graph -f parse_file --depth 3
trailmark diagram -t path/to/project -T complexity --threshold 5 --direction LR
```

### Rilevamento degli entrypoint

Trailmark popola automaticamente `graph.entrypoints` in modo che `attack_surface()`, la propagazione del taint e l'attraversamento dei confini di privilegio abbiano dati su cui lavorare. Il rilevamento avviene in quattro livelli, ciascuno dei quali sovrascrive il precedente:

1. **Euristica generica su `main`.** Qualsiasi funzione denominata `main` in qualsiasi linguaggio. Contrassegnata come `user_input` / `trusted_internal` / `low`.
2. **Scansione consapevole del framework.** Pattern di decoratori, attributi e visibilità per linguaggio — vedi la tabella sottostante.
3. **`pyproject.toml [project.scripts]`.** I target CLI espliciti ricevono una classificazione trust/asset aggiornata.
4. **File di override locale al repository.** Gli entrypoint curati manualmente in `.trailmark/entrypoints.toml` hanno sempre la precedenza.

Copertura dei framework:

| Linguaggio | Framework rilevati |
| --- | --- |
| Python | Flask, FastAPI, aiohttp, Click, Typer, Celery |
| JavaScript / TypeScript | NestJS, Next.js (App Router + Pages API), AWS Lambda |
| Java | Spring MVC / WebFlux, JAX-RS, listener Kafka, servlet |
| C# | ASP.NET Core, Azure Functions |
| PHP | Attributi `#[Route]` di Symfony + annotazioni legacy |
| Rust | actix-web, rocket, esportazioni FFI (`#[no_mangle]`, `pub extern "C"`), attributi async-main |
| Solidity | Visibilità `external` / `public` |
| Cairo / StarkNet | `#[external]`, `#[view]`, `#[l1_handler]`, `#[constructor]` |
| Circom | Dichiarazioni `component main` |
| Miden Assembly | Direttive `export.<name>` |
| Haskell | `main ::` / `main =` di livello superiore |
| Erlang | Funzioni elencate in `-export([...])` |
| Swift | Attributo app `@main` |
| Objective-C | Selettori del ciclo di vita di `UIApplicationDelegate` (es. `application:openURL:options:`) |
| Kotlin | Annotazioni Spring MVC / WebFlux (condivise con Java), metodi del ciclo di vita dei componenti Android (`onCreate`, `onReceive`, `onBind`, ...) |
| Dart | Marcatori native-callable `@pragma('vm:entry-point')` |
| Go | Registrazioni stdlib `http.HandleFunc` / `http.Handle`, registrazioni di handler in stile gin/chi/echo `<router>.GET/POST/...` |
| Ruby | Azioni dei controller Rails (classi che ereditano da `ApplicationController` / `ActionController::*`), metodi `perform` dei worker Sidekiq |
| C / C++ | Linkage `extern "C"`, `__attribute__((visibility("default")))`, `__declspec(dllexport)` |

Per tutto ciò che le euristiche non rilevano, dichiara gli entrypoint esplicitamente in `.trailmark/entrypoints.toml` nella radice del progetto. Il file supporta sia voci a nodo singolo sia voci basate su regole:

```toml
# Single-node entry
[[entrypoint]]
node = "my_module:handle_request"  # node id, or "module.path:function"
kind = "api"                       # user_input | api | database | file_system | third_party
trust = "untrusted_external"       # untrusted_external | semi_trusted_external | trusted_internal
asset_value = "high"               # high | medium | low
description = "HTTP POST /auth"

# Rule: every PHP script under public_html/ is a web-exposed entrypoint.
[[entrypoint]]
file_glob = "public_html/**/*.php"
kind = "user_input"
trust = "untrusted_external"
asset_value = "high"
description = "Web-exposed PHP script"

# Rule: any function that takes a PSR-7 request object.
[[entrypoint]]
param_type = "ServerRequestInterface"
kind = "api"
trust = "untrusted_external"
asset_value = "high"
description = "PSR-7 HTTP handler"

# Rule: functions named `handle_*`.
[[entrypoint]]
name_regex = "^handle_"
kind = "api"
trust = "untrusted_external"

# Rule: conditions compose with AND — web.py files AND name starts with handle_.
[[entrypoint]]
file_glob = "public/*.py"
name_regex = "^handle_"
kind = "api"
trust = "untrusted_external"
```

Le voci successive sovrascrivono quelle precedenti quando due regole etichettano lo stesso nodo, quindi inserisci prima le regole generiche e dopo le correzioni specifiche.

Consulta [docs/entrypoint-patterns.md](https://github.com/trailofbits/trailmark/blob/main/docs/entrypoint-patterns.md) per il riferimento completo, inclusi framework non ancora implementati (Express / Koa / Fastify, Laravel, Cobra, axum, warp, clap e altri) con pattern pronti per grep che i contributori possono usare per aggiungere nuovi rilevatori.

Il rilevamento Solidity utilizza i metadati del parser anziché regex sulle firme. Le dichiarazioni di interfaccia sono escluse, e gli override derivati annotano le implementazioni base corrispondenti con `solidity_overridden_by` invece di rimuovere l'entrypoint base. Le funzioni concrete `public` ed `external` rimangono entrypoint, incluse le funzioni `view` e `pure`; i loro attributi `solidity_visibility`, `solidity_mutability` e di override sono restituiti da `attack_surface()` così che i chiamanti possano distinguere l'esposizione in sola lettura e filtrare le implementazioni base sovrascritte. `attack_surface()` include gli attributi degli entrypoint specifici del parser quando sono associati al nodo del grafo sottostante.

### Collegamenti cross-language ed esterni

L'analisi polyglot unisce i grafi dei linguaggi, ma molte relazioni RPC, FFI, subprocess e host/contratto non sono visibili nella sintassi del codice sorgente. Dichiara queste relazioni in modo deterministico in `.trailmark/links.toml`:

```toml
[[link]]
source = "backend:submit"
target = "contract:Verifier.verify"
kind = "calls"                 # defaults to calls
confidence = "certain"         # defaults to inferred
description = "JSON-RPC eth_call"

[[link]]
source = "backend:notify"
target = "payments-webhook"
target_external = true         # required because target is unresolved
```

I riferimenti possono essere ID di nodo esatti o nomi/suffissi univoci. Riferimenti ambigui,
endpoint interni sconosciuti, valori enum non validi e TOML malformato generano
`ValueError`. Impostare `source_external = true` o `target_external = true`
consente esplicitamente quell'endpoint non risolto e crea un nodo proxy. Questo file
è un'interfaccia di configurazione pubblica stabile.

### Limitazioni dell'analisi

- `entrypoint_paths_to()` riporta la raggiungibilità del call-graph, non il flusso di dati
  controllato dall'attaccante. Usa i risultati della taint preanalysis come segnale separato
  approssimativo; Trailmark non esegue ancora analisi di taint interprocedurale.
- TypeScript risolve le chiamate dirette e i receiver semplici assegnati con
  `new ConcreteClass()`. Il dispatch tramite interfacce attraverso manifest, nomi di proprietà
  calcolati, container di dependency-injection e altri meccanismi dinamici rimane
  best-effort.
- Il supporto SQL è orientato a PostgreSQL ed estrae schemi, tabelle, viste,
  funzioni, procedure e dipendenze di routine/viste. Non è un validatore completo
  dei dialetti SQL né un analizzatore semantico delle query.

### API programmatica

```python
from trailmark.parse import parse_directory, parse_file
from trailmark.query.api import QueryEngine

# Parse-only API: get the raw CodeGraph without building GraphStore/QueryEngine.
graph = parse_file("path/to/file.py")
graph = parse_directory("path/to/project", language="auto")

# Single-language (default) or auto-detect + merge across all languages
engine = QueryEngine.from_directory("path/to/project")
engine = QueryEngine.from_directory("path/to/project", language="auto")
engine = QueryEngine.from_directory("path/to/project", language="python,rust")

# Direct neighbors
engine.callers_of("handle_request")
engine.callees_of("handle_request")

# Transitive slicing — who could reach this sink, or what could it reach?
engine.ancestors_of("Auth._check_sig")
engine.reachable_from("handle_request")

# Attack-surface paths from any detected entrypoint
engine.entrypoint_paths_to("Auth._check_sig")

# All call paths between two nodes
engine.paths_between("handle_request", "Auth._check_sig")

# Functions with cyclomatic complexity >= 10
engine.complexity_hotspots(10)

# What functions can raise a given exception? (uses parser-detected
# exception_types; no runtime tracing required)
engine.functions_that_raise("PermissionError")

# Add and query semantic annotations
from trailmark.models.annotations import AnnotationKind

engine.annotate(
    "handle_request",
    AnnotationKind.ASSUMPTION,
    "Caller has already authenticated the session token",
    source="llm",
)
engine.annotations_of("handle_request")
engine.nodes_with_annotation(AnnotationKind.FINDING)

# Diff against an earlier snapshot of the same codebase
before = QueryEngine.from_directory("before/")
diff = engine.diff_against(before)
# diff contains: summary_delta, nodes {added/removed/modified},
# edges {added/removed}, entrypoints {added/removed/modified}

# Run the built-in audit-oriented preanalysis passes
engine.preanalysis()
engine.findings()
engine.subgraph_names()

# Programmatic augmentation hooks for external tooling
engine.augment_sarif("results.sarif")
engine.augment_weaudit("findings.json")
```

`NodeKind.SCHEMA`, `TABLE`, `VIEW` e `PROCEDURE` sono additivi in v0.5.0;
i consumatori che effettuano il match esaustivo dei valori enum dovrebbero aggiungere casi per essi.

## Sviluppo

```bash
# Install package and dev dependencies
npm ci
uv sync --all-groups

# Lint and format
uv run ruff check --fix
uv run ruff format

# Type check
uv run --locked ty check

# Tests
uv run pytest -q tests/

# Mutation testing with pytest-gremlins
uv run python -m tests.run_mutations --reviewed --workers=4
uv run python -m tests.mutation_gate

# Full repository mutation campaign
uv run python -m tests.run_mutations --workers=4
```

I report di mutazione vengono scritti in `coverage/gremlins/`. Le pull request mutano i file Rust/Verus indicati in `tests/mutation_baseline.json` e sottopongono a gate le funzioni nuove e modificate presenti in essi. Le campagne sull'intero repository vengono eseguite sui push verso main e tramite l'attivazione manuale del workflow di mutazione. Le funzioni legacy non modificate sono identificate tramite hash del sorgente ed entrano automaticamente nel gate quando vengono modificate. Il gate rifiuta i sopravvissuti non revisionati, i test/risultati mancanti, gli errori e i timeout. Le eccezioni richiedono una motivazione e un'impronta (fingerprint) della funzione e della mutazione; modificare quella funzione richiede di rivedere nuovamente le sue eccezioni.

Il runner fissa pytest-gremlins alla versione 1.9.0 e utilizza veri sottoprocessi pytest per ogni mutante, incluse fixture e test parametrizzati. Ogni mutante mantiene l'intera suite di test selezionata: i test unitari dei componenti vengono eseguiti per primi, seguiti dai test rimanenti, fermandosi al primo fallimento. I mutanti degli helper condivisi mantengono i test di ogni linguaggio. I test restano nei loro file esistenti; `tests/mutation_order.py` definisce l'ordinamento, e i componenti sconosciuti mantengono l'ordine di raccolta. Il runner salta la pre-scansione ridondante della copertura, verifica la baseline pulita e ogni distinto ordinamento dei test strumentati, e preserva i metadati del pacchetto e gli ID esatti dei parametri durante il caricamento del codice mutato.
Queste misure di compatibilità affrontano problemi del runner upstream e della selezione; i loro test di controllo devono passare prima di aggiornare il plugin.
Gli aggiornamenti degli snapshot (`TRAILMARK_UPDATE_SNAPSHOTS=1`) sono disabilitati durante CI e le esecuzioni di mutazione. Per concentrare una campagna locale, passare `--targets` con percorsi sorgente separati da virgole, seguiti dai file di test pertinenti. `--workers=4` abilita già l'esecuzione parallela. Usare `--test-order=collection` per confrontare con l'ordine originale di pytest.

## Licenza

Apache-2.0
Scarica lo strumento