nyx/benches/scan_bench.rs
Eli Peter 58f1794a4e
Added Cap::DATA_EXFIL and taint fp and fn fixes on real repos (#59)
* feat: Enhance data exfiltration detection with source sensitivity gating for cookies and headers

* feat: Implement cross-file data exfiltration detection with parameter-specific gate filters

* feat: Add calibration tests and refine DATA_EXFIL severity scoring logic

* feat: Introduce per-detector configuration for data exfiltration suppression

* feat: Enhance DATA_EXFIL findings with destination field tracking in diagnostics and SARIF output

* feat: Add tainted body and URL handling for data exfiltration detection

* feat: Add integration tests and fixtures for DATA_EXFIL and SSRF detection in Go

* feat: Add Java integration tests and fixtures for DATA_EXFIL detection across multiple HTTP clients

* feat: Add synthetic externals handling for closure-captured variables in SSA

* feat: Implement closure-based suppression for resource leak findings

* feat: Add regression guards for shell-injection and taint propagation in for-of destructure patterns

* feat: Implement constructor cap narrowing for data exfiltration detection in HTTP request builders

* feat: Add gated sinks for data exfiltration detection in C and C++ using curl_easy_setopt

* feat: Implement DATA_EXFIL cap parity for backwards analysis and add integration tests

* feat: Add data exfiltration sinks for various languages and enhance documentation

* refactor: Simplify formatting and improve readability in various files

* refactor: Improve readability by simplifying conditional statements and adding clippy linting

* docs: Update CHANGELOG and comments for data exfiltration features and configuration

* docs: Clarify configuration instructions for data exfiltration trusted destinations

* docs: Enhance comments for evidence routing logic in data exfiltration
2026-05-01 10:59:52 -04:00

185 lines
6.1 KiB
Rust

use criterion::{Criterion, criterion_group, criterion_main};
use nyx_scanner::utils::Config;
use nyx_scanner::utils::config::AnalysisMode;
use std::path::Path;
const FIXTURES: &str = "benches/fixtures";
fn bench_ast_only_scan(c: &mut Criterion) {
let fixtures = Path::new(FIXTURES).canonicalize().expect("fixtures dir");
let mut cfg = Config::default();
cfg.scanner.mode = AnalysisMode::Ast;
cfg.performance.worker_threads = Some(1);
cfg.performance.channel_multiplier = 1;
cfg.performance.batch_size = 64;
c.bench_function("ast_only_scan", |b| {
b.iter(|| {
let (rx, handle) = nyx_scanner::walk::spawn_file_walker(&fixtures, &cfg);
if let Err(err) = handle.join() {
panic!("walker panicked: {err:#?}");
}
let paths: Vec<_> = rx.into_iter().flatten().collect();
let mut diags = Vec::new();
for path in &paths {
if let Ok(mut d) =
nyx_scanner::ast::run_rules_on_file(path, &cfg, None, Some(&fixtures))
{
diags.append(&mut d);
}
}
diags
});
});
}
fn bench_full_scan(c: &mut Criterion) {
let fixtures = Path::new(FIXTURES).canonicalize().expect("fixtures dir");
let mut cfg = Config::default();
cfg.scanner.mode = AnalysisMode::Full;
cfg.performance.worker_threads = Some(1);
cfg.performance.channel_multiplier = 1;
cfg.performance.batch_size = 64;
c.bench_function("full_scan", |b| {
b.iter(|| {
let (rx, handle) = nyx_scanner::walk::spawn_file_walker(&fixtures, &cfg);
if let Err(err) = handle.join() {
panic!("walker panicked: {err:#?}");
}
let paths: Vec<_> = rx.into_iter().flatten().collect();
// Pass 1: extract summaries
let mut all_sums = Vec::new();
for path in &paths {
if let Ok(sums) = nyx_scanner::ast::extract_summaries_from_file(path, &cfg) {
all_sums.extend(sums);
}
}
let root_str = fixtures.to_string_lossy();
let global = nyx_scanner::summary::merge_summaries(all_sums, Some(&root_str));
// Pass 2: full analysis
let mut diags = Vec::new();
for path in &paths {
if let Ok(mut d) =
nyx_scanner::ast::run_rules_on_file(path, &cfg, Some(&global), Some(&fixtures))
{
diags.append(&mut d);
}
}
diags
});
});
}
fn bench_full_scan_with_state(c: &mut Criterion) {
let fixtures = Path::new(FIXTURES).canonicalize().expect("fixtures dir");
let mut cfg = Config::default();
cfg.scanner.mode = AnalysisMode::Full;
cfg.scanner.enable_state_analysis = true;
cfg.performance.worker_threads = Some(1);
cfg.performance.channel_multiplier = 1;
cfg.performance.batch_size = 64;
c.bench_function("full_scan_with_state", |b| {
b.iter(|| {
let (rx, handle) = nyx_scanner::walk::spawn_file_walker(&fixtures, &cfg);
if let Err(err) = handle.join() {
panic!("walker panicked: {err:#?}");
}
let paths: Vec<_> = rx.into_iter().flatten().collect();
// Pass 1: extract summaries
let mut all_sums = Vec::new();
for path in &paths {
if let Ok(sums) = nyx_scanner::ast::extract_summaries_from_file(path, &cfg) {
all_sums.extend(sums);
}
}
let root_str = fixtures.to_string_lossy();
let global = nyx_scanner::summary::merge_summaries(all_sums, Some(&root_str));
// Pass 2: full analysis with state
let mut diags = Vec::new();
for path in &paths {
if let Ok(mut d) =
nyx_scanner::ast::run_rules_on_file(path, &cfg, Some(&global), Some(&fixtures))
{
diags.append(&mut d);
}
}
diags
});
});
}
fn bench_single_file_parse_and_cfg(c: &mut Criterion) {
let fixture = Path::new(FIXTURES).join("sample.rs");
let fixture = fixture.canonicalize().expect("sample.rs fixture");
let cfg = Config::default();
c.bench_function("single_file_parse_cfg", |b| {
b.iter(|| {
nyx_scanner::ast::extract_summaries_from_file(&fixture, &cfg)
.expect("extract summaries")
});
});
}
fn bench_state_analysis_only(c: &mut Criterion) {
let fixture = Path::new(FIXTURES)
.join("state_bench.c")
.canonicalize()
.expect("state_bench.c fixture");
let mut cfg = Config::default();
cfg.scanner.mode = AnalysisMode::Full;
cfg.scanner.enable_state_analysis = true;
// Parse and build CFG once (outside benchmark loop)
let (file_cfg, lang) = nyx_scanner::ast::build_cfg_for_file(&fixture, &cfg)
.expect("build cfg")
.expect("supported language");
let source_bytes = std::fs::read(&fixture).expect("read fixture");
let top = file_cfg.toplevel();
c.bench_function("state_analysis_only", |b| {
b.iter(|| {
nyx_scanner::state::run_state_analysis(
&top.graph,
top.entry,
lang,
&source_bytes,
&file_cfg.summaries,
None,
true,
&[],
&[],
&std::collections::HashSet::new(),
None,
None,
)
});
});
}
fn bench_classify(c: &mut Criterion) {
c.bench_function("classify_hit", |b| {
b.iter(|| nyx_scanner::labels::classify("rust", "std::env::var", None));
});
c.bench_function("classify_miss", |b| {
b.iter(|| nyx_scanner::labels::classify("rust", "some_random_function", None));
});
}
criterion_group!(
benches,
bench_ast_only_scan,
bench_full_scan,
bench_full_scan_with_state,
bench_single_file_parse_and_cfg,
bench_state_analysis_only,
bench_classify,
);
criterion_main!(benches);