From 68133f8eb06a0f80c54825ca1efaa803a7404472 Mon Sep 17 00:00:00 2001 From: CyberSecurityUP Date: Thu, 15 Oct 2026 18:05:29 -0300 Subject: [PATCH] test: regression tests for the scope/model/graph fixes + /sandbox dispatch Lock in the bugs just fixed so they can't come back: - scope: host-relative "METHOD /path" findings stay in scope; host_of strips a leading HTTP method (covered in scope.rs). - graph_memory: a finding endpoint's method never becomes a HOST node. - models/rectify: claude-opus-4-6 is in the anthropic catalog and resolves to itself instead of being corrected to claude-opus-4-8. - repl: add /sandbox to ACCEPTED so the completable command is dispatchable (every_completable_command_is_accepted_by_the_dispatch now passes). Full suite: 491 passed. Co-Authored-By: Claude Opus 4.8 --- neurosploit-rs/app/src/rectify.rs | 14 ++++++++++++ neurosploit-rs/app/src/repl.rs | 2 +- .../crates/harness/src/graph_memory.rs | 22 +++++++++++++++++++ neurosploit-rs/crates/harness/src/models.rs | 9 ++++++++ 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/neurosploit-rs/app/src/rectify.rs b/neurosploit-rs/app/src/rectify.rs index 215c732..b4cc88c 100644 --- a/neurosploit-rs/app/src/rectify.rs +++ b/neurosploit-rs/app/src/rectify.rs @@ -240,6 +240,20 @@ mod tests { } } + #[test] + fn a_known_model_variant_is_not_corrected_to_a_sibling() { + // Regression: claude-opus-4-6 and claude-opus-4-8 differ by one digit. + // Once 4-6 is a real catalog entry it must resolve to itself and never + // be silently rewritten to 4-8 (which is what starved the opus-4-6 run). + let catalog: Vec = [ + "anthropic:claude-opus-4-8", + "anthropic:claude-opus-4-6", + "anthropic:claude-sonnet-4-6", + ].iter().map(|s| s.to_string()).collect(); + assert_eq!(nearest_model("anthropic:claude-opus-4-6", &catalog), None, "4-6 is exact"); + assert_eq!(nearest_model("anthropic:claude-opus-4-8", &catalog), None, "4-8 is exact"); + } + #[test] fn a_transposition_is_one_edit_away() { assert_eq!(distance("/staus", "/status"), 1, "a dropped character"); diff --git a/neurosploit-rs/app/src/repl.rs b/neurosploit-rs/app/src/repl.rs index 6ff76a3..d143023 100644 --- a/neurosploit-rs/app/src/repl.rs +++ b/neurosploit-rs/app/src/repl.rs @@ -154,7 +154,7 @@ pub(crate) const ACCEPTED: &[&str] = &[ "/observe-only", "/offline", "/onboard", "/only", "/oos", "/outofscope", "/policy", "/providers", "/proxy", "/class", "/classes", "/focus-class", "/compliance", "/comply", "/budget", "/research", "/quick", "/economy", "/eco", "/q", "/quit", "/recon", "/pause", "/repo", "/report", "/results", "/resume", "/retest", "/revalidate", "/run", "/runs", - "/scope", "/scope-out", "/show", "/status", "/stop", "/sub", "/subscription", "/target", + "/sandbox", "/scope", "/scope-out", "/show", "/status", "/stop", "/sub", "/subscription", "/target", "/temp-email", "/tempmail", "/theme", "/timeout", "/ua", "/url", "/useragent", "/validate", "/votes", ]; diff --git a/neurosploit-rs/crates/harness/src/graph_memory.rs b/neurosploit-rs/crates/harness/src/graph_memory.rs index 213704d..5d69019 100644 --- a/neurosploit-rs/crates/harness/src/graph_memory.rs +++ b/neurosploit-rs/crates/harness/src/graph_memory.rs @@ -2383,6 +2383,28 @@ mod tests { } } + #[test] + fn an_endpoint_method_is_not_treated_as_a_host() { + // Regression: "METHOD /path" endpoints must graph under the target host, + // never create a bogus HOST "get"/"post" node (which also split real + // findings off from the target and hid them from the report). + let finding = Finding { + id: "X1".into(), + title: "Search API exposes internals".into(), + cwe: "CWE-200".into(), + severity: "Medium".into(), + endpoint: "GET /api/search?q=x".into(), + review_status: "confirmed".into(), + validated: true, + confidence: 0.9, + ..Default::default() + }; + let b = build("https://www.evidelo.com.br", "r1", &[finding], None, None); + let dump = export_json(&b); + assert!(!dump.contains("\"get\""), "the HTTP method must not become a host node: {dump}"); + assert!(dump.contains("evidelo.com.br"), "the finding must graph under the target host"); + } + #[test] fn secrets_never_become_properties() { let mut x = f("F1", "SQLi dumps users", "CWE-89", "High", "https://app.test/search?q=1&token=SUPERSECRET", "admin password hash $2y$10$abc"); diff --git a/neurosploit-rs/crates/harness/src/models.rs b/neurosploit-rs/crates/harness/src/models.rs index 0bbc55c..8524309 100644 --- a/neurosploit-rs/crates/harness/src/models.rs +++ b/neurosploit-rs/crates/harness/src/models.rs @@ -1037,6 +1037,15 @@ mod response_tests { use super::*; use serde_json::json; + #[test] + fn anthropic_catalog_lists_opus_4_6() { + // Regression: the web console offers claude-opus-4-6, so the harness + // catalog must list it too — otherwise the model id is "corrected" to a + // sibling and the requested model never runs. + let a = providers().into_iter().find(|p| p.key == "anthropic").expect("anthropic provider"); + assert!(a.models.contains(&"claude-opus-4-6"), "catalog: {:?}", a.models); + } + #[test] fn reasoning_models_get_more_headroom() { for m in ["glm-5.3", "glm-4.6", "deepseek-v4.1", "z-ai/glm-5.3", "qwen3.8-max", "kimi-k3", "o3", "gpt-6-sol"] {