Previously, re-enrichment and incomplete entry retries would increment
change counters even when the resulting data was identical, causing
update_timestamp() to fire and produce commits with only a timestamp diff.
Now we compare old vs new symbols/SHAs before counting a change, so the
timestamp (and thus the commit) is only produced when data actually differs.
https://claude.ai/code/session_01CR3Wstpf1J7Pkbyx8xoCWm
Three issues caused RUSTSEC-2026-0013 to get corrupted during CI enrichment:
1. fetch_pr_diff fell back to a fake "pr-N" commit_sha when merge_commit_sha
was missing from the GitHub API response. Now returns an error instead —
commit_sha must always be a real commit.
2. fetch_pr_diff used the PR files endpoint (all changes across the PR) instead
of the merge commit's diff. This produced hundreds of false positive symbols
from unrelated files. Now resolves the PR to its merge commit SHA and fetches
that commit's diff directly — deterministic and scoped.
3. Re-enrich and incomplete-retry paths unconditionally overwrote existing
entries even when the fetch failed or produced no data. Now preserves
existing entries when fetches fail.
https://claude.ai/code/session_01RFE3T5f4nSC94KNSJfXHyb
Three improvements to the symbol enrichment pipeline:
1. Prepend package name as crate prefix for non-workspace repos, so
functions in src/lib.rs get properly qualified names (e.g.,
"base64::encode_config" instead of bare "encode_config").
2. Filter files to target crate in workspace repos, preventing symbols
from sibling crates from leaking in (e.g., RUSTSEC-2024-0431 for
"xous" no longer pulls in symbols from unrelated services/).
3. Deduplicate symbols by function name with priority ordering
(Deleted > Modified > Added), collapsing duplicates from multiple
trait impls (e.g., four "Value::from" entries into one).
https://claude.ai/code/session_01XbCbPY7bkQmWaGLYkv1qTu
Three improvements to the enrichment pipeline:
1. Entries with commit_sha but empty vulnerable_symbols are now treated as
incomplete and retried automatically (previously they were skipped forever
once added to the DB).
2. PR diff fetching now resolves the merge commit's first parent for the
"before" file contents, instead of using base.sha which reflects the
current branch tip and drifts for old PRs.
3. Restored 141 entries' symbols that were lost across CI merge conflicts
(141 → 6 → 0 regression). Also improved is_test_file to filter out
proptests.rs, *_tests.rs, and -tests/ path components.
https://claude.ai/code/session_017qkaszgfEhmS9ifowCV8YX
- Sort entries by advisory_id before writing
- Sort vulnerable_symbols within each entry by file/function/change_type
- Sort diff_symbols output in ast_differ (HashSet iteration was random)
- Add Ord/PartialOrd to ChangeType enum for sorting
- Only update generated_at timestamp when entries actually changed
- Add trailing newline to JSON output
https://claude.ai/code/session_01P1LKP6aqGt68rQAXrF6kSE
Remove extract_symbols_regex and all its helpers (parse_impl_type,
qualify_fn_name, is_std_trait, skip_balanced_angles, find_top_level_keyword)
plus all 17 regex-specific tests. The AST path is the only extraction
method now — files that fail to parse are skipped with a warning rather
than falling back to brittle regex matching.
-853 lines of regex code that was the source of every symbol extraction
bug (for leaks, where leaks, trait-as-type, duplicates, <method>
placeholders).
https://claude.ai/code/session_01P1LKP6aqGt68rQAXrF6kSE
Instead of regex-matching unified diff lines, the enrichment pipeline
now fetches the full before/after file contents from GitHub, parses
them with syn::parse_file, walks the AST to extract all fn items with
their fully-qualified paths (including impl block context), and diffs
the two symbol sets to classify Added/Modified/Deleted.
This eliminates entire classes of regex bugs:
- impl Trait for Type: syn's ItemImpl has separate trait_ and self_ty
fields, so the implementing type is always correct
- where clauses: parsed into generics.where_clause, never in the type
- nested generics: syn handles all nesting correctly
- #[cfg(test)] modules: checked via attributes, not path heuristics
- Duplicate symbols: set-based diffing produces each symbol exactly once
Falls back to regex per-file when:
- AST parsing fails (syntax errors, macro-heavy files)
- File contents can't be fetched (missing metadata, API errors)
- File didn't exist at either ref (handled as all-Added or all-Deleted)
New files:
- src/ast_differ.rs: AST visitor + symbol set diffing (8 tests)
Changes:
- src/github.rs: Added parent_sha, owner, repo to PatchDiff;
added fetch_file_contents() for raw file retrieval
- src/diff_analyzer.rs: extract_symbols() now tries AST first;
regex path renamed to extract_symbols_regex()
- Cargo.toml: Added quote dependency for token stream hashing
https://claude.ai/code/session_01P1LKP6aqGt68rQAXrF6kSE
Bug 4 - Duplicate symbols (~43 entries, 195 extra): When a fn signature
was both deleted and added (modified), each line independently produced
a symbol. Added dedup to the fn-declaration path that upgrades existing
entries to Modified instead of adding duplicates.
Bug 5 - Trait name as type (~146 symbols): Truncated hunk headers like
`impl From<X>` (without `for ActualType`) caused the trait name to be
captured as the implementing type, producing wrong symbols like
`From::from`. Added is_std_trait() blocklist to return None for
well-known traits when `for` is absent, falling through to the safer
`<method>` placeholder.
Added 4 regression tests. All 51 tests pass.
https://claude.ai/code/session_01P1LKP6aqGt68rQAXrF6kSE
The old --existing-db flag skipped already-enriched entries by advisory
ID, so improvements to diff_analyzer.rs had no effect on the committed
vuln_db.json. The new --re-enrich flag re-fetches diffs for existing
entries and re-extracts symbols using the current code, updating them
in-place.
The CI enrich job now passes --re-enrich so that code changes to the
symbol extraction pipeline are reflected in the PR's vuln_db.json diff.
https://claude.ai/code/session_01P1LKP6aqGt68rQAXrF6kSE
1. Fix `for` keyword leaking into symbol names (~14 entries): replaced
greedy regex `<.*>` with bracket-counting parser `parse_impl_type()`
that correctly handles nested generics like `impl<T> From<Py<T>> for PyObject`.
2. Fix `where` clause leaking into symbol names (~5 entries): the new
parser stops at top-level `where` keywords.
3. Filter test functions from symbol extraction (~39 entries): skip
`#[test]`/`#[cfg(test)]` attributed functions and `test_*` named
functions in diff output.
Added 9 regression tests. All 47 tests pass.
https://claude.ai/code/session_01P1LKP6aqGt68rQAXrF6kSE
- Add --existing-db flag to enrich command to skip already-enriched advisories
- Add --timeout-secs flag to stop processing after a time budget
- Create .github/workflows/enrich.yml that runs hourly on a 55-minute budget
- Workflow commits enriched vuln_db.json to repo, checkpointing progress
- Each run picks up where the last left off, progressively enriching the backlog
https://claude.ai/code/session_01Pp3k3jGUtnTTqvMMr1uP4Y
Scanner fixes:
- Block comment tracking: skip lines inside /* ... */ blocks
- Skip `use` statements from being matched as call sites
- Free function call detection: `parse_cookie(...)` after direct import
- Type-aware suppression: when the receiver's type is positively known
and doesn't match the vulnerable type, suppress the finding entirely
(e.g., .abort() on MyHandle vs JoinHandle)
Golden test improvements:
- All assertions are now specific: exact counts (deps, reachable symbols,
files scanned), exact advisory IDs listed vs reachable, exact call site
locations (file:line), exact confidence levels (HIGH vs MEDIUM)
- Each test documents its fixture layout and expected positive/negative
findings in a header comment
New edge_cases_project fixture covering:
- Aliased imports (Decoder as HyperDecoder → dec.decode detected)
- Free function calls (parse_cookie after direct import)
- Commented-out code (block & line comments correctly skipped)
- False positive suppression (abort() on MyHandle, parse() on String)
- Typed method calls (jar.add(c) with explicit CookieJar type)
38 tests total (33 unit + 5 integration), all passing.
https://claude.ai/code/session_011sdj18ZvQYbDVwEKqrWDj1
Type tracking (type_tracker.rs):
- Uses syn AST parsing to resolve variable types from function parameters,
let bindings with annotations, constructor calls (Type::new()), struct
literals, builder patterns, and closure parameters
- Integrated into scanner to promote method-call matches from Medium to
High confidence when the receiver's type is known
Golden integration tests:
- vulnerable_project: 4 vulnerable deps, 3 with reachable calls across
multiple files (qualified calls, typed method calls, constructor-inferred
types). Verifies regex dep is listed but Compiler::compile is NOT reachable.
- safe_project: patched deps (hyper, smallvec) excluded, vulnerable deps
(tokio, regex) listed but no symbols reachable. Reports POSSIBLY SAFE.
- clean_project: no vulnerable deps at all. Reports CLEAN.
37 tests total (33 unit + 4 integration), all passing.
https://claude.ai/code/session_011sdj18ZvQYbDVwEKqrWDj1
Implements the next phase of cargo-deep-audit: given a Rust project and the
enriched vulnerability database, determine which vulnerable symbols are
actually called from user code.
New modules:
- lockfile.rs: Parse Cargo.lock to extract dependency names and versions
- scanner.rs: Scan .rs source files for references to vulnerable symbols
using import tracking (use statements) and call pattern matching, with
High/Medium confidence levels
- analyzer.rs: Orchestrate the full pipeline — load vuln_db, match against
lockfile versions (semver-aware), scan source, produce structured report
Changes:
- main.rs refactored into subcommands: `enrich` (Phase 1) and `analyze` (Phase 2)
- db.rs/diff_analyzer.rs: Added Deserialize support to load vuln_db.json
- CI workflow updated for subcommand syntax
- 24 tests total (21 unit + 3 integration), all passing
https://claude.ai/code/session_011sdj18ZvQYbDVwEKqrWDj1
1. Impl body changes with no fn context: When the fn declaration is too
far above for git to include in hunk headers or context lines, body
changes were silently dropped. Now emits symbols at the impl-type
level with a <method> placeholder (e.g. KeccakXofState::<method>).
2. PR diff uses files endpoint: Previously resolved PRs to their merge
commit, which could be a version-only bump with no .rs files (e.g.
quinn-proto PR #2559). Now uses /pulls/{n}/files to get all changes
across the entire PR.
3. Nested generics in impl regex: The impl block regex used [^>]* which
broke on nested generics like impl<T: Foo<Bar>>. Now uses .* for
greedy matching to the last >.
https://claude.ai/code/session_01AeNG3herWfKhos9uZVUY5d
When a multi-hunk diff has consecutive hunks in the same function,
git may show the impl block (not the fn) in the second hunk header.
Previously this reset current_context_fn to None, causing body-only
changes to be silently dropped.
Now hunk headers with impl but no fn preserve the existing fn context.
This fixes cases like libcrux-sha3's squeeze() where the actual
vulnerability fix (loop index change) was missed because the second
hunk header showed `impl KeccakXofState` instead of `fn squeeze`.
https://claude.ai/code/session_01AeNG3herWfKhos9uZVUY5d
- Parse markdown heading as title fallback when TOML title is missing,
fixing the "(no title)" entries
- Filter out test files (tests/, *_test.rs) from vulnerable symbols
since test code isn't callable library API
- Fix module path generation to handle workspace layouts
(crates/foo/src/bar.rs → foo::bar) and strip src/ properly
- Replace hyphens with underscores in module paths to match Rust
crate naming conventions (pyo3-ffi → pyo3_ffi)
https://claude.ai/code/session_01AeNG3herWfKhos9uZVUY5d
The RustSec advisory-db uses .md files with TOML in a fenced code block,
not plain .toml files. The parser now extracts the TOML block from markdown.
Also fixes:
- --limit now means "collect up to N enriched entries" instead of
"take first N candidates", so it keeps scanning until enough are found
- Suppressed dead_code warnings on Issue variant and deserialization-only fields
- Updated test fixtures to match the .md format
https://claude.ai/code/session_01AeNG3herWfKhos9uZVUY5d