mirror of
https://github.com/CyberSecurityUP/NeuroSploit.git
synced 2026-08-12 20:50:26 +02:00
v3.4.x: intelligent agent selection, whitebox, recon/code agents, Gemini, artifacts, RL, XBOW GUI
Harness intelligence: - After recon, the model SELECTS which specialist agents match the target (select_agents) — runs the relevant subset, not blindly top-N - RL reward store (rl.rs): per-agent weights persist to data/rl_state_rs.json, reward validated findings (severity-weighted), decay idle, bias next run - Run artifacts persisted as JSON + MD (recon, exploitation transcript, findings, html report) under runs/<target>-<ts>/ for reuse by other AIs Whitebox mode: - run_whitebox: walks a repo, builds bounded source context, runs code agents, validates by adversarial vote. CLI `whitebox <path>` + web "White-box" mode Agents: +12 recon (subdomain/tech/js/api/secrets/dns/content/param/waf/cloud/ graphql/osint) and +24 code SAST reviewers (sqli/cmdi/path/ssrf/xss/deser/ secrets/crypto/authz/idor/xxe/redirect/ssti/race/eval/csrf/random/logging/ upload/mass-assign/jwt/cors). Loader gains recon/ + code/ categories → 249 total Models: +Google Gemini provider (API + gemini CLI subscription); installed_cli_ backends now detects gemini; chat_cli handles gemini/codex/grok + optional Playwright MCP (.mcp.json) on the subscription path with autonomy flags GUI: full XBOW-style redesign — sidebar (Operate/Library), topbar status, mode segment (black-box/white-box), model panel, live console, severity cards, agent browser with category filters, models view; responsive + aligned Verified: cargo build --release clean; CLI agents/whitebox; LIVE subscription run shows model selecting 23→4 agents, RL update, artifacts written; GUI + white-box toggle in Playwright. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
bf56184912
commit
3ca3f269ee
+100
-23
@@ -3,8 +3,8 @@
|
||||
mod web;
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
use harness::{agents, models::ModelRef, pool::ModelPool, report, types::RunConfig};
|
||||
use std::path::PathBuf;
|
||||
use harness::{agents, models::ModelRef, pool::ModelPool, types::RunConfig, RunOutput};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "neurosploit", version, about = "NeuroSploit v3.4.0 — multi-model autonomous pentest harness")]
|
||||
@@ -37,6 +37,24 @@ enum Cmd {
|
||||
/// instead of HTTP API keys.
|
||||
#[arg(long)]
|
||||
subscription: bool,
|
||||
/// Enable Playwright MCP (browser proof) on the subscription/CLI path.
|
||||
#[arg(long)]
|
||||
mcp: bool,
|
||||
},
|
||||
/// White-box: analyse a local repository's source code for vulnerabilities.
|
||||
Whitebox {
|
||||
/// Path to the repository to analyse.
|
||||
path: String,
|
||||
#[arg(long = "model")]
|
||||
models: Vec<String>,
|
||||
#[arg(long, default_value_t = 0)]
|
||||
max_agents: usize,
|
||||
#[arg(long, default_value_t = 2)]
|
||||
vote_n: usize,
|
||||
#[arg(long)]
|
||||
offline: bool,
|
||||
#[arg(long)]
|
||||
subscription: bool,
|
||||
},
|
||||
/// Show agent library counts.
|
||||
Agents,
|
||||
@@ -88,7 +106,7 @@ async fn main() -> anyhow::Result<()> {
|
||||
}
|
||||
}
|
||||
}
|
||||
Cmd::Run { url, models, max_agents, vote_n, offline, subscription } => {
|
||||
Cmd::Run { url, models, max_agents, vote_n, offline, subscription, mcp } => {
|
||||
let url = if url.starts_with("http") { url } else { format!("https://{url}") };
|
||||
let mut cfg = RunConfig::new(&url);
|
||||
cfg.max_agents = max_agents;
|
||||
@@ -98,26 +116,20 @@ async fn main() -> anyhow::Result<()> {
|
||||
if !models.is_empty() {
|
||||
cfg.models = models;
|
||||
}
|
||||
let lib = agents::load(&base);
|
||||
let refs: Vec<ModelRef> = cfg.models.iter().map(|s| ModelRef::parse(s)).collect();
|
||||
let pool = ModelPool::with_auth(refs, cfg.concurrency, cfg.subscription);
|
||||
|
||||
let (tx, mut rx) = tokio::sync::mpsc::channel::<String>(256);
|
||||
let printer = tokio::spawn(async move {
|
||||
while let Some(line) = rx.recv().await {
|
||||
println!(" [*] {line}");
|
||||
}
|
||||
});
|
||||
let out = harness::run(cfg.clone(), &lib, &pool, tx).await;
|
||||
let _ = printer.await;
|
||||
|
||||
println!("\n=== {} validated finding(s) ===", out.findings.len());
|
||||
println!("{}", serde_json::to_string_pretty(&out.findings)?);
|
||||
let html = report::html(&url, &out.findings);
|
||||
std::fs::create_dir_all(base.join("reports")).ok();
|
||||
let rp = base.join("reports").join("report_rs.html");
|
||||
std::fs::write(&rp, html).ok();
|
||||
println!("report → {}", rp.display());
|
||||
let out = run_engagement(&base, cfg, mcp, false).await?;
|
||||
print_findings(&out);
|
||||
}
|
||||
Cmd::Whitebox { path, models, max_agents, vote_n, offline, subscription } => {
|
||||
let mut cfg = RunConfig::new(&path);
|
||||
cfg.max_agents = max_agents;
|
||||
cfg.vote_n = vote_n;
|
||||
cfg.offline = offline;
|
||||
cfg.subscription = subscription;
|
||||
if !models.is_empty() {
|
||||
cfg.models = models;
|
||||
}
|
||||
let out = run_engagement(&base, cfg, false, true).await?;
|
||||
print_findings(&out);
|
||||
}
|
||||
Cmd::Serve { port } => {
|
||||
web::serve(base, port).await?;
|
||||
@@ -125,3 +137,68 @@ async fn main() -> anyhow::Result<()> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Shared engagement runner for CLI `run` / `whitebox`.
|
||||
async fn run_engagement(base: &Path, mut cfg: RunConfig, mcp: bool, whitebox: bool) -> anyhow::Result<RunOutput> {
|
||||
let lib = agents::load(base);
|
||||
let workdir = base.join("runs").join(format!("{}-{}", sanitize(&cfg.target), now_ts()));
|
||||
cfg.workdir = Some(workdir.display().to_string());
|
||||
cfg.rl_path = Some(base.join("data").join("rl_state_rs.json").display().to_string());
|
||||
|
||||
let mcp_config = if mcp && cfg.subscription {
|
||||
match harness::write_mcp_config(&workdir) {
|
||||
Ok(p) => {
|
||||
println!(" [*] Playwright MCP enabled → {}", p.display());
|
||||
Some(p.display().to_string())
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!(" [!] MCP config failed: {e}");
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let refs: Vec<ModelRef> = cfg.models.iter().map(|s| ModelRef::parse(s)).collect();
|
||||
let pool = ModelPool::with_auth(refs, cfg.concurrency, cfg.subscription, mcp_config);
|
||||
|
||||
let (tx, mut rx) = tokio::sync::mpsc::channel::<String>(256);
|
||||
let printer = tokio::spawn(async move {
|
||||
while let Some(line) = rx.recv().await {
|
||||
println!(" [*] {line}");
|
||||
}
|
||||
});
|
||||
let out = if whitebox {
|
||||
harness::run_whitebox(cfg, &lib, &pool, tx).await
|
||||
} else {
|
||||
harness::run(cfg, &lib, &pool, tx).await
|
||||
};
|
||||
let _ = printer.await;
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
fn print_findings(out: &RunOutput) {
|
||||
println!("\n=== {} validated finding(s) ===", out.findings.len());
|
||||
println!("{}", serde_json::to_string_pretty(&out.findings).unwrap_or_default());
|
||||
if !out.artifacts.is_empty() {
|
||||
println!("artifacts: {}", out.artifacts.join(", "));
|
||||
}
|
||||
}
|
||||
|
||||
fn sanitize(s: &str) -> String {
|
||||
let s = s.replace("https://", "").replace("http://", "");
|
||||
let mut o: String = s.chars().map(|c| if c.is_alphanumeric() { c } else { '_' }).collect();
|
||||
o.truncate(50);
|
||||
let o = o.trim_matches('_').to_string();
|
||||
if o.is_empty() {
|
||||
"target".into()
|
||||
} else {
|
||||
o
|
||||
}
|
||||
}
|
||||
|
||||
fn now_ts() -> u64 {
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_secs()).unwrap_or(0)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user