Skip to content

Commit 5a80709

Browse files
committed
feat: add --deep MIR-based analysis via capsec-driver
Adds a `--deep` flag to `cargo capsec audit` that uses rustcs MIR (Mid-level IR) to analyze all crates in the dependency tree, catching authority usage that syntactic analysis misses — macro-expanded FFI calls try_call!, trait dispatch, and generic instantiation. Architecture: - New `crates/capsec-deep/` crate (excluded from workspace, requires nightly) - `capsec-driver` binary implements `rustc_driver::Callbacks::after_analysis` - Walks MIR BasicBlocks → TerminatorKind::Call → extracts DefId targets - Classifies calls against authority patterns via def_path_str - Detects FFI calls via tcx.is_foreign_item — sees through macro wrappers - Filters std/core/alloc and proc-macro crates to reduce noise - Uses RUSTC_WRAPPER to analyze all crates including transitive dependencies - Communicates findings via JSONL temp file ($CAPSEC_DEEP_OUTPUT) - CLI reads JSONL, patches crate names/versions to match Cargo metadata, deduplicates against syntactic findings, merges into unified output Tested on heartwood: 613 → 837 findings, 12 → 26 crates analyzed. New findings include std::net socket config calls, std::fs::OpenOptions, std::env::temp_dir, and FFI calls resolved through macros that the syntactic scanner could not see.
1 parent 5f0bddd commit 5a80709

17 files changed

Lines changed: 756 additions & 1 deletion

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ members = [
1212
]
1313
exclude = [
1414
"crates/capsec-example-db",
15+
"crates/capsec-deep",
1516
]
1617

1718
[workspace.package]

crates/capsec-deep/Cargo.lock

Lines changed: 107 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/capsec-deep/Cargo.toml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[package]
2+
name = "capsec-deep"
3+
version = "0.1.0"
4+
edition = "2024"
5+
license = "Apache-2.0"
6+
description = "MIR-based deep analysis driver for capsec — requires nightly"
7+
publish = false
8+
9+
[[bin]]
10+
name = "capsec-driver"
11+
path = "src/main.rs"
12+
13+
[dependencies]
14+
serde = { version = "1", features = ["derive"] }
15+
serde_json = "1"
16+
17+
[package.metadata.rust-analyzer]
18+
rustc_private = true

crates/capsec-deep/build.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/// Embeds the sysroot library path so the binary can find librustc_driver at runtime.
2+
fn main() {
3+
let rustc = std::env::var("RUSTC").unwrap_or_else(|_| "rustc".to_string());
4+
let output = std::process::Command::new(&rustc)
5+
.arg("--print=sysroot")
6+
.output()
7+
.expect("Failed to run rustc --print=sysroot");
8+
let sysroot = String::from_utf8(output.stdout)
9+
.expect("Invalid UTF-8 from rustc --print=sysroot");
10+
let sysroot = sysroot.trim();
11+
12+
// Link against the sysroot lib directory so librustc_driver.dylib/.so is found
13+
println!("cargo:rustc-link-arg=-Wl,-rpath,{sysroot}/lib");
14+
}

crates/capsec-deep/capsec_test

452 KB
Binary file not shown.

crates/capsec-deep/clean

452 KB
Binary file not shown.
1.25 KB
Binary file not shown.
Binary file not shown.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[toolchain]
2+
channel = "nightly-2026-02-17"
3+
components = ["rustc-dev", "llvm-tools", "rust-src"]
4+
profile = "minimal"

crates/capsec-deep/simple_fs

456 KB
Binary file not shown.

0 commit comments

Comments
 (0)