mirror of
https://github.com/CyberSecurityUP/NeuroSploit.git
synced 2026-07-22 18:50:55 +02:00
recon: intense multi-round active recon (deep_recon) with tool auto-install
Recon was a single quick model pass — now it's deep and iterative: - deep_recon(): an initial deep enumeration pass then follow-up EXPANSION rounds that chase discovered subdomains/hosts/endpoints/params, converging when a round finds nothing new. Rounds scale with intensity. - recon_intensity_directive(): tells the agent HOW hard to recon and to INSTALL the tools it needs (apt/pip/go/npm/cargo) — subfinder/amass/httpx/gau/katana/ gf/arjun/ffuf/nuclei/nmap/dnsx/linkfinder/whatweb/nikto/testssl — chained (subfinder->httpx->katana/gau->gf->ffuf); covers subdomains, crawl+wayback, JS, content/param discovery, ports, versions, API, exposures, TLS/headers. - RunConfig.recon_intensity (default 3) + REPL /recon <1-4> + CLI --recon <1-4> (1 quick .. 4 exhaustive); shown in /show.
This commit is contained in:
+14
@@ -41,6 +41,20 @@ and pick your engagement type up front in a new **onboarding wizard**. Library
|
||||
Skills/Plugins/n8n** — then the box type (black/white/grey for web) and the
|
||||
minimal setup, so a plain `/run` does the right thing. Scope shown in `/show`.
|
||||
|
||||
## Intense, multi-round recon
|
||||
|
||||
- Recon is no longer a single quick pass. **`deep_recon`** runs an initial deep
|
||||
enumeration then **follow-up expansion rounds** that chase what the previous
|
||||
round found (new subdomains/hosts, unmapped endpoints, promising paths/params),
|
||||
converging when nothing new appears.
|
||||
- Agents are told to **install the tools they need** (apt/pip/go/npm/cargo) —
|
||||
subfinder/amass, httpx, gau/waybackurls/katana/hakrawler, gf, arjun/paramspider,
|
||||
ffuf/feroxbuster, nuclei, nmap/rustscan, dnsx, linkfinder, whatweb, nikto,
|
||||
testssl — and chain them (subfinder→httpx→katana/gau→gf→ffuf).
|
||||
- **`/recon <1-4>`** (REPL) and **`--recon <1-4>`** (CLI) set the intensity:
|
||||
1 quick · 2 standard · 3 deep (default) · 4 exhaustive — more rounds + wider
|
||||
enumeration at higher levels. Best on Kali; degrades to curl/nc if installs fail.
|
||||
|
||||
## Models
|
||||
|
||||
- Added **`anthropic:claude-sonnet-5`** and **`xai:grok-4.5`**.
|
||||
|
||||
@@ -49,6 +49,9 @@ enum Cmd {
|
||||
/// Attack-chaining rounds (post-exploitation pivots; 0 disables).
|
||||
#[arg(long, default_value_t = 2)]
|
||||
chain_depth: usize,
|
||||
/// Recon intensity 1-4 (1 quick .. 4 exhaustive; installs tools).
|
||||
#[arg(long, default_value_t = 3)]
|
||||
recon: usize,
|
||||
#[arg(long)]
|
||||
offline: bool,
|
||||
/// Use local agentic CLI subscription (Claude/Codex/Gemini/Grok login).
|
||||
@@ -85,6 +88,9 @@ enum Cmd {
|
||||
/// Attack-chaining rounds (post-exploitation pivots; 0 disables).
|
||||
#[arg(long, default_value_t = 2)]
|
||||
chain_depth: usize,
|
||||
/// Recon intensity 1-4 (1 quick .. 4 exhaustive; installs tools).
|
||||
#[arg(long, default_value_t = 3)]
|
||||
recon: usize,
|
||||
#[arg(long)]
|
||||
offline: bool,
|
||||
#[arg(long)]
|
||||
@@ -117,6 +123,9 @@ enum Cmd {
|
||||
/// Attack-chaining rounds (post-exploitation pivots; 0 disables).
|
||||
#[arg(long, default_value_t = 2)]
|
||||
chain_depth: usize,
|
||||
/// Recon intensity 1-4 (1 quick .. 4 exhaustive; installs tools).
|
||||
#[arg(long, default_value_t = 3)]
|
||||
recon: usize,
|
||||
#[arg(long)]
|
||||
offline: bool,
|
||||
#[arg(long)]
|
||||
@@ -145,6 +154,9 @@ enum Cmd {
|
||||
/// Attack-chaining rounds (post-exploitation pivots; 0 disables).
|
||||
#[arg(long, default_value_t = 2)]
|
||||
chain_depth: usize,
|
||||
/// Recon intensity 1-4 (1 quick .. 4 exhaustive; installs tools).
|
||||
#[arg(long, default_value_t = 3)]
|
||||
recon: usize,
|
||||
#[arg(long)]
|
||||
subscription: bool,
|
||||
#[arg(long)]
|
||||
@@ -169,6 +181,9 @@ enum Cmd {
|
||||
/// Attack-chaining rounds (post-exploitation pivots; 0 disables).
|
||||
#[arg(long, default_value_t = 2)]
|
||||
chain_depth: usize,
|
||||
/// Recon intensity 1-4 (1 quick .. 4 exhaustive; installs tools).
|
||||
#[arg(long, default_value_t = 3)]
|
||||
recon: usize,
|
||||
#[arg(long)]
|
||||
offline: bool,
|
||||
#[arg(long)]
|
||||
@@ -228,6 +243,9 @@ enum Cmd {
|
||||
/// Attack-chaining rounds (post-exploitation pivots; 0 disables).
|
||||
#[arg(long, default_value_t = 2)]
|
||||
chain_depth: usize,
|
||||
/// Recon intensity 1-4 (1 quick .. 4 exhaustive; installs tools).
|
||||
#[arg(long, default_value_t = 3)]
|
||||
recon: usize,
|
||||
#[arg(long)]
|
||||
subscription: bool,
|
||||
/// Post a summary comment back on the PR (needs github integration on).
|
||||
@@ -349,12 +367,13 @@ async fn main() -> anyhow::Result<()> {
|
||||
}
|
||||
}
|
||||
}
|
||||
Cmd::Run { url, models, max_agents, vote_n, chain_depth, offline, subscription, mcp, creds, focus, jira, verbose } => {
|
||||
Cmd::Run { url, models, max_agents, vote_n, chain_depth, recon, offline, subscription, mcp, creds, focus, jira, verbose } => {
|
||||
let url = if url.starts_with("http") { url } else { format!("https://{url}") };
|
||||
let mut cfg = RunConfig::new(&url);
|
||||
cfg.max_agents = max_agents;
|
||||
cfg.vote_n = vote_n;
|
||||
cfg.chain_depth = chain_depth;
|
||||
cfg.recon_intensity = recon;
|
||||
cfg.offline = offline;
|
||||
cfg.subscription = subscription;
|
||||
cfg.verbose = verbose;
|
||||
@@ -368,12 +387,13 @@ async fn main() -> anyhow::Result<()> {
|
||||
let ig = harness::integrations::Integrations::load(&repl::proj_dir());
|
||||
post_integrations(&ig, &url, &out, jira, false, None).await;
|
||||
}
|
||||
Cmd::Whitebox { path, models, max_agents, vote_n, chain_depth, offline, subscription, jira, verbose } => {
|
||||
Cmd::Whitebox { path, models, max_agents, vote_n, chain_depth, recon, offline, subscription, jira, verbose } => {
|
||||
let path = resolve_source(&base, &path)?; // local path OR github URL/owner/repo
|
||||
let mut cfg = RunConfig::new(&path);
|
||||
cfg.max_agents = max_agents;
|
||||
cfg.vote_n = vote_n;
|
||||
cfg.chain_depth = chain_depth;
|
||||
cfg.recon_intensity = recon;
|
||||
cfg.offline = offline;
|
||||
cfg.subscription = subscription;
|
||||
cfg.verbose = verbose;
|
||||
@@ -385,7 +405,7 @@ async fn main() -> anyhow::Result<()> {
|
||||
let ig = harness::integrations::Integrations::load(&repl::proj_dir());
|
||||
post_integrations(&ig, &path, &out, jira, false, None).await;
|
||||
}
|
||||
Cmd::Greybox { repo, url, models, creds, focus, max_agents, vote_n, chain_depth, offline, subscription, mcp, verbose } => {
|
||||
Cmd::Greybox { repo, url, models, creds, focus, max_agents, vote_n, chain_depth, recon, offline, subscription, mcp, verbose } => {
|
||||
let repo = resolve_source(&base, &repo)?; // local path OR github URL/owner/repo
|
||||
let url = if url.starts_with("http") { url } else { format!("https://{url}") };
|
||||
let mut cfg = RunConfig::new(&url);
|
||||
@@ -393,6 +413,7 @@ async fn main() -> anyhow::Result<()> {
|
||||
cfg.max_agents = max_agents;
|
||||
cfg.vote_n = vote_n;
|
||||
cfg.chain_depth = chain_depth;
|
||||
cfg.recon_intensity = recon;
|
||||
cfg.offline = offline;
|
||||
cfg.subscription = subscription;
|
||||
cfg.verbose = verbose;
|
||||
@@ -404,13 +425,14 @@ async fn main() -> anyhow::Result<()> {
|
||||
let out = run_greybox_engagement(&base, cfg, mcp).await?;
|
||||
print_findings(&out);
|
||||
}
|
||||
Cmd::Tui { url, models, repo, creds, focus, max_agents, vote_n, chain_depth, subscription, mcp } => {
|
||||
Cmd::Tui { url, models, repo, creds, focus, max_agents, vote_n, chain_depth, recon, subscription, mcp } => {
|
||||
let repo = match repo { Some(r) => Some(resolve_source(&base, &r)?), None => None }; // github URL ok
|
||||
let url = if url.starts_with("http") { url } else { format!("https://{url}") };
|
||||
let mut cfg = RunConfig::new(&url);
|
||||
cfg.max_agents = max_agents;
|
||||
cfg.vote_n = vote_n;
|
||||
cfg.chain_depth = chain_depth;
|
||||
cfg.recon_intensity = recon;
|
||||
cfg.subscription = subscription;
|
||||
cfg.instructions = focus;
|
||||
cfg.repo = repo.clone();
|
||||
@@ -421,11 +443,12 @@ async fn main() -> anyhow::Result<()> {
|
||||
let mode = if repo.is_some() { Mode::Grey } else { Mode::Black };
|
||||
tui::run(&base, cfg, mcp, mode).await?;
|
||||
}
|
||||
Cmd::Host { target, models, creds, focus, max_agents, vote_n, chain_depth, offline, subscription, verbose } => {
|
||||
Cmd::Host { target, models, creds, focus, max_agents, vote_n, chain_depth, recon, offline, subscription, verbose } => {
|
||||
let mut cfg = RunConfig::new(&target);
|
||||
cfg.max_agents = max_agents;
|
||||
cfg.vote_n = vote_n;
|
||||
cfg.chain_depth = chain_depth;
|
||||
cfg.recon_intensity = recon;
|
||||
cfg.offline = offline;
|
||||
cfg.subscription = subscription;
|
||||
cfg.verbose = verbose;
|
||||
@@ -462,7 +485,7 @@ async fn main() -> anyhow::Result<()> {
|
||||
let out = run_mode(&base, cfg, false, Mode::Skills).await?;
|
||||
print_findings(&out);
|
||||
}
|
||||
Cmd::Pr { repo, number, models, vote_n, chain_depth, subscription, comment, jira, verbose } => {
|
||||
Cmd::Pr { repo, number, models, vote_n, chain_depth, recon, subscription, comment, jira, verbose } => {
|
||||
let ig = harness::integrations::Integrations::load(&repl::proj_dir());
|
||||
let owner_repo = normalize_repo(&repo);
|
||||
let path = clone_pr(&base, &ig, &owner_repo, number)?;
|
||||
@@ -470,6 +493,7 @@ async fn main() -> anyhow::Result<()> {
|
||||
let mut cfg = RunConfig::new(&path);
|
||||
cfg.vote_n = vote_n;
|
||||
cfg.chain_depth = chain_depth;
|
||||
cfg.recon_intensity = recon;
|
||||
cfg.subscription = subscription;
|
||||
cfg.verbose = verbose;
|
||||
cfg.instructions = Some(format!("This is the code of pull request #{number} of {owner_repo}. Focus on vulnerabilities introduced or touched by this change."));
|
||||
|
||||
@@ -119,7 +119,7 @@ struct LiveCheckpoint {
|
||||
const COMMANDS: &[&str] = &[
|
||||
"/help", "/onboard", "/show", "/config", "/providers", "/model", "/key", "/sub", "/target",
|
||||
"/repo", "/auth", "/creds", "/focus", "/attach", "/context", "/mcp", "/offline",
|
||||
"/votes", "/chain", "/timeout", "/proxy", "/burp", "/ua", "/agents", "/theme", "/clear", "/run", "/stop", "/continue", "/runs", "/results", "/report",
|
||||
"/votes", "/chain", "/recon", "/timeout", "/proxy", "/burp", "/ua", "/agents", "/theme", "/clear", "/run", "/stop", "/continue", "/runs", "/results", "/report",
|
||||
"/status", "/diff", "/retest", "/validate", "/finding", "/expand", "/integrations", "/quit",
|
||||
];
|
||||
|
||||
@@ -214,6 +214,7 @@ struct Session {
|
||||
vote_n: usize,
|
||||
max_agents: usize,
|
||||
chain_depth: usize,
|
||||
recon_intensity: usize,
|
||||
/// Idle guardrail: stop a run if no NEW finding lands in this many seconds
|
||||
/// (0 = disabled). Set in minutes via `/timeout <mins>`.
|
||||
idle_secs: u64,
|
||||
@@ -244,6 +245,7 @@ impl Default for Session {
|
||||
vote_n: 3,
|
||||
max_agents: 0,
|
||||
chain_depth: 2,
|
||||
recon_intensity: 3,
|
||||
idle_secs: 300, // 5-minute idle guardrail by default
|
||||
proxy: None,
|
||||
user_agent: None,
|
||||
@@ -567,6 +569,11 @@ pub async fn repl(base: &Path) -> anyhow::Result<()> {
|
||||
if arg.is_empty() { println!(" attack-chain depth: {} (0 disables) — set with /chain <n>", s.chain_depth); }
|
||||
else { s.chain_depth = arg.parse().unwrap_or(s.chain_depth); println!(" attack-chain depth: {}", s.chain_depth); }
|
||||
}
|
||||
"/recon" => {
|
||||
let lvl = |n: usize| ["", "quick", "standard", "deep", "exhaustive"].get(n).copied().unwrap_or("deep");
|
||||
if arg.is_empty() { println!(" recon intensity: {} ({}) — set with /recon <1-4> [1 quick · 2 standard · 3 deep · 4 exhaustive]", s.recon_intensity, lvl(s.recon_intensity)); }
|
||||
else { s.recon_intensity = arg.parse::<usize>().unwrap_or(s.recon_intensity).clamp(1, 4); println!(" recon intensity: {} ({}) — more rounds, more enumeration, auto-installs tools", s.recon_intensity, lvl(s.recon_intensity)); }
|
||||
}
|
||||
"/agents" => {
|
||||
if arg == "list" || arg == "ls" {
|
||||
let lib = agents::load(base);
|
||||
@@ -932,6 +939,7 @@ async fn run(base: &Path, s: &Session, history: &mut Vec<RunRecord>) {
|
||||
cfg.subscription = s.subscription;
|
||||
cfg.vote_n = s.vote_n;
|
||||
cfg.chain_depth = s.chain_depth;
|
||||
cfg.recon_intensity = s.recon_intensity;
|
||||
cfg.proxy = s.proxy.clone();
|
||||
cfg.user_agent = s.user_agent.clone();
|
||||
cfg.max_agents = s.max_agents;
|
||||
@@ -1007,6 +1015,7 @@ async fn start_background(base: &Path, s: &Session, reader: &mut Reader,
|
||||
cfg.subscription = s.subscription;
|
||||
cfg.vote_n = s.vote_n;
|
||||
cfg.chain_depth = s.chain_depth;
|
||||
cfg.recon_intensity = s.recon_intensity;
|
||||
cfg.proxy = s.proxy.clone();
|
||||
cfg.user_agent = s.user_agent.clone();
|
||||
cfg.max_agents = s.max_agents;
|
||||
@@ -1467,8 +1476,8 @@ fn show(s: &Session) {
|
||||
println!(" │ proxy : {}", s.proxy.clone().unwrap_or_else(|| "(none — /proxy for Burp/ZAP)".into()));
|
||||
println!(" │ user-agent: {}", s.user_agent.clone().unwrap_or_else(|| "NeuroSploit (default)".into()));
|
||||
println!(" │ focus : {}", s.instructions.clone().unwrap_or_else(|| "(none — tests everything)".into()));
|
||||
println!(" │ opts : mcp={} offline={} votes={} chain-depth={} max-agents={} idle-stop={}",
|
||||
onoff(s.mcp), onoff(s.offline), s.vote_n, s.chain_depth, s.max_agents,
|
||||
println!(" │ opts : mcp={} offline={} votes={} recon={} chain-depth={} max-agents={} idle-stop={}",
|
||||
onoff(s.mcp), onoff(s.offline), s.vote_n, s.recon_intensity, s.chain_depth, s.max_agents,
|
||||
if s.idle_secs == 0 { "off".to_string() } else { format!("{}m", s.idle_secs / 60) });
|
||||
// Integrations at a glance (see /integrations for detail).
|
||||
{
|
||||
@@ -1535,6 +1544,7 @@ fn help() {
|
||||
h("/offline on|off", "pipeline self-test (no API keys / no model calls)");
|
||||
h("/votes <n>", "number of validator votes per finding");
|
||||
h("/chain <n>", "attack-chain depth (post-exploitation pivots; 0 = off)");
|
||||
h("/recon <1-4>", "recon intensity: 1 quick · 2 standard · 3 deep · 4 exhaustive (installs tools)");
|
||||
h("/timeout <min>", "idle guardrail: stop if no new finding in <min> (0 = off)");
|
||||
h("/proxy <url>|off", "route agent HTTP through Burp/ZAP (/burp = default :8080)");
|
||||
h("/ua <string>", "identifying User-Agent for NeuroSploit traffic (default = NeuroSploit)");
|
||||
|
||||
@@ -241,25 +241,8 @@ pub async fn run(cfg: RunConfig, lib: &Library, pool: &ModelPool, tx: Sender<Str
|
||||
let _ = tx.send("recon: offline mode — skipping model calls".into()).await;
|
||||
"{}".to_string()
|
||||
} else {
|
||||
let recon_user = format!(
|
||||
"{}{}OBSERVED HTTP PROBE (real request/response facts — build on these, verify, and go deeper):\n{}\n\nTarget: {}",
|
||||
operator_directives(&cfg), tool_doctrine(pool.mcp_config.is_some()), probe_facts, cfg.target);
|
||||
match pool.complete_routed(Task::Recon, "recon", RECON_SYS, &recon_user).await {
|
||||
Ok((m, t)) => {
|
||||
let _ = tx.send(format!("recon complete via {}", m.label())).await;
|
||||
if cfg.verbose {
|
||||
let snip: String = t.chars().take(280).collect();
|
||||
let _ = tx.send(format!(" recon> {}", snip.replace('\n', " "))).await;
|
||||
}
|
||||
// Keep the deterministic probe facts alongside the model recon so
|
||||
// exploitation agents always see the observed evidence.
|
||||
format!("{}\n\nMODEL RECON:\n{}", probe_facts, t)
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = tx.send(format!("recon failed ({e}) — continuing with probe facts only")).await;
|
||||
probe_facts.clone()
|
||||
}
|
||||
}
|
||||
// Intense, multi-round active recon (installs tools, expands the surface).
|
||||
deep_recon(&cfg, pool, &probe_facts, &tx).await
|
||||
};
|
||||
|
||||
// ---- 2. Intelligent, RL-ranked agent selection ---------------------
|
||||
@@ -1303,6 +1286,78 @@ and the model's own response. Map every finding to OWASP LLM Top 10 (2025) and,
|
||||
Exchange. NON-DESTRUCTIVE: never exfiltrate real user data or weaponise the model against third parties — a redacted, \
|
||||
minimal proof is enough. Chain findings (e.g. system-prompt leak → tailored injection → excessive-agency tool abuse).\n\n";
|
||||
|
||||
/// Recon-phase directive by intensity — tells the agent HOW HARD to recon and
|
||||
/// to INSTALL the tools it needs (the user wants an intense, active recon, not a
|
||||
/// quick one-shot). Best on Kali; degrades to curl/nc if installs fail.
|
||||
fn recon_intensity_directive(level: usize) -> String {
|
||||
let (label, rounds, extra) = match level {
|
||||
0 | 1 => ("QUICK", "one focused pass", ""),
|
||||
2 => ("STANDARD", "crawl + JS + params", ""),
|
||||
3 => ("DEEP", "multi-angle active enumeration",
|
||||
"Go WIDE and DEEP — do NOT stop after the homepage. This should take real effort."),
|
||||
_ => ("EXHAUSTIVE", "leave no stone unturned",
|
||||
"Be EXHAUSTIVE — enumerate everything, brute wordlists, chase every referenced host/asset."),
|
||||
};
|
||||
format!(
|
||||
"RECON INTENSITY: {label} — {rounds}. {extra}\n\
|
||||
INSTALL WHAT YOU NEED (authorized): if a recon tool is missing, install it before falling back — \
|
||||
`apt-get install -y <t>`, `pip install <t>`, `go install <pkg>@latest`, `npm i -g <t>`, or `cargo install <t>`. \
|
||||
Recommended arsenal: subfinder/amass/assetfinder (subdomains), httpx/httprobe (probe live), \
|
||||
gau/waybackurls/katana/hakrawler/gospider (URL harvest & crawl), gf (pattern-filter urls), \
|
||||
arjun/paramspider (params), ffuf/feroxbuster/dirsearch (content discovery), nuclei (targeted templates), \
|
||||
nmap/rustscan/naabu (ports), dnsx (dns), subjs/linkfinder/getjs (JS endpoints), whatweb/wappalyzer (fingerprint), \
|
||||
nikto (server issues), testssl.sh/sslscan (TLS). Chain them: subfinder→httpx→katana/gau→gf→ffuf.\n\
|
||||
COVER, at this intensity: (1) subdomain & vhost enumeration + resolve live; (2) full crawl + historical \
|
||||
URLs (wayback/gau) + JS analysis (endpoints, params, secrets, source maps); (3) content & parameter \
|
||||
discovery with wordlists; (4) port/service scan; (5) tech + EXACT version fingerprinting; (6) auth/API \
|
||||
(REST+GraphQL) mapping; (7) classic exposures (.git/.env/backups/swagger/actuator, dangling CNAMEs); \
|
||||
(8) TLS/headers/cookies. Report counts (how many subdomains/urls/params/endpoints you actually found).\n\n")
|
||||
}
|
||||
|
||||
/// Intense, multi-round recon: an initial deep pass, then follow-up rounds that
|
||||
/// EXPAND the surface (chase discovered subdomains/endpoints/params, install
|
||||
/// tools, dig where the previous round found signal). Returns the merged recon
|
||||
/// text. Rounds scale with `recon_intensity` (2→1 extra, 3→2, 4→3).
|
||||
async fn deep_recon(cfg: &RunConfig, pool: &ModelPool, probe_facts: &str, tx: &Sender<String>) -> String {
|
||||
let intensity = cfg.recon_intensity.max(1);
|
||||
let extra_rounds = intensity.saturating_sub(1).min(3);
|
||||
let doctrine = tool_doctrine(pool.mcp_config.is_some());
|
||||
let intensity_dir = recon_intensity_directive(intensity);
|
||||
let dir = operator_directives(cfg);
|
||||
let mut accum = format!("OBSERVED HTTP PROBE:\n{probe_facts}");
|
||||
|
||||
// Initial deep pass.
|
||||
let user = format!("{dir}{intensity_dir}{doctrine}OBSERVED HTTP PROBE (build on these, verify, go deeper):\n{probe_facts}\n\nTarget: {}", cfg.target);
|
||||
let _ = tx.send(format!("recon: intensity {} — actively enumerating (installing tools as needed)…", intensity)).await;
|
||||
match pool.complete_routed(Task::Recon, "recon", RECON_SYS, &user).await {
|
||||
Ok((m, t)) => { let _ = tx.send(format!("recon round 1 complete via {}", m.label())).await; accum.push_str(&format!("\n\nMODEL RECON (round 1):\n{t}")); }
|
||||
Err(e) => { let _ = tx.send(format!("recon round 1 failed ({e}) — probe facts only")).await; return accum; }
|
||||
}
|
||||
|
||||
// Follow-up expansion rounds — each digs further using what's known so far.
|
||||
for r in 0..extra_rounds {
|
||||
if pool.stop_exploiting() { break; }
|
||||
let round = r + 2;
|
||||
let known: String = accum.chars().rev().take(3000).collect::<String>().chars().rev().collect();
|
||||
let follow = format!(
|
||||
"{dir}{intensity_dir}{doctrine}CONTINUE the recon — this is round {round}. Here is what recon has found so far:\n{known}\n\n\
|
||||
Now EXPAND: pick the most promising leads and go deeper — resolve & probe any NEW subdomains/hosts, crawl \
|
||||
and harvest URLs for endpoints not yet mapped, run content/parameter discovery where you saw interesting \
|
||||
paths, fingerprint exact versions of anything unclear, and enumerate the API/GraphQL further. Install any \
|
||||
tool you still need. Report ONLY the NEW facts found this round as the same COMPACT JSON schema. No repetition of prior facts.",
|
||||
);
|
||||
match pool.complete_routed(Task::Recon, "recon", RECON_SYS, &follow).await {
|
||||
Ok((m, t)) => {
|
||||
let novel = t.trim();
|
||||
if novel.len() > 20 { let _ = tx.send(format!("recon round {round} via {} — expanded surface", m.label())).await; accum.push_str(&format!("\n\nMODEL RECON (round {round}):\n{novel}")); }
|
||||
else { let _ = tx.send(format!("recon round {round}: no new surface — recon converged")).await; break; }
|
||||
}
|
||||
Err(e) => { let _ = tx.send(format!("recon round {round} failed ({e})")).await; break; }
|
||||
}
|
||||
}
|
||||
accum
|
||||
}
|
||||
|
||||
/// AI recon system prompt.
|
||||
const AI_RECON_SYS: &str = "You are an AI-security recon specialist on an AUTHORIZED engagement. Probe the AI endpoint: \
|
||||
identify the model/provider if leaked, the system/assistant behaviour, available tools/functions/MCP servers, RAG/retrieval, \
|
||||
|
||||
@@ -137,12 +137,20 @@ pub struct RunConfig {
|
||||
/// Defaults to the NeuroSploit UA when unset.
|
||||
#[serde(default)]
|
||||
pub user_agent: Option<String>,
|
||||
/// Recon intensity (1=quick, 2=standard, 3=deep, 4=exhaustive). Higher =
|
||||
/// more recon rounds, more active enumeration, and auto-installing tools.
|
||||
#[serde(default = "default_recon")]
|
||||
pub recon_intensity: usize,
|
||||
}
|
||||
|
||||
fn default_vote() -> usize {
|
||||
3
|
||||
}
|
||||
|
||||
fn default_recon() -> usize {
|
||||
3
|
||||
}
|
||||
|
||||
fn default_chain_depth() -> usize {
|
||||
2
|
||||
}
|
||||
@@ -170,6 +178,7 @@ impl RunConfig {
|
||||
chain_depth: 2,
|
||||
proxy: None,
|
||||
user_agent: None,
|
||||
recon_intensity: 3,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user