1
0
Fork 0
mirror of https://github.com/imjasonh/rustvulncheck synced 2026-07-16 12:32:30 +00:00

Precise golden integration tests and scanner fixes for edge cases

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
This commit is contained in:
Claude 2026-03-25 13:01:33 +00:00
parent a2db0d4d15
commit 76921cfde0
No known key found for this signature in database
10 changed files with 530 additions and 99 deletions

27
tests/fixtures/edge_cases_project/Cargo.lock generated vendored Normal file
View file

@ -0,0 +1,27 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "edge-cases-app"
version = "0.1.0"
[[package]]
name = "hyper"
version = "0.14.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde_json"
version = "1.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cookie"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "tokio"
version = "1.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"

View file

@ -0,0 +1,12 @@
//! Edge case: aliased import.
//!
//! `use hyper::proto::h1::decode::Decoder as HyperDecoder;`
//! Then `HyperDecoder::decode(...)` should still be detected.
use hyper::proto::h1::decode::Decoder as HyperDecoder;
pub fn run() {
let dec = HyperDecoder::new();
let result = dec.decode(data);
println!("{:?}", result);
}

View file

@ -0,0 +1,19 @@
//! Edge case: vulnerable calls that are commented out.
//!
//! These should NOT be detected as call sites.
use hyper::proto::h1::role::Client;
use serde_json::de::Deserializer;
pub fn run() {
// Client::encode(raw_request);
// let d = Deserializer::parse(input);
/* Also in block comments:
Client::encode(raw_request);
Deserializer::parse(input);
*/
// Only safe code actually runs here
println!("nothing vulnerable here");
}

View file

@ -0,0 +1,39 @@
//! Edge case: same method name on a different type.
//!
//! `tokio::runtime::task::JoinHandle::abort` is vulnerable, but calling
//! `.abort()` on an unrelated type should NOT be flagged.
//!
//! Also: `serde_json::de::Deserializer::parse` is vulnerable, but calling
//! `.parse()` on a String (a very common Rust pattern) should NOT be flagged.
use tokio::runtime::task::JoinHandle;
struct MyHandle {
id: u32,
}
impl MyHandle {
fn abort(&self) {
println!("aborting {}", self.id);
}
}
pub fn run() {
// .abort() on our own MyHandle — should NOT match JoinHandle::abort
let h = MyHandle { id: 42 };
h.abort();
// .parse() is very common on strings — should NOT match serde_json Deserializer::parse
// because serde_json::de::Deserializer is not imported
let n: u32 = "42".parse().unwrap();
let port: u16 = "8080".parse().unwrap();
// JoinHandle is imported but we never call .abort() on one
let handles: Vec<JoinHandle> = Vec::new();
for handle in handles {
// We only print, never abort
println!("handle exists");
}
println!("n={}, port={}", n, port);
}

View file

@ -0,0 +1,16 @@
//! Edge case: grouped import.
//!
//! `use cookie::{parse::parse_cookie, jar::CookieJar};`
//! Both symbols from the cookie advisory should be detected.
use cookie::parse::parse_cookie;
use cookie::jar::CookieJar;
pub fn run() {
// Qualified call to a free function
let c = parse_cookie("session=abc123");
// Method call on a typed local
let jar: CookieJar = CookieJar::new();
jar.add(c);
}

View file

@ -0,0 +1,11 @@
mod aliased;
mod commented;
mod false_positive;
mod grouped;
fn main() {
aliased::run();
grouped::run();
commented::run();
false_positive::run();
}

View file

@ -65,6 +65,41 @@
"change_type": "Modified"
}
]
},
{
"advisory_id": "RUSTSEC-2024-0040",
"package": "serde_json",
"title": "Stack overflow on deeply nested input",
"date": "2024-05-01",
"patched_versions": [">= 1.0.100"],
"commit_sha": "mno345",
"vulnerable_symbols": [
{
"file": "src/de.rs",
"function": "serde_json::de::Deserializer::parse",
"change_type": "Modified"
}
]
},
{
"advisory_id": "RUSTSEC-2024-0050",
"package": "cookie",
"title": "Cookie parsing allows header injection",
"date": "2024-06-01",
"patched_versions": [">= 0.18.0", ">= 0.17.1, < 0.18.0"],
"commit_sha": "pqr678",
"vulnerable_symbols": [
{
"file": "src/parse.rs",
"function": "cookie::parse::parse_cookie",
"change_type": "Modified"
},
{
"file": "src/jar.rs",
"function": "cookie::jar::CookieJar::add",
"change_type": "Modified"
}
]
}
]
}

View file

@ -25,3 +25,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "regex"
version = "1.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde_json"
version = "1.0.120"
source = "registry+https://github.com/rust-lang/crates.io-index"