mirror of
https://github.com/CyberSecurityUP/NeuroSploit.git
synced 2026-08-26 11:22:37 +02:00
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
276 lines
11 KiB
Markdown
276 lines
11 KiB
Markdown
# 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.
|
|
|
|
```json
|
|
{ "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.
|
|
|
|
```json
|
|
{
|
|
"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"`).
|
|
|
|
```json
|
|
[ { "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):
|
|
|
|
```json
|
|
[ { "provider": "anthropic", "set": true }, { "provider": "openai", "set": false } ]
|
|
```
|
|
|
|
### `POST /api/keys`
|
|
|
|
Body `{ "provider": "anthropic", "key": "sk-..." }`. Stores the key in an in-memory `Map` —
|
|
**never 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`.
|
|
|
|
```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](#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:
|
|
|
|
```jsonc
|
|
{
|
|
"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:
|
|
|
|
```json
|
|
{
|
|
"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:
|
|
|
|
```js
|
|
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:
|
|
|
|
```ts
|
|
{
|
|
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
|
|
|
|
```bash
|
|
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.
|