From 8a3cb42c1f8656fbcddd03f1c40ff5f8b931f6c1 Mon Sep 17 00:00:00 2001 From: CyberSecurityUP Date: Sat, 3 Oct 2026 01:16:25 -0300 Subject: [PATCH] fix(repl/cli): /target re-derives scope; wildcard target tests subdomains; version banners MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two reported bugs: 1) A scope left over from a previous session persisted in the project session and kept denying every new target (DENY_TARGET_OUTSIDE_GRANT ... scope *.example.com even after /target zoom.us). With no verified capability, /target now re-derives the authorized scope from the new target (preserving excludes + guardrails), so the target you pick is the target you test — same model as `neurosploit run `. 2) A wildcard target (`*.zoom.us`) now authorizes the apex AND all subdomains and seeds recon with the apex (a literal `*.zoom.us` has no DNS record to probe), so subdomain enumeration happens inside the wildcard scope. Applied in both the REPL /target and the one-shot `run` path. Also: the run banner and clap about showed v4.1.0 — now use CARGO_PKG_VERSION / 4.2.1. 421 tests passing. Co-Authored-By: Claude Opus 4.8 --- neurosploit-rs/app/src/main.rs | 20 ++++++++++++--- neurosploit-rs/app/src/repl.rs | 47 +++++++++++++++++++++++++++++++--- 2 files changed, 60 insertions(+), 7 deletions(-) diff --git a/neurosploit-rs/app/src/main.rs b/neurosploit-rs/app/src/main.rs index 787e227..dd1e5de 100644 --- a/neurosploit-rs/app/src/main.rs +++ b/neurosploit-rs/app/src/main.rs @@ -1,4 +1,4 @@ -//! NeuroSploit v4.1.0 — interactive harness + CLI (`run` / `whitebox` / `agents` / `models`). +//! NeuroSploit — interactive harness + CLI (`run` / `whitebox` / `agents` / `models`). mod rectify; mod repl; @@ -13,8 +13,8 @@ use std::path::{Path, PathBuf}; #[command( name = "neurosploit", version, - about = "NeuroSploit v4.1.0 — multi-model autonomous pentest harness", - long_about = "NeuroSploit v4.1.0 — a Rust multi-model harness that drives a pool of LLMs \ + about = "NeuroSploit v4.2.1 — multi-model autonomous pentest harness", + long_about = "NeuroSploit v4.2.1 — a Rust multi-model harness that drives a pool of LLMs \ (API key or local subscription: Claude/Codex/Gemini/Grok/OpenCode/Hermes) to autonomously test a target. \ After recon it INTELLIGENTLY selects only the agents matching the discovered surface, runs \ them in parallel, then validates every finding by cross-model voting before reporting.\n\n\ @@ -846,6 +846,18 @@ async fn main() -> anyhow::Result<()> { } Cmd::Run { url, models, max_agents, vote_n, chain_depth, recon, quick, offline, subscription, mcp, creds, focus, objective, out_of_scope, in_scope, scope_file, environment, policy, budget, token_limit, deep_test_limit, coverage_first, depth_first, sample_per_route, revalidate_poc, compliance, jira, only, verbose } => { let url = if url.starts_with("http") { url } else { format!("https://{url}") }; + // A wildcard target (`*.zoom.us`) is domain-wide: seed recon with the + // apex (a literal `*.zoom.us` has no DNS record to probe) and widen + // the grant to every subdomain so enumeration stays in scope. + let mut in_scope = in_scope; + let url = { + let h = harness::scope::host_of(&url); + if let Some(apex) = h.strip_prefix("*.") { + in_scope.push(format!("*.{apex}")); + if recon < 3 { /* leave as set; recon arg is explicit here */ } + format!("https://{apex}") + } else { url } + }; let mut cfg = RunConfig::new(&url); cfg.max_agents = max_agents; cfg.vote_n = vote_n; @@ -1266,7 +1278,7 @@ pub(crate) fn spawn_engagement(base: &Path, mut cfg: RunConfig, mcp: bool, mode: println!(" │ ua : {ua}"); write_status(&workdir, "running", &format!("\"target\":{:?}", cfg.target)); - println!(" ┌─ NeuroSploit v4.1.0 · by Joas A Santos & Red Team Leaders"); + println!(" ┌─ NeuroSploit v{} · by Joas A Santos & Red Team Leaders", env!("CARGO_PKG_VERSION")); println!(" │ run id : {run_id}"); println!(" │ target : {}", cfg.target); println!(" │ models : {}", cfg.models.join(", ")); diff --git a/neurosploit-rs/app/src/repl.rs b/neurosploit-rs/app/src/repl.rs index b959503..dbb8976 100644 --- a/neurosploit-rs/app/src/repl.rs +++ b/neurosploit-rs/app/src/repl.rs @@ -697,9 +697,50 @@ pub async fn repl(base: &Path, auth: SessionAuth) -> anyhow::Result<()> { let ts: Vec = arg.split(',').map(|x| x.trim()).filter(|x| !x.is_empty()) .map(|x| crate::rectify::rectify_url(x).unwrap_or_else(|| x.to_string())) .collect(); - s.target = Some(ts.join(",")); - if ts.len() > 1 { println!(" targets ({}): {}", ts.len(), ts.join(", ")); println!(" \x1b[2m/run tests them sequentially, one report each\x1b[0m"); } - else { println!(" target: {}", ts.first().cloned().unwrap_or_default()); } + // A wildcard target (`*.zoom.us`) means "the whole domain": + // authorize the apex AND every subdomain, and seed recon with + // the apex (a literal `*.zoom.us` has no DNS record to probe), + // so subdomain enumeration happens inside the wildcard scope. + let mut wildcard_domain: Option = None; + let seeds: Vec = ts.iter().map(|t| { + let h = harness::scope::host_of(t); + if let Some(apex) = h.strip_prefix("*.") { + wildcard_domain = Some(apex.to_string()); + format!("https://{apex}") + } else { t.clone() } + }).collect(); + s.target = Some(seeds.join(",")); + // Re-derive the authorized scope from the NEW target unless a + // verified capability sets the ceiling. Without this, a scope + // left over from a previous session (persisted in the project + // session) keeps denying every new target — the operator sets + // /target zoom.us but the grant still says *.example.com. With + // no capability, the target the operator picks IS the grant + // (same model as `neurosploit run `); explicit excludes + // and guardrails are preserved. + if s.capability.is_none() { + let keep_exclude = s.policy.exclude.clone(); + let keep_soft = s.policy.soft.clone(); + let mut np = harness::scope::ScopePolicy::for_target(&seeds[0]); + for extra in seeds.iter().skip(1) { np.allow(&harness::scope::host_of(extra)); } + // Each wildcard entry widens the grant to all its subdomains. + for t in &ts { + let h = harness::scope::host_of(t); + if h.starts_with("*.") { np.allow(&h); } + } + np.exclude = keep_exclude; + np.soft = keep_soft; + s.policy = np; + } + if ts.len() > 1 { println!(" targets ({}): {}", ts.len(), seeds.join(", ")); println!(" \x1b[2m/run tests them sequentially, one report each\x1b[0m"); } + else { println!(" target: {}", seeds.first().cloned().unwrap_or_default()); } + if let Some(d) = &wildcard_domain { + println!(" \x1b[2mscope: *.{d} — apex + all subdomains authorized; recon will enumerate subdomains\x1b[0m"); + // Nudge recon toward active subdomain discovery for a domain-wide engagement. + if s.recon_intensity < 3 { s.recon_intensity = 3; } + } else if s.capability.is_none() { + println!(" \x1b[2mscope: authorized against this target (add more with /inscope, exclude with /scope-out)\x1b[0m"); + } } } "/timeout" | "/idle" => {