diff --git a/README.md b/README.md index 3e5c6f7..9380b3d 100755 --- a/README.md +++ b/README.md @@ -846,6 +846,7 @@ git clone https://github.com/digininja/DVWA /tmp/DVWA | `--model provider:model` | Repeatable. First = primary; the rest fail over **and** form the voting jury. | | `--subscription` | Use the local CLI login (Claude/Codex/Gemini/Grok) instead of an API key. | | `--mcp` | Enable Playwright MCP (auto-provisioned via `npx`; backends without MCP use built-in tools). | +| `--quick` | **Economy preset for a short, low-cost test** — one voter, one chain round, light recon, ≤6 agents, `eco` budget. The single switch for a fast, cheap pass; dropping voting to one model is the biggest token saver. (REPL: `/quick`; web: the ⚡ Quick-mode checkbox.) | | `--vote-n N` | How many models must agree a finding is real (default 3 / 2 for whitebox). | | `--max-agents N` | Cap agents run (`0` = all matching the recon). | | `--offline` | Exercise the full pipeline without calling any model. | diff --git a/neurosploit-rs/app/src/main.rs b/neurosploit-rs/app/src/main.rs index da9e6f5..dbc1186 100644 --- a/neurosploit-rs/app/src/main.rs +++ b/neurosploit-rs/app/src/main.rs @@ -109,6 +109,11 @@ enum Cmd { /// Recon intensity 1-4 (1 quick .. 4 exhaustive; installs tools). #[arg(long, default_value_t = 3)] recon: usize, + /// Economy preset for a short, low-cost test: one voter, one chain + /// round, light recon, ≤6 agents, eco budget. Applied last, so it wins + /// over the per-knob flags above. + #[arg(long)] + quick: bool, #[arg(long)] offline: bool, /// Use local agentic CLI subscription (Claude/Codex/Gemini/Grok/OpenCode/Hermes login). @@ -310,6 +315,9 @@ enum Cmd { /// Recon intensity 1-4 (1 quick .. 4 exhaustive; installs tools). #[arg(long, default_value_t = 3)] recon: usize, + /// Economy preset for a short, low-cost review (see `run --quick`). + #[arg(long)] + quick: bool, #[arg(long)] offline: bool, #[arg(long)] @@ -348,6 +356,9 @@ enum Cmd { /// Recon intensity 1-4 (1 quick .. 4 exhaustive; installs tools). #[arg(long, default_value_t = 3)] recon: usize, + /// Economy preset for a short, low-cost test (see `run --quick`). + #[arg(long)] + quick: bool, #[arg(long)] offline: bool, #[arg(long)] @@ -827,7 +838,7 @@ async fn main() -> anyhow::Result<()> { Cmd::Internal { graph, scaffold, from, expand, mermaid, save } => { handle_internal(graph.as_deref(), scaffold.as_deref(), &from, expand, mermaid, save.as_deref())? } - Cmd::Run { url, models, max_agents, vote_n, chain_depth, recon, 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 } => { + 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}") }; let mut cfg = RunConfig::new(&url); cfg.max_agents = max_agents; @@ -853,6 +864,7 @@ async fn main() -> anyhow::Result<()> { } apply_authorization(&mut cfg, &in_scope, cli.capability_token.clone(), &environment, &policy)?; apply_budget(&mut cfg, budget.as_deref(), token_limit, deep_test_limit, coverage_first, depth_first, sample_per_route)?; + if quick { apply_quick(&mut cfg); } apply_network(&mut cfg, &cli)?; cfg.intercept = cli.intercept.clone(); cfg.sandbox = cli.sandbox.clone(); @@ -868,7 +880,7 @@ 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, recon, offline, subscription, jira, only, verbose } => { + Cmd::Whitebox { path, models, max_agents, vote_n, chain_depth, recon, quick, offline, subscription, jira, only, 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; @@ -879,6 +891,7 @@ async fn main() -> anyhow::Result<()> { cfg.subscription = subscription; cfg.verbose = verbose; cfg.pinned = parse_only(&only); + if quick { apply_quick(&mut cfg); } if !models.is_empty() { cfg.models = models; } @@ -887,7 +900,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, recon, offline, subscription, mcp, only, verbose } => { + Cmd::Greybox { repo, url, models, creds, focus, max_agents, vote_n, chain_depth, recon, quick, offline, subscription, mcp, only, 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); @@ -901,6 +914,7 @@ async fn main() -> anyhow::Result<()> { cfg.verbose = verbose; cfg.instructions = focus; cfg.pinned = parse_only(&only); + if quick { apply_quick(&mut cfg); } if !models.is_empty() { cfg.models = models; } @@ -1965,6 +1979,23 @@ fn apply_budget( Ok(()) } +/// `--quick`: a single economy preset for a short, low-cost test. Applied LAST, +/// so it deliberately wins over the per-knob flags — one switch the operator +/// reaches for when they just want a fast, cheap pass instead of a full +/// engagement: one voter, one chaining round, light recon, a hard cap on +/// breadth, under the `eco` budget (deep reasoning only on the strongest +/// signals). The single biggest token saver here is dropping voting from 2-3 +/// models to one. +fn apply_quick(cfg: &mut RunConfig) { + use harness::budget::{Budget, Mode}; + cfg.vote_n = 1; + cfg.chain_depth = 1; + cfg.recon_intensity = 1; + cfg.max_agents = 6; + cfg.budget = Budget::with_mode(Mode::Eco); + println!(" \x1b[2mquick: economy preset — 1 voter, 1 chain round, light recon, ≤6 agents, eco budget\x1b[0m"); +} + /// Egress route, out-of-band channel and inbound SMS. /// /// The transport spec is parsed here rather than at run time so a typo fails diff --git a/neurosploit-rs/app/src/repl.rs b/neurosploit-rs/app/src/repl.rs index cf33d83..c8e5d60 100644 --- a/neurosploit-rs/app/src/repl.rs +++ b/neurosploit-rs/app/src/repl.rs @@ -152,7 +152,7 @@ pub(crate) const ACCEPTED: &[&str] = &[ "/history", "/idle", "/inscope", "/instructions", "/integration", "/integrations", "/key", "/log", "/logs", "/mcp", "/memory", "/model", "/models", "/objective", "/objectives", "/observe", "/observe-only", "/offline", - "/onboard", "/only", "/oos", "/outofscope", "/policy", "/providers", "/proxy", "/q", "/quit", "/recon", + "/onboard", "/only", "/oos", "/outofscope", "/policy", "/providers", "/proxy", "/quick", "/economy", "/eco", "/q", "/quit", "/recon", "/pause", "/repo", "/report", "/results", "/resume", "/retest", "/revalidate", "/run", "/runs", "/scope", "/scope-out", "/show", "/status", "/stop", "/sub", "/subscription", "/target", "/temp-email", "/tempmail", "/theme", "/timeout", "/ua", "/url", "/useragent", "/validate", @@ -163,7 +163,7 @@ pub(crate) const ACCEPTED: &[&str] = &[ const COMMANDS: &[&str] = &[ "/help", "/onboard", "/show", "/config", "/providers", "/model", "/key", "/sub", "/target", "/repo", "/auth", "/creds", "/focus", "/objective", "/scope-out", "/attach", "/context", "/mcp", "/offline", - "/votes", "/chain", "/recon", "/tempmail", "/timeout", "/proxy", "/burp", "/ua", "/agents", "/only", "/theme", "/clear", "/run", "/stop", "/pause", "/continue", "/runs", "/results", "/report", + "/quick", "/economy", "/eco", "/votes", "/chain", "/recon", "/tempmail", "/timeout", "/proxy", "/burp", "/ua", "/agents", "/only", "/theme", "/clear", "/run", "/stop", "/pause", "/continue", "/runs", "/results", "/report", "/status", "/logs", "/diff", "/retest", "/validate", "/finding", "/expand", "/integrations", "/memory", "/forget", "/graph", "/inscope", "/observe", "/guardrail", "/policy", "/capability", "/audit", "/quit", @@ -846,6 +846,18 @@ pub async fn repl(base: &Path, auth: SessionAuth) -> anyhow::Result<()> { 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::().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)); } } + "/quick" | "/economy" | "/eco" => { + // Economy preset for a short, low-cost test — the single switch + // for "fast and cheap" instead of tuning each knob. The big + // saver is one voter instead of two or three. + s.vote_n = 1; + s.chain_depth = 1; + s.recon_intensity = 1; + s.max_agents = 6; + println!(" \x1b[1;32m⚡ quick mode\x1b[0m — economy preset for a short, low-cost run:"); + println!(" 1 voter · 1 chain round · light recon · ≤6 agents"); + println!(" \x1b[2m(raise any back up with /votes /chain /recon /agents — or /run to go)\x1b[0m"); + } "/tempmail" | "/temp-email" => { match arg.trim() { "on" | "true" | "1" => { s.temp_email = true; println!(" temp-email: \x1b[32mon\x1b[0m — register flows may use the free mail.tm inbox to read a confirmation code"); } @@ -2224,6 +2236,7 @@ fn help() { h("/votes ", "number of validator votes per finding"); h("/chain ", "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("/quick", "economy preset: short, low-cost run (1 voter · 1 chain round · light recon · ≤6 agents)"); h("/tempmail on|off", "opt-in disposable inbox (mail.tm) to read a register confirmation code"); h("/timeout ", "idle guardrail: stop if no new finding in (0 = off)"); h("/proxy |off", "route agent HTTP through Burp/ZAP (/burp = default :8080)"); diff --git a/web/public/app.js b/web/public/app.js index 1bbfe71..32778e4 100644 --- a/web/public/app.js +++ b/web/public/app.js @@ -532,6 +532,7 @@ async function startExploitation() { votes: Number($('#fieldVotes').value) || 3, chainDepth: Number($('#fieldChain').value), recon: Number($('#fieldRecon').value), + quick: $('#fieldQuick') ? $('#fieldQuick').checked : false, subscription: state.authMode === 'subscription', mcp: $('#fieldMcp').checked, agents: [...state.selected], diff --git a/web/public/index.html b/web/public/index.html index 8853218..1c2f5d1 100644 --- a/web/public/index.html +++ b/web/public/index.html @@ -221,6 +221,9 @@
Optional. Left on unlimited, the run behaves exactly as it always has — full depth, no cap.
+
+
Economy preset: 1 voter, 1 chain round, light recon, ≤6 agents, eco budget. The big token saver. Wins over the settings below.
+