From e8b7f618905a63044c9c2bfce2e2b181093a9d1d Mon Sep 17 00:00:00 2001 From: CyberSecurityUP Date: Fri, 16 Oct 2026 12:17:14 -0300 Subject: [PATCH] fix(web): read the console version from the CLI binary, not a hardcoded string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The web console had the version hardcoded in three places (/api/meta, the startup log, and the page title/badge), so it drifted from the CLI binary on every release and had to be bumped by hand. Resolve it once at startup from `neurosploit --version` and serve that through /api/meta; the sidebar badge and the document title now follow it too. CLI and Web stay in lockstep — bump the crate, rebuild, and the UA, the badge and the title all move together with no web edit. Falls back to the package string only when the binary is missing. Co-Authored-By: Claude Opus 4.8 --- web/public/app.js | 6 +++++- web/server.js | 21 ++++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/web/public/app.js b/web/public/app.js index 08c9870..12601b4 100644 --- a/web/public/app.js +++ b/web/public/app.js @@ -2655,7 +2655,11 @@ async function boot() { renderFindings('live'); renderFindings('detail'); const meta = await api('/api/meta').catch(() => ({})); - $('#sbVersion').textContent = `v${meta.version || '4.0.0'}`; + if (meta.version) { + // Version is authoritative from the CLI binary — keep badge + title in sync. + $('#sbVersion').textContent = `v${meta.version}`; + document.title = `NeuroSploit v${meta.version} — Console`; + } await Promise.all([loadAgents(), loadProviders()]); await refreshRuns(); await tryResumeActiveJob(); // survive an F5 while watching a live run diff --git a/web/server.js b/web/server.js index 8fc16e3..bf5ccab 100644 --- a/web/server.js +++ b/web/server.js @@ -19,7 +19,7 @@ const fs = require('node:fs'); const fsp = fs.promises; const os = require('node:os'); const path = require('node:path'); -const { spawn } = require('node:child_process'); +const { spawn, execFileSync } = require('node:child_process'); const crypto = require('node:crypto'); const { EventEmitter } = require('node:events'); const { StringDecoder } = require('node:string_decoder'); @@ -89,6 +89,21 @@ function findBinary() { } const BIN = findBinary(); +// The console version is whatever the compiled CLI reports — never a hardcoded +// string. This keeps the web UI and the CLI binary in lockstep: bump the crate, +// rebuild, and the title/badge/UA all move together with no web edit. Falls back +// to the package string only when the binary is missing. +const FALLBACK_VERSION = '4.3.1'; +function binaryVersion() { + if (!BIN) return FALLBACK_VERSION; + try { + const out = execFileSync(BIN, ['--version'], { encoding: 'utf8', timeout: 5000 }); + const m = out.match(/\b(\d+\.\d+\.\d+(?:[-+][\w.]+)?)\b/); + return (m && m[1]) || FALLBACK_VERSION; + } catch { return FALLBACK_VERSION; } +} +const VERSION = binaryVersion(); + const PORT = Number(process.env.NEUROSPLOIT_WEB_PORT || process.env.PORT || 4173); // --------------------------------------------------------------------------- @@ -1890,7 +1905,7 @@ const server = http.createServer(async (req, res) => { } if (req.method === 'GET' && p === '/api/meta') { - return sendJson(res, 200, { version: "4.3.1", binary: BIN, root: ROOT }); + return sendJson(res, 200, { version: VERSION, binary: BIN, root: ROOT }); } // ---- providers / API keys (in-memory only, never persisted) ---- @@ -1936,7 +1951,7 @@ const server = http.createServer(async (req, res) => { loadPersistedJobs(); server.listen(PORT, () => { - console.log(`NeuroSploit v4.3.1 web console → http://localhost:${PORT}`); + console.log(`NeuroSploit v${VERSION} web console → http://localhost:${PORT}`); console.log(` binary : ${BIN || '(not found — build neurosploit-rs first)'}`); console.log(` agents : ${AGENTS_DIR}`); console.log(` runs : ${RUNS_DIR}`);