Files
NeuroSploit/web/public/index.html
T
CyberSecurityUPandClaude Opus 5 f1fb6b8bc7 feat: PoC validator, Kali sandbox, intercept proxy, compliance, +8 validators
Closes the three benchmark gaps and adds the two the user asked for.

poc.rs — re-runs each finding's recorded proof and sorts it into reproduced /
changed / gone / unverifiable. The last two are kept apart deliberately: a PoC
that could not be tested (out of scope now, state-changing, nothing recorded)
is never reported as one that failed. Never re-runs a mutating request to
"confirm" it. Can only lower a finding's standing, never raise it. Wired as a
run pass (--revalidate-poc) and a subcommand (neurosploit poc <run> --apply).

proxy.rs — own recording forward proxy (HTTP in full; HTTPS tunnelled with
honest metadata, no fake CA) that chains upstream to Burp / Caido / ZAP /
mitmproxy. A bare tool routes straight through it; own+tool records here and
forwards for full TLS interception. Flows -> flows.jsonl, distinct hosts become
passive-discovery leads. Harness and agent child commands share one route.

sandbox.rs — Kali docker/podman container: no host network, no mounted socket,
no-new-privileges, workdir mounted, proxy/transport env inherited. A missing
runtime is an explicit error, never a silent fallback to host execution — the
whole point being to keep attack payloads off the operator's host. Subcommands
sandbox up|exec|install|down.

compliance.rs — maps confirmed findings onto PCI-DSS v4.0, HIPAA Security Rule
and SOC 2 controls. Phrased as "bears on control X", never "compliant/non-
compliant"; the disclaimer is rendered on top and absence of a finding is never
presented as compliance. Report section + `neurosploit compliance <run>`.

validation.rs — 8 new deterministic validators (19 -> 27 classes): verbose
errors/stack traces (CWE-209), cleartext/HSTS (319), CRLF response splitting
(113), dangerous HTTP methods (650), GraphQL introspection, exposed backup
files (530), Host header injection (644), cacheable private responses (525).
Each names exactly what it saw and rejects the classic false positives (a
block page echoing a payload, the SPA served under a bogus path, a copyright
year mistaken for a code).

All wired through RunConfig, the CLI (global --intercept/--sandbox; run-level
--revalidate-poc/--compliance) and the web console's Tooling & assurance block.
328 tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-14 10:26:06 -03:00

599 lines
33 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>NeuroSploit v4.0.0 — Console</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🧠</text></svg>">
<link rel="stylesheet" href="/vendor/xterm.css" />
<link rel="stylesheet" href="/style.css" />
</head>
<body>
<button class="sidebar-toggle" id="btnSidebarToggle" title="Show / hide run history" aria-label="Show or hide run history" aria-expanded="false" aria-controls="sidebar">☰</button>
<div class="sidebar-scrim" id="sidebarScrim" hidden></div>
<div class="app">
<!-- ============ SIDEBAR ============ -->
<aside class="sidebar" id="sidebar">
<div class="sb-top">
<div class="brand"><span class="mark">NS</span> NeuroSploit</div>
<button class="icon-btn" id="btnThemeToggle" title="Toggle light / dark theme">☾</button>
</div>
<button class="sb-new" id="btnNewEngagement">+ New engagement</button>
<button class="sb-link" id="btnDashboard">▤ Dashboard</button>
<div class="sb-search">
<span class="search-icon" aria-hidden="true">⌕</span>
<input id="runFilter" type="text" autocomplete="off" placeholder="Filter runs" aria-label="Filter runs" />
</div>
<div class="sb-groups" id="sbGroups"><!-- populated by app.js --></div>
<div class="sb-bottom">
<span class="sb-version" id="sbVersion">v4.0.0</span>
<div class="sb-bottom-actions">
<button class="icon-btn" id="btnOpenAuth" title="Auth &amp; API keys">🔑</button>
<button class="icon-btn" id="btnOpenRepl" title="Open terminal (Ctrl+`)">❭_</button>
</div>
</div>
</aside>
<!-- ============ MAIN ============ -->
<main class="main">
<!-- ============ DASHBOARD ============ -->
<section class="dashboard" id="dashView" hidden>
<header class="topbar">
<div>
<div class="topbar-title">Dashboard</div>
<div class="topbar-sub">Coverage, findings and FAIR loss exposure across every run</div>
</div>
<div class="topbar-spacer"></div>
<select id="dashRange" class="dash-range" title="Time range">
<option value="0">all time</option>
<option value="7">last 7 days</option>
<option value="30">last 30 days</option>
<option value="90">last 90 days</option>
</select>
<button class="btn" id="btnDashRefresh">Refresh</button>
</header>
<div class="dash-body" id="dashBody">
<div class="empty-state">Loading…</div>
</div>
</section>
<!-- ============ WIZARD (new engagement) ============ -->
<section class="wizard" id="wizardView">
<header class="topbar">
<div>
<div class="topbar-title">New engagement</div>
<div class="topbar-sub">Asset → Scope &amp; Auth → Leads → Model &amp; Run → Review</div>
</div>
<div class="topbar-spacer"></div>
<button class="btn" id="btnOpenTerm2">❭_ Terminal</button>
<button class="btn" id="btnOpenAuth2">🔑 Auth &amp; Keys</button>
</header>
<nav class="stepper" id="stepper">
<button class="step-tab active" data-step="0"><span class="n">1</span> Asset</button>
<button class="step-tab" data-step="1"><span class="n">2</span> Scope &amp; Auth</button>
<button class="step-tab" data-step="2"><span class="n">3</span> Leads</button>
<button class="step-tab" data-step="3"><span class="n">4</span> Model &amp; Run</button>
<button class="step-tab" data-step="4"><span class="n">5</span> Review</button>
</nav>
<div class="wizard-body">
<!-- Step 1 — Asset -->
<div class="wizard-panel" data-panel="0">
<div class="field-group">
<label class="field-label" for="fieldName">Engagement name</label>
<input id="fieldName" type="text" placeholder="e.g. Keystone – Digital Banking" aria-describedby="errName" />
<div class="field-error" id="errName" hidden></div>
<div class="field-help">Identifies this engagement in the sidebar and run history — required.</div>
</div>
<div>
<div class="section-title">What are you testing?</div>
<div class="section-desc">Pick the engagement type — this decides which CLI subcommand runs underneath.</div>
</div>
<div class="mode-tiles" id="modeTiles">
<button class="mode-tile" data-mode="run"><span class="t">Black-box</span><span class="d">URL only — recon-driven</span></button>
<button class="mode-tile" data-mode="whitebox"><span class="t">White-box</span><span class="d">Source repo — SAST agents</span></button>
<button class="mode-tile" data-mode="greybox"><span class="t">Grey-box</span><span class="d">URL + source together</span></button>
<button class="mode-tile" data-mode="host"><span class="t">Host / Infra</span><span class="d">IP — Linux/Windows/AD</span></button>
<button class="mode-tile" data-mode="aitest"><span class="t">AI / LLM</span><span class="d">Live AI agent or MCP endpoint</span></button>
</div>
<div class="field-group" id="fieldTargetGroup">
<label class="field-label" id="targetLabel" for="fieldTarget">Target URL</label>
<input id="fieldTarget" type="text" placeholder="https://target.example.com" aria-describedby="errTarget" />
<div class="field-error" id="errTarget" hidden></div>
<div class="field-help" id="targetHelp">The application, host, or endpoint to test.</div>
</div>
<div class="field-group" id="fieldRepoGroup" hidden>
<label class="field-label" for="fieldRepo">Source repo</label>
<input id="fieldRepo" type="text" placeholder="owner/repo, a GitHub URL, or a local path" aria-describedby="errRepo" />
<div class="field-error" id="errRepo" hidden></div>
<div class="field-help">Cloned automatically if it's a GitHub URL or owner/repo shorthand.</div>
</div>
</div>
<!-- Step 2 — Scope & Auth -->
<div class="wizard-panel" data-panel="1" hidden>
<div>
<div class="section-title">Objective &amp; focus</div>
<div class="section-desc">Steers what the agents prioritise and what counts as impact.</div>
</div>
<div class="field-group">
<label class="field-label" for="fieldObjective">Objective</label>
<textarea id="fieldObjective" placeholder="e.g. Pre-launch review of the checkout flow — prove any path to unauthorized order access."></textarea>
</div>
<div class="field-group">
<label class="field-label" for="fieldFocus">Focus</label>
<textarea id="fieldFocus" placeholder="e.g. Prioritize the paths most likely to cause data leakage."></textarea>
</div>
<div class="field-group">
<label class="field-label" for="fieldOutOfScope">Out of scope</label>
<textarea id="fieldOutOfScope" placeholder="Hosts, paths, or techniques the agents must not touch."></textarea>
</div>
<div>
<div class="section-title">Authentication</div>
<div class="section-desc">Test as a logged-in user. Configured in the <button class="btn btn-sm" id="btnOpenAuth3">🔑 Auth &amp; Keys</button> menu.</div>
</div>
</div>
<!-- Step 3 — Leads -->
<div class="wizard-panel wizard-panel-wide" data-panel="2" hidden>
<div>
<div class="section-title">Set the action plan</div>
<div class="section-desc">Toggle specific leads to test, or leave everything off to let recon-driven auto-selection choose.</div>
</div>
<div class="lead-toolbar">
<div class="search-wrap">
<span class="search-icon" aria-hidden="true">⌕</span>
<input id="leadSearch" type="text" placeholder="Search leads by name or CWE" />
</div>
<div class="chips">
<button class="chip chip-active" data-filter="all">All <span id="chipAll">0</span></button>
<button class="chip" data-filter="selected">Pinned <span id="chipSelected">0</span></button>
<button class="chip" data-filter="excluded">Not pinned <span id="chipExcluded">0</span></button>
</div>
<div class="topbar-spacer"></div>
<button class="btn btn-sm" id="btnExpandAll">Expand all</button>
<button class="btn btn-sm" id="btnSelectAll">Select all</button>
<button class="btn btn-sm" id="btnClearAll">Clear all</button>
<button class="btn btn-sm" id="btnCustomLead">+ Custom lead</button>
</div>
<div class="field-help lead-tip">Categories start collapsed — click one to open it, or click its switch to pin every lead inside at once.</div>
<div class="custom-leads" id="customLeadsList" hidden></div>
<div class="categories" id="categories"><!-- populated --></div>
<div class="empty-state" id="leadsEmpty" hidden>No lead matches that search.</div>
</div>
<!-- Step 4 — Model & Run -->
<div class="wizard-panel" data-panel="3" hidden>
<div>
<div class="section-title">Model</div>
<div class="section-desc">Pick a provider and model from the harness's live catalog.</div>
</div>
<div class="field-row">
<div class="field-group">
<label class="field-label" for="fieldProvider">Provider</label>
<select id="fieldProvider"></select>
</div>
<div class="field-group">
<label class="field-label" for="fieldModelSelect">Model</label>
<select id="fieldModelSelect"></select>
</div>
</div>
<div class="field-group">
<label class="field-label">Auth mode</label>
<div class="auth-mode-toggle" id="authModeToggle">
<button data-mode="api" class="selected">API key</button>
<button data-mode="subscription">Subscription (local CLI login)</button>
</div>
<div class="field-help" id="authModeHelp">Uses the API key set in Auth &amp; Keys for this provider.</div>
</div>
<div class="check-row"><input type="checkbox" id="fieldMcp" /> <label for="fieldMcp">Playwright MCP (browser tool access, subscription backends only)</label></div>
<div>
<div class="section-title">Run settings</div>
</div>
<div class="field-row">
<div class="field-group"><label class="field-label" for="fieldVotes">Votes</label><input class="narrow" id="fieldVotes" type="number" min="1" max="9" value="3" /><div class="field-help">Models that must agree before a finding is kept.</div></div>
<div class="field-group"><label class="field-label" for="fieldChain">Chain depth</label><input class="narrow" id="fieldChain" type="number" min="0" max="5" value="2" /><div class="field-help">How far findings get chained into attack paths.</div></div>
<div class="field-group"><label class="field-label" for="fieldRecon">Recon intensity</label>
<select class="narrow" id="fieldRecon">
<option value="1">1 · quick</option>
<option value="2">2 · standard</option>
<option value="3" selected>3 · deep</option>
<option value="4">4 · exhaustive</option>
</select>
<div class="field-help">Deeper recon finds more surface but costs more tokens.</div>
</div>
</div>
<div>
<div class="section-title">Reasoning budget</div>
<div class="section-desc">Optional. Left on <em>unlimited</em>, the run behaves exactly as it always has — full depth, no cap.</div>
</div>
<div class="field-row">
<div class="field-group"><label class="field-label" for="fieldBudget">Budget mode</label>
<select class="narrow" id="fieldBudget">
<option value="unlimited" selected>unlimited · full run (default)</option>
<option value="eco">eco · cover ground, reason only on strong signal</option>
<option value="balanced">balanced · investigate what looks suspicious</option>
<option value="aggressive">aggressive · multiple hypotheses, deep validation</option>
</select>
<div class="field-help">How compute is spent, not how much there is.</div>
</div>
<div class="field-group"><label class="field-label" for="fieldTokenLimit">Token limit</label><input class="narrow" id="fieldTokenLimit" type="number" min="0" step="10000" placeholder="none" /><div class="field-help">Hard ceiling for the run. Empty or 0 = none.</div></div>
<div class="field-group"><label class="field-label" for="fieldOrder">Order</label>
<select class="narrow" id="fieldOrder">
<option value="coverage-first" selected>coverage-first</option>
<option value="depth-first">depth-first</option>
</select>
<div class="field-help">Map everything first, or chase a lead as it appears.</div>
</div>
<div class="field-group"><label class="field-label" for="fieldSampleRoute">Sample / route</label><input class="narrow" id="fieldSampleRoute" type="number" min="1" max="50" value="3" /><div class="field-help">Requests per endpoint family (<code>/api/users/{id}</code> is sampled, not enumerated).</div></div>
</div>
<div>
<div class="section-title">Tooling &amp; assurance</div>
<div class="section-desc">Optional. Route through a proxy, run in a container, re-check every PoC, and frame findings against a compliance framework.</div>
</div>
<div class="field-row">
<div class="field-group"><label class="field-label" for="fieldIntercept">Intercepting proxy</label>
<select class="narrow" id="fieldIntercept">
<option value="off" selected>off · direct</option>
<option value="own">own interceptor (record + passive discovery)</option>
<option value="burp">Burp Suite</option>
<option value="caido">Caido</option>
<option value="zap">OWASP ZAP</option>
<option value="mitmproxy">mitmproxy</option>
<option value="own+burp">own + Burp</option>
<option value="own+caido">own + Caido</option>
</select>
<div class="field-help">The harness and agent commands route through it. Full HTTPS interception needs one of the tools (own tunnels TLS).</div>
</div>
<div class="field-group"><label class="field-label" for="fieldSandbox">Sandbox</label>
<select class="narrow" id="fieldSandbox">
<option value="" selected>host (no container)</option>
<option value="default">Kali container (kalilinux/kali-rolling)</option>
</select>
<div class="field-help">Runs attack commands off the host, with the Kali toolbox. Needs docker or podman.</div>
</div>
<div class="field-group"><label class="field-label">PoC re-validation</label>
<div class="check-row"><input type="checkbox" id="fieldRevalidatePoc" /> <label for="fieldRevalidatePoc">Re-run every PoC; demote what no longer reproduces</label></div>
</div>
</div>
<div class="field-group">
<label class="field-label">Compliance mapping</label>
<div class="check-row"><input type="checkbox" id="fieldCompPci" value="pci-dss" /> <label for="fieldCompPci">PCI-DSS v4.0</label></div>
<div class="check-row"><input type="checkbox" id="fieldCompHipaa" value="hipaa" /> <label for="fieldCompHipaa">HIPAA Security Rule</label></div>
<div class="check-row"><input type="checkbox" id="fieldCompSoc2" value="soc2" /> <label for="fieldCompSoc2">SOC 2 (Trust Services Criteria)</label></div>
<div class="field-help">Maps confirmed findings onto control requirements in the report. Indicates gaps for an assessor — never a compliance verdict.</div>
</div>
</div>
<!-- Step 5 — Review -->
<div class="wizard-panel" data-panel="4" hidden>
<div>
<div class="section-title">Review</div>
<div class="section-desc">Confirm before launching — this spawns the real CLI harness.</div>
</div>
<div class="review-grid" id="reviewGrid"></div>
</div>
</div>
<footer class="wizard-footer">
<div class="summary-line" id="wizardSummary"></div>
<div class="footer-actions">
<button class="btn" id="btnStepBack">← Back</button>
<button class="btn btn-primary" id="btnStepNext">Next →</button>
<button class="btn btn-primary" id="btnLaunch" hidden>Start Exploitation →</button>
</div>
</footer>
</section>
<!-- ============ LIVE RUN ============ -->
<section class="runpage" id="liveView" hidden>
<header class="run-head">
<div class="run-head-main">
<div class="run-target" id="liveTarget">—</div>
<div class="run-meta" id="liveTargetSub"></div>
<div class="run-meta"><span class="phase-dot" id="phaseDot"></span><span id="livePhase">starting</span></div>
<div class="run-meta" id="livePinned"></div>
</div>
<div class="run-actions">
<button class="btn" id="btnOpenTerm3">❭_ Terminal</button>
<a class="btn" id="btnOpenReport" target="_blank" hidden>Open report</a>
<button class="btn" id="btnPauseRun" title="Hold the run where it is — in-flight agents finish, nothing is lost">⏸ Pause</button>
<a class="btn" id="btnDownloadLog" download>Download log</a>
<button class="btn" id="btnReportNow" title="Write the report from what has been found so far">Report so far</button>
<button class="btn btn-danger" id="btnStopRun">Stop</button>
<button class="btn" id="btnBackToBoard">← New engagement</button>
</div>
</header>
<div class="progress-wrap">
<div class="progress-bar" id="progressBar"><div class="progress-fill" id="progressFill"></div></div>
<div class="progress-label" id="progressLabel">0 / 0 agents</div>
</div>
<nav class="run-tabs">
<button class="run-tab active" data-tab="findings">Findings <span id="liveFindingsCount">0</span></button>
<button class="run-tab" data-tab="attackpath">Generative Attack Path Chaining</button>
<button class="run-tab" data-tab="log">Activity log</button>
</nav>
<div class="run-body">
<div class="run-tab-panel" data-tabpanel="findings">
<div class="findings-toolbar">
<div class="sev-summary" id="liveSevSummary"></div>
<div class="topbar-spacer"></div>
<div class="search-wrap">
<span class="search-icon" aria-hidden="true">⌕</span>
<input id="liveFindingSearch" type="text" placeholder="Filter findings" />
</div>
</div>
<div class="table-wrap">
<table class="data-table" id="liveFindingsTable">
<thead><tr><th data-sort="severity">Severity</th><th data-sort="title">Title</th><th data-sort="endpoint">Endpoint</th><th data-sort="cwe">CWE</th><th data-sort="agent">Agent</th><th data-sort="confidence" class="col-conf">Conf.</th></tr></thead>
<tbody></tbody>
</table>
</div>
<div class="empty-state" id="liveFindingsEmpty">No validated findings yet.</div>
</div>
<div class="run-tab-panel" data-tabpanel="attackpath" hidden><div id="liveAttackPath"></div></div>
<div class="run-tab-panel log-tab-panel" data-tabpanel="log" hidden>
<div class="log-panel" id="logList"></div>
<div class="send-prompt-row" id="sendPromptRow" hidden>
<span class="repl-prompt">❭</span>
<input id="sendPromptInput" type="text" autocomplete="off" spellcheck="false" placeholder="/status · /stop · /continue · or describe it in plain language" />
<button class="btn btn-sm" id="btnSendPromptTerm" title="Open this session in the full terminal">Open in terminal</button>
</div>
<div class="field-help" id="sendPromptHelp" hidden>This session stays interactive while the engagement runs — type a command or plain instruction and press Enter.</div>
</div>
</div>
</section>
<!-- ============ RUN DETAIL (past run) ============ -->
<section class="runpage" id="detailView" hidden>
<header class="run-head">
<div class="run-head-main">
<div class="run-target" id="detailTarget">—</div>
<div class="run-meta" id="detailTargetSub"></div>
<div class="run-meta"><span class="phase-dot static" id="detailDot"></span><span id="detailState">—</span></div>
<div class="run-meta run-facts" id="detailFacts"></div>
</div>
<div class="run-actions">
<a class="btn" id="detailOpenReport" target="_blank" hidden>Open report</a>
<a class="btn" id="detailOpenPdf" target="_blank" hidden>⤓ PDF</a>
<button class="btn" id="btnBuildReport" title="Regenerate this run's report from its findings">Generate report</button>
<a class="btn" id="detailOpenAudit" target="_blank" hidden title="Every action this run took, hash-chained">Audit trail</a>
<button class="btn" id="btnDetailBack">← New engagement</button>
</div>
</header>
<nav class="run-tabs">
<button class="run-tab active" data-tab="findings">Findings <span id="detailFindingsCount">0</span></button>
<button class="run-tab" data-tab="attackpath">Generative Attack Path Chaining</button>
</nav>
<div class="run-body">
<div class="run-tab-panel" data-tabpanel="findings">
<div class="findings-toolbar">
<div class="sev-summary" id="detailSevSummary"></div>
<div class="topbar-spacer"></div>
<div class="search-wrap">
<span class="search-icon" aria-hidden="true">⌕</span>
<input id="detailFindingSearch" type="text" placeholder="Filter findings" />
</div>
</div>
<div class="table-wrap">
<table class="data-table" id="detailFindingsTable">
<thead><tr><th data-sort="severity">Severity</th><th data-sort="title">Title</th><th data-sort="endpoint">Endpoint</th><th data-sort="cwe">CWE</th><th data-sort="agent">Agent</th><th data-sort="confidence" class="col-conf">Conf.</th></tr></thead>
<tbody></tbody>
</table>
</div>
<div class="empty-state" id="detailFindingsEmpty">No validated findings.</div>
</div>
<div class="run-tab-panel" data-tabpanel="attackpath" hidden><div id="detailAttackPath"></div></div>
</div>
</section>
<!-- ============ TERMINAL DOCK (real CLI harness session) ============ -->
<section class="term-dock" id="termDock" hidden>
<div class="term-resize" id="termResize" title="Drag to resize"></div>
<div class="term-head">
<span class="term-dot" id="termDot" data-state="off"></span>
<span class="term-title">NeuroSploit harness</span>
<select class="term-target" id="termTarget" title="Where this terminal's input goes">
<option value="session">standalone REPL session</option>
</select>
<span class="term-status" id="termStatus">disconnected</span>
<div class="topbar-spacer"></div>
<button class="term-btn" id="btnTermClear" title="Clear screen (Ctrl+L)">clear</button>
<button class="term-btn" id="btnTermRestart" title="Start a fresh harness session">restart</button>
<button class="term-btn" id="btnTermExpand" title="Expand / collapse the dock">expand</button>
<button class="icon-btn" id="btnTermClose" title="Close (Esc)">✕</button>
</div>
<div class="term-alert" id="termAlert" hidden></div>
<div class="term-host" id="termHost"></div>
<div class="term-hints" id="termHints">
<span class="k">Enter</span> send · <span class="k">↑↓</span> history · <span class="k">Tab</span> complete · <span class="k">Ctrl+C</span> interrupt · <span class="k">Ctrl+L</span> clear
</div>
</section>
</main>
</div>
<!-- ============ AUTH & KEYS MODAL ============ -->
<div class="modal-overlay" id="authModal" hidden>
<div class="modal">
<div class="modal-head">
<div class="title">Auth &amp; Keys</div>
<button class="icon-btn" id="btnCloseAuth">✕</button>
</div>
<div class="modal-tabs">
<button class="modal-tab active" data-mtab="target">Target auth</button>
<button class="modal-tab" data-mtab="keys">API keys</button>
<button class="modal-tab" data-mtab="creds">Creds file</button>
<button class="modal-tab" data-mtab="authz">Authorization</button>
</div>
<div class="modal-body">
<div class="modal-panel" data-mpanel="target">
<div class="field-group">
<label class="field-label" for="authHeader">Auth header</label>
<input id="authHeader" type="text" placeholder="Authorization: Bearer &lt;token&gt; or Cookie: session=..." />
<div class="field-help">Used so agents test as a logged-in user. Kept only for this session, sent to the CLI as an ephemeral creds file.</div>
</div>
<div class="field-group">
<label class="field-label">Named roles (multi-identity access-control testing)</label>
<div class="role-list" id="roleList"></div>
<button class="btn btn-sm" id="btnAddRole" style="align-self:flex-start;">+ Add role</button>
<div class="field-help">Two or more roles enable IDOR/BOLA/BFLA cross-role testing.</div>
</div>
</div>
<div class="modal-panel" data-mpanel="keys" hidden>
<div class="field-help" style="margin-bottom:12px;">Keys are kept in this server process's memory only — never written to disk. Cleared on restart.</div>
<div id="providerKeyList"></div>
</div>
<div class="modal-panel" data-mpanel="authz" hidden>
<div class="field-group">
<label class="field-label" for="capToken">Capability token</label>
<input id="capToken" type="text" placeholder="ns-cap.v1.…" spellcheck="false" />
<div class="field-status" id="capStatus"></div>
<div class="field-help">
The signed grant for this engagement: who authorized it, which hosts, which environment, until when.
It is verified by the harness (<code>NEUROSPLOIT_CAPABILITY_KEY</code>) and acts as a <strong>ceiling</strong> —
the settings below can narrow the scope, never widen it. Mint one with
<code>neurosploit capability issue</code>.
</div>
</div>
<div class="field-group">
<label class="field-label" for="inScope">Additional authorized hosts</label>
<input id="inScope" type="text" placeholder="app.example.com, *.api.example.com, 10.0.0.0/24" />
<div class="field-help">Without this the engagement is authorized against the target and nothing else — discovering a host is not permission to test it.</div>
</div>
<div class="field-row">
<div class="field-group">
<label class="field-label" for="envSelect">Environment</label>
<select id="envSelect">
<option value="production" selected>production</option>
<option value="staging">staging</option>
<option value="development">development</option>
<option value="lab">lab</option>
<option value="ot-production">ot-production (ICS/SCADA)</option>
</select>
<div class="field-help">Scales every risk score — the same action is a different act on a lab bench and on a live substation.</div>
</div>
<div class="field-group">
<label class="field-label" for="policySelect">Policy profile</label>
<select id="policySelect">
<option value="web" selected>web — standard</option>
<option value="ot">ot — read-only, paced, industrial writes blocked</option>
</select>
<div class="field-help">OT blocks writes, disruptive actions, fuzzing and exploit payloads over industrial protocols, and caps the rate at ~1 req/s.</div>
</div>
</div>
<div class="section-title" style="margin-top:18px;">Egress &amp; out-of-band</div>
<div class="field-group">
<label class="field-label" for="transportSpec">Transport (how traffic reaches the target)</label>
<input id="transportSpec" type="text" placeholder="direct · socks5://127.0.0.1:1080 · openvpn:/path/client.ovpn · ssh://red@bastion.corp?forward=10.0.0.5:445 · cloudflared://db.internal:5432" spellcheck="false" />
<div class="field-help">
An <strong>internal</strong> target with no transport is <strong>refused</strong>, not tested — with the VPN down,
<code>10.20.0.15</code> belongs to whatever network this host is on, which is not the client's.
</div>
</div>
<div class="field-row">
<div class="field-group">
<label class="field-label" for="oobDomain">Out-of-band domain</label>
<input id="oobDomain" type="text" placeholder="oob.yourdomain.com" spellcheck="false" />
<div class="field-help">A wildcard pointed at this host. Without it blind SSRF/XXE/RCE can only be reported as leads — never confirmed.</div>
</div>
<div class="field-group">
<label class="field-label" for="oobHttp">OOB HTTP listener</label>
<input id="oobHttp" type="text" placeholder="0.0.0.0:8080" spellcheck="false" />
<div class="field-help">Where callbacks land. An HTTP callback proves egress; a DNS query alone does not.</div>
</div>
<div class="field-group">
<label class="field-label" for="oobDns">OOB DNS listener</label>
<input id="oobDns" type="text" placeholder="0.0.0.0:5353" spellcheck="false" />
<div class="field-help">Only if the zone is delegated here (NS record).</div>
</div>
</div>
<div class="field-group">
<label class="field-label" for="smsSpec">Inbound SMS (OTP &amp; rate-limit work)</label>
<input id="smsSpec" type="text" placeholder="twilio:&lt;sid&gt;:&lt;token&gt;:+15550101 · webhook:https://…/inbox:+15550101" spellcheck="false" />
<div class="field-help">A throttling claim counts <strong>delivered messages carrying distinct codes</strong>, not HTTP 200s.</div>
</div>
</div>
<div class="modal-panel" data-mpanel="creds" hidden>
<div class="field-group">
<label class="field-label" for="credsPath">creds.yaml path (overrides target auth above)</label>
<input id="credsPath" type="text" placeholder="creds.yaml" />
<div class="field-help">An explicit file on disk — see neurosploit-rs/creds.example.yaml for the schema (jwt/header/cookie/login/roles/ssh/windows/cloud).</div>
</div>
</div>
</div>
</div>
</div>
<!-- ============ CUSTOM LEAD MODAL ============ -->
<div class="modal-overlay" id="leadModal" hidden>
<div class="modal modal-sm">
<div class="modal-head">
<div class="title">Custom lead</div>
<button class="icon-btn" id="btnCloseLead">✕</button>
</div>
<div class="modal-body">
<div class="field-group">
<label class="field-label" for="leadDesc">What should this lead test?</label>
<textarea id="leadDesc" placeholder="e.g. Abuse the invoice PDF export to read internal files via the template engine."></textarea>
<div class="field-help">Claude (Opus, subscription) generates a real specialist agent file for it and pins it. If generation is unavailable, the text is folded into <code>--focus</code> instead.</div>
</div>
<div class="field-error" id="errLead" hidden></div>
</div>
<div class="modal-foot">
<button class="btn" id="btnLeadCancel">Cancel</button>
<button class="btn btn-primary" id="btnLeadGenerate">Generate lead</button>
</div>
</div>
</div>
<!-- ============ FINDING DETAIL MODAL ============ -->
<div class="modal-overlay" id="findingModal" hidden>
<div class="modal modal-lg">
<div class="modal-head">
<div>
<span class="sev" id="fmSev">—</span>
<span class="title" id="fmTitle" style="margin-left:8px;">—</span>
</div>
<button class="icon-btn" id="btnCloseFinding">✕</button>
</div>
<div class="modal-body">
<div class="review-grid" id="fmMeta" style="margin-bottom: var(--sp-5);"></div>
<div id="fmSection-evidence"></div>
<div id="fmSection-impact"></div>
<div id="fmSection-remediation"></div>
<div id="fmSection-chains"></div>
<div class="field-group">
<label class="field-label">Other scripts from this run</label>
<div id="fmPocList" class="field-help">—</div>
</div>
</div>
</div>
</div>
<div class="toasts" id="toasts"></div>
<script src="/vendor/xterm.js"></script>
<script src="/vendor/xterm-addon-fit.js"></script>
<script src="/app.js"></script>
</body>
</html>