mirror of
https://github.com/CyberSecurityUP/NeuroSploit.git
synced 2026-07-20 09:40:55 +02:00
v3.5.1: live findings + /finding + Ctrl+O/expand + 3-way /stop (soft validate) + report URL + structured Typst + IIS/CMS/CVE agents
REPL interactivity & findings: - Live findings registered during a run: /results shows them accumulating; /finding opens a selection menu with FULL details (PoC, command, evidence, CVSS, OWASP/CWE, remediation). Past runs too. - /expand (and Ctrl+O) dump the last full, untruncated commands. - Findings colored by severity in the feed (not all-yellow); confirmed vote = green. Stop & report: - CRITICAL: /stop no longer kills validation. New SOFT stop (pool.soft) halts launching new agents but lets in-flight + VALIDATION finish — so confirmed findings are kept. /stop now asks 3 ways: [1] validate then report, [2] report raw (no validation), [3] discard. - Report file:// URL printed on completion/stop. Report: - Typst report restructured: executive summary, a Vulnerability Summary TABLE (#, vuln, severity, CVSS, OWASP/CWE), and per-finding sections with criticality, CVSS, OWASP/CWE, description/impact, PoC, evidence, remediation. owasp passed through. Agents: +14 app-stack/CVE (IIS tilde/WebDAV/ViewState/debug/handler-bypass, CMS fingerprint + WordPress/Joomla/Drupal/default-admin, app-server consoles, exposed VCS, known-CVE & outdated-component exploitation) → 343 total. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -159,7 +159,7 @@ pub async fn run(cfg: RunConfig, lib: &Library, pool: &ModelPool, tx: Sender<Str
|
||||
let directives = directives.clone();
|
||||
let txc = tx.clone();
|
||||
async move {
|
||||
if pool.is_cancelled() {
|
||||
if pool.stop_exploiting() {
|
||||
return (ag.name.clone(), String::new(), vec![]);
|
||||
}
|
||||
if verbose {
|
||||
@@ -184,6 +184,7 @@ pub async fn run(cfg: RunConfig, lib: &Library, pool: &ModelPool, tx: Sender<Str
|
||||
// Live findings feed: surface each candidate the moment it appears.
|
||||
for c in &f {
|
||||
let _ = txc.send(format!("finding: [{}] {} @ {}", c.severity, c.title, c.endpoint)).await;
|
||||
if let Ok(j) = serde_json::to_string(c) { let _ = txc.send(format!("finding_json: {j}")).await; }
|
||||
}
|
||||
(ag.name.clone(), text, f)
|
||||
}
|
||||
@@ -378,7 +379,7 @@ pub async fn run_greybox(cfg: RunConfig, lib: &Library, pool: &ModelPool, tx: Se
|
||||
let leads = leads_ctx.clone();
|
||||
let txc = tx.clone();
|
||||
async move {
|
||||
if pool.is_cancelled() {
|
||||
if pool.stop_exploiting() {
|
||||
return (ag.name.clone(), String::new(), vec![]);
|
||||
}
|
||||
if verbose {
|
||||
@@ -931,7 +932,7 @@ pub async fn run_host(cfg: RunConfig, lib: &Library, pool: &ModelPool, tx: Sende
|
||||
let directives = directives.clone();
|
||||
let txc = tx.clone();
|
||||
async move {
|
||||
if pool.is_cancelled() { return (ag.name.clone(), String::new(), vec![]); }
|
||||
if pool.stop_exploiting() { return (ag.name.clone(), String::new(), vec![]); }
|
||||
if verbose {
|
||||
let _ = txc.send(format!(" ▶ launching agent: {} ({})", ag.name, ag.title.replace(" Agent", ""))).await;
|
||||
}
|
||||
@@ -944,7 +945,10 @@ pub async fn run_host(cfg: RunConfig, lib: &Library, pool: &ModelPool, tx: Sende
|
||||
Ok((m, text)) => {
|
||||
let f = extract_findings(&text, &ag.name);
|
||||
let _ = txc.send(format!("test {} via {} → {} candidate(s)", ag.name, m.label(), f.len())).await;
|
||||
for c in &f { let _ = txc.send(format!("finding: [{}] {} @ {}", c.severity, c.title, c.endpoint)).await; }
|
||||
for c in &f {
|
||||
let _ = txc.send(format!("finding: [{}] {} @ {}", c.severity, c.title, c.endpoint)).await;
|
||||
if let Ok(j) = serde_json::to_string(c) { let _ = txc.send(format!("finding_json: {j}")).await; }
|
||||
}
|
||||
(ag.name.clone(), text, f)
|
||||
}
|
||||
Err(e) => { let _ = txc.send(format!("test {} failed: {e}", ag.name)).await;
|
||||
|
||||
@@ -34,9 +34,11 @@ pub struct ModelPool {
|
||||
/// Progress channel: when set, the subscription CLI streams structured
|
||||
/// activity (tools called, commands run, files read) here live.
|
||||
progress: std::sync::Mutex<Option<tokio::sync::mpsc::Sender<String>>>,
|
||||
/// Cooperative cancellation: when set, in-flight model calls short-circuit
|
||||
/// and the pipeline stops launching new agents (graceful stop).
|
||||
/// HARD cancellation: when set, in-flight model calls short-circuit (abort).
|
||||
cancel: std::sync::Arc<std::sync::atomic::AtomicBool>,
|
||||
/// SOFT stop: stop launching new EXPLOIT agents, but let in-flight finish and
|
||||
/// VALIDATION still run — so "stop and validate what was found" works.
|
||||
soft: std::sync::Arc<std::sync::atomic::AtomicBool>,
|
||||
}
|
||||
|
||||
impl ModelPool {
|
||||
@@ -65,6 +67,7 @@ impl ModelPool {
|
||||
mcp_config,
|
||||
progress: std::sync::Mutex::new(None),
|
||||
cancel: Arc::new(std::sync::atomic::AtomicBool::new(false)),
|
||||
soft: Arc::new(std::sync::atomic::AtomicBool::new(false)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,13 +83,23 @@ impl ModelPool {
|
||||
self.progress.lock().ok().and_then(|g| g.clone())
|
||||
}
|
||||
|
||||
/// Handle to request graceful cancellation of an in-progress engagement.
|
||||
/// Handle to request HARD cancellation (abort all model calls).
|
||||
pub fn cancel_handle(&self) -> Arc<std::sync::atomic::AtomicBool> {
|
||||
self.cancel.clone()
|
||||
}
|
||||
/// Handle to request a SOFT stop (stop launching new exploit agents; keep
|
||||
/// validation running).
|
||||
pub fn soft_handle(&self) -> Arc<std::sync::atomic::AtomicBool> {
|
||||
self.soft.clone()
|
||||
}
|
||||
pub fn is_cancelled(&self) -> bool {
|
||||
self.cancel.load(std::sync::atomic::Ordering::Relaxed)
|
||||
}
|
||||
/// Should the exploit phase stop launching new agents? (hard OR soft stop)
|
||||
pub fn stop_exploiting(&self) -> bool {
|
||||
self.cancel.load(std::sync::atomic::Ordering::Relaxed)
|
||||
|| self.soft.load(std::sync::atomic::Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// One completion for a model, via subscription CLI (optionally with MCP) or
|
||||
/// HTTP API, with a short retry/backoff. `label` (e.g. the agent name) tags
|
||||
|
||||
@@ -139,9 +139,10 @@ pub fn typst_report(target: &str, findings: &[Finding], dir: &Path) -> std::io::
|
||||
));
|
||||
data.push_str("#let findings = (\n");
|
||||
for f in sorted_findings(findings) {
|
||||
let owasp = if f.owasp.is_empty() { f.cwe.clone() } else { f.owasp.clone() };
|
||||
data.push_str(&format!(
|
||||
" (severity: {}, title: {}, agent: {}, cwe: {}, cvss: {}, endpoint: {}, payload: {}, evidence: {}, impact: {}, remediation: {}, votes: {}, confidence: {}),\n",
|
||||
tq(&f.severity), tq(&f.title), tq(&f.agent), tq(&f.cwe), tq(&f.cvss),
|
||||
" (severity: {}, title: {}, agent: {}, cwe: {}, owasp: {}, cvss: {}, endpoint: {}, payload: {}, evidence: {}, impact: {}, remediation: {}, votes: {}, confidence: {}),\n",
|
||||
tq(&f.severity), tq(&f.title), tq(&f.agent), tq(&f.cwe), tq(&owasp), tq(&f.cvss),
|
||||
tq(&f.endpoint), tq(&f.payload), tq(&f.evidence), tq(&f.impact),
|
||||
tq(&f.remediation), tq(&f.votes), f.confidence,
|
||||
));
|
||||
|
||||
Reference in New Issue
Block a user