mirror of
https://github.com/trailofbits/skills
synced 2026-06-21 14:12:00 +00:00
trailmark: update skills to use v0.4.0 features
This commit is contained in:
@@ -225,8 +225,8 @@
|
||||
},
|
||||
{
|
||||
"name": "trailmark",
|
||||
"version": "0.8.2",
|
||||
"description": "Builds multi-language source code graphs for security analysis: call graphs, attack surface mapping, blast radius, taint propagation, complexity hotspots, and entry point enumeration. Generates Mermaid diagrams (call graphs, class hierarchies, dependency maps, heatmaps). Compares code graph snapshots for structural diff and evolution analysis. Runs graph-informed mutation testing triage (genotoxic). Generates mutation-driven test vectors (vector-forge). Extracts crypto protocol message flows and converts Mermaid diagrams to ProVerif models. Projects SARIF and weAudit findings onto code graphs. Use when analyzing call paths, mapping attack surface, visualizing code architecture, triaging survived mutants, generating cryptographic test vectors, diagramming crypto protocols, formally verifying protocols, or augmenting audits with static analysis findings.",
|
||||
"version": "0.8.3",
|
||||
"description": "Builds multi-language source and binary code graphs for security analysis: call graphs, attack surface mapping, blast radius, taint propagation, complexity hotspots, entry point enumeration, proxy/unresolved-call tracking, type/reference analysis, and structural diffs. Generates Mermaid diagrams, runs graph-informed mutation testing triage (genotoxic), generates mutation-driven test vectors (vector-forge), extracts crypto protocol message flows, converts Mermaid diagrams to ProVerif models, and projects SARIF/weAudit/binary findings onto code graphs. Use when analyzing call paths, mapping attack surface, visualizing code architecture, triaging survived mutants, generating cryptographic test vectors, diagramming crypto protocols, formally verifying protocols, or augmenting audits with static analysis findings.",
|
||||
"author": {
|
||||
"name": "Scott Arciszewski",
|
||||
"url": "https://github.com/tob-scott-a"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "trailmark",
|
||||
"version": "0.8.2",
|
||||
"description": "Builds multi-language source code graphs for security analysis: call graphs, attack surface mapping, blast radius, taint propagation, complexity hotspots, and entry point enumeration. Generates Mermaid diagrams (call graphs, class hierarchies, dependency maps, heatmaps). Compares code graph snapshots for structural diff and evolution analysis. Runs graph-informed mutation testing triage (genotoxic). Generates mutation-driven test vectors (vector-forge). Extracts crypto protocol message flows and converts Mermaid diagrams to ProVerif models. Projects SARIF and weAudit findings onto code graphs. Use when analyzing call paths, mapping attack surface, visualizing code architecture, triaging survived mutants, generating cryptographic test vectors, diagramming crypto protocols, formally verifying protocols, or augmenting audits with static analysis findings.",
|
||||
"version": "0.8.3",
|
||||
"description": "Builds multi-language source and binary code graphs for security analysis: call graphs, attack surface mapping, blast radius, taint propagation, complexity hotspots, entry point enumeration, proxy/unresolved-call tracking, type/reference analysis, and structural diffs. Generates Mermaid diagrams, runs graph-informed mutation testing triage (genotoxic), generates mutation-driven test vectors (vector-forge), extracts crypto protocol message flows, converts Mermaid diagrams to ProVerif models, and projects SARIF/weAudit/binary findings onto code graphs. Use when analyzing call paths, mapping attack surface, visualizing code architecture, triaging survived mutants, generating cryptographic test vectors, diagramming crypto protocols, formally verifying protocols, or augmenting audits with static analysis findings.",
|
||||
"author": {
|
||||
"name": "Scott Arciszewski",
|
||||
"url": "https://github.com/tob-scott-a"
|
||||
|
||||
@@ -2,9 +2,33 @@
|
||||
|
||||
**Source code graph analysis for security auditing.** Parses code into queryable graphs of functions, classes, and calls, then uses that structure for diagram generation, mutation testing triage, protocol verification, and differential review.
|
||||
|
||||
These skills target Trailmark 0.2.x. Prefer `--language auto`,
|
||||
`trailmark.parse.detect_languages()`, and `QueryEngine.preanalysis()`
|
||||
instead of older 0.1.x-era manual language detection workflows.
|
||||
These skills support Trailmark 0.2.x through the 0.4.0 release line. Prefer
|
||||
`--language auto`, `trailmark.parse.detect_languages()`, and
|
||||
`QueryEngine.preanalysis()` for the v0.2-safe core workflow. Before using
|
||||
features added for v0.4.0, check the installed Trailmark version or probe for
|
||||
the method/CLI command first.
|
||||
|
||||
## Compatibility
|
||||
|
||||
Use this guard before relying on v0.4-only features:
|
||||
|
||||
```bash
|
||||
trailmark --version 2>/dev/null || uv run trailmark --version 2>/dev/null
|
||||
```
|
||||
|
||||
If the command reports `trailmark 0.4.0` or newer, the expanded v0.4 feature
|
||||
set is available. If the command is missing or reports an older version, stay
|
||||
on the v0.2-safe surface: `analyze`, `--language auto`, `detect_languages()`,
|
||||
`QueryEngine.from_directory()`, `summary()`, `complexity_hotspots()`,
|
||||
`attack_surface()`, `preanalysis()`, annotations, and SARIF/weAudit
|
||||
augmentation.
|
||||
|
||||
v0.4.0 adds expanded parser coverage, explicit proxy nodes for unresolved calls,
|
||||
node origins (`source`, `proxy`, `binary`, `synthetic`), new edge kinds
|
||||
(`resolves_to`, `type_uses`, `specializes`, `corresponds_to`), transitive query
|
||||
helpers, entrypoint path queries, subgraph edge queries, generic/type-reference
|
||||
queries, CLI `version`/`entrypoints`/`diff`/`diagram`, and binary graph
|
||||
augmentation via `augment_binary()`.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
@@ -18,14 +42,14 @@ uv pip install trailmark
|
||||
|
||||
| Skill | Description |
|
||||
|-------|-------------|
|
||||
| `trailmark` | Build and query multi-language code graphs with pre-analysis passes (blast radius, taint, privilege boundaries, entrypoints) |
|
||||
| `diagramming-code` | Generate Mermaid diagrams from code graphs (call graphs, class hierarchies, complexity heatmaps, data flow) |
|
||||
| `trailmark` | Build and query multi-language source/binary code graphs with pre-analysis passes, v0.4 feature gates, proxy nodes, type/reference queries, and structural traversal helpers |
|
||||
| `diagramming-code` | Generate Mermaid diagrams from code graphs (call graphs, class hierarchies, complexity heatmaps, data flow); v0.4 native diagram support is feature-gated |
|
||||
| `crypto-protocol-diagram` | Extract protocol message flow from source code or specs (RFC, ProVerif, Tamarin) into sequence diagrams |
|
||||
| `genotoxic` | Triage mutation testing results using graph analysis — classify survived mutants as false positives, missing tests, or fuzzing targets |
|
||||
| `vector-forge` | Mutation-driven test vector generation — find coverage gaps via mutation testing, then generate Wycheproof-style vectors that close them |
|
||||
| `graph-evolution` | Compare code graphs at two snapshots to surface security-relevant structural changes text diffs miss |
|
||||
| `mermaid-to-proverif` | Convert Mermaid sequence diagrams into ProVerif formal verification models |
|
||||
| `audit-augmentation` | Project SARIF and weAudit findings onto code graphs as annotations and subgraphs |
|
||||
| `audit-augmentation` | Project SARIF, weAudit, and v0.4 binary-analysis graph findings onto code graphs as annotations and subgraphs |
|
||||
| `trailmark-summary` | Quick structural overview (auto-detected languages, entry points, dependencies) for vivisect/galvanize |
|
||||
| `trailmark-structural` | Full structural analysis with all pre-analysis passes (blast radius, taint, privilege boundaries, complexity) |
|
||||
|
||||
|
||||
@@ -2,23 +2,28 @@
|
||||
name: audit-augmentation
|
||||
description: >
|
||||
Augments Trailmark code graphs with external audit findings from SARIF static
|
||||
analysis results and weAudit annotation files. Maps findings to graph nodes by
|
||||
analysis results, weAudit annotation files, and version-gated Trailmark 0.4.x
|
||||
binary-analysis graph exports. Maps findings to graph nodes by
|
||||
file and line overlap, creates severity-based subgraphs, and enables
|
||||
cross-referencing findings with pre-analysis data (blast radius, taint, etc.).
|
||||
Use when projecting SARIF results onto a code graph, overlaying weAudit
|
||||
annotations, cross-referencing Semgrep or CodeQL findings with call graph
|
||||
data, or visualizing audit findings in the context of code structure.
|
||||
annotations, importing binary graph findings, cross-referencing Semgrep,
|
||||
CodeQL, or binary-analysis findings with call graph data, or visualizing audit
|
||||
findings in the context of code structure.
|
||||
---
|
||||
|
||||
# Audit Augmentation
|
||||
|
||||
Projects findings from external tools (SARIF) and human auditors (weAudit)
|
||||
onto Trailmark code graphs as annotations and subgraphs.
|
||||
onto Trailmark code graphs as annotations and subgraphs. Trailmark 0.4.0+ can
|
||||
also import an external binary-analysis graph JSON export via
|
||||
`engine.augment_binary()`.
|
||||
|
||||
## When to Use
|
||||
|
||||
- Importing Semgrep, CodeQL, or other SARIF-producing tool results into a graph
|
||||
- Importing weAudit audit annotations into a graph
|
||||
- Importing binary-analysis graph data into a source graph (Trailmark 0.4.0+)
|
||||
- Cross-referencing static analysis findings with blast radius or taint data
|
||||
- Querying which functions have high-severity findings
|
||||
- Visualizing audit coverage alongside code structure
|
||||
@@ -50,6 +55,16 @@ onto Trailmark code graphs as annotations and subgraphs.
|
||||
uv pip install trailmark
|
||||
```
|
||||
|
||||
## Version Gate
|
||||
|
||||
SARIF and weAudit augmentation are v0.2-safe. Binary graph augmentation is
|
||||
Trailmark 0.4.0+ only. Before calling `engine.augment_binary()`, check:
|
||||
|
||||
```python
|
||||
if not hasattr(engine, "augment_binary"):
|
||||
raise RuntimeError("Binary augmentation requires Trailmark >= 0.4.0")
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### CLI
|
||||
@@ -68,6 +83,9 @@ uv run trailmark augment {targetDir} \
|
||||
--json
|
||||
```
|
||||
|
||||
Binary graph augmentation is programmatic in Trailmark 0.4.0+; do not invent a
|
||||
CLI flag if `trailmark augment --help` does not show one.
|
||||
|
||||
### Programmatic API
|
||||
|
||||
```python
|
||||
@@ -85,6 +103,10 @@ result = engine.augment_sarif("results.sarif")
|
||||
# Augment with weAudit
|
||||
result = engine.augment_weaudit(".vscode/alice.weaudit")
|
||||
|
||||
# Augment with an external binary graph export (v0.4+)
|
||||
if hasattr(engine, "augment_binary"):
|
||||
result = engine.augment_binary("binary_graph.json")
|
||||
|
||||
# Query findings
|
||||
engine.findings() # All findings
|
||||
engine.subgraph("sarif:error") # High-severity SARIF
|
||||
@@ -101,7 +123,7 @@ comma-separated list such as `python,rust`.
|
||||
```
|
||||
Augmentation Progress:
|
||||
- [ ] Step 1: Build graph and run pre-analysis
|
||||
- [ ] Step 2: Locate SARIF/weAudit files
|
||||
- [ ] Step 2: Locate SARIF/weAudit/binary graph files
|
||||
- [ ] Step 3: Run augmentation
|
||||
- [ ] Step 4: Inspect results and subgraphs
|
||||
- [ ] Step 5: Cross-reference with pre-analysis
|
||||
@@ -122,10 +144,15 @@ comma-separated list such as `python,rust`.
|
||||
- **SARIF**: Usually output by tools like `semgrep --sarif -o results.sarif`
|
||||
or `codeql database analyze --format=sarif-latest`
|
||||
- **weAudit**: Stored in `.vscode/<username>.weaudit` within the workspace
|
||||
- **Binary graph (v0.4+)**: External JSON with `artifact`, `functions`, and
|
||||
`calls` fields. Trailmark imports this graph; it does not disassemble
|
||||
binaries itself.
|
||||
|
||||
**Step 3:** Run augmentation via `engine.augment_sarif()` or
|
||||
`engine.augment_weaudit()`. Check `unmatched_findings` in the result — these
|
||||
are findings whose file/line locations didn't overlap any parsed code unit.
|
||||
`engine.augment_weaudit()`. For binary graphs, run `engine.augment_binary()`
|
||||
only after the Version Gate succeeds. Check `unmatched_findings` in SARIF and
|
||||
weAudit results — these are findings whose file/line locations didn't overlap
|
||||
any parsed code unit.
|
||||
|
||||
**Step 4:** Query findings and subgraphs. Use `engine.findings()` to list all
|
||||
annotated nodes. Use `engine.subgraph_names()` to see available subgraphs.
|
||||
@@ -157,6 +184,7 @@ Findings are stored as standard Trailmark annotations:
|
||||
| `weaudit:low` | Nodes with low-severity weAudit findings |
|
||||
| `weaudit:findings` | All weAudit findings (entryType=0) |
|
||||
| `weaudit:notes` | All weAudit notes (entryType=1) |
|
||||
| `binary:<artifact>` | Binary function nodes imported from a v0.4+ binary graph |
|
||||
|
||||
## How Matching Works
|
||||
|
||||
@@ -171,6 +199,24 @@ Findings are matched to graph nodes by file path and line range overlap:
|
||||
SARIF paths may be relative, absolute, or `file://` URIs — all are handled.
|
||||
weAudit uses 0-indexed lines which are converted to 1-indexed automatically.
|
||||
|
||||
Binary graph imports create `origin=binary` function nodes, `origin=proxy`
|
||||
external proxy nodes for unresolved binary calls, and inferred
|
||||
`corresponds_to` edges when a binary function maps back to a source node. The
|
||||
expected JSON shape is intentionally small:
|
||||
|
||||
```json
|
||||
{
|
||||
"artifact": {"name": "libexample", "architecture": "x86_64", "sha256": "..."},
|
||||
"functions": [
|
||||
{"symbol": "parse_packet", "address": "0x401000",
|
||||
"source": {"file": "src/parser.c", "line": 42}}
|
||||
],
|
||||
"calls": [
|
||||
{"source": "parse_packet", "target": "malloc", "confidence": "inferred"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Supporting Documentation
|
||||
|
||||
- **[references/formats.md](references/formats.md)** — SARIF 2.1.0 and
|
||||
|
||||
@@ -13,7 +13,9 @@ description: >
|
||||
|
||||
Generates Mermaid diagrams from Trailmark's code graph. A pre-made script
|
||||
handles Mermaid syntax generation; Claude selects the diagram type and
|
||||
parameters.
|
||||
parameters. Trailmark 0.4.0 includes a native `trailmark diagram` command; use
|
||||
it only after a version/command check, otherwise use this skill's bundled
|
||||
script.
|
||||
|
||||
## When to Use
|
||||
|
||||
@@ -42,6 +44,18 @@ uv pip install trailmark
|
||||
script uses Trailmark's parsed graph for accuracy. If installation fails,
|
||||
report the error to the user.
|
||||
|
||||
## Version Gate
|
||||
|
||||
Check whether native v0.4 diagram support exists:
|
||||
|
||||
```bash
|
||||
trailmark diagram --help 2>/dev/null || uv run trailmark diagram --help 2>/dev/null
|
||||
```
|
||||
|
||||
If this succeeds, you may use `trailmark diagram`. If it fails, use
|
||||
`uv run {baseDir}/scripts/diagram.py`, which keeps the older skill workflow
|
||||
intact. Do not assume the native CLI exists on Trailmark 0.2.x.
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
@@ -50,6 +64,11 @@ report the error to the user.
|
||||
uv run {baseDir}/scripts/diagram.py \
|
||||
--target {targetDir} --language auto --type call-graph \
|
||||
--focus main --depth 2
|
||||
|
||||
# Trailmark 0.4.0+ equivalent after the Version Gate succeeds
|
||||
uv run trailmark diagram \
|
||||
--target {targetDir} --language auto --type call-graph \
|
||||
--focus main --depth 2
|
||||
```
|
||||
|
||||
Output is raw Mermaid text. Wrap in a fenced code block:
|
||||
@@ -114,6 +133,8 @@ above.
|
||||
Default `--depth 2`. Use `--direction LR` for dependency flows.
|
||||
|
||||
**Step 4:** Run the script and capture stdout.
|
||||
If the native v0.4 CLI is available, either command is acceptable; prefer the
|
||||
bundled script when you need behavior consistent with this skill's references.
|
||||
|
||||
**Step 5:** Check: output starts with `flowchart` or `classDiagram`,
|
||||
contains at least one node. If empty or malformed, consult
|
||||
@@ -127,6 +148,8 @@ contains at least one node. If empty or malformed, consult
|
||||
|
||||
```
|
||||
uv run {baseDir}/scripts/diagram.py [OPTIONS]
|
||||
# or, on Trailmark 0.4.0+:
|
||||
uv run trailmark diagram [OPTIONS]
|
||||
```
|
||||
|
||||
| Argument | Short | Default | Description |
|
||||
|
||||
@@ -15,7 +15,9 @@ description: >
|
||||
Builds Trailmark code graphs at two source snapshots and computes a
|
||||
structural diff. Surfaces security-relevant changes that text-level
|
||||
diffs miss: new attack paths, complexity shifts, blast radius growth,
|
||||
taint propagation changes, and privilege boundary modifications.
|
||||
taint propagation changes, and privilege boundary modifications. Native
|
||||
`trailmark diff` is a Trailmark 0.4.0+ feature; use the API/plugin fallback
|
||||
when the installed build is older.
|
||||
|
||||
## When to Use
|
||||
|
||||
@@ -43,6 +45,7 @@ taint propagation changes, and privilege boundary modifications.
|
||||
| "Low-severity structural changes can be ignored" | INFO-level changes (dead code removal) can mask removed security checks | Classify every change, review removals for replaced functionality |
|
||||
| "One snapshot's graph is enough for comparison" | Single-snapshot analysis can't detect evolution — you need both before and after | Always build and export both graphs |
|
||||
| "Tool isn't installed, I'll compare manually" | Manual comparison misses what graph analysis catches | Install trailmark first |
|
||||
| "Native diff is always available" | Trailmark 0.2.x installs may not have the `diff` CLI | Check `trailmark diff --help` before using it |
|
||||
|
||||
---
|
||||
|
||||
@@ -58,6 +61,17 @@ uv pip install trailmark
|
||||
substitute for running trailmark. The tool must be installed and used
|
||||
programmatically. If installation fails, report the error.
|
||||
|
||||
Check for native v0.4 diff support before using `trailmark diff`:
|
||||
|
||||
```bash
|
||||
trailmark diff --help 2>/dev/null || uv run trailmark diff --help 2>/dev/null
|
||||
```
|
||||
|
||||
If unavailable, skip the native diff command and rely on `engine.diff_against()`
|
||||
when present plus `graph_diff.py` over exported JSON. If neither native diff nor
|
||||
`diff_against()` exists, still compute the plugin's JSON diff from the two
|
||||
exports and state that native entrypoint diff data was unavailable.
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
@@ -84,7 +98,7 @@ programmatically. If installation fails, report the error.
|
||||
│ └─ Read: references/report-format.md
|
||||
│
|
||||
├─ Already have two graph JSON exports?
|
||||
│ └─ Jump to Phase 3 (run native diff + graph_diff.py)
|
||||
│ └─ Jump to Phase 3 (run native diff if available + graph_diff.py)
|
||||
│
|
||||
└─ Starting from two git refs?
|
||||
└─ Start at Phase 1
|
||||
@@ -158,7 +172,10 @@ instead of `auto`.
|
||||
|
||||
### Phase 3: Compute Structural Diff
|
||||
|
||||
Run **both**:
|
||||
Run the native Trailmark diff only when the version gate above succeeds. Always
|
||||
run the plugin's `graph_diff.py` helper for subgraph membership changes.
|
||||
|
||||
When native diff is available, run **both**:
|
||||
|
||||
1. Trailmark's native structural diff for nodes, edges, and entrypoints
|
||||
2. The plugin's `graph_diff.py` helper for subgraph membership changes
|
||||
@@ -174,8 +191,19 @@ uv run {baseDir}/scripts/graph_diff.py \
|
||||
--after "{after_json}" > "{work_dir}/subgraph_diff.json"
|
||||
```
|
||||
|
||||
If either diff command fails or writes an empty JSON file, stop and report the
|
||||
error instead of continuing to Phase 4.
|
||||
If a diff command you chose to run fails or writes an empty JSON file, stop and
|
||||
report the error instead of continuing to Phase 4.
|
||||
|
||||
Fallback for Trailmark 0.2.x:
|
||||
|
||||
```bash
|
||||
uv run {baseDir}/scripts/graph_diff.py \
|
||||
--before "{before_json}" \
|
||||
--after "{after_json}" > "{work_dir}/subgraph_diff.json"
|
||||
```
|
||||
|
||||
In the report, mark native `trailmark_diff.json` as unavailable and interpret
|
||||
the `graph_diff.py` node/edge/subgraph output instead.
|
||||
|
||||
The native Trailmark diff contains:
|
||||
|
||||
@@ -197,8 +225,9 @@ The subgraph diff contains:
|
||||
|
||||
### Phase 4: Interpret Diff and Generate Report
|
||||
|
||||
Read **both** diff JSON files and generate a security-focused markdown
|
||||
report.
|
||||
Read every diff JSON file produced in Phase 3 and generate a security-focused
|
||||
markdown report. On Trailmark 0.2.x, this may be only `subgraph_diff.json`;
|
||||
state that native entrypoint diff data was unavailable.
|
||||
See [references/report-format.md](references/report-format.md) for
|
||||
the full template.
|
||||
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
---
|
||||
name: trailmark-structural
|
||||
description: "Runs full Trailmark structural analysis on Trailmark 0.2.x by building a graph, running `preanalysis()`, and reporting hotspots, taint, blast radius, privilege boundaries, and attack surface. Use when vivisect needs detailed structural data for a target. Triggers: structural analysis, blast radius, taint analysis, complexity hotspots."
|
||||
description: "Runs full Trailmark structural analysis by building a graph, running `preanalysis()`, and reporting hotspots, taint, blast radius, privilege boundaries, attack surface, and version-gated Trailmark 0.4.x data such as proxy counts, subgraph edges, and type/reference summaries. Use when vivisect needs detailed structural data for a target. Triggers: structural analysis, blast radius, taint analysis, complexity hotspots, proxy nodes, type references."
|
||||
allowed-tools: Bash Read Grep Glob
|
||||
---
|
||||
|
||||
# Trailmark Structural Analysis
|
||||
|
||||
Builds a Trailmark graph and runs `engine.preanalysis()` to compute all
|
||||
four pre-analysis passes.
|
||||
four pre-analysis passes. The core workflow is v0.2-safe; v0.4-only details
|
||||
are included only after checking method availability.
|
||||
|
||||
## When to Use
|
||||
|
||||
- Vivisect Phase 1 needs full structural data (hotspots, taint, blast radius, privilege boundaries)
|
||||
- Detailed pre-analysis passes for a specific target scope
|
||||
- Generating complexity and taint data for audit prioritization
|
||||
- Inspecting proxy/unresolved-call counts, subgraph edges, or type-reference
|
||||
summaries when Trailmark 0.4.0+ is installed
|
||||
|
||||
## When NOT to Use
|
||||
|
||||
@@ -29,6 +32,7 @@ four pre-analysis passes.
|
||||
| "One pass is sufficient" | Passes cross-reference each other — taint without blast radius misses critical nodes | Run all four passes |
|
||||
| "Tool isn't installed, I'll analyze manually" | Manual analysis misses what tooling catches | Report "trailmark is not installed" and return |
|
||||
| "Empty pass output means the pass failed" | Some passes produce no data for some codebases (e.g., no privilege boundaries) | Return full output regardless |
|
||||
| "A v0.4 field is always present" | Users may still have Trailmark 0.2.x installed | Probe with `hasattr()` before querying v0.4-only methods |
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -48,6 +52,14 @@ and return. Do NOT run `pip install`, `uv pip install`,
|
||||
`git clone`, or any install command. The user must install
|
||||
trailmark themselves.
|
||||
|
||||
Optionally record the version:
|
||||
|
||||
```bash
|
||||
trailmark --version 2>/dev/null || uv run trailmark --version 2>/dev/null || true
|
||||
```
|
||||
|
||||
Do not fail if this command is missing; use API feature probes below.
|
||||
|
||||
**Step 2: Detect languages with Trailmark's parse API.**
|
||||
|
||||
```bash
|
||||
@@ -85,10 +97,20 @@ preanalysis = engine.preanalysis()
|
||||
|
||||
def summarize_subgraph(name: str, limit: int = 25) -> dict[str, object]:
|
||||
nodes = engine.subgraph(name)
|
||||
return {
|
||||
summary = {
|
||||
"count": len(nodes),
|
||||
"sample_ids": [node["id"] for node in nodes[:limit]],
|
||||
}
|
||||
if hasattr(engine, "subgraph_edges"):
|
||||
summary["edge_count"] = len(engine.subgraph_edges(name))
|
||||
return summary
|
||||
|
||||
graph = json.loads(engine.to_json())
|
||||
nodes = graph.get("nodes", {})
|
||||
proxy_nodes = [
|
||||
node_id for node_id, node in nodes.items()
|
||||
if node.get("kind") == "proxy" or node.get("origin") == "proxy"
|
||||
]
|
||||
|
||||
payload = {
|
||||
"languages": languages,
|
||||
@@ -96,12 +118,19 @@ payload = {
|
||||
"preanalysis": preanalysis,
|
||||
"attack_surface": engine.attack_surface()[:25],
|
||||
"hotspots": engine.complexity_hotspots(10)[:25],
|
||||
"proxy_nodes": proxy_nodes[:25],
|
||||
"subgraphs": {
|
||||
name: summarize_subgraph(name)
|
||||
for name in engine.subgraph_names()
|
||||
},
|
||||
}
|
||||
|
||||
if hasattr(engine, "type_references"):
|
||||
payload["type_reference_samples"] = {
|
||||
node_id: engine.type_references(node_id)[:10]
|
||||
for node_id in list(nodes)[:25]
|
||||
}
|
||||
|
||||
print(json.dumps(payload, indent=2))
|
||||
PY
|
||||
```
|
||||
@@ -113,6 +142,7 @@ The output should include:
|
||||
- `summary`
|
||||
- `preanalysis`
|
||||
- `hotspots` (possibly empty)
|
||||
- `proxy_nodes` (empty on v0.2.x or when there are no unresolved calls)
|
||||
- `subgraphs` with counts and sample IDs
|
||||
|
||||
Some subgraphs may have zero nodes for some codebases (this is
|
||||
|
||||
@@ -7,6 +7,8 @@ allowed-tools: Bash Read Grep Glob
|
||||
# Trailmark Summary
|
||||
|
||||
Runs `trailmark analyze --language auto --summary` on a target directory.
|
||||
This is a v0.2-safe workflow; do not require Trailmark 0.4.0 just to produce a
|
||||
summary.
|
||||
|
||||
## When to Use
|
||||
|
||||
@@ -47,6 +49,15 @@ and return. Do NOT run `pip install`, `uv pip install`,
|
||||
`git clone`, or any install command. The user must install
|
||||
trailmark themselves.
|
||||
|
||||
Optionally record the version if the installed build supports it:
|
||||
|
||||
```bash
|
||||
trailmark --version 2>/dev/null || uv run trailmark --version 2>/dev/null || true
|
||||
```
|
||||
|
||||
Do not fail if the version command is missing; older v0.2.x builds may still
|
||||
support the summary workflow.
|
||||
|
||||
**Step 2: Detect languages with Trailmark's parse API.**
|
||||
|
||||
```bash
|
||||
@@ -81,3 +92,4 @@ The output must include ALL THREE of:
|
||||
If any are missing, report the gap. Do not fabricate output.
|
||||
|
||||
Return the detected language list plus the full Trailmark summary output.
|
||||
If a version string was available, include it in the returned metadata.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: trailmark
|
||||
description: "Builds and queries multi-language source code graphs for security analysis. Includes pre-analysis passes for blast radius, taint propagation, privilege boundaries, and entry point enumeration. Use when analyzing call paths, mapping attack surface, finding complexity hotspots, enumerating entry points, tracing taint propagation, measuring blast radius, or building a code graph for audit prioritization. Prefer `trailmark.parse.detect_languages()` or `--language auto` when the target language is unknown or polyglot."
|
||||
description: "Builds and queries multi-language source and binary code graphs for security analysis. Includes pre-analysis passes for blast radius, taint propagation, privilege boundaries, entry point enumeration, proxy/unresolved-call tracking, type/reference queries, structural traversal, graph diffs, and audit augmentation. Use when analyzing call paths, mapping attack surface, finding complexity hotspots, enumerating entry points, tracing taint propagation, measuring blast radius, importing SARIF/weAudit/binary findings, or building a code graph for audit prioritization. Feature-gate Trailmark 0.4.x APIs before using them; prefer `trailmark.parse.detect_languages()` or `--language auto` when the target language is unknown or polyglot."
|
||||
---
|
||||
|
||||
# Trailmark
|
||||
@@ -16,6 +16,8 @@ semantic metadata for security analysis.
|
||||
- Understanding call relationships in unfamiliar codebases
|
||||
- Security review or audit preparation across polyglot projects
|
||||
- Adding LLM-inferred annotations (assumptions, preconditions) to code units
|
||||
- Importing external binary-analysis graphs to connect source and binary views
|
||||
- Querying transitive slices, entrypoint paths, subgraph edges, or type references
|
||||
- Pre-analysis before mutation testing (genotoxic skill) or diagramming
|
||||
|
||||
## When NOT to Use
|
||||
@@ -35,6 +37,7 @@ semantic metadata for security analysis.
|
||||
| "Uncertain edges don't matter" | Dynamic dispatch is where type confusion bugs hide | Account for `uncertain` edges in security claims |
|
||||
| "Single-language analysis is enough" | Polyglot repos have FFI boundaries where bugs cluster | Use the correct `--language` flag per component |
|
||||
| "Complexity hotspots are the only thing worth checking" | Low-complexity functions on tainted paths are high-value targets | Combine complexity with taint and blast radius data |
|
||||
| "The docs mention a v0.4 method, so I can call it anywhere" | Many environments still have Trailmark 0.2.x installed | Check the installed version or probe method availability before using v0.4-only features |
|
||||
|
||||
---
|
||||
|
||||
@@ -52,6 +55,41 @@ source files by hand as a substitute for running trailmark. The tool must be
|
||||
installed and used programmatically. If installation fails, report the error
|
||||
to the user instead of silently switching to manual code reading.
|
||||
|
||||
## Version Gate
|
||||
|
||||
Trailmark 0.4.0 expands the graph model and query surface. Before using a
|
||||
feature listed as **v0.4+**, check the installed version:
|
||||
|
||||
```bash
|
||||
trailmark --version 2>/dev/null || uv run trailmark --version 2>/dev/null
|
||||
```
|
||||
|
||||
Accept `trailmark 0.4.0` or newer. If the version command is unavailable or
|
||||
reports `< 0.4.0`, use only the v0.2-safe baseline below. When working
|
||||
programmatically and version output is unavailable, probe with `hasattr()` and
|
||||
fall back instead of assuming the method exists:
|
||||
|
||||
```python
|
||||
if hasattr(engine, "entrypoint_paths_to"):
|
||||
paths = engine.entrypoint_paths_to("sensitive_sink")
|
||||
else:
|
||||
paths = [] # v0.2 fallback: use attack_surface() + paths_between()
|
||||
```
|
||||
|
||||
**v0.2-safe baseline:** `analyze`, `--language auto`,
|
||||
`detect_languages()`, `supported_languages()`, `QueryEngine.from_directory()`,
|
||||
`callers_of()`, `callees_of()`, `paths_between()`, `complexity_hotspots()`,
|
||||
`attack_surface()`, `summary()`, `to_json()`, `preanalysis()`,
|
||||
`annotate()`, `annotations_of()`, `findings()`, `subgraph()`,
|
||||
`subgraph_names()`, `augment_sarif()`, and `augment_weaudit()`.
|
||||
|
||||
**v0.4+ features:** CLI `--version`/`version`, native `entrypoints`,
|
||||
`diff`, and `diagram` subcommands; expanded parser coverage; proxy nodes for
|
||||
unresolved calls; node origins; binary graph augmentation; transitive
|
||||
`ancestors_of()` / `reachable_from()`; `connect_subgraphs()`;
|
||||
`entrypoint_paths_to()`; `nodes_with_annotation()`; `clear_annotations()`;
|
||||
`subgraph_edges()`; `generic_parameters()`; and `type_references()`.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
@@ -64,6 +102,12 @@ uv run trailmark analyze --language python,rust {targetDir}
|
||||
|
||||
# Complexity hotspots
|
||||
uv run trailmark analyze --language auto --complexity 10 {targetDir}
|
||||
|
||||
# v0.4+: version, entrypoint, diff, and native diagram commands
|
||||
uv run trailmark --version
|
||||
uv run trailmark entrypoints --language auto {targetDir}
|
||||
uv run trailmark diff --repo {repoDir} main HEAD --json
|
||||
uv run trailmark diagram -t {targetDir} -T call-graph -f main --depth 2
|
||||
```
|
||||
|
||||
### Programmatic API
|
||||
@@ -88,6 +132,15 @@ engine.attack_surface()
|
||||
engine.summary()
|
||||
engine.to_json()
|
||||
|
||||
# v0.4+: transitive slices and entrypoint path queries
|
||||
if hasattr(engine, "ancestors_of"):
|
||||
engine.ancestors_of("sensitive_sink")
|
||||
engine.reachable_from("entry_func")
|
||||
if hasattr(engine, "entrypoint_paths_to"):
|
||||
engine.entrypoint_paths_to("sensitive_sink")
|
||||
if hasattr(engine, "connect_subgraphs"):
|
||||
engine.connect_subgraphs("tainted", "privilege_boundary")
|
||||
|
||||
# Run pre-analysis (blast radius, entrypoints, privilege
|
||||
# boundaries, taint propagation)
|
||||
result = engine.preanalysis()
|
||||
@@ -98,6 +151,8 @@ engine.subgraph("tainted")
|
||||
engine.subgraph("high_blast_radius")
|
||||
engine.subgraph("privilege_boundary")
|
||||
engine.subgraph("entrypoint_reachable")
|
||||
if hasattr(engine, "subgraph_edges"):
|
||||
engine.subgraph_edges("tainted")
|
||||
|
||||
# Add LLM-inferred annotations
|
||||
from trailmark.models import AnnotationKind
|
||||
@@ -111,6 +166,18 @@ engine.annotations_of("function_name",
|
||||
kind=AnnotationKind.BLAST_RADIUS)
|
||||
engine.annotations_of("function_name",
|
||||
kind=AnnotationKind.TAINT_PROPAGATION)
|
||||
if hasattr(engine, "nodes_with_annotation"):
|
||||
engine.nodes_with_annotation(AnnotationKind.FINDING)
|
||||
if hasattr(engine, "clear_annotations"):
|
||||
engine.clear_annotations("function_name", kind=AnnotationKind.ASSUMPTION)
|
||||
|
||||
# v0.4+: generic/type-reference and binary augmentation APIs
|
||||
if hasattr(engine, "generic_parameters"):
|
||||
engine.generic_parameters("GenericTypeOrFunction")
|
||||
if hasattr(engine, "type_references"):
|
||||
engine.type_references("function_name")
|
||||
if hasattr(engine, "augment_binary"):
|
||||
engine.augment_binary("binary_graph.json")
|
||||
```
|
||||
|
||||
## Pre-Analysis Passes
|
||||
@@ -154,13 +221,26 @@ uv run trailmark analyze --language auto {targetDir}
|
||||
uv run trailmark analyze --language python,rust {targetDir}
|
||||
```
|
||||
|
||||
As of Trailmark 0.4.0, parser names include: `python`, `javascript`,
|
||||
`typescript`, `php`, `ruby`, `c`, `cpp`, `c_sharp`, `java`, `go`, `rust`,
|
||||
`solidity`, `cairo`, `circom`, `haskell`, `erlang`, `masm`, `swift`, `objc`,
|
||||
`kotlin`, `dart`, `move`, `tact`, `func`, `sway`, `rego`, `proto`, `thrift`,
|
||||
and `graphql`. Treat this list as documentation, not a source of truth; call
|
||||
`supported_languages()` on the installed build before relying on a parser.
|
||||
|
||||
## Graph Model
|
||||
|
||||
**Node kinds:** `function`, `method`, `class`, `module`, `struct`,
|
||||
`interface`, `trait`, `enum`, `namespace`, `contract`, `library`,
|
||||
`template`
|
||||
`template`; **v0.4+** also materializes unresolved references as `proxy`
|
||||
nodes.
|
||||
|
||||
**Edge kinds:** `calls`, `inherits`, `implements`, `contains`, `imports`
|
||||
**Node origins:** **v0.4+** nodes may carry origin `source`, `proxy`,
|
||||
`binary`, or `synthetic`. v0.2 exports may omit origin.
|
||||
|
||||
**Edge kinds:** `calls`, `inherits`, `implements`, `contains`, `imports`;
|
||||
**v0.4+** adds `resolves_to`, `type_uses`, `specializes`, and
|
||||
`corresponds_to`.
|
||||
|
||||
**Edge confidence:** `certain` (direct call, `self.method()`), `inferred`
|
||||
(attribute access on non-self object), `uncertain` (dynamic dispatch)
|
||||
@@ -192,6 +272,15 @@ paths. Mismatches are where vulnerabilities hide:
|
||||
**Edge confidence:** Dynamic dispatch produces `uncertain` edges. Account for
|
||||
confidence when making security claims.
|
||||
|
||||
**Proxy nodes (v0.4+):** Unresolved calls are preserved as nodes such as
|
||||
`proxy.unresolved:<symbol>`. Do not treat these as source code functions; use
|
||||
them to identify resolution gaps, dynamic dispatch, external APIs, or binary
|
||||
linkage candidates.
|
||||
|
||||
**Binary augmentation (v0.4+):** `engine.augment_binary()` imports an external
|
||||
binary-analysis graph JSON file. Trailmark connects it to source nodes when
|
||||
possible; it does not disassemble binaries itself.
|
||||
|
||||
**Subgraphs:** Named collections of node IDs produced by pre-analysis.
|
||||
Query with `engine.subgraph("name")`. Available after `engine.preanalysis()`.
|
||||
|
||||
|
||||
@@ -144,8 +144,16 @@ Query any subgraph:
|
||||
```python
|
||||
nodes = engine.subgraph("tainted")
|
||||
names = engine.subgraph_names()
|
||||
|
||||
# Trailmark 0.4.0+
|
||||
if hasattr(engine, "subgraph_edges"):
|
||||
tainted_call_edges = engine.subgraph_edges("tainted", edge_kinds=("calls",))
|
||||
```
|
||||
|
||||
Use `subgraph_edges()` only after checking for Trailmark 0.4.0+ or probing the
|
||||
method. On v0.2.x, export `engine.to_json()` and filter edges whose endpoints
|
||||
are both in `engine.subgraph(name)`.
|
||||
|
||||
---
|
||||
|
||||
## Annotation Reference
|
||||
|
||||
@@ -2,6 +2,24 @@
|
||||
|
||||
Common patterns for using Trailmark in security reviews.
|
||||
|
||||
## Version-Gated Queries
|
||||
|
||||
Use v0.2-safe APIs unless the installed build is Trailmark 0.4.0 or newer, or
|
||||
the method exists when probed with `hasattr()`.
|
||||
|
||||
```python
|
||||
from trailmark.query.api import QueryEngine
|
||||
|
||||
engine = QueryEngine.from_directory("{targetDir}", language="auto")
|
||||
|
||||
if hasattr(engine, "entrypoint_paths_to"):
|
||||
paths = engine.entrypoint_paths_to("sensitive_sink") # v0.4+
|
||||
else:
|
||||
paths = []
|
||||
for ep in engine.attack_surface():
|
||||
paths.extend(engine.paths_between(ep["node_id"], "sensitive_sink"))
|
||||
```
|
||||
|
||||
## 1. Mapping Attack Surface
|
||||
|
||||
Find all entrypoints and trace what they can reach:
|
||||
@@ -56,14 +74,71 @@ for caller in callers:
|
||||
Check if a function is reachable from any entrypoint:
|
||||
|
||||
```python
|
||||
paths = engine.entrypoint_paths_to("sensitive_function_id")
|
||||
if hasattr(engine, "entrypoint_paths_to"):
|
||||
paths = engine.entrypoint_paths_to("sensitive_function_id")
|
||||
else:
|
||||
paths = []
|
||||
for ep in engine.attack_surface():
|
||||
paths.extend(engine.paths_between(ep["node_id"], "sensitive_function_id"))
|
||||
|
||||
if paths:
|
||||
print(f"Reachable via {len(paths)} path(s)")
|
||||
else:
|
||||
print("Not reachable from any entrypoint")
|
||||
```
|
||||
|
||||
## 6. Full Graph Export
|
||||
For Trailmark 0.4.0+, prefer `entrypoint_paths_to()` because it searches all
|
||||
detected entrypoints directly. On older versions, use `attack_surface()` plus
|
||||
`paths_between()` as shown in the version-gated example above.
|
||||
|
||||
## 6. Transitive Slices
|
||||
|
||||
Trailmark 0.4.0+ exposes upward and downward transitive slices:
|
||||
|
||||
```python
|
||||
if hasattr(engine, "ancestors_of"):
|
||||
callers_to_sink = engine.ancestors_of("execute_query")
|
||||
downstream = engine.reachable_from("handle_request")
|
||||
else:
|
||||
callers_to_sink = engine.callers_of("execute_query")
|
||||
downstream = engine.callees_of("handle_request")
|
||||
```
|
||||
|
||||
Use `ancestors_of()` for "who could eventually reach this sink?" and
|
||||
`reachable_from()` for "what could this entrypoint or helper eventually call?"
|
||||
|
||||
## 7. Subgraph Connections
|
||||
|
||||
After `engine.preanalysis()`, Trailmark 0.4.0+ can connect named subgraphs and
|
||||
return induced edges:
|
||||
|
||||
```python
|
||||
engine.preanalysis()
|
||||
|
||||
if hasattr(engine, "connect_subgraphs"):
|
||||
paths = engine.connect_subgraphs("tainted", "privilege_boundary")
|
||||
if hasattr(engine, "subgraph_edges"):
|
||||
tainted_edges = engine.subgraph_edges("tainted")
|
||||
```
|
||||
|
||||
Use this when prioritizing tainted paths that cross trust boundaries.
|
||||
|
||||
## 8. Type and Generic Queries
|
||||
|
||||
Trailmark 0.4.0+ records type references and generic parameters where parsers
|
||||
can extract them:
|
||||
|
||||
```python
|
||||
if hasattr(engine, "type_references"):
|
||||
refs = engine.type_references("deserialize_request")
|
||||
if hasattr(engine, "generic_parameters"):
|
||||
params = engine.generic_parameters("Container")
|
||||
```
|
||||
|
||||
Use these to find parser, deserializer, FFI, or generic-bound hotspots where
|
||||
declared types are narrower than the effective input domain.
|
||||
|
||||
## 9. Full Graph Export
|
||||
|
||||
Export for use with other tools:
|
||||
|
||||
@@ -79,7 +154,11 @@ with open("graph.json", "w") as f:
|
||||
# metadata and per-node annotations.
|
||||
```
|
||||
|
||||
## 7. Multi-Language Analysis
|
||||
Trailmark 0.4.0+ exports proxy nodes for unresolved calls and may include
|
||||
`origin` on non-source nodes. Do not treat `origin=proxy` or `origin=binary`
|
||||
nodes as source locations during manual review.
|
||||
|
||||
## 10. Multi-Language Analysis
|
||||
|
||||
Ask Trailmark which languages it supports, detect what exists under the
|
||||
target tree, then choose `auto` or an explicit list:
|
||||
@@ -95,9 +174,19 @@ engine = QueryEngine.from_directory("{targetDir}", language="auto")
|
||||
engine = QueryEngine.from_directory("{targetDir}", language="python,rust")
|
||||
```
|
||||
|
||||
## 8. CLI Patterns
|
||||
As of Trailmark 0.4.0, supported parser names include `python`, `javascript`,
|
||||
`typescript`, `php`, `ruby`, `c`, `cpp`, `c_sharp`, `java`, `go`, `rust`,
|
||||
`solidity`, `cairo`, `circom`, `haskell`, `erlang`, `masm`, `swift`, `objc`,
|
||||
`kotlin`, `dart`, `move`, `tact`, `func`, `sway`, `rego`, `proto`, `thrift`,
|
||||
and `graphql`. Always call `supported_languages()` on the installed build
|
||||
before relying on this list.
|
||||
|
||||
## 11. CLI Patterns
|
||||
|
||||
```bash
|
||||
# Version check before v0.4-only commands
|
||||
uv run trailmark --version
|
||||
|
||||
# Quick summary with auto-detection
|
||||
uv run trailmark analyze --language auto --summary {targetDir}
|
||||
|
||||
@@ -108,11 +197,15 @@ uv run trailmark analyze --language python,rust --complexity 8 {targetDir}
|
||||
# Entrypoint inventory
|
||||
uv run trailmark entrypoints --language auto {targetDir}
|
||||
|
||||
# v0.4+: native diff and native diagram
|
||||
uv run trailmark diff --repo {repoDir} main HEAD --json
|
||||
uv run trailmark diagram -t {targetDir} -T call-graph -f main --depth 2
|
||||
|
||||
# Full JSON output for piping to other tools
|
||||
uv run trailmark analyze {targetDir} | jq '.nodes | to_entries[] | select(.value.cyclomatic_complexity > 10)'
|
||||
```
|
||||
|
||||
## 9. Annotation Workflow
|
||||
## 12. Annotation Workflow
|
||||
|
||||
Add semantic annotations after analyzing code with an LLM. Annotations
|
||||
persist on the in-memory graph and can be queried later:
|
||||
@@ -132,8 +225,13 @@ for ann in engine.annotations_of("handle_request"):
|
||||
assumptions = engine.annotations_of("handle_request", kind=AnnotationKind.ASSUMPTION)
|
||||
|
||||
# Clear annotations (all, or by kind)
|
||||
engine.clear_annotations("handle_request", kind=AnnotationKind.ASSUMPTION)
|
||||
engine.clear_annotations("handle_request")
|
||||
if hasattr(engine, "clear_annotations"):
|
||||
engine.clear_annotations("handle_request", kind=AnnotationKind.ASSUMPTION)
|
||||
engine.clear_annotations("handle_request")
|
||||
|
||||
# v0.4+: nodes with a given annotation
|
||||
if hasattr(engine, "nodes_with_annotation"):
|
||||
finding_nodes = engine.nodes_with_annotation(AnnotationKind.FINDING)
|
||||
```
|
||||
|
||||
**Annotation kinds:** `ASSUMPTION`, `PRECONDITION`, `POSTCONDITION`, `INVARIANT`.
|
||||
|
||||
Reference in New Issue
Block a user