---
name: 2b-rust-source-analyzer
description: "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."
model: inherit
tools: Read, Grep, Glob, Write, Bash
---

# 2b-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.

```bash
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:

```bash
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:

```bash
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)
- `Zeroize` without auto-trigger → `MISSING_SOURCE_ZEROIZE` (high)
- Partial `Drop` impl → `PARTIAL_WIPE` (high)
- `ZeroizeOnDrop` with heap fields → `PARTIAL_WIPE` (medium)
- `Clone` on zeroizing type → `SECRET_COPY` (medium)
- `From`/`Into` returning non-zeroizing type → `SECRET_COPY` (medium)
- Source file containing `ptr::write_bytes` and no `compiler_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 `zeroize` crate in `Cargo.toml` → `MISSING_SOURCE_ZEROIZE` (low)

If the script is missing or fails: write a status-bearing error object to the output file and continue:

```json
{
  "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:

```bash
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:

```json
{
  "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:
```json
{
  "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:
```json
{
  "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).

```json
{ "<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-failure` in `rust-notes.md`.
- **Always write `sensitive-objects.json`** (even if unchanged from C/C++ run) and **`tu-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) |
