mirror of
https://github.com/CyberSecurityUP/NeuroSploit.git
synced 2026-09-29 20:41:51 +02:00
A PDF was only ever produced while a run was finishing. If `typst` was missing at that moment — or the template improved afterwards — the operator had no way to get one without re-running the whole engagement against the target. report::rebuild() regenerates every artifact (md · json · html · pdf) from the findings already on disk, exposed as `neurosploit rebuild <run-id|dir>` and as POST /api/runs/:id/report with a "Generate report" button in the run view. The endpoint shells out to the harness rather than reimplementing report generation in JavaScript, so there is one implementation instead of two that drift, and it says plainly when the PDF was skipped for want of `typst` instead of handing back a link to a file that was never produced. Also fixes write_all() to pass the run's pocs/ listing into the HTML report, so a rebuilt report links the scripts each finding cites — the run-time path already did this and the rebuild path silently did not. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
502 lines
26 KiB
HTML
502 lines
26 KiB
HTML
<!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 & 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 & Auth → Leads → Model & Run → Review</div>
|
||
</div>
|
||
<div class="topbar-spacer"></div>
|
||
<button class="btn" id="btnOpenTerm2">❭_ Terminal</button>
|
||
<button class="btn" id="btnOpenAuth2">🔑 Auth & 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 & 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 & 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 & 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 & 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 & 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>
|
||
|
||
<!-- 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 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 & 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 <token> 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>
|
||
<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>
|