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:
parent
a2db0d4d15
commit
76921cfde0
10 changed files with 530 additions and 99 deletions
27
tests/fixtures/edge_cases_project/Cargo.lock
generated
vendored
Normal file
27
tests/fixtures/edge_cases_project/Cargo.lock
generated
vendored
Normal 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"
|
||||
12
tests/fixtures/edge_cases_project/src/aliased.rs
vendored
Normal file
12
tests/fixtures/edge_cases_project/src/aliased.rs
vendored
Normal 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);
|
||||
}
|
||||
19
tests/fixtures/edge_cases_project/src/commented.rs
vendored
Normal file
19
tests/fixtures/edge_cases_project/src/commented.rs
vendored
Normal 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");
|
||||
}
|
||||
39
tests/fixtures/edge_cases_project/src/false_positive.rs
vendored
Normal file
39
tests/fixtures/edge_cases_project/src/false_positive.rs
vendored
Normal 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);
|
||||
}
|
||||
16
tests/fixtures/edge_cases_project/src/grouped.rs
vendored
Normal file
16
tests/fixtures/edge_cases_project/src/grouped.rs
vendored
Normal 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);
|
||||
}
|
||||
11
tests/fixtures/edge_cases_project/src/main.rs
vendored
Normal file
11
tests/fixtures/edge_cases_project/src/main.rs
vendored
Normal 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();
|
||||
}
|
||||
35
tests/fixtures/golden_vuln_db.json
vendored
35
tests/fixtures/golden_vuln_db.json
vendored
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
5
tests/fixtures/vulnerable_project/Cargo.lock
generated
vendored
5
tests/fixtures/vulnerable_project/Cargo.lock
generated
vendored
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue