From a064b4e49742f6dd52009e0ad237eeae55a8156a Mon Sep 17 00:00:00 2001 From: CyberSecurityUP Date: Sun, 5 Jul 2026 18:31:02 -0300 Subject: [PATCH] setup: global install (download prebuilt + PATH + NEUROSPLOIT_BASE), run from anywhere MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - setup.sh: downloads the prebuilt release asset for the detected OS/arch (no Rust needed; latest release auto-resolved), installs binary + agents_md to ~/.neurosploit-app, symlinks into ~/.local/bin, and PERSISTS PATH + NEUROSPLOIT_BASE into the shell rc (bash/zsh/fish). Falls back to a source build (NEUROSPLOIT_BUILD=1 to force). Idempotent. - install.ps1: same for Windows — downloads windows-x64 zip, installs to %LOCALAPPDATA%\NeuroSploit, sets User PATH + NEUROSPLOIT_BASE (setx), source-build fallback (incl. arm64). - find_base(): auto-discovers agents_md/ NEXT TO THE EXECUTABLE (resolves the PATH symlink via current_exe) and at common install dirs — so `neurosploit` runs from ANY folder even without the env var. Env override still takes precedence. Verified: symlinked binary run from /tmp with no env finds all 383 agents. --- install.ps1 | 125 ++++++++++++++------- neurosploit-rs/app/src/main.rs | 28 ++++- setup.sh | 193 +++++++++++++++++++++------------ 3 files changed, 238 insertions(+), 108 deletions(-) diff --git a/install.ps1 b/install.ps1 index 9b5947f..5fcb073 100644 --- a/install.ps1 +++ b/install.ps1 @@ -2,8 +2,11 @@ # # irm https://raw.githubusercontent.com/JoasASantos/NeuroSploit/main/install.ps1 | iex # -# Installs the Rust toolchain if needed, clones the repo, builds the release -# binary, and adds it to your PATH. Works on x64 and arm64. +# Downloads the prebuilt neurosploit.exe + agent library, installs them, and sets +# your User PATH + NEUROSPLOIT_BASE so you can run `neurosploit` from ANY folder — +# no need to cd into the repo. Falls back to building from source if needed. +# Env: NEUROSPLOIT_DIR (install dir), NEUROSPLOIT_REF (release tag), +# NEUROSPLOIT_BUILD=1 (force source build). $ErrorActionPreference = "Stop" function Say($m) { Write-Host " > $m" -ForegroundColor Magenta } @@ -12,50 +15,98 @@ function Warn($m){ Write-Host " ! $m" -ForegroundColor Yellow } Write-Host "" Write-Host " NeuroSploit installer (Windows) — v3.5.5" -ForegroundColor Cyan -$arch = $env:PROCESSOR_ARCHITECTURE + +# arch → asset arch (only x64 prebuilt today; arm64 falls back to source) +$rawArch = $env:PROCESSOR_ARCHITECTURE +$arch = if ($rawArch -match 'ARM64') { "arm64" } else { "x64" } Say "Platform: Windows / $arch" -$dir = if ($env:NEUROSPLOIT_DIR) { $env:NEUROSPLOIT_DIR } else { Join-Path $HOME ".neurosploit-src" } -$ref = if ($env:NEUROSPLOIT_REF) { $env:NEUROSPLOIT_REF } else { "main" } +$slug = "JoasASantos/NeuroSploit" +$dir = if ($env:NEUROSPLOIT_DIR) { $env:NEUROSPLOIT_DIR } else { Join-Path $env:LOCALAPPDATA "NeuroSploit" } +$ref = $env:NEUROSPLOIT_REF -# 1) git -if (-not (Get-Command git -ErrorAction SilentlyContinue)) { throw "git is required (install Git for Windows) and re-run." } +# resolve latest release tag unless pinned +if (-not $ref) { + try { $ref = (Invoke-RestMethod "https://api.github.com/repos/$slug/releases/latest").tag_name } catch { } +} +if (-not $ref) { $ref = "v3.5.5" } +Say "Release: $ref" -# 2) Rust (rustup) — winget if available, else the rustup-init bootstrap -if (-not (Get-Command cargo -ErrorAction SilentlyContinue)) { - Say "Rust not found — installing rustup..." - if (Get-Command winget -ErrorAction SilentlyContinue) { - winget install -e --id Rustlang.Rustup --accept-source-agreements --accept-package-agreements - } else { - $ri = Join-Path $env:TEMP "rustup-init.exe" - Invoke-WebRequest "https://win.rustup.rs/$arch" -OutFile $ri - & $ri -y --default-toolchain stable --profile minimal +New-Item -ItemType Directory -Force -Path $dir | Out-Null +$installed = $false + +# ---- try the prebuilt asset (no Rust needed; x64 only) ---- +if ($env:NEUROSPLOIT_BUILD -ne "1" -and $arch -eq "x64") { + $asset = "neurosploit-$ref-windows-x64.zip" + $url = "https://github.com/$slug/releases/download/$ref/$asset" + $tmp = Join-Path $env:TEMP "ns-dl" + Remove-Item -Recurse -Force $tmp -ErrorAction SilentlyContinue + New-Item -ItemType Directory -Force -Path $tmp | Out-Null + try { + Say "Downloading prebuilt binary: $asset" + Invoke-WebRequest $url -OutFile (Join-Path $tmp "a.zip") + Expand-Archive -Path (Join-Path $tmp "a.zip") -DestinationPath $tmp -Force + $exe = Get-ChildItem -Path $tmp -Recurse -Filter neurosploit.exe | Select-Object -First 1 + if (-not $exe) { throw "no neurosploit.exe in archive" } + $srcdir = $exe.DirectoryName + Copy-Item (Join-Path $srcdir "neurosploit.exe") (Join-Path $dir "neurosploit.exe") -Force + Remove-Item -Recurse -Force (Join-Path $dir "agents_md") -ErrorAction SilentlyContinue + Copy-Item (Join-Path $srcdir "agents_md") (Join-Path $dir "agents_md") -Recurse -Force + Remove-Item -Recurse -Force $tmp -ErrorAction SilentlyContinue + $installed = $true + Ok "Downloaded & unpacked -> $dir" + } catch { + Warn "Prebuilt download failed ($($_.Exception.Message)) — building from source." } - $env:Path = "$HOME\.cargo\bin;$env:Path" -} -Ok ("Rust: " + (cargo --version)) - -# 3) clone or update -if (Test-Path (Join-Path $dir ".git")) { - Say "Updating $dir..."; git -C $dir fetch --depth 1 origin $ref; git -C $dir reset --hard "origin/$ref" -} else { - Say "Cloning to $dir..."; git clone --depth 1 --branch $ref "https://github.com/JoasASantos/NeuroSploit.git" $dir } -# 4) build -Say "Building release binary (first build downloads crates)..." -Push-Location (Join-Path $dir "neurosploit-rs"); cargo build --release; Pop-Location -$bin = Join-Path $dir "neurosploit-rs\target\release\neurosploit.exe" -if (-not (Test-Path $bin)) { throw "build did not produce $bin" } -Ok ("Built: " + (& $bin --version)) +# ---- build from source (needs git + Rust) ---- +if (-not $installed) { + if (-not (Get-Command git -ErrorAction SilentlyContinue)) { throw "git is required to build from source (install Git for Windows)." } + if (-not (Get-Command cargo -ErrorAction SilentlyContinue)) { + Say "Rust not found — installing rustup..." + if (Get-Command winget -ErrorAction SilentlyContinue) { + winget install -e --id Rustlang.Rustup --accept-source-agreements --accept-package-agreements + } else { + $ri = Join-Path $env:TEMP "rustup-init.exe" + Invoke-WebRequest "https://win.rustup.rs/$rawArch" -OutFile $ri + & $ri -y --default-toolchain stable --profile minimal + } + $env:Path = "$HOME\.cargo\bin;$env:Path" + } + Ok ("Rust: " + (cargo --version)) + $src = Join-Path $dir "src" + if (Test-Path (Join-Path $src ".git")) { + Say "Updating $src..."; git -C $src fetch --depth 1 origin $ref; git -C $src checkout -q FETCH_HEAD + } else { + Say "Cloning to $src..."; git clone --depth 1 --branch $ref "https://github.com/$slug.git" $src + } + Say "Building release binary (first build downloads crates)..." + Push-Location (Join-Path $src "neurosploit-rs"); cargo build --release; Pop-Location + Copy-Item (Join-Path $src "neurosploit-rs\target\release\neurosploit.exe") (Join-Path $dir "neurosploit.exe") -Force + Remove-Item -Recurse -Force (Join-Path $dir "agents_md") -ErrorAction SilentlyContinue + Copy-Item (Join-Path $src "agents_md") (Join-Path $dir "agents_md") -Recurse -Force + Ok "Built -> $dir" +} -# 5) add to PATH (user) -$binDir = Split-Path $bin +$exePath = Join-Path $dir "neurosploit.exe" +if (-not (Test-Path $exePath)) { throw "install did not produce $exePath" } + +# ---- set User PATH + NEUROSPLOIT_BASE (so it runs from any folder) ---- $userPath = [Environment]::GetEnvironmentVariable("Path", "User") -if ($userPath -notlike "*$binDir*") { - [Environment]::SetEnvironmentVariable("Path", "$userPath;$binDir", "User") - Ok "Added $binDir to your PATH (open a new terminal)." +if ($userPath -notlike "*$dir*") { + [Environment]::SetEnvironmentVariable("Path", "$userPath;$dir", "User") + Ok "Added $dir to your User PATH." } +[Environment]::SetEnvironmentVariable("NEUROSPLOIT_BASE", $dir, "User") +Ok "Set NEUROSPLOIT_BASE=$dir (User)." +# make it work in THIS session too +$env:Path = "$dir;$env:Path"; $env:NEUROSPLOIT_BASE = $dir + +Ok ("Version: " + (& $exePath --version)) Write-Host "" -Ok "Done. Launch: neurosploit" +Ok "Installed. Open a NEW terminal, then from ANY folder:" +Write-Host " neurosploit # interactive session" Write-Host " neurosploit run http://testphp.vulnweb.com/ --subscription --model anthropic:claude-opus-4-8 -v" +Write-Host " neurosploit --help" +Warn "Update later: just re-run this script." diff --git a/neurosploit-rs/app/src/main.rs b/neurosploit-rs/app/src/main.rs index d74f91e..6dea6ee 100644 --- a/neurosploit-rs/app/src/main.rs +++ b/neurosploit-rs/app/src/main.rs @@ -233,9 +233,13 @@ enum Cmd { /// Locate the repo root that holds `agents_md/`. fn find_base() -> PathBuf { + // 1) Explicit override (set by the installer for a global, run-from-anywhere install). if let Ok(b) = std::env::var("NEUROSPLOIT_BASE") { - return PathBuf::from(b); + if !b.trim().is_empty() { + return PathBuf::from(b); + } } + // 2) Walk up from the current directory (running inside a checkout). if let Ok(cwd) = std::env::current_dir() { let mut dir = cwd.as_path(); for _ in 0..6 { @@ -248,6 +252,28 @@ fn find_base() -> PathBuf { } } } + // 3) Next to the ACTUAL executable (a global install ships the binary and + // agents_md/ together). current_exe() resolves the PATH symlink to the real + // install dir — so `neurosploit` works from any folder without any env var. + if let Ok(exe) = std::env::current_exe() { + let real = std::fs::canonicalize(&exe).unwrap_or(exe); + for cand in [real.parent(), real.parent().and_then(|p| p.parent())].into_iter().flatten() { + if cand.join("agents_md").is_dir() { + return cand.to_path_buf(); + } + } + } + // 4) Common install locations (matches setup.sh / install.ps1 defaults). + if let Some(home) = std::env::var_os("HOME").map(PathBuf::from) { + for c in [home.join(".neurosploit-app"), home.join(".local/share/neurosploit")] { + if c.join("agents_md").is_dir() { return c; } + } + } + if let Some(la) = std::env::var_os("LOCALAPPDATA").map(PathBuf::from) { + let c = la.join("NeuroSploit"); + if c.join("agents_md").is_dir() { return c; } + } + // 5) Last resort: the build-time layout. PathBuf::from(env!("CARGO_MANIFEST_DIR")) .parent() .and_then(|p| p.parent()) diff --git a/setup.sh b/setup.sh index 99afcfa..465359b 100755 --- a/setup.sh +++ b/setup.sh @@ -3,23 +3,26 @@ # # curl -fsSL https://raw.githubusercontent.com/JoasASantos/NeuroSploit/main/setup.sh | bash # -# Builds the v3.5.0 Rust harness and installs the `neurosploit` binary. -# Safe to re-run (idempotent). Honors: -# NEUROSPLOIT_DIR install/clone dir (default: ~/.neurosploit) -# NEUROSPLOIT_REF git branch/tag (default: main) -# PREFIX bin install prefix (default: ~/.local/bin) +# Downloads the prebuilt `neurosploit` binary + agent library, installs them, and +# sets up PATH + NEUROSPLOIT_BASE so you can run `neurosploit` from ANY folder — +# no need to cd into the repo. Falls back to building from source if no prebuilt +# asset fits (or NEUROSPLOIT_BUILD=1). Safe to re-run (idempotent). Honors: +# NEUROSPLOIT_DIR install dir (default: ~/.neurosploit-app) +# NEUROSPLOIT_REF release tag/branch (default: latest release) +# NEUROSPLOIT_BUILD 1 = build from source instead of downloading +# PREFIX bin symlink dir (default: ~/.local/bin) set -euo pipefail -REPO="https://github.com/JoasASantos/NeuroSploit.git" -DIR="${NEUROSPLOIT_DIR:-$HOME/.neurosploit}" -REF="${NEUROSPLOIT_REF:-main}" +REPO_SLUG="JoasASantos/NeuroSploit" +REPO="https://github.com/${REPO_SLUG}.git" +DIR="${NEUROSPLOIT_DIR:-$HOME/.neurosploit-app}" PREFIX="${PREFIX:-$HOME/.local/bin}" c() { printf '\033[%sm%s\033[0m\n' "$1" "$2"; } -say() { c '1;35' " ▌ $*"; } -ok() { c '1;32' " ✓ $*"; } +say(){ c '1;35' " ▌ $*"; } +ok() { c '1;32' " ✓ $*"; } warn(){ c '1;33' " ! $*"; } -die() { c '1;31' " ✗ $*"; exit 1; } +die(){ c '1;31' " ✗ $*"; exit 1; } cat <<'BANNER' @@ -31,79 +34,129 @@ cat <<'BANNER' ╚═╝ ╚═══╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ BANNER -# ---- platform detection (Linux / macOS / Windows-via-WSL/MSYS · x64 / arm64) ---- -OS_RAW="$(uname -s)" -ARCH_RAW="$(uname -m)" +# ---- platform detection (Linux / macOS / Windows-WSL · x64 / arm64) ---- +OS_RAW="$(uname -s)"; ARCH_RAW="$(uname -m)" case "$OS_RAW" in - Linux*) OS="Linux" ;; - Darwin*) OS="macOS" ;; - MINGW*|MSYS*|CYGWIN*) OS="Windows" ;; - *) OS="$OS_RAW" ;; + Linux*) OS="linux" ;; + Darwin*) OS="macos" ;; + MINGW*|MSYS*|CYGWIN*) OS="windows" ;; + *) OS="$OS_RAW" ;; esac case "$ARCH_RAW" in - x86_64|amd64) ARCH="x64" ;; - arm64|aarch64) ARCH="arm64" ;; - *) ARCH="$ARCH_RAW" ;; + x86_64|amd64) ARCH="x64" ;; + arm64|aarch64) ARCH="arm64" ;; + *) ARCH="$ARCH_RAW" ;; esac -say "Platform: $OS / $ARCH" -if [ "$OS" = "Windows" ]; then - warn "On native Windows, run this in WSL2, Git Bash or MSYS2. (Or build with: cargo build --release)" +PLAT="${OS}-${ARCH}" +say "Platform: $PLAT" +[ "$OS" = "windows" ] && warn "Native Windows: prefer install.ps1 (PowerShell). This path works under WSL2/Git Bash." + +dl() { # dl + if command -v curl >/dev/null 2>&1; then curl -fsSL "$1" -o "$2" + elif command -v wget >/dev/null 2>&1; then wget -qO "$2" "$1" + else return 1; fi +} + +# ---- resolve the release tag (latest, unless pinned) ---- +REF="${NEUROSPLOIT_REF:-}" +if [ -z "$REF" ]; then + 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)" fi -if [ "$OS" != "Linux" ] && [ "$OS" != "macOS" ] && [ "$OS" != "Windows" ]; then - warn "Unrecognized OS '$OS_RAW' — attempting a generic Rust build anyway." +[ -z "$REF" ] && REF="v3.5.5" +say "Release: $REF" + +installed=0 +if [ "${NEUROSPLOIT_BUILD:-0}" != "1" ] && [ "$OS" != "windows" ]; then + # ---- try the prebuilt asset (no Rust needed) ---- + ASSET="neurosploit-${REF}-${PLAT}.tar.gz" + URL="https://github.com/${REPO_SLUG}/releases/download/${REF}/${ASSET}" + TMP="$(mktemp -d)" + say "Downloading prebuilt binary: $ASSET" + if dl "$URL" "$TMP/a.tar.gz"; then + mkdir -p "$DIR" + tar -xzf "$TMP/a.tar.gz" -C "$TMP" 2>/dev/null || die "download was not a valid archive" + SRC="$(find "$TMP" -maxdepth 2 -name neurosploit -type f | head -1)" + [ -n "$SRC" ] || die "extracted archive has no neurosploit binary" + SRCDIR="$(dirname "$SRC")" + rm -rf "$DIR/neurosploit" "$DIR/agents_md" + cp "$SRCDIR/neurosploit" "$DIR/neurosploit" + cp -R "$SRCDIR/agents_md" "$DIR/agents_md" + chmod +x "$DIR/neurosploit" + rm -rf "$TMP" + installed=1 + ok "Downloaded & unpacked → $DIR" + else + rm -rf "$TMP" + warn "No prebuilt asset for $PLAT ($REF) — building from source instead." + fi fi -# 1) git -command -v git >/dev/null 2>&1 || die "git is required. Install git and re-run." - -# 2) Rust toolchain (rustup) -if ! command -v cargo >/dev/null 2>&1; then - [ -f "$HOME/.cargo/env" ] && . "$HOME/.cargo/env" || true -fi -if ! command -v cargo >/dev/null 2>&1; then - say "Rust not found — installing rustup (stable, minimal)…" - curl --proto '=https' --tlsv1.2 -fsSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --profile minimal - . "$HOME/.cargo/env" -fi -ok "Rust: $(cargo --version)" - -# 3) clone or update -if [ -d "$DIR/.git" ]; then - say "Updating existing checkout at $DIR…" - git -C "$DIR" fetch --depth 1 origin "$REF" && git -C "$DIR" checkout -q "$REF" && git -C "$DIR" reset -q --hard "origin/$REF" 2>/dev/null || git -C "$DIR" pull -q -else - say "Cloning $REPO ($REF) → $DIR…" - git clone --depth 1 --branch "$REF" "$REPO" "$DIR" 2>/dev/null || git clone --depth 1 "$REPO" "$DIR" +if [ "$installed" != "1" ]; then + # ---- build from source (needs git + Rust) ---- + command -v git >/dev/null 2>&1 || die "git is required to build from source." + if ! command -v cargo >/dev/null 2>&1; then [ -f "$HOME/.cargo/env" ] && . "$HOME/.cargo/env" || true; fi + if ! command -v cargo >/dev/null 2>&1; then + say "Rust not found — installing rustup (stable, minimal)…" + curl --proto '=https' --tlsv1.2 -fsSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --profile minimal + . "$HOME/.cargo/env" + fi + ok "Rust: $(cargo --version)" + SRC_CO="$DIR/src" + if [ -d "$SRC_CO/.git" ]; then + say "Updating checkout…"; git -C "$SRC_CO" fetch --depth 1 origin "$REF" 2>/dev/null && git -C "$SRC_CO" checkout -q FETCH_HEAD 2>/dev/null || git -C "$SRC_CO" pull -q || true + else + say "Cloning $REPO ($REF)…"; git clone --depth 1 --branch "$REF" "$REPO" "$SRC_CO" 2>/dev/null || git clone --depth 1 "$REPO" "$SRC_CO" + fi + say "Building release binary (first build downloads crates)…" + ( cd "$SRC_CO/neurosploit-rs" && cargo build --release ) + cp "$SRC_CO/neurosploit-rs/target/release/neurosploit" "$DIR/neurosploit" + rm -rf "$DIR/agents_md"; cp -R "$SRC_CO/agents_md" "$DIR/agents_md" + chmod +x "$DIR/neurosploit" + ok "Built → $DIR" fi -# 4) build -say "Building release binary (first build downloads crates; grab a coffee)…" -( cd "$DIR/neurosploit-rs" && cargo build --release ) -BIN="$DIR/neurosploit-rs/target/release/neurosploit" -[ -x "$BIN" ] || die "build did not produce $BIN" -ok "Built: $("$BIN" --version 2>/dev/null || echo neurosploit)" - -# 5) install on PATH +# ---- install on PATH ---- mkdir -p "$PREFIX" -ln -sf "$BIN" "$PREFIX/neurosploit" -ok "Installed → $PREFIX/neurosploit" +ln -sf "$DIR/neurosploit" "$PREFIX/neurosploit" +ok "Linked → $PREFIX/neurosploit" +ok "Version: $(NEUROSPLOIT_BASE="$DIR" "$DIR/neurosploit" --version 2>/dev/null || echo neurosploit)" -# 6) optional tooling hints (don't fail if absent) -say "Recommended tools for richer testing (optional):" -for t in curl nmap rustscan ffuf node npx typst; do - if command -v "$t" >/dev/null 2>&1; then ok "$t present"; else warn "$t missing"; fi -done -echo -warn "Best run on Kali Linux → docker run -it --rm kalilinux/kali-rolling" -warn "typst (PDF reports): cargo install typst-cli · rustscan: cargo install rustscan" - -case ":$PATH:" in - *":$PREFIX:"*) ;; - *) warn "Add to PATH: echo 'export PATH=\"$PREFIX:\$PATH\"' >> ~/.bashrc && source ~/.bashrc" ;; +# ---- persist env (PATH + NEUROSPLOIT_BASE) so it runs from any folder ---- +persist() { # append an idempotent block to a shell rc that exists + local rc="$1"; [ -e "$rc" ] || return 0 + grep -q 'NEUROSPLOIT_BASE' "$rc" 2>/dev/null && return 0 + { echo ''; echo '# NeuroSploit (added by setup.sh)' + echo "export NEUROSPLOIT_BASE=\"$DIR\"" + echo "export PATH=\"$PREFIX:\$PATH\""; } >> "$rc" + ok "Configured $rc" +} +SHELL_NAME="$(basename "${SHELL:-bash}")" +case "$SHELL_NAME" in + zsh) touch "$HOME/.zshrc"; persist "$HOME/.zshrc" ;; + bash) touch "$HOME/.bashrc"; persist "$HOME/.bashrc"; persist "$HOME/.bash_profile" ;; + *) touch "$HOME/.profile"; persist "$HOME/.profile" ;; esac +if [ -d "$HOME/.config/fish" ]; then + FCONF="$HOME/.config/fish/config.fish" + if ! grep -q 'NEUROSPLOIT_BASE' "$FCONF" 2>/dev/null; then + { echo ''; echo '# NeuroSploit'; echo "set -gx NEUROSPLOIT_BASE \"$DIR\""; echo "set -gx PATH \"$PREFIX\" \$PATH"; } >> "$FCONF" + ok "Configured $FCONF" + fi +fi + +# ---- optional tooling hints ---- +say "Recommended tools (optional): curl nmap rustscan ffuf node npx typst" +for t in curl nmap rustscan ffuf node npx typst; do + command -v "$t" >/dev/null 2>&1 && ok "$t present" || warn "$t missing" +done echo -ok "Done. Authenticate a model, then launch:" +ok "Installed. Open a NEW terminal — or run now with:" +echo " export NEUROSPLOIT_BASE=\"$DIR\"; export PATH=\"$PREFIX:\$PATH\"" +echo " then, from ANY folder:" echo " neurosploit # interactive session" echo " neurosploit run http://testphp.vulnweb.com/ --subscription --model anthropic:claude-opus-4-8 -v" echo " neurosploit --help" +echo +warn "Update later: just re-run this script. Best runtime: Kali (docker run -it kalilinux/kali-rolling)."