mirror of
https://github.com/CyberSecurityUP/NeuroSploit.git
synced 2026-10-20 06:27:31 +02:00
feat(web): import an API collection from the console
Add an API-collection field to the launch form (Creds panel): upload a Postman/OpenAPI/Swagger/HAR file or point at a path on disk. Uploaded content is written to a temp file and passed to the CLI via --collection (one-shot modes) or /collection (REPL-backed run/greybox/whitebox), so the console imports exactly the surface the CLI does. The picker previews the endpoint count client-side. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
7a0b2cb244
commit
2512388b6b
3 files changed
+53
-2
No files matched your search
@@ -28,6 +28,8 @@ const state = {
|
||||
providers: [],
|
||||
auth: { header: '', roles: [] },
|
||||
credsPath: '',
|
||||
collectionPath: '',
|
||||
collectionData: '',
|
||||
// Engagement authorization: the grant, plus settings that may only narrow it.
|
||||
authz: { capability: '', inScope: '', environment: 'production', policyProfile: 'web', transport: '', oobDomain: '', oobHttp: '', oobDns: '', sms: '' },
|
||||
keys: [],
|
||||
@@ -627,6 +629,8 @@ async function startExploitation() {
|
||||
auth: state.auth.header || undefined,
|
||||
roles: state.auth.roles.length ? state.auth.roles : undefined,
|
||||
creds: state.credsPath || undefined,
|
||||
collectionPath: state.collectionPath || undefined,
|
||||
collectionData: state.collectionData || undefined,
|
||||
capability: state.authz.capability || undefined,
|
||||
inScope: state.authz.inScope.split(/[,;\s]+/).filter(Boolean),
|
||||
environment: state.authz.environment,
|
||||
@@ -1725,6 +1729,7 @@ function openAuthModal() {
|
||||
show($('#authModal'), true);
|
||||
renderRoleList();
|
||||
$('#credsPath').value = state.credsPath;
|
||||
$('#collectionPath').value = state.collectionPath;
|
||||
refreshKeyStatus();
|
||||
}
|
||||
['#btnOpenAuth', '#btnOpenAuth2', '#btnOpenAuth3'].forEach((sel) => $(sel)?.addEventListener('click', openAuthModal));
|
||||
@@ -1740,6 +1745,28 @@ $$('.modal-tab').forEach((tab) => tab.addEventListener('click', () => {
|
||||
$('#authHeader').addEventListener('input', (e) => { state.auth.header = e.target.value.trim(); });
|
||||
$('#authHeader').value = state.auth.header;
|
||||
$('#credsPath').addEventListener('input', (e) => { state.credsPath = e.target.value.trim(); });
|
||||
$('#collectionPath').addEventListener('input', (e) => { state.collectionPath = e.target.value.trim(); });
|
||||
$('#collectionFile').addEventListener('change', async (e) => {
|
||||
const help = $('#collectionHelp');
|
||||
const file = e.target.files && e.target.files[0];
|
||||
if (!file) { state.collectionData = ''; return; }
|
||||
try {
|
||||
const text = await file.text();
|
||||
let n = 0;
|
||||
try {
|
||||
const j = JSON.parse(text);
|
||||
if (j.log && Array.isArray(j.log.entries)) n = j.log.entries.length;
|
||||
else if (j.paths) n = Object.values(j.paths).reduce((a, o) => a + Object.keys(o || {}).length, 0);
|
||||
else if (Array.isArray(j.item)) n = JSON.stringify(j.item).match(/"request"/g)?.length || 0;
|
||||
} catch { throw new Error('not valid JSON (convert a YAML spec to JSON first)'); }
|
||||
state.collectionData = text;
|
||||
state.collectionPath = ''; // uploaded content wins over a path
|
||||
help.textContent = `Loaded ${file.name}${n ? ` — ~${n} endpoint(s)` : ''}. Tested as known surface.`;
|
||||
} catch (err) {
|
||||
state.collectionData = '';
|
||||
help.textContent = `Could not read collection: ${err.message}`;
|
||||
}
|
||||
});
|
||||
|
||||
$('#capToken').addEventListener('input', (e) => {
|
||||
state.authz.capability = e.target.value.trim();
|
||||
|
||||
@@ -637,6 +637,12 @@
|
||||
<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 class="field-group">
|
||||
<label class="field-label" for="collectionFile">API collection (Postman / OpenAPI / Swagger / HAR)</label>
|
||||
<input id="collectionFile" type="file" accept=".json,application/json,.har" />
|
||||
<input id="collectionPath" type="text" placeholder="…or a path on disk: api.postman_collection.json" />
|
||||
<div class="field-help" id="collectionHelp">Imports the endpoints as known surface — every route is tested directly, alongside recon. JSON only (convert YAML specs first).</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+20
-2
@@ -263,6 +263,20 @@ async function materializeCreds(body, jobId) {
|
||||
return file;
|
||||
}
|
||||
|
||||
// An imported API collection (Postman/OpenAPI/HAR). The UI either uploads the
|
||||
// file content (collectionData) or points at a path already on disk
|
||||
// (collectionPath). Uploaded content is written to a temp file so the CLI's
|
||||
// --collection flag can read it.
|
||||
async function materializeCollection(body, jobId) {
|
||||
if (body.collectionPath) return body.collectionPath; // path on disk wins
|
||||
if (!body.collectionData) return undefined;
|
||||
const dir = path.join(os.tmpdir(), 'neurosploit-web');
|
||||
await fsp.mkdir(dir, { recursive: true });
|
||||
const file = path.join(dir, `${jobId}.collection.json`);
|
||||
await fsp.writeFile(file, body.collectionData);
|
||||
return file;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Agent library — read agents_md/{vulns,ai,infra,code,chains,recon,meta}/*.md
|
||||
// and classify each into a lead category the UI can group + toggle.
|
||||
@@ -1022,6 +1036,7 @@ function buildArgs(body) {
|
||||
if (body.subscription) args.push('--subscription');
|
||||
if (body.mcp) args.push('--mcp');
|
||||
if (body.creds) args.push('--creds', body.creds);
|
||||
if (body.collectionPath) args.push('--collection', body.collectionPath);
|
||||
if (body.focus) args.push('--focus', body.focus);
|
||||
if (body.objective) args.push('--objective', body.objective);
|
||||
if (body.outOfScope) args.push('--out-of-scope', body.outOfScope);
|
||||
@@ -1060,7 +1075,8 @@ async function startJob(body) {
|
||||
const id = crypto.randomUUID();
|
||||
const credsPath = await materializeCreds(body, id);
|
||||
const scopePath = await materializeScope(body, id);
|
||||
const args = buildArgs({ ...body, creds: credsPath, scopePath });
|
||||
const collectionPath = await materializeCollection(body, id);
|
||||
const args = buildArgs({ ...body, creds: credsPath, scopePath, collectionPath });
|
||||
const job = new Job(id, BIN, args, body.repo || body.target || '', body.name || '');
|
||||
job.pinnedAgents = body.agents || [];
|
||||
jobs.set(id, job);
|
||||
@@ -1170,6 +1186,7 @@ function buildReplScript(body) {
|
||||
if (body.objective) lines.push(`/objective ${body.objective}`);
|
||||
if (body.outOfScope) lines.push(`/scope-out ${body.outOfScope}`);
|
||||
if (body.creds) lines.push(`/creds ${body.creds}`);
|
||||
if (body.collectionPath) lines.push(`/collection ${body.collectionPath}`);
|
||||
if (body.research) lines.push(`/research on`);
|
||||
// Settings the wizard collects that are NOT already applied as launch flags
|
||||
// (authArgs) or the scope file. Emitted via the REPL's own commands so a web
|
||||
@@ -1200,7 +1217,8 @@ async function startJobViaRepl(body) {
|
||||
if (!BIN) throw new Error('neurosploit binary not found — run `cargo build --release` in neurosploit-rs/');
|
||||
const id = crypto.randomUUID();
|
||||
const credsPath = await materializeCreds(body, id);
|
||||
const script = buildReplScript({ ...body, creds: credsPath });
|
||||
const collectionPath = await materializeCollection(body, id);
|
||||
const script = buildReplScript({ ...body, creds: credsPath, collectionPath });
|
||||
const scopePath = await materializeScope(body, id);
|
||||
const auth = authArgs({ ...body, scopePath });
|
||||
const job = new Job(id, BIN, auth, body.repo || body.target || '', body.name || '');
|
||||
|
||||
Reference in new issue
Block a user