Rust developers love saying "if it compiles, it's safe." Your dependencies didn't get the memo.
Rust's memory safety guarantees are real and powerful. They eliminate entire classes of vulnerabilities — buffer overflows, use-after-free, data races. But those guarantees are a function of the code you write. The moment you pull in a dependency, you're trusting someone else's code. And that trust is often misplaced.
Rust adoption is exploding: infrastructure tools (Cloudflare), security utilities, embedded systems, backends. The ecosystem has matured rapidly. But unlike npm, there's no single authoritative advisory source. Unlike Python, there's no unified scanning experience. Instead, you have multiple databases (RustSec, NVD, OSV), limited tooling, and vulnerability categories that don't exist in other languages.
So what does the vulnerability landscape actually look like for Rust in 2026? What tools exist, when should you reach for each one, and how do you build a practical dependency security workflow?
TL;DR:
cargo auditis excellent for local development — it uses the curated RustSec advisory database and catches unmaintained crates that no other ecosystem flags. But it doesn't tell you if anyone is actively exploiting your vulnerabilities. For production security, layer in EPSS and CISA KEV to prioritize the handful of findings that actually warrant emergency patches. Also:unsafecode changes everything — a vulnerability in a crate with heavyunsafeusage (likeringormio) deserves higher urgency than the same CVSS score in pure-Rust code.
What We'll Cover
- The Rust security ecosystem in 2026 (RustSec, OSV, NVD)
- What files GeekWala scans for Rust projects
- cargo audit vs GeekWala: comparison table
- Rust-specific vulnerability categories scanners miss
- Mapping unsafe code with cargo-geiger
- Step-by-step scanning workflow
- The blast radius factor: serde, tokio, ring
- Best practices for Rust dependency security
The Rust Security Ecosystem in 2026
Rust's advisory landscape is smaller than npm's or Python's, but growing fast. The current state:
RustSec Advisory DB Volunteer-maintained
├─ Rust-specific
├─ Curated by Rust Security Working Group
└─ ~600 advisories (most comprehensive)
OSV (Open Source Vulnerabilities)
├─ Multi-ecosystem format
├─ Includes Rust crates
└─ Cross-indexes with npm, Python, Java
NVD (National Vulnerability Database)
├─ Some Rust CVEs
└─ Secondary source (incomplete)
GitHub Security Advisory
├─ Auto-detected from Cargo.toml
└─ Sometimes leads RustSec on disclosures
RustSec is the gold standard for Rust vulnerabilities. It's what cargo audit uses by default. But "most comprehensive" doesn't mean "everything"—some vulns appear in NVD or OSV before RustSec, and vice versa. This fragmentation is smaller than Python's chaos, but it exists.
The main gap: RustSec has advisory data. It doesn't have EPSS scores (exploitability) or CISA KEV entries (actively exploited). That's where commercial tools add value. You get "this crate has a vulnerability" but not "is anyone actually exploiting it?"
What Files GeekWala Accepts for Rust Scanning
GeekWala scans Rust projects using:
Primary formats:
Cargo.lock(lockfile with exact versions; strongly recommended)Cargo.toml(manifest file with version specifications)
Best practice: Always commit Cargo.lock to version control. Without it, scanning against abstract version specifications like tokio = "1" can't determine exact vulnerability matches. GeekWala scans Cargo.lock with complete accuracy; Cargo.toml is scanned but might miss edge cases with version resolution.
Special cases:
- Workspace projects with multiple
Cargo.tomlfiles: Upload the workspace root - Binary projects vs library projects: Both supported, different vulnerability profiles
- Dependencies with git sources: GeekWala tracks vulnerabilities by exact version/commit hash
cargo audit vs GeekWala: When to Use Which
cargo audit is the official Rust vulnerability scanner. It's lightweight, integrates directly into Cargo, and runs locally. But "official" doesn't solve the fragmentation problem.
cargo audit's strengths are Rust-native: it integrates with cargo-deny for policy enforcement, supports TOML-based allow/deny lists, and reads RustSec's Rust-specific advisory format (which includes "informational" advisories for unmaintained crates — something no other ecosystem does well).
| Capability | cargo audit | GeekWala |
|---|---|---|
| Advisory sources | RustSec (Rust-specific, curated) | RustSec + OSV + NVD + CISA KEV |
cargo-deny integration | ✓ native | ✗ (separate workflow) |
| Unmaintained crate warnings | ✓ (RustSec informational) | ✗ |
| Exploitation signals | ✗ (severity only) | EPSS + CISA KEV |
| C binding vuln coverage | Limited (RustSec only) | Cross-database (catches libc/openssl vulns in NVD) |
| Workspace scanning | ✓ native | ✓ via Cargo.lock |
| Unsafe code flagging | ✗ (use cargo-geiger) | ✗ (use cargo-geiger) |
| Historical trend tracking | ✗ | ✓ (track EPSS evolution over time) |
Use cargo audit for local development — run it in CI alongside cargo-deny for policy enforcement. Its unmaintained crate detection catches risks that vulnerability databases miss entirely.
Use GeekWala for production monitoring — when you need to know not just "does this crate have a vulnerability?" but "is anyone actually exploiting it?" EPSS and KEV answer that question. Scan your Rust dependencies →
Rust Vulnerability Patterns: What Scanners Miss
Rust's memory safety guarantees are real — but they shift vulnerabilities into categories that traditional scanners don't cover well. Here's the landscape:
Rust Vulnerability Category Matrix
─────────────────────────────────────────────────────────────────────
Category │ Detected by │ Priority │ Example Crates
│ cargo audit? │ Multiplier │
──────────────────┼───────────────┼─────────────┼──────────────────
unsafe blocks │ Sometimes │ 🔴 HIGH │ tokio-util, crossbeam
(memory bugs) │ │ (as bad │ hyper, mio
│ │ as C) │
──────────────────┼───────────────┼─────────────┼──────────────────
Serde deser. │ Yes │ 🔴 HIGH │ serde-json, bincode
(type confusion)│ │ (code exec) │ serde-yaml
──────────────────┼───────────────┼─────────────┼──────────────────
Crypto impl bugs │ Yes │ 🔴 HIGH │ ring, rustcrypto
(timing, zeroing│ │ (data leak) │ zeroize, sodiumoxide
──────────────────┼───────────────┼─────────────┼──────────────────
C library bindings│ Partial │ 🟠 MEDIUM │ openssl, libsodium
(FFI wrapper) │ (NVD needed) │ │ libc bindings
──────────────────┼───────────────┼─────────────┼──────────────────
Panics as DoS │ Rarely │ 🟡 CONTEXT │ Any crate with
(availability) │ │ -DEPENDENT │ untrusted input
──────────────────┼───────────────┼─────────────┼──────────────────
Integer overflow │ No │ 🟡 CONTEXT │ Serialization crates
(release builds)│ │ -DEPENDENT │ (release mode only)
──────────────────┼───────────────┼─────────────┼──────────────────
Async/concurrency │ Sometimes │ 🟡 MEDIUM │ tokio, async-std
(races, deadlock│ │ │ channels
──────────────────┼───────────────┼─────────────┼──────────────────
Regex DoS │ Yes │ 🟢 LOW │ regex, fancy-regex
(backtracking) │ │ (availabil.)│
──────────────────┼───────────────┼─────────────┼──────────────────
Supply chain │ No │ 🔴 HIGH │ Typosquatting
(typosquatting) │ │ (trust) │ on crates.io
─────────────────────────────────────────────────────────────────────
The key insight: vulnerabilities in unsafe code, serde deserialization, and crypto crates are categorically worse than the same CVSS score in a pure-Rust formatting utility. Scanners can't make this distinction — you need to layer this context on top of EPSS and KEV signals.
Mapping Your Unsafe Surface with cargo-geiger
Before you can prioritize Rust vulnerabilities, you need to know where unsafe code lives in your dependency tree. cargo-geiger gives you this map:
$ cargo geiger --output-format ascii
Metric output format: x/y
x = unsafe code used by the build
y = total unsafe code found in the crate
Crate unsafe/total Rating
────────────────────────────────────────────────────
my-app 0/0 🟢 #![forbid(unsafe_code)]
├── tokio v1.42.0 14/28 🟡 Some unsafe (async runtime)
│ ├── mio v1.0.2 22/22 🔴 All unsafe (syscalls)
│ ├── bytes v1.7.0 8/12 🟡 Some unsafe (zero-copy)
│ └── pin-project v1.1.0 0/0 🟢 Safe
├── serde v1.0.210 0/4 🟢 Mostly safe
├── ring v0.17.0 48/48 🔴 All unsafe (crypto FFI)
└── uuid v1.10.0 0/0 🟢 Safe
────────────────────────────────────────────────────
When a vulnerability hits a 🔴 crate (heavy unsafe usage), treat it as higher priority than the same vulnerability in a 🟢 crate. A vulnerability in ring or mio can lead to memory corruption — something that shouldn't be possible in Rust but is, because unsafe opts out of the compiler's guarantees.
Scanning Rust Dependencies: A Practical Workflow
Step 1: Prepare your Cargo.lock
# Update dependencies to latest
cargo update
# Or ensure Cargo.lock is generated
cargo build
Always commit Cargo.lock. Without it, scanning is guesswork—your local environment might resolve to different versions than CI or production.
Step 2: Upload to GeekWala
Visit GeekWala's Rust scanning page and upload Cargo.lock (or Cargo.toml as fallback). GeekWala queries RustSec, OSV, NVD, and CISA KEV all at once.
Step 3: Interpret the results with Rust-specific context
GeekWala enriches each finding with EPSS (exploitation probability), CVSS (severity), and CISA KEV (confirmed active exploitation).
For Rust, layer in two additional factors that other ecosystems don't have:
Step 4: Apply Rust-specific priority modifiers
Standard EPSS/KEV priority (see EPSS deep dive)
+ Does the crate use unsafe? (cargo-geiger)
+ What vulnerability category? (see matrix above)
Examples:
ring vuln, EPSS 0.4, CVSS 6.0, unsafe: ALL
→ Bump to "patch this week" (unsafe + crypto = escalate)
uuid vuln, EPSS 0.4, CVSS 6.0, unsafe: NONE
→ Keep at "standard cycle" (pure Rust, low-risk category)
tokio vuln, EPSS 0.7, CVSS 5.5, unsafe: SOME
→ "Patch this week" (high EPSS + ubiquitous + some unsafe)
Step 5: Set up continuous monitoring
- Scheduled scans (daily or weekly)
- Webhooks notify Slack on new vulnerabilities
- Custom thresholds (e.g., "fail CI if EPSS > 0.8")
Best Practices for Rust Dependency Security
1. Commit Cargo.lock, always
Your Cargo.lock is the source of truth. Without it, cargo resolves versions differently in different environments. Your local build uses serde 1.0.0, CI uses 1.0.2, production uses 1.0.5. Scanning becomes meaningless. Commit Cargo.lock to every repo.
2. Map unsafe code in your dependency tree
Use cargo-geiger to identify which crates use unsafe code. A vulnerability in a crate with unsafe blocks is worse than a vulnerability in pure Rust code. When a high-EPSS vulnerability hits an unsafe crate, prioritize it higher.
3. Minimize attack surface with cargo features
Most Rust crates have optional dependencies gated by features. Example:
[dependencies]
tokio = { version = "1.42", features = ["macros", "rt-multi-thread"] }
# Don't enable ["signal-hook"] if you don't use signals
Disable features you don't use. Fewer dependencies = fewer vulnerabilities.
4. Keep your advisory allow list clean
If you use cargo-deny to maintain an allow list of dependencies, review it quarterly. Don't grandfather in old versions just because you've used them before. If a version has a known vulnerability, upgrade or remove it.
5. Monitor RustSec continuously
Subscribe to the RustSec mailing list. Check it weekly. Automated scanning helps, but you want to catch advisory announcements in real-time, not 48 hours later.
6. Test updates before merging
Rust's semver is strict, but breaking changes happen. When you update a transitive dependency, test in CI first. A newer version that fixes a vulnerability might also break an integration point.
7. Track EPSS trends, not just scores
A Rust vulnerability might open at EPSS 0.1, spike to 0.8 when a PoC emerges, then stabilize at 0.6. GeekWala tracks this evolution. Set up alerts for EPSS spikes—that's your signal that threat actors are interested.
The Rust "Blast Radius" Factor
Rust's crate ecosystem is more concentrated than npm's or Python's. A handful of foundational crates are in nearly every project:
Crate Adoption in Rust Projects (approximate)
──────────────────────────────────────────────
serde ████████████████████████████ ~85%
tokio ██████████████████████████ ~75%
rand ████████████████████████ ~70%
regex ██████████████████████ ~65%
hyper ████████████████████ ~60%
ring █████████████████ ~50%
clap ███████████████ ~45%
──────────────────────────────────────────────
This concentration means a vulnerability in tokio or serde has an enormous blast radius — it affects most of the ecosystem. When you see a high EPSS score on one of these crates, take it seriously: threat actors know the same adoption numbers you do, and they target widely-used crates for maximum impact.
Conversely, a vulnerability in a niche crate with EPSS 0.03 and no KEV entry is genuinely low priority. The blast radius is small, and threat actors have bigger targets. This is where EPSS saves you from treating all CVSS 7.0+ findings equally.
Frequently Asked Questions
How do I handle a vulnerability in a crate that uses unsafe but I don't call the unsafe path?
Patch it anyway. Unsafe bugs are subtle — tokio-util had a vulnerability where memory leaked across task boundaries, and most users had no idea they were hitting the affected code path. Future code changes, compiler optimizations, or upstream updates could expose you. Run cargo-geiger to understand your unsafe surface, but don't use "I don't call that path" as a reason to defer patching.
cargo-deny vs cargo audit — do I need both?
They serve different purposes. cargo audit checks for known vulnerabilities. cargo-deny enforces policies: license compliance, duplicate crate detection, banned crates, and source restrictions. Use both in CI. The combo catches more than either alone.
A crate I depend on is unmaintained. RustSec flagged it as "informational." Is that urgent?
Not immediately, but it's a ticking clock. Unmaintained crates won't receive vulnerability patches. Check if there's an actively maintained fork (RustSec advisories usually link one). If not, plan a migration before a real vulnerability hits. This is one of RustSec's best features — no other ecosystem's advisory database tracks maintenance status this well.
How does GeekWala handle crates with git sources instead of crates.io?
GeekWala tracks vulnerabilities by commit hash and version tag. For git dependencies, the exact commit in your Cargo.lock is matched against advisory affected-version ranges. This works well when the git repo tags releases; for untagged commits, matching is best-effort based on the nearest version tag.
My project mixes Rust and TypeScript (e.g., Tauri app). Can I scan both?
Yes — that's where GeekWala's multi-ecosystem support helps most. Upload your Cargo.lock for the Rust backend and package-lock.json for the frontend. You get unified prioritization across both, sorted by EPSS and KEV rather than comparing apples (npm audit severity) to oranges (cargo audit severity).
Does Rust have a supply chain security story?
Rust's supply chain is stronger than npm but weaker than curated ecosystems. Key practices:
cargo denyto review and audit dependencies- Dependency review in pull requests
cargo-sbomto track dependencies for compliance- Regular geiger scans to monitor unsafe code trends
When you scan your Rust dependencies, GeekWala's vulnerability integration helps you catch supply chain attacks early with full EPSS and CISA KEV context.
cargo audit tells you what's vulnerable. GeekWala tells you what's being exploited.
Upload your Cargo.lock and see what's actually urgent → — every vulnerability ranked by EPSS exploitation probability and CISA KEV status. Know in 60 seconds which Rust findings need an emergency patch. No account needed.


