Files
NeuroSploit/web/API.md
T
CyberSecurityUPandClaude Sonnet 5 bb659412fc feat(web): engagement wizard, model/auth picker, Auth & Keys menu, attack-path graph
Full frontend rewrite following a deliberate visual direction (dense
security-operations console — borders over shadows, two radii, one accent,
no gradients/glassmorphism) and fixing real bugs found in review:

- EventSource on the exploit stream never called es.close() on 'done',
  so the browser silently reconnected and re-streamed the whole job
  (duplicate log lines/findings). Fixed.
- Sidebar 'running' step indicator and openRun() matched ANY running run
  instead of the one belonging to the current job (by runId). Fixed.

New:
- 5-step engagement wizard (Asset -> Scope & Auth -> Leads -> Model & Run
  -> Review) replacing the single flat board — inspired by the
  Discovery/Plan/Exploit/Remediate stage model both a.security and
  terra.security use publicly.
- Model is now a real dropdown sourced from /api/providers (mirrors
  harness::models::providers()), with an API-key vs. subscription toggle
  that disables subscription for API-only providers.
- One Auth & Keys menu: target auth header + named roles (IDOR/BOLA/BFLA
  multi-identity testing) materialize into an ephemeral creds.yaml passed
  via --creds; per-provider API keys live in server memory only (never on
  disk) and are merged into every spawned child's env.
- Generative Attack Path Chaining: findings rendered as kill-chain columns
  (recon -> initial-access -> ... -> impact) with chains_from resolved to
  parent titles, live in the run view and static in run detail.
- Findings are now a proper table (severity/title/endpoint/CWE/agent/
  confidence) instead of stacked cards.
- Explicit light/dark theme toggle persisted in localStorage, defaulting
  to light (previously light only won when the OS wasn't in dark mode).
- All UI strings in English.

Backend additions: GET /api/providers, GET/POST/DELETE /api/keys,
ephemeral creds.yaml generation for auth/roles, env override merged into
every exploit-job and REPL child spawn.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0129WdYHccPsH27k5GGuwijd
2026-08-23 14:16:46 -03:00

11 KiB

NeuroSploit Web Console — API reference

Backend: web/server.js (Node, zero external dependencies). It does three things:

  1. Serves the SPA in web/public/.
  2. Reads agents_md/ and runs/ from the repo root to build the lead board and run history.
  3. Shells out to the compiled neurosploit CLI binary (neurosploit-rs/target/release/neurosploit) for every exploitation run and for the REPL — the web UI never reimplements harness logic, it only drives the real CLI and parses its stdout.

Base URL: http://localhost:4173 (override with PORT or NEUROSPLOIT_WEB_PORT).

All responses are JSON unless noted. All endpoints are same-origin; there is no auth layer — run this only on a trusted machine/network, same trust model as the CLI itself.


Meta

GET /api/meta

Server/version info.

{ "version": "4.0.0", "binary": "/opt/neurosploit-rs/neurosploit-rs/target/release/neurosploit", "root": "/opt/neurosploit-rs" }

Agents / lead board

GET /api/agents

Reads every agents_md/{vulns,ai,infra,code,chains,recon,meta}/*.md, extracts name (filename stem), title (first # heading), cwe (first CWE-\d+ match), kind (source directory), and classifies each into a UI category (category) via a keyword taxonomy (Business Logic, Broken Access Control, Injection, Cross-Site Scripting, LLM Application, Auth & Session, SSRF & Network, API & GraphQL, Cloud & Infra, Client-Side, Cryptography, Rate Limiting & DoS, Cache & CDN, Recon & Fingerprint, Linux Host, Windows Host, Attack Chains, Code Review, Recon, Other). meta/ (orchestration/doctrine agents) is loaded but excluded from categories — those aren't selectable "leads". Cached in-memory for 5s.

{
  "total": 435,
  "agents": [ { "id": "sqli_error", "name": "sqli_error", "title": "SQL Injection (Error-Based) Specialist Agent", "cwe": "CWE-89", "kind": "vuln", "category": "Injection" } ],
  "categories": [ { "category": "Business Logic", "agents": [ /* Agent[] */ ] } ]
}

An agent's id/name is exactly what the CLI's --only <name> flag expects (see neurosploit agents).


Providers / models / API keys

GET /api/providers

Static mirror of crates/harness/src/models.rs providers() — every provider the harness supports, its models, and whether it's usable via a local CLI subscription login (kind: "cli") or API key only (kind: "api").

[ { "key": "anthropic", "label": "Anthropic Claude", "kind": "cli", "models": ["claude-opus-5", "..."] } ]

GET /api/keys

Which providers currently have an API key set in this server process's memory (booleans only — never the value):

[ { "provider": "anthropic", "set": true }, { "provider": "openai", "set": false } ]

POST /api/keys

Body { "provider": "anthropic", "key": "sk-..." }. Stores the key in an in-memory Mapnever written to disk, lost on server restart. Every subsequent /api/exploit and /api/repl child process is spawned with <provider>.envKey set from this store (merged over process.env). Omitting key (or passing an empty string) clears it. 400 on an unknown provider.

DELETE /api/keys/:provider

Clears one provider's key.


Runs (history)

GET /api/runs

Lists runs/ns-* directories, newest first, with a summary read from each run's meta.json / status.json / findings.json.

[ { "id": "ns-1787504238-testphp_vulnweb_com", "ts": 1787504238, "target": "http://testphp.vulnweb.com/", "state": "running", "findings": 3, "severities": { "High": 1, "Medium": 2 }, "hasReport": false } ]

state mirrors the CLI's status.json: running | complete | stopped-raw | discarded | unknown.

GET /api/runs/:id

Full detail for one run: { id, meta, status, findings, assets }. findings is the raw findings.json array (see Finding shape below). assets lists which generated files exist (report.html, report.pdf, report.md, recon.md, exploitation.md).

GET /api/runs/:id/asset/:path

Serves a file from that run's workdir (e.g. report.html, report.pdf, evidence/foo.png). Path-traversal-guarded (resolved path must stay under the run dir). Use this to embed/open the generated report from the browser.


Exploitation jobs (live runs)

Starting a job spawns neurosploit <mode> <target> [flags...] --verbose as a child process and parses its stdout/stderr line-by-line into structured events — the same signal the interactive REPL's status line uses (phase, agent counts, findings, report path).

POST /api/exploit

Body:

{
  "mode": "run",            // run | whitebox | greybox | host | aitest | skills
  "target": "https://example.com",   // required for run/host/aitest/greybox
  "repo": "owner/repo",      // required for whitebox; source repo for greybox
  "models": ["anthropic:claude-opus-4-8"],  // optional, repeatable in the CLI
  "votes": 3,                // --vote-n
  "chainDepth": 2,           // --chain-depth
  "recon": 3,                // --recon (1-4)
  "maxAgents": 0,            // --max-agents (0 = all)
  "subscription": false,     // --subscription
  "offline": false,          // --offline
  "mcp": false,              // --mcp
  "creds": "creds.yaml",     // --creds
  "focus": "injection and business logic",   // --focus
  "objective": "pre-launch review of checkout",  // --objective
  "outOfScope": "staging.example.com",           // --out-of-scope
  "agents": ["sqli_error", "idor"],  // --only <name>, repeated — the lead-board selection
  "auth": "Authorization: Bearer <token>",  // target auth header — see Target auth below
  "roles": [{ "name": "admin", "header": "Authorization: Bearer ..." }],  // multi-identity access-control testing
}

Target auth (auth / roles)

If creds is omitted and either auth or roles is set, the server writes a minimal creds.yaml-compatible file (matching neurosploit-rs/creds.example.yaml's schema) to os.tmpdir()/neurosploit-web/<job-id>.creds.yaml and passes it via --creds. An explicit creds path always wins over auth/roles. These ephemeral files are not cleaned up automatically — they live in the OS temp dir, never in the repo.

Response: { "id": "<job-uuid>" }. This id is the web job id, not the run id — the CLI's own ns-<timestamp>-<target> run id is discovered from its own log line and exposed as runId in the job snapshot once the engagement starts writing to runs/.

If agents is empty, no --only flag is passed and the harness falls back to its normal recon-driven agent selection (the intelligent default) — the lead board's "0 selected" state is a valid, meaningful choice, not an error.

GET /api/exploit

List all jobs known to this server process (in-memory; lost on restart) as snapshots (see below).

GET /api/exploit/:id

One job's current snapshot:

{
  "id": "d98f51ad-...", "target": "http://testphp.vulnweb.com/",
  "runId": "ns-1787504238-testphp_vulnweb_com",
  "phase": "exploiting", "findings": [ /* Finding[] */ ],
  "agents": 245, "agentsDone": 12, "done": false, "exitCode": null,
  "reportUrl": null, "startedAt": 1787504238594
}

phase tracks the same lifecycle the REPL's /status shows: starting → recon → planning → exploiting → validating → chaining → complete, or paused (quota) / paused (auth) if the harness parks the run (token/quota exhaustion or auth failure — findings are preserved either way).

POST /api/exploit/:id/stop

Sends SIGINT to the child process — identical to pressing Ctrl-C in the terminal. The harness's own graceful-stop logic decides whether to keep partial findings.

GET /api/exploit/:id/events (Server-Sent Events)

Live stream. On connect, replays every buffered event so a reconnecting client doesn't miss history, then streams new ones. Named SSE events:

event data meaning
log { "type": "log", "line": "..." } one stdout/stderr line (ANSI stripped)
finding { "type": "finding", "finding": {…} } a finding_json: line, parsed
snapshot job snapshot (see above) phase/progress update
done job snapshot with done: true process exited; stream closes

Client example:

const es = new EventSource(`/api/exploit/${id}/events`);
es.addEventListener('finding', (e) => console.log(JSON.parse(e.data).finding));
es.addEventListener('done', () => es.close());

REPL sessions

Spawns the CLI with no subcommand — the same interactive session neurosploit launches from a terminal — and pipes stdin/stdout. Because stdin isn't a TTY, the CLI's Reader::Plain path takes over: it prints each prompt to stdout then reads one line at a time from stdin, so it works perfectly over a plain pipe. This is a real harness process; every /command (/run, /status, /stop, /model, /target, natural-language input, etc.) behaves exactly as it would in a terminal.

POST /api/repl

Starts a session. Response: { "id": "<session-uuid>" }.

POST /api/repl/:id/input

Body: { "line": "/status" }. Writes line + "\n" to the child's stdin.

POST /api/repl/:id/stop

Sends SIGTERM to the session's child process.

GET /api/repl/:id/events (SSE)

event data meaning
data { "chunk": "..." } raw stdout/stderr chunk (ANSI stripped), not line-buffered
close {} child process exited

Replays the session's buffered output (capped at the last 5000 chunks) on connect, same as the exploit stream.


Finding shape

Findings are exactly the harness's harness::types::Finding struct (see neurosploit-rs/crates/harness/src/types.rs), serialized as JSON — the web UI does not transform or rename any field:

{
  id: string, agent: string, title: string, severity: string, cwe: string, cvss: string,
  endpoint: string, payload: string, evidence: string, impact: string, remediation: string,
  confidence: number, validated: boolean, votes: string,
  owasp: string, mitre: string, stage: string, exploitability: string, business_impact: string,
  chains_from: string[], auth_context: string, account: string, secret: string,
  review_status: string, review_reason: string, screenshots: string[],
}

Running it

cd neurosploit-rs && cargo build --release   # once, or after a harness change
node web/server.js                            # http://localhost:4173

PORT (or NEUROSPLOIT_WEB_PORT) overrides the port. The server auto-locates the compiled binary under neurosploit-rs/target/{release,debug}/neurosploit relative to the repo root; if neither exists, /api/exploit and /api/repl return a 500 with a build hint.