nyx/tests/cross_file_alias_tests.rs
Eli Peter a438886217
Python fp and docs updtes (#58)
* refactor: Update comments for clarity and add expectations.json files for performance metrics

* feat: Implement FP guard for JS/TS local-collection receivers to suppress missing ownership checks

* feat: Enhance Rust parameter handling to classify local collections and prevent false ownership checks

* refactor: Simplify code formatting for better readability in multiple files

* refactor: Improve UTF-8 sequence length handling and enhance clarity in loop iteration

* feat: Update Java and Python patterns to include new security rules

* refactor: Improve comment clarity and consistency across multiple Rust files

* refactor: Simplify code formatting for improved readability in integration tests and module files

* refactor: Improve comment formatting and enhance clarity in assertions across multiple files
2026-04-29 19:53:34 -04:00

98 lines
3.9 KiB
Rust

//! Integration tests for parameter-granularity points-to summaries:
//! they carry alias information across file boundaries, closing
//! cross-file taint flows that travel through shared-object mutation
//! rather than through return values.
//!
//! Three fixtures cover distinct structural shapes of the summary
//! channel:
//!
//! * `cross_file_alias_mutating_helper` (Java), a void-returning
//! helper that stores its second argument into a field of its first
//! argument. Without the points-to channel the cross-file summary
//! loses every taint edge (void return, no container-op in
//! pointsto.rs). With it the helper emits a `Param(1) → Param(0)`
//! edge and the caller observes the field write through the argument
//! alias, producing a Runtime.exec finding.
//!
//! * `cross_file_alias_returned_alias` (JS), a passthrough helper
//! whose return aliases its first parameter. `param_to_return` with
//! `Identity` already covered the taint cap; the points-to channel
//! adds the heap-identity alias `Param(0) → Return` so the caller
//! threads the points-to set through the call. The existing
//! shell-exec sink must still fire, a regression guard on the
//! return-alias channel.
//!
//! * `cross_file_alias_bounded_graph` (Python), a helper with a 20-
//! edge alias graph that intentionally overflows `MAX_ALIAS_EDGES`.
//! The assertion is that the scan *terminates* under the bounded
//! analysis and falls back to the conservative
//! `PointsToSummary::overflow` behaviour, not a specific finding
//! count, overflow is an operational guarantee, not a precision one.
mod common;
use common::{scan_fixture_dir, validate_expectations};
use nyx_scanner::utils::config::AnalysisMode;
use std::path::{Path, PathBuf};
fn fixture_path(name: &str) -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("tests")
.join("fixtures")
.join(name)
}
#[test]
fn cross_file_alias_mutating_helper() {
let dir = fixture_path("cross_file_alias_mutating_helper");
let diags = scan_fixture_dir(&dir, AnalysisMode::Full);
validate_expectations(&diags, &dir);
}
#[test]
fn cross_file_alias_returned_alias() {
let dir = fixture_path("cross_file_alias_returned_alias");
let diags = scan_fixture_dir(&dir, AnalysisMode::Full);
validate_expectations(&diags, &dir);
}
#[test]
fn cross_file_alias_bounded_graph() {
let dir = fixture_path("cross_file_alias_bounded_graph");
let diags = scan_fixture_dir(&dir, AnalysisMode::Full);
validate_expectations(&diags, &dir);
}
/// Factory pattern: `makeBag()` returns a fresh `[]` and
/// `fillBag(bag, val)` stores into it. Exercises
/// `PointsToSummary.returns_fresh_alloc`, which synthesises a heap
/// identity for the factory's call result so subsequent container ops
/// in the caller have a heap cell to write into and read from.
#[test]
fn cross_file_container_factory() {
let dir = fixture_path("cross_file_container_factory");
let diags = scan_fixture_dir(&dir, AnalysisMode::Full);
validate_expectations(&diags, &dir);
}
/// Receiver-chain regression: tainted receiver flows through
/// `tainted.trim().toLowerCase()`, both zero-arg, and into
/// `Runtime.exec`. Pins the existing receiver-fallback behaviour so
/// heap-aliasing changes do not regress it.
#[test]
fn receiver_chain_taint_java() {
let dir = fixture_path("receiver_chain_taint_java");
let diags = scan_fixture_dir(&dir, AnalysisMode::Full);
validate_expectations(&diags, &dir);
}
/// Callback-alias regression: cross-file sink reached through
/// a two-hop local alias chain (`const f = helpers.dangerous; const g = f; g(x)`).
/// Pins whether the engine's name-keyed callback-binding resolution
/// walks the alias chain far enough to connect `g` → `dangerous`.
#[test]
fn cross_file_callback_alias() {
let dir = fixture_path("cross_file_callback_alias");
let diags = scan_fixture_dir(&dir, AnalysisMode::Full);
validate_expectations(&diags, &dir);
}