Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .capsec.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,8 @@ crate = "capsec-std"

[[allow]]
crate = "capsec-tokio"

# cargo-capsec is the CLI tool — it necessarily performs I/O to scan,
# cache, and invoke the deep analysis driver.
[[allow]]
crate = "cargo-capsec"
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ members = [
]
exclude = [
"crates/capsec-example-db",
"crates/capsec-deep",
]

[workspace.package]
Expand Down
8 changes: 8 additions & 0 deletions crates/capsec-deep/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/target
*.o
*.d
# Binaries produced when testing the driver on fixture files
/clean
/simple_fs
/macro_ffi
/capsec_test
107 changes: 107 additions & 0 deletions crates/capsec-deep/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions crates/capsec-deep/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "capsec-deep"
version = "0.1.0"
edition = "2024"
license = "Apache-2.0"
description = "MIR-based deep analysis driver for capsec — requires nightly"
publish = false

[[bin]]
name = "capsec-driver"
path = "src/main.rs"

[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"

[package.metadata.rust-analyzer]
rustc_private = true
60 changes: 60 additions & 0 deletions crates/capsec-deep/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# capsec-deep

MIR-based deep analysis driver for capsec. Uses `rustc`'s Mid-level IR to detect ambient authority usage that syntactic analysis misses — macro-expanded FFI calls, trait dispatch, and generic instantiation.

## Requirements

- Nightly Rust toolchain (pinned in `rust-toolchain.toml`)
- `rustc-dev` and `llvm-tools` components

## Install

```bash
cd crates/capsec-deep
cargo install --path .
```

This installs the `capsec-driver` binary, which `cargo capsec audit --deep` invokes automatically.

## How it works

`capsec-driver` is a custom Rust compiler driver. When invoked via `RUSTC_WRAPPER`, it intercepts every crate compilation, runs the normal compiler pipeline through type checking, then walks the MIR of every function looking for:

- **Authority calls** — `std::fs::*`, `std::net::*`, `std::env::*`, `std::process::*` resolved through the full type system (including macro expansion)
- **FFI calls** — any call to a `DefKind::ForeignFn` item (catches `-sys` crate wrappers like `libgit2-sys`, `sqlite3-sys`)

Findings are written as JSONL to a temp file, which the main `cargo-capsec` CLI reads, merges with syntactic findings, and feeds into the cross-crate export map system for transitive propagation.

## Architecture

```mermaid
flowchart TD
A["cargo capsec audit --deep"] --> B["cargo check\n(RUSTC_WRAPPER=capsec-driver)"]
B --> C["capsec-driver replaces rustc\nfor each crate"]
C --> D["after_analysis callback"]
D --> E["Walk MIR BasicBlocks\nTerminatorKind::Call"]
E --> F["Extract callee DefId\ntcx.def_path_str()"]
F --> G{Classify call}
G -->|"std::fs, std::net,\nstd::env, std::process"| H["Authority finding\n(FS/NET/ENV/PROC)"]
G -->|"tcx.is_foreign_item()"| I["FFI finding"]
G -->|"No match"| J["Skip"]
H --> K["Write JSONL to\n$CAPSEC_DEEP_OUTPUT"]
I --> K
K --> L["cargo-capsec reads JSONL\nbuilds export maps"]
L --> M["Phase 2: workspace scan\nwith MIR export maps injected"]
M --> N["Unified cross-crate\ntransitive findings"]
```

## Standalone testing

```bash
# Test on a single file
CAPSEC_DEEP_DEBUG=1 cargo run -- --edition 2024 tests/fixtures/simple_fs.rs

# Test FFI detection through macros
CAPSEC_DEEP_DEBUG=1 cargo run -- --edition 2024 tests/fixtures/macro_ffi.rs
```

## Excluded from workspace

This crate requires nightly and is listed in the workspace `exclude` list. It builds independently and does not affect `cargo test --workspace` or `cargo check --workspace` on stable.
14 changes: 14 additions & 0 deletions crates/capsec-deep/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/// Embeds the sysroot library path so the binary can find librustc_driver at runtime.
fn main() {
let rustc = std::env::var("RUSTC").unwrap_or_else(|_| "rustc".to_string());
let output = std::process::Command::new(&rustc)
.arg("--print=sysroot")
.output()
.expect("Failed to run rustc --print=sysroot");
let sysroot = String::from_utf8(output.stdout)
.expect("Invalid UTF-8 from rustc --print=sysroot");
let sysroot = sysroot.trim();

// Link against the sysroot lib directory so librustc_driver.dylib/.so is found
println!("cargo:rustc-link-arg=-Wl,-rpath,{sysroot}/lib");
}
4 changes: 4 additions & 0 deletions crates/capsec-deep/rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[toolchain]
channel = "nightly-2026-02-17"
components = ["rustc-dev", "llvm-tools", "rust-src"]
profile = "minimal"
Loading
Loading