trailmark: update skills to use v0.4.0 features

This commit is contained in:
Scott Arciszewski
2026-06-10 16:08:30 -04:00
parent d5fe2e6a78
commit bfcf747c5e
11 changed files with 397 additions and 38 deletions
+2 -2
View File
@@ -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"
+2 -2
View File
@@ -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"
+30 -6
View File
@@ -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.
+92 -3
View File
@@ -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`.