Compare commits

...
2 Commits
Author SHA1 Message Date
CyberSecurityUPandClaude Opus 4.6 105c62af61 docs: bump version references to 3.6.8 across README, TUTORIAL, setup.sh, install.ps1
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011739wMqPJJPttTLLX6YoQH
2026-08-06 15:27:22 -03:00
CyberSecurityUPandClaude Opus 4.6 a0a477a2bf fix(3.6.8): better Ollama error messages, empty-evidence findings go to needs-review, single-model vote warning
- models.rs: detect connection-refused and timeout on local providers
  (ollama/litellm/llamacpp), show actionable error instead of raw reqwest
- pipeline.rs: findings with empty evidence skip adversarial vote (which
  always rejects per 'default to rejected' prompt) and go straight to
  needs-review for human triage
- pipeline.rs: warn when single-model panel + vote_n=1 (same model
  validates its own findings = weaker validation)
- Bump version 3.6.7 → 3.6.8

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011739wMqPJJPttTLLX6YoQH
2026-08-06 15:21:37 -03:00
13 changed files with 74 additions and 23 deletions
+1
View File
@@ -108,3 +108,4 @@ data/repl_history.txt
# Cloned source repos (whitebox/greybox from a git URL) # Cloned source repos (whitebox/greybox from a git URL)
repos/ repos/
neurosploit-rs/repos/ neurosploit-rs/repos/
target/
+2 -2
View File
@@ -1,4 +1,4 @@
<h1 align="center">🧠 NeuroSploit v3.6.7</h1> <h1 align="center">🧠 NeuroSploit v3.6.8</h1>
<p align="center"> <p align="center">
<a href="https://trendshift.io/repositories/22624?utm_source=trendshift-badge&amp;utm_medium=badge&amp;utm_campaign=badge-trendshift-22624" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/trendshift/repositories/22624/daily?language=Python" alt="JoasASantos%2FNeuroSploit | Trendshift" width="250" height="55"/></a> <a href="https://trendshift.io/repositories/22624?utm_source=trendshift-badge&amp;utm_medium=badge&amp;utm_campaign=badge-trendshift-22624" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/trendshift/repositories/22624/daily?language=Python" alt="JoasASantos%2FNeuroSploit | Trendshift" width="250" height="55"/></a>
@@ -12,7 +12,7 @@
</p> </p>
<p align="center"> <p align="center">
<img src="https://img.shields.io/badge/Version-3.6.7-blue?style=flat-square"> <img src="https://img.shields.io/badge/Version-3.6.8-blue?style=flat-square">
<img src="https://img.shields.io/badge/Harness-Rust%20%7C%20tokio-e6b673?style=flat-square"> <img src="https://img.shields.io/badge/Harness-Rust%20%7C%20tokio-e6b673?style=flat-square">
<img src="https://img.shields.io/badge/License-MIT-green?style=flat-square"> <img src="https://img.shields.io/badge/License-MIT-green?style=flat-square">
<img src="https://img.shields.io/badge/MD%20Agents-435-red?style=flat-square"> <img src="https://img.shields.io/badge/MD%20Agents-435-red?style=flat-square">
+17 -2
View File
@@ -1,11 +1,26 @@
# NeuroSploit v3.6.7 — Release Notes # NeuroSploit v3.6.8 — Release Notes
**Release Date:** August 2026 **Release Date:** August 2026
**Codename:** Chain & Exploit **Codename:** Chain & Exploit
**License:** MIT **License:** MIT
**Credits:** Joas A Santos & Red Team Leaders **Credits:** Joas A Santos & Red Team Leaders
## Highlights ## v3.6.8 — Bugfix: Ollama error handling, empty-evidence validation, single-model warnings
- **Better Ollama/local provider error messages.** Connection-refused and timeout
errors now name the provider, URL, and suggest checking if the server is running.
Previously showed raw reqwest errors.
- **Empty-evidence findings skip the vote and go straight to `needs-review`.**
Findings with no evidence are unverifiable by the adversarial validator (which
always rejects "no evidence" per its system prompt). Now they bypass the vote
and are flagged for human review instead of being silently dropped.
- **Single-model + vote_n=1 warning.** When only one model is configured and
vote_n is 1, the pipeline emits a warning that validation is weaker (same model
validates its own findings).
---
## v3.6.7 Highlights
- **CVE exploitation pipeline — 4 new agents.** `cve_version_fingerprint` (pin - **CVE exploitation pipeline — 4 new agents.** `cve_version_fingerprint` (pin
exact versions) → `cve_research_analyst` (map to NVD/GHSA, judge reachability) → exact versions) → `cve_research_analyst` (map to NVD/GHSA, judge reachability) →
+2 -2
View File
@@ -1,4 +1,4 @@
# NeuroSploit — Tutorial & User Guide (v3.6.5) # NeuroSploit — Tutorial & User Guide (v3.6.8)
A complete, hands-on guide to installing, configuring and running NeuroSploit — A complete, hands-on guide to installing, configuring and running NeuroSploit —
the autonomous, multi-model penetration-testing harness. the autonomous, multi-model penetration-testing harness.
@@ -98,7 +98,7 @@ Agents **degrade gracefully**: if `rustscan` is absent they use `nmap`; if neith
### Verify ### Verify
```bash ```bash
neurosploit --version # neurosploit 3.6.5 neurosploit --version # neurosploit 3.6.8
neurosploit agents # {"vulns":241,...,"ai":30,...,"total":430} neurosploit agents # {"vulns":241,...,"ai":30,...,"total":430}
neurosploit models # all providers & models neurosploit models # all providers & models
``` ```
+2 -2
View File
@@ -14,7 +14,7 @@ function Ok ($m) { Write-Host " + $m" -ForegroundColor Green }
function Warn($m){ Write-Host " ! $m" -ForegroundColor Yellow } function Warn($m){ Write-Host " ! $m" -ForegroundColor Yellow }
Write-Host "" Write-Host ""
Write-Host " NeuroSploit installer (Windows) — v3.6.1" -ForegroundColor Cyan Write-Host " NeuroSploit installer (Windows) — v3.6.8" -ForegroundColor Cyan
# arch → asset arch (only x64 prebuilt today; arm64 falls back to source) # arch → asset arch (only x64 prebuilt today; arm64 falls back to source)
$rawArch = $env:PROCESSOR_ARCHITECTURE $rawArch = $env:PROCESSOR_ARCHITECTURE
@@ -29,7 +29,7 @@ $ref = $env:NEUROSPLOIT_REF
if (-not $ref) { if (-not $ref) {
try { $ref = (Invoke-RestMethod "https://api.github.com/repos/$slug/releases/latest").tag_name } catch { } try { $ref = (Invoke-RestMethod "https://api.github.com/repos/$slug/releases/latest").tag_name } catch { }
} }
if (-not $ref) { $ref = "v3.6.1" } if (-not $ref) { $ref = "v3.6.8" }
Say "Release: $ref" Say "Release: $ref"
New-Item -ItemType Directory -Force -Path $dir | Out-Null New-Item -ItemType Directory -Force -Path $dir | Out-Null
+2 -2
View File
@@ -871,7 +871,7 @@ dependencies = [
[[package]] [[package]]
name = "neurosploit" name = "neurosploit"
version = "3.6.7" version = "3.6.8"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
@@ -888,7 +888,7 @@ dependencies = [
[[package]] [[package]]
name = "neurosploit-harness" name = "neurosploit-harness"
version = "3.6.7" version = "3.6.8"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"futures", "futures",
+1 -1
View File
@@ -3,7 +3,7 @@ members = ["crates/harness", "app"]
resolver = "2" resolver = "2"
[workspace.package] [workspace.package]
version = "3.6.7" version = "3.6.8"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
repository = "https://github.com/JoasASantos/NeuroSploit" repository = "https://github.com/JoasASantos/NeuroSploit"
+4 -4
View File
@@ -1,4 +1,4 @@
//! NeuroSploit v3.6.7 — interactive harness + CLI (`run` / `whitebox` / `agents` / `models`). //! NeuroSploit v3.6.8 — interactive harness + CLI (`run` / `whitebox` / `agents` / `models`).
mod repl; mod repl;
mod tui; mod tui;
@@ -11,8 +11,8 @@ use std::path::{Path, PathBuf};
#[command( #[command(
name = "neurosploit", name = "neurosploit",
version, version,
about = "NeuroSploit v3.6.7 — multi-model autonomous pentest harness", about = "NeuroSploit v3.6.8 — multi-model autonomous pentest harness",
long_about = "NeuroSploit v3.6.7 — a Rust multi-model harness that drives a pool of LLMs \ long_about = "NeuroSploit v3.6.8 — a Rust multi-model harness that drives a pool of LLMs \
(API key or local subscription: Claude/Codex/Gemini/Grok) to autonomously test a target. \ (API key or local subscription: Claude/Codex/Gemini/Grok) to autonomously test a target. \
After recon it INTELLIGENTLY selects only the agents matching the discovered surface, runs \ 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\ them in parallel, then validates every finding by cross-model voting before reporting.\n\n\
@@ -765,7 +765,7 @@ pub(crate) fn spawn_engagement(base: &Path, mut cfg: RunConfig, mcp: bool, mode:
println!(" │ ua : {ua}"); println!(" │ ua : {ua}");
write_status(&workdir, "running", &format!("\"target\":{:?}", cfg.target)); write_status(&workdir, "running", &format!("\"target\":{:?}", cfg.target));
println!(" ┌─ NeuroSploit v3.6.7 · by Joas A Santos & Red Team Leaders"); println!(" ┌─ NeuroSploit v3.6.8 · by Joas A Santos & Red Team Leaders");
println!(" │ run id : {run_id}"); println!(" │ run id : {run_id}");
println!(" │ target : {}", cfg.target); println!(" │ target : {}", cfg.target);
println!(" │ models : {}", cfg.models.join(", ")); println!(" │ models : {}", cfg.models.join(", "));
+2 -2
View File
@@ -1,4 +1,4 @@
//! NeuroSploit v3.6.7 — interactive session (Claude-Code / Codex / Cursor-CLI style). //! NeuroSploit v3.6.8 — interactive session (Claude-Code / Codex / Cursor-CLI style).
//! //!
//! Launched when `neurosploit` runs with no subcommand. A persistent REPL with //! Launched when `neurosploit` runs with no subcommand. A persistent REPL with
//! real line editing (arrow-key history recall, Ctrl-A/E/K, paste), model //! real line editing (arrow-key history recall, Ctrl-A/E/K, paste), model
@@ -370,7 +370,7 @@ pub async fn repl(base: &Path) -> anyhow::Result<()> {
let backends = harness::installed_cli_backends(); let backends = harness::installed_cli_backends();
println!("\x1b[1m"); println!("\x1b[1m");
println!(" ███╗ ██╗███████╗██╗ ██╗██████╗ ██████╗"); println!(" ███╗ ██╗███████╗██╗ ██╗██████╗ ██████╗");
println!(" ████╗ ██║██╔════╝██║ ██║██╔══██╗██╔═══██╗ NeuroSploit v3.6.7"); println!(" ████╗ ██║██╔════╝██║ ██║██╔══██╗██╔═══██╗ NeuroSploit v3.6.8");
println!(" ██╔██╗ ██║█████╗ ██║ ██║██████╔╝██║ ██║ interactive harness"); println!(" ██╔██╗ ██║█████╗ ██║ ██║██████╔╝██║ ██║ interactive harness");
println!(" ██║╚██╗██║██╔══╝ ██║ ██║██╔══██╗██║ ██║ by Joas A Santos"); println!(" ██║╚██╗██║██╔══╝ ██║ ██║██╔══██╗██║ ██║ by Joas A Santos");
println!(" ██║ ╚████║███████╗╚██████╔╝██║ ██║╚██████╔╝ & Red Team Leaders"); println!(" ██║ ╚████║███████╗╚██████╔╝██║ ██║╚██████╔╝ & Red Team Leaders");
+1 -1
View File
@@ -1,4 +1,4 @@
//! NeuroSploit v3.6.7 — TUI "Mission Control" mode. //! NeuroSploit v3.6.8 — TUI "Mission Control" mode.
//! //!
//! Concurrent panels that update live while the engagement runs in the //! Concurrent panels that update live while the engagement runs in the
//! background, with a composer input that stays active during execution: //! background, with a composer input that stays active during execution:
+14 -1
View File
@@ -163,7 +163,20 @@ impl ChatClient {
if !key.is_empty() { if !key.is_empty() {
if azure { req = req.header("api-key", &key); } else { req = req.bearer_auth(&key); } if azure { req = req.header("api-key", &key); } else { req = req.bearer_auth(&key); }
} }
let resp = req.send().await?; let resp = req.send().await.map_err(|e| {
if e.is_connect() {
let local = matches!(p.key, "ollama" | "litellm" | "llamacpp");
if local {
anyhow!("{} connection refused at {} — is the server running? ({})", p.key, url, e)
} else {
anyhow!("{} connection error: {}", p.key, e)
}
} else if e.is_timeout() {
anyhow!("{} request timed out (120s) for model '{}' — model may be too large for available memory", p.key, m.model)
} else {
anyhow!("{} request error: {}", p.key, e)
}
})?;
let status = resp.status(); let status = resp.status();
let text = resp.text().await.unwrap_or_default(); let text = resp.text().await.unwrap_or_default();
if !status.is_success() { if !status.is_success() {
+24 -2
View File
@@ -619,6 +619,9 @@ pub async fn run(cfg: RunConfig, lib: &Library, pool: &ModelPool, tx: Sender<Str
let transcript = transcript_of(&raw); let transcript = transcript_of(&raw);
let candidates = dedup_findings(raw.iter().flat_map(|(_, _, f)| f.clone()).collect()); let candidates = dedup_findings(raw.iter().flat_map(|(_, _, f)| f.clone()).collect());
let _ = tx.send(format!("{} candidate finding(s) (deduped) — validating by {}-model vote", candidates.len(), cfg.vote_n)).await; let _ = tx.send(format!("{} candidate finding(s) (deduped) — validating by {}-model vote", candidates.len(), cfg.vote_n)).await;
if pool.candidates.len() == 1 && cfg.vote_n <= 1 {
let _ = tx.send("⚠ single-model panel with vote_n=1 — validation is weaker (same model validates its own findings). Consider --vote-n 2 or adding a second model for cross-validation.".into()).await;
}
// ---- 4. Validate by N-model voting --------------------------------- // ---- 4. Validate by N-model voting ---------------------------------
let mut findings = validate(candidates, pool, VOTE_SYS, cfg.vote_n, &tx).await; let mut findings = validate(candidates, pool, VOTE_SYS, cfg.vote_n, &tx).await;
@@ -1148,9 +1151,26 @@ fn heuristic_select(ranked: &[Agent], recon: &str, focus: &str, cap: usize) -> V
} }
async fn validate(candidates: Vec<Finding>, pool: &ModelPool, sys: &str, vote_n: usize, tx: &Sender<String>) -> Vec<Finding> { async fn validate(candidates: Vec<Finding>, pool: &ModelPool, sys: &str, vote_n: usize, tx: &Sender<String>) -> Vec<Finding> {
// Fast-track: findings with no evidence are unverifiable — skip the vote
// and flag for human review instead of wasting a validator call that will
// always reject ("default to rejected when uncertain" + empty evidence).
let (have_evidence, no_evidence): (Vec<_>, Vec<_>) = candidates.into_iter().partition(|f| {
let e = f.evidence.trim();
!e.is_empty() && e != "N/A" && e != "n/a" && e != "none" && e != "-"
});
let mut flagged: Vec<Finding> = no_evidence.into_iter().map(|mut f| {
f.validated = false;
f.review_status = "needs-review".into();
f.review_reason = "no concrete evidence provided by agent — manual verification required".into();
f.votes = "0/0".into();
f
}).collect();
for f in &flagged {
let _ = tx.send(format!("vote {} → needs-review (no evidence)", f.title)).await;
}
// Prefer a model other than the primary (likely finder) to adjudicate. // Prefer a model other than the primary (likely finder) to adjudicate.
let finder = pool.candidates.first().map(|m| m.label()); let finder = pool.candidates.first().map(|m| m.label());
let validated: Vec<Finding> = stream::iter(candidates) let validated: Vec<Finding> = stream::iter(have_evidence)
.map(|mut f| { .map(|mut f| {
let txc = tx.clone(); let txc = tx.clone();
let finder = finder.clone(); let finder = finder.clone();
@@ -1184,7 +1204,9 @@ async fn validate(candidates: Vec<Finding>, pool: &ModelPool, sys: &str, vote_n:
.collect() .collect()
.await; .await;
// Keep confirmed AND needs-review (human decides); drop only zero-support noise. // Keep confirmed AND needs-review (human decides); drop only zero-support noise.
validated.into_iter().filter(|f| f.validated || f.review_status == "needs-review").collect() // Include no-evidence flagged findings so the human loop sees them.
flagged.extend(validated.into_iter().filter(|f| f.validated || f.review_status == "needs-review"));
flagged
} }
/// Adversarial refutation pass: every confirmed **High/Critical** finding is /// Adversarial refutation pass: every confirmed **High/Critical** finding is
+2 -2
View File
@@ -28,7 +28,7 @@ cat <<'BANNER'
███╗ ██╗███████╗██╗ ██╗██████╗ ██████╗ ███╗ ██╗███████╗██╗ ██╗██████╗ ██████╗
████╗ ██║██╔════╝██║ ██║██╔══██╗██╔═══██╗ NeuroSploit installer ████╗ ██║██╔════╝██║ ██║██╔══██╗██╔═══██╗ NeuroSploit installer
██╔██╗ ██║█████╗ ██║ ██║██████╔╝██║ ██║ v3.6.1 — Rust harness ██╔██╗ ██║█████╗ ██║ ██║██████╔╝██║ ██║ v3.6.8 — Rust harness
██║╚██╗██║██╔══╝ ██║ ██║██╔══██╗██║ ██║ by Joas A Santos ██║╚██╗██║██╔══╝ ██║ ██║██╔══██╗██║ ██║ by Joas A Santos
██║ ╚████║███████╗╚██████╔╝██║ ██║╚██████╔╝ & Red Team Leaders ██║ ╚████║███████╗╚██████╔╝██║ ██║╚██████╔╝ & Red Team Leaders
╚═╝ ╚═══╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝
@@ -63,7 +63,7 @@ if [ -z "$REF" ]; then
REF="$(dl "https://api.github.com/repos/${REPO_SLUG}/releases/latest" /dev/stdout 2>/dev/null \ REF="$(dl "https://api.github.com/repos/${REPO_SLUG}/releases/latest" /dev/stdout 2>/dev/null \
| grep -m1 '"tag_name"' | sed -E 's/.*"tag_name" *: *"([^"]+)".*/\1/' || true)" | grep -m1 '"tag_name"' | sed -E 's/.*"tag_name" *: *"([^"]+)".*/\1/' || true)"
fi fi
[ -z "$REF" ] && REF="v3.6.1" [ -z "$REF" ] && REF="v3.6.8"
say "Release: $REF" say "Release: $REF"
installed=0 installed=0