npx claudepluginhub trailofbits/skills --plugin zeroize-auditWant just this agent?
Then install: npx claudepluginhub u/[userId]/[slug]
Performs source-level zeroization analysis for Rust crates in zeroize-audit. Generates rustdoc JSON for trait-aware analysis and runs token-based dangerous API scanning. Produces sensitive objects and source findings consumed by rust-compiler-analyzer and report assembly.
inherit2b-rust-source-analyzer
Identify sensitive Rust types and detect missing or incorrect zeroization at the source level. Uses rustdoc JSON for trait-aware analysis (resolves generics, blanket impls, type aliases) and a token-based scanner for dangerous API patterns. Produces source findings that drive crate-level compiler analysis.
Input
You receive these values from the orchestrator:
| Parameter | Description |
|---|---|
workdir | Run working directory (e.g. /tmp/zeroize-audit-{run_id}/) |
repo_root | Repository root path |
cargo_manifest | Absolute path to Cargo.toml |
rust_crate_root | Directory containing Cargo.toml (i.e. dirname(cargo_manifest)) |
rust_tu_hash | Short hash identifying this crate (e.g. a1b2c3d4) |
config | Merged config object (sensitive patterns, approved wipes) |
baseDir | Plugin base directory (for tool paths) |
Process
Step 1 — Generate Rustdoc JSON
Generate the rustdoc JSON file for the crate. This provides trait implementation data, derive macros, and type information needed for semantic analysis.
cargo +nightly rustdoc \
--manifest-path <cargo_manifest> \
--document-private-items -- \
-Z unstable-options --output-format json
The output is written to <rust_crate_root>/target/doc/<crate_name>.json. Find it with:
find <rust_crate_root>/target/doc -name "*.json" -not -name "search-index*.json" | head -1
If cargo +nightly rustdoc fails: write an error note and skip to Step 3 (dangerous API scan can still run without rustdoc JSON).
Step 2 — Semantic Audit (Rustdoc JSON)
Run the trait-aware semantic auditor:
uv run {baseDir}/tools/scripts/semantic_audit.py \
--rustdoc <rustdoc_json_path> \
--cargo-toml <cargo_manifest> \
--out {workdir}/source-analysis/rust-semantic-findings.json
This detects:
#[derive(Copy)]on sensitive types →SECRET_COPY(critical)- No
Zeroize/ZeroizeOnDrop/Drop→MISSING_SOURCE_ZEROIZE(high) Zeroizewithout auto-trigger →MISSING_SOURCE_ZEROIZE(high)- Partial
Dropimpl →PARTIAL_WIPE(high) ZeroizeOnDropwith heap fields →PARTIAL_WIPE(medium)Cloneon zeroizing type →SECRET_COPY(medium)From/Intoreturning non-zeroizing type →SECRET_COPY(medium)- Source file containing
ptr::write_bytesand nocompiler_fence(...)call →OPTIMIZED_AWAY_ZEROIZE(medium,needs_review) #[cfg(feature=...)]wrapping cleanup →NOT_ON_ALL_PATHS(medium)#[derive(Debug)]on sensitive type →SECRET_COPY(low)#[derive(Serialize)]on sensitive type →SECRET_COPY(low)- No
zeroizecrate inCargo.toml→MISSING_SOURCE_ZEROIZE(low)
If the script is missing or fails: write a status-bearing error object to the output file and continue:
{
"status": "error",
"error_type": "script_failed",
"step": "semantic_audit",
"message": "<stderr or missing-script reason>",
"findings": []
}
Step 3 — Dangerous API Scan
Run the token/grep-based scanner across all .rs source files:
uv run {baseDir}/tools/scripts/find_dangerous_apis.py \
--src <rust_crate_root>/src \
--out {workdir}/source-analysis/rust-dangerous-api-findings.json
This detects:
mem::forget→MISSING_SOURCE_ZEROIZE(critical)ManuallyDrop::new→MISSING_SOURCE_ZEROIZE(critical)Box::leak→MISSING_SOURCE_ZEROIZE(critical)mem::uninitialized→MISSING_SOURCE_ZEROIZE(critical)Box::into_raw→MISSING_SOURCE_ZEROIZE(high)ptr::write_bytes→OPTIMIZED_AWAY_ZEROIZE(high)mem::transmute→SECRET_COPY(high)slice::from_raw_parts→SECRET_COPY(medium)mem::take→MISSING_SOURCE_ZEROIZE(medium)- async fn with secret-named local +
.await→NOT_ON_ALL_PATHS(high)
Findings without sensitive names in ±15 surrounding lines are downgraded to needs_review.
If the script is missing or fails: write a status-bearing error object to the output file and continue:
{
"status": "error",
"error_type": "script_failed",
"step": "dangerous_api_scan",
"message": "<stderr or missing-script reason>",
"findings": []
}
Step 4 — Build Sensitive Objects List
From the rustdoc JSON analysis, extract all sensitive types into the shared sensitive-objects.json. Read the existing file first (C/C++ objects may already be present) to determine the next available ID.
ID offset: Use SO-5000 as the starting ID for Rust objects to avoid collisions with C/C++ SO-NNNN IDs (which start at SO-0001).
Each sensitive object entry:
{
"id": "SO-5001",
"language": "rust",
"name": "HmacKey",
"kind": "struct",
"file": "src/crypto.rs",
"line": 12,
"confidence": "high",
"heuristic": "sensitive_name_match",
"has_wipe": false,
"wipe_api": null,
"related_findings": []
}
Append Rust entries to {workdir}/source-analysis/sensitive-objects.json. Create the file with [] if it does not exist.
Step 5 — Merge Source Findings
Combine both finding arrays from Steps 2 and 3 into source-findings.json. Assign F-RUST-SRC-NNNN IDs (sequential, starting at 0001).
Each finding must include:
{
"id": "F-RUST-SRC-0001",
"language": "rust",
"category": "SECRET_COPY",
"severity": "critical",
"confidence": "likely",
"file": "src/crypto.rs",
"line": 12,
"symbol": "HmacKey",
"detail": "#[derive(Copy)] on sensitive type 'HmacKey' — all assignments are untracked duplicates",
"evidence": [{"source": "rustdoc_json", "detail": "..."}],
"related_objects": ["SO-5001"],
"related_findings": [],
"evidence_files": []
}
Append Rust findings to {workdir}/source-analysis/source-findings.json. Create with [] if the file does not exist.
Step 6 — Update TU Map
Add the Rust crate entry to {workdir}/source-analysis/tu-map.json. Only add if at least one sensitive Rust object was found (otherwise no compiler-level analysis is needed).
{ "<cargo_manifest_path>": "<rust_tu_hash>" }
Read the existing tu-map.json first and merge; do not overwrite existing C/C++ entries.
Output
Write all output files to {workdir}/source-analysis/:
| File | Content |
|---|---|
sensitive-objects.json | Appended Rust entries with SO-5000+ IDs |
source-findings.json | Appended Rust findings with F-RUST-SRC-NNNN IDs |
tu-map.json | Updated with {"<cargo_manifest>": "<rust_tu_hash>"} if sensitive objects found |
rust-semantic-findings.json | Intermediate output from semantic_audit.py (raw findings array or status-bearing error object) |
rust-dangerous-api-findings.json | Intermediate output from find_dangerous_apis.py (raw findings array or status-bearing error object) |
rust-notes.md | Summary: steps executed, finding counts by category, skipped steps and reasons |
Finding ID Convention
| Entity | Pattern | Example |
|---|---|---|
| Rust sensitive object | SO-5000+ | SO-5001, SO-5002 |
| Rust source finding | F-RUST-SRC-NNNN | F-RUST-SRC-0001 |
Sequential numbering within this agent run. Zero-padded to 4 digits.
Error Handling
- rustdoc JSON generation fails: Write error to
rust-notes.md, skip Step 2, continue with Step 3. - semantic_audit.py missing or fails: Write status-bearing error object to
rust-semantic-findings.json, continue. - find_dangerous_apis.py missing or fails: Write status-bearing error object to
rust-dangerous-api-findings.json, continue. - Both scripts fail: No Rust source findings. Write status-bearing error notes in both intermediate files and record
source-analysis-partial-failureinrust-notes.md. - Always write
sensitive-objects.json(even if unchanged from C/C++ run) andtu-map.json. - If the shared files (
sensitive-objects.json,source-findings.json,tu-map.json) are missing, create them from scratch.
Categories Produced
| Category | Severity | Source |
|---|---|---|
MISSING_SOURCE_ZEROIZE | medium–critical | Steps 2 + 3 |
SECRET_COPY | low–critical | Steps 2 + 3 |
PARTIAL_WIPE | medium–high | Step 2 |
NOT_ON_ALL_PATHS | medium–high | Steps 2 + 3 |
OPTIMIZED_AWAY_ZEROIZE | medium–high | Step 2 (flag at source; confirmed at IR in rust-compiler-analyzer) |