espilon-source/tools/C3PO/templates/redteam.html
Eun0us 79c2a4d4bf c3po: full server rewrite with modular routes and honeypot dashboard
Replace monolithic CLI and web server with route-based Flask API.
New routes: api_commands, api_build, api_can, api_monitor, api_ota,
api_tunnel. Add honeypot security dashboard with real-time SSE,
MITRE ATT&CK mapping, kill chain analysis.

New TUI with commander/help modules. Add session management,
tunnel proxy core, CAN bus data store. Docker support.
2026-02-28 20:12:27 +01:00

129 lines
6.2 KiB
HTML

{% extends "base.html" %}
{% block title %}Red Team - ESPILON{% endblock %}
{% block content %}
<div class="page" x-data="redteamApp()" x-init="init()">
<div class="split-h" style="flex:1;">
<!-- Left: Controls -->
<div class="panel" style="width:360px;min-width:280px;">
<div class="panel-header"><span>Red Team Controls</span></div>
<div class="panel-body panel-body-pad" style="overflow-y:auto;">
<div class="form-group">
<label>Device</label>
<select class="select w-full" x-model="device">
<option value="">select device...</option>
<template x-for="d in $store.app.connectedDevices()" :key="d.id">
<option :value="d.id" x-text="d.id"></option>
</template>
</select>
</div>
<!-- Hunt -->
<div style="border-bottom:1px solid var(--border-subtle);padding-bottom:12px;margin-bottom:12px;">
<div class="toolbar-label" style="margin-bottom:6px;">Autonomous Hunt</div>
<div class="flex gap-2">
<button class="btn btn-sm btn-success" @click="run('rt_hunt')">Start Hunt</button>
<button class="btn btn-sm btn-danger" @click="run('rt_stop')">Stop</button>
<button class="btn btn-sm" @click="run('rt_status')">Status</button>
</div>
</div>
<!-- WiFi Scan -->
<div style="border-bottom:1px solid var(--border-subtle);padding-bottom:12px;margin-bottom:12px;">
<div class="toolbar-label" style="margin-bottom:6px;">WiFi Scan</div>
<button class="btn btn-sm" @click="run('rt_scan')">Scan Networks</button>
</div>
<!-- Known Networks -->
<div style="border-bottom:1px solid var(--border-subtle);padding-bottom:12px;margin-bottom:12px;">
<div class="toolbar-label" style="margin-bottom:6px;">Known Networks</div>
<div class="form-row">
<input type="text" class="input flex-1" x-model="netSsid" placeholder="SSID">
<input type="text" class="input" x-model="netPass" placeholder="password" style="width:100px;">
</div>
<div class="flex gap-2" style="margin-top:6px;">
<button class="btn btn-sm btn-success" @click="run('rt_net_add', [netSsid, netPass].filter(Boolean))">Add</button>
<button class="btn btn-sm btn-danger" @click="run('rt_net_add', [netSsid, ''])">Remove</button>
<button class="btn btn-sm" @click="run('rt_net_list')">List</button>
</div>
</div>
<!-- Mesh -->
<div>
<div class="toolbar-label" style="margin-bottom:6px;">ESP-NOW Mesh</div>
<div class="flex gap-2">
<button class="btn btn-sm btn-success" @click="run('rt_mesh', ['start'])">Start Mesh</button>
<button class="btn btn-sm btn-danger" @click="run('rt_mesh', ['stop'])">Stop Mesh</button>
</div>
</div>
</div>
</div>
<div class="resizer"></div>
<!-- Right: Output -->
<div class="panel flex-1">
<div class="panel-header">
<span>Output</span>
<button class="btn btn-sm" @click="outputLines = []">Clear</button>
</div>
<div class="term-output" style="flex:1;">
<template x-for="(l, i) in outputLines" :key="i">
<div class="term-line" :class="l.cls || ''" x-html="l.html"></div>
</template>
<template x-if="outputLines.length === 0">
<div class="term-line term-system">Red Team command output will appear here.</div>
</template>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
function redteamApp() {
return {
...commander(),
device: '', outputLines: [],
netSsid: '', netPass: '',
init() {},
async run(command, argv) {
if (!this.device) { toast('Select a device', 'error'); return; }
argv = (argv || []).filter(Boolean);
this.outputLines.push({ html: '<span class="term-cmd">' + escapeHtml(command + ' ' + argv.join(' ')) + '</span>' });
try {
const data = await this.sendCommand([this.device], command, argv);
const r = (data.results || [])[0];
if (r && r.status === 'ok' && r.request_id) {
const pendingIdx = this.outputLines.length;
this.outputLines.push({ html: '<span class="term-pending">pending...</span>' });
let attempts = 0;
const iv = setInterval(async () => {
attempts++;
try {
const res = await fetch('/api/commands/' + encodeURIComponent(r.request_id));
const d = await res.json();
if (d.status === 'completed' || d.status === 'error' || attempts >= 60) {
clearInterval(iv);
this.outputLines.splice(pendingIdx, 1);
if (d.output && d.output.length) d.output.forEach(l => this.outputLines.push({ html: escapeHtml(l) }));
else this.outputLines.push({ html: '<span class="term-success">OK</span>' });
}
} catch (e) {}
}, 500);
} else if (r) {
this.outputLines.push({ html: '<span class="term-error">' + escapeHtml(r.message || 'Error') + '</span>' });
}
} catch (e) {
this.outputLines.push({ html: '<span class="term-error">' + escapeHtml(e.message) + '</span>' });
}
}
};
}
</script>
{% endblock %}