AUTARCH v1.9 — remote monitoring, SSH manager, daemon, vault, cleanup
- Add Remote Monitoring Station with PIAP device profile system - Add SSH/SSHD manager with fail2ban integration - Add privileged daemon architecture for safe root operations - Add encrypted vault, HAL memory, HAL auto-analyst - Add network security suite, module creator, codex training - Add start.sh launcher script and GTK3 desktop launcher - Remove Output/ build artifacts, installer files, loose docs - Update .gitignore for runtime data and build artifacts - Update README for v1.9 with new launch method, screenshots, and features Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -657,47 +657,63 @@ textarea.form-control { font-family: monospace; resize: vertical; }
|
||||
}
|
||||
.hal-toggle-btn:hover { background: var(--accent-hover, #2563eb); }
|
||||
|
||||
@keyframes halPulse {
|
||||
0%, 100% { box-shadow: 0 0 4px rgba(0,255,65,0.3); }
|
||||
50% { box-shadow: 0 0 16px rgba(0,255,65,0.7), 0 0 32px rgba(0,255,65,0.3); }
|
||||
}
|
||||
|
||||
/* ── HAL Chat Panel ──────────────────────────────────────────── */
|
||||
|
||||
.hal-panel {
|
||||
position: fixed;
|
||||
bottom: 64px;
|
||||
right: 20px;
|
||||
z-index: 1100;
|
||||
width: 360px;
|
||||
height: 480px;
|
||||
background: var(--bg-card, #1a1a2e);
|
||||
border: 1px solid var(--border, #2a2a3e);
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.5);
|
||||
width: 420px;
|
||||
height: 520px;
|
||||
min-width: 300px;
|
||||
min-height: 240px;
|
||||
max-width: 90vw;
|
||||
max-height: 85vh;
|
||||
background: #111;
|
||||
border: 1px solid #333;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.6);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
resize: both;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ── Header bar ── */
|
||||
|
||||
.hal-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px 14px;
|
||||
background: var(--bg-nav, #12122a);
|
||||
border-bottom: 1px solid var(--border, #2a2a3e);
|
||||
background: #12122a;
|
||||
border-bottom: 1px solid #2a2a3e;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
color: var(--accent);
|
||||
color: var(--accent, #6366f1);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.hal-close {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-secondary, #888);
|
||||
color: #888;
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
line-height: 1;
|
||||
}
|
||||
.hal-close:hover { color: var(--text, #e0e0e0); background: var(--bg-hover, #2a2a3e); }
|
||||
.hal-close:hover { color: #e0e0e0; background: #2a2a3e; }
|
||||
|
||||
/* ── Mode toggle switch ── */
|
||||
|
||||
/* Hal mode toggle switch */
|
||||
.hal-mode-switch {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -706,21 +722,22 @@ textarea.form-control { font-family: monospace; resize: vertical; }
|
||||
user-select: none;
|
||||
}
|
||||
.hal-mode-switch input { display: none; }
|
||||
|
||||
.hal-mode-slider {
|
||||
width: 28px;
|
||||
height: 14px;
|
||||
background: var(--bg-input, #2a2d3e);
|
||||
background: #2a2d3e;
|
||||
border-radius: 7px;
|
||||
position: relative;
|
||||
transition: background 0.2s;
|
||||
border: 1px solid var(--border, #333650);
|
||||
border: 1px solid #333650;
|
||||
}
|
||||
.hal-mode-slider::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background: var(--text-secondary, #888);
|
||||
background: #888;
|
||||
border-radius: 50%;
|
||||
top: 1px;
|
||||
left: 1px;
|
||||
@@ -734,61 +751,164 @@ textarea.form-control { font-family: monospace; resize: vertical; }
|
||||
transform: translateX(14px);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.hal-mode-label {
|
||||
font-size: 0.7rem;
|
||||
font-weight: 500;
|
||||
color: var(--text-secondary, #888);
|
||||
color: #888;
|
||||
min-width: 36px;
|
||||
}
|
||||
|
||||
/* ── Message area — flexbox column chat feed ── */
|
||||
|
||||
.hal-messages {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 10px 12px;
|
||||
flex: 1 1 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
padding: 12px 10px;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
/* ── Base message bubble ── */
|
||||
|
||||
.hal-msg {
|
||||
padding: 7px 10px;
|
||||
border-radius: 8px;
|
||||
font-size: 0.82rem;
|
||||
line-height: 1.5;
|
||||
padding: 10px 14px;
|
||||
border-radius: 12px;
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.55;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
max-width: 92%;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
max-width: 85%;
|
||||
width: fit-content;
|
||||
align-self: flex-start;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* ── User bubble — right-aligned, blue ── */
|
||||
|
||||
.hal-msg-user {
|
||||
background: var(--accent);
|
||||
background: #1a56db;
|
||||
color: #fff;
|
||||
align-self: flex-end;
|
||||
border-bottom-right-radius: 2px;
|
||||
border-bottom-right-radius: 3px;
|
||||
}
|
||||
|
||||
/* ── HAL text response — left-aligned, dark ── */
|
||||
|
||||
.hal-msg-bot {
|
||||
background: var(--bg-hover, #2a2a3e);
|
||||
color: var(--text, #e0e0e0);
|
||||
background: #1e1e2e;
|
||||
color: #ddd;
|
||||
align-self: flex-start;
|
||||
border-bottom-left-radius: 2px;
|
||||
border-bottom-left-radius: 3px;
|
||||
}
|
||||
|
||||
/* ── Command HAL is running — code-style accent ── */
|
||||
|
||||
.hal-msg-action {
|
||||
background: #0a1628;
|
||||
color: #7dd3fc;
|
||||
align-self: flex-start;
|
||||
max-width: 90%;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: 0.82rem;
|
||||
border-left: 3px solid #38bdf8;
|
||||
border-radius: 6px;
|
||||
border-bottom-left-radius: 3px;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
/* ── Command output — scrollable code block ── */
|
||||
|
||||
.hal-msg-result {
|
||||
background: #0f0f0f;
|
||||
color: #999;
|
||||
align-self: stretch;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: 0.78rem;
|
||||
border: 1px solid #2a2a2a;
|
||||
border-radius: 6px;
|
||||
padding: 8px 12px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
/* ── HAL thinking — subtle italic ── */
|
||||
|
||||
.hal-msg-thought {
|
||||
background: transparent;
|
||||
color: #666;
|
||||
align-self: flex-start;
|
||||
max-width: 85%;
|
||||
font-style: italic;
|
||||
font-size: 0.8rem;
|
||||
padding: 4px 14px;
|
||||
}
|
||||
|
||||
/* ── Status line — centered, no bubble ── */
|
||||
|
||||
.hal-msg-status {
|
||||
background: transparent;
|
||||
color: #555;
|
||||
font-size: 0.78rem;
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
align-self: center;
|
||||
max-width: 100%;
|
||||
width: auto;
|
||||
padding: 3px 0;
|
||||
}
|
||||
|
||||
/* ── Error — red accent ── */
|
||||
|
||||
.hal-msg-error {
|
||||
background: #1a0505;
|
||||
color: #f87171;
|
||||
align-self: flex-start;
|
||||
max-width: 90%;
|
||||
border-left: 3px solid #dc2626;
|
||||
border-radius: 6px;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
/* ── Footer input bar ── */
|
||||
|
||||
.hal-footer {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
padding: 8px 10px;
|
||||
border-top: 1px solid var(--border, #2a2a3e);
|
||||
background: var(--bg-nav, #12122a);
|
||||
border-top: 1px solid #2a2a3e;
|
||||
background: #12122a;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.hal-footer input {
|
||||
flex: 1;
|
||||
font-size: 0.82rem;
|
||||
padding: 5px 9px;
|
||||
background: var(--bg-input, #0d0d1a);
|
||||
border: 1px solid var(--border, #2a2a3e);
|
||||
padding: 6px 10px;
|
||||
background: #0d0d1a;
|
||||
border: 1px solid #2a2a3e;
|
||||
border-radius: 6px;
|
||||
color: var(--text, #e0e0e0);
|
||||
color: #e0e0e0;
|
||||
}
|
||||
.hal-footer input:focus { outline: none; border-color: var(--accent, #6366f1); }
|
||||
|
||||
.hal-footer button {
|
||||
font-size: 0.78rem;
|
||||
padding: 5px 10px;
|
||||
background: #1e1e2e;
|
||||
color: #ccc;
|
||||
border: 1px solid #333;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
transition: background 0.15s, color 0.15s;
|
||||
}
|
||||
.hal-footer button:hover {
|
||||
background: #2a2a3e;
|
||||
color: #fff;
|
||||
}
|
||||
.hal-footer input:focus { outline: none; border-color: var(--accent); }
|
||||
|
||||
/* ── Debug Console Window ─────────────────────────────────────── */
|
||||
|
||||
|
||||
@@ -265,6 +265,7 @@ function startOsintSearch() {
|
||||
if (osintResults.length > 0) {
|
||||
document.getElementById('result-actions').style.display = 'flex';
|
||||
}
|
||||
halAnalyze('OSINT', JSON.stringify(data, null, 2), 'osint results', 'default');
|
||||
} else if (data.type === 'error' || data.error) {
|
||||
progressText.textContent = 'Error: ' + (data.message || data.error);
|
||||
source.close();
|
||||
@@ -564,6 +565,7 @@ function runCounterScan() {
|
||||
// Summary
|
||||
var sumEl = document.getElementById('scan-summary');
|
||||
if (sumEl && data.summary) sumEl.textContent = data.summary;
|
||||
halAnalyze('Counter: Threat Scan', JSON.stringify(data, null, 2), 'threat detection', 'counter');
|
||||
}).catch(function() { setLoading(btn, false); });
|
||||
}
|
||||
|
||||
@@ -576,6 +578,7 @@ function runCounterCheck(name) {
|
||||
return '[' + t.severity.toUpperCase() + '] ' + t.category + ': ' + t.message;
|
||||
});
|
||||
if (resultEl) resultEl.textContent = lines.join('\n') || data.message || 'No threats found';
|
||||
halAnalyze('Counter: ' + name, JSON.stringify(data, null, 2), 'threat detection', 'counter');
|
||||
}).catch(function() { if (resultEl) resultEl.textContent = 'Request failed'; });
|
||||
}
|
||||
|
||||
@@ -598,6 +601,7 @@ function loadLogins() {
|
||||
});
|
||||
html += '</tbody></table>';
|
||||
container.innerHTML = html;
|
||||
halAnalyze('Counter: Login Analysis', JSON.stringify(data, null, 2), 'threat detection', 'counter');
|
||||
}).catch(function() { setLoading(btn, false); });
|
||||
}
|
||||
|
||||
@@ -624,6 +628,7 @@ function analyzeFile() {
|
||||
lines.push(' SHA256: ' + (data.hashes.sha256 || ''));
|
||||
}
|
||||
renderOutput('file-output', lines.join('\n'));
|
||||
halAnalyze('Analyze: File Analysis', JSON.stringify(data, null, 2), 'forensics', 'analyze');
|
||||
}).catch(function() { setLoading(btn, false); });
|
||||
}
|
||||
|
||||
@@ -642,6 +647,7 @@ function extractStrings() {
|
||||
if (data.emails && data.emails.length) { lines.push('Emails (' + data.emails.length + '):'); data.emails.forEach(function(e){lines.push(' ' + e);}); lines.push(''); }
|
||||
if (data.paths && data.paths.length) { lines.push('Paths (' + data.paths.length + '):'); data.paths.forEach(function(p){lines.push(' ' + p);}); }
|
||||
renderOutput('strings-output', lines.join('\n') || 'No interesting strings found');
|
||||
halAnalyze('Analyze: String Extraction', JSON.stringify(data, null, 2), 'forensics', 'analyze');
|
||||
}).catch(function() { setLoading(btn, false); });
|
||||
}
|
||||
|
||||
@@ -653,6 +659,7 @@ function hashLookup() {
|
||||
var lines = ['Hash Type: ' + (data.hash_type || 'Unknown'), ''];
|
||||
(data.links || []).forEach(function(l) { lines.push(l.name + ': ' + l.url); });
|
||||
renderOutput('hash-output', lines.join('\n'));
|
||||
halAnalyze('Analyze: Hash Lookup', JSON.stringify(data, null, 2), 'forensics', 'analyze');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -673,6 +680,7 @@ function analyzeLog() {
|
||||
lines.push('Errors: ' + (data.error_count || 0));
|
||||
if (data.time_range) { lines.push('Time Range: ' + data.time_range.first + ' - ' + data.time_range.last); }
|
||||
renderOutput('log-analyze-output', lines.join('\n'));
|
||||
halAnalyze('Analyze: Log Analysis', JSON.stringify(data, null, 2), 'forensics', 'analyze');
|
||||
}).catch(function() { setLoading(btn, false); });
|
||||
}
|
||||
|
||||
@@ -687,6 +695,7 @@ function hexDump() {
|
||||
setLoading(btn, false);
|
||||
if (data.error) { renderOutput('hex-output', 'Error: ' + data.error); return; }
|
||||
renderOutput('hex-output', data.hex || 'No data');
|
||||
halAnalyze('Analyze: Hex Dump', JSON.stringify(data, null, 2), 'forensics', 'analyze');
|
||||
}).catch(function() { setLoading(btn, false); });
|
||||
}
|
||||
|
||||
@@ -708,6 +717,7 @@ function compareFiles() {
|
||||
lines.push('SHA256: ' + (data.sha256_match ? 'MATCH' : 'DIFFERENT'));
|
||||
if (data.diff) { lines.push('\nDiff:\n' + data.diff); }
|
||||
renderOutput('compare-output', lines.join('\n'));
|
||||
halAnalyze('Analyze: File Compare', JSON.stringify(data, null, 2), 'forensics', 'analyze');
|
||||
}).catch(function() { setLoading(btn, false); });
|
||||
}
|
||||
|
||||
@@ -947,6 +957,7 @@ function wsStartCapture() {
|
||||
document.getElementById('ws-capture-status').innerHTML = '<span class="status-dot inactive"></span>Idle';
|
||||
document.getElementById('ws-analysis-section').style.display = 'block';
|
||||
wsShowPacketsTable();
|
||||
halAnalyze('Wireshark', JSON.stringify(d, null, 2), 'packet capture', 'network');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -993,6 +1004,7 @@ function wsAnalyzePcap() {
|
||||
|
||||
document.getElementById('ws-analysis-section').style.display = 'block';
|
||||
wsRenderPackets(data.packets || []);
|
||||
halAnalyze('Wireshark', JSON.stringify(data, null, 2), 'packet capture', 'network');
|
||||
}).catch(function() { setLoading(btn, false); });
|
||||
}
|
||||
|
||||
@@ -1045,6 +1057,7 @@ function wsLoadProtocols() {
|
||||
+ '</div>';
|
||||
});
|
||||
container.innerHTML = html;
|
||||
halAnalyze('Wireshark', JSON.stringify(data, null, 2), 'packet capture', 'network');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1064,6 +1077,7 @@ function wsLoadConversations() {
|
||||
});
|
||||
html += '</tbody></table>';
|
||||
container.innerHTML = html;
|
||||
halAnalyze('Wireshark', JSON.stringify(data, null, 2), 'packet capture', 'network');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1083,6 +1097,7 @@ function wsLoadDNS() {
|
||||
});
|
||||
html += '</tbody></table>';
|
||||
container.innerHTML = html;
|
||||
halAnalyze('Wireshark', JSON.stringify(data, null, 2), 'packet capture', 'network');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1104,6 +1119,7 @@ function wsLoadHTTP() {
|
||||
});
|
||||
html += '</tbody></table>';
|
||||
container.innerHTML = html;
|
||||
halAnalyze('Wireshark', JSON.stringify(data, null, 2), 'packet capture', 'network');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1125,6 +1141,7 @@ function wsLoadCredentials() {
|
||||
});
|
||||
html += '</tbody></table>';
|
||||
container.innerHTML = html;
|
||||
halAnalyze('Wireshark', JSON.stringify(data, null, 2), 'packet capture', 'network');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1448,6 +1465,28 @@ function hwArchonStop() {
|
||||
|
||||
// ── ADB (mode-aware) ──
|
||||
|
||||
function hwAdbKillServer() {
|
||||
var status = document.getElementById('hw-adb-server-status');
|
||||
if (status) status.textContent = 'Killing...';
|
||||
fetch('/hardware/adb/kill-server', {method: 'POST'})
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(d) {
|
||||
if (status) status.textContent = d.ok ? 'ADB server killed' : 'Error: ' + (d.output || '');
|
||||
setTimeout(function() { if (status) status.textContent = ''; }, 3000);
|
||||
});
|
||||
}
|
||||
|
||||
function hwAdbStartServer() {
|
||||
var status = document.getElementById('hw-adb-server-status');
|
||||
if (status) status.textContent = 'Starting...';
|
||||
fetch('/hardware/adb/start-server', {method: 'POST'})
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(d) {
|
||||
if (status) status.textContent = d.ok ? 'ADB server started' : 'Error: ' + (d.output || '');
|
||||
setTimeout(function() { if (status) status.textContent = ''; hwRefreshAdbDevices(); }, 2000);
|
||||
});
|
||||
}
|
||||
|
||||
function hwRefreshAdbDevices() {
|
||||
if (hwConnectionMode === 'direct') return; // Direct mode uses connect buttons
|
||||
var container = document.getElementById('hw-adb-devices');
|
||||
@@ -2128,6 +2167,9 @@ function halToggle() {
|
||||
}
|
||||
}
|
||||
|
||||
var _halReader = null; // Track active stream reader for stop button
|
||||
var _halAbortController = null;
|
||||
|
||||
function halSend() {
|
||||
var inp = document.getElementById('hal-input');
|
||||
if (!inp) return;
|
||||
@@ -2138,17 +2180,26 @@ function halSend() {
|
||||
halAppend('user', msg);
|
||||
var container = document.getElementById('hal-messages');
|
||||
|
||||
// Show stop button, hide send
|
||||
var sendBtn = document.getElementById('hal-send-btn');
|
||||
var stopBtn = document.getElementById('hal-stop-btn');
|
||||
if (sendBtn) sendBtn.style.display = 'none';
|
||||
if (stopBtn) stopBtn.style.display = '';
|
||||
|
||||
_halAbortController = new AbortController();
|
||||
|
||||
fetch('/api/chat', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({message: msg, mode: halAgentMode ? 'agent' : 'chat'})
|
||||
body: JSON.stringify({message: msg, mode: halAgentMode ? 'agent' : 'chat'}),
|
||||
signal: _halAbortController.signal
|
||||
}).then(function(res) {
|
||||
var reader = res.body.getReader();
|
||||
_halReader = res.body.getReader();
|
||||
var dec = new TextDecoder();
|
||||
var buf = '';
|
||||
function pump() {
|
||||
reader.read().then(function(chunk) {
|
||||
if (chunk.done) { inp.disabled = false; inp.focus(); return; }
|
||||
_halReader.read().then(function(chunk) {
|
||||
if (chunk.done) { _halFinished(inp); return; }
|
||||
buf += dec.decode(chunk.value, {stream: true});
|
||||
var parts = buf.split('\n\n');
|
||||
buf = parts.pop();
|
||||
@@ -2170,7 +2221,6 @@ function halSend() {
|
||||
} else if (d.type === 'error') {
|
||||
halAppendStyled('error', d.content);
|
||||
} else if (d.token) {
|
||||
// Legacy streaming token mode
|
||||
var last = container.lastElementChild;
|
||||
if (!last || !last.classList.contains('hal-msg-bot')) {
|
||||
last = halAppend('bot', '');
|
||||
@@ -2178,40 +2228,51 @@ function halSend() {
|
||||
last.textContent += d.token;
|
||||
halScroll();
|
||||
} else if (d.done) {
|
||||
inp.disabled = false;
|
||||
inp.focus();
|
||||
_halFinished(inp);
|
||||
}
|
||||
} catch(e) {}
|
||||
});
|
||||
pump();
|
||||
});
|
||||
}).catch(function() { _halFinished(inp); });
|
||||
}
|
||||
pump();
|
||||
}).catch(function(e) {
|
||||
halAppendStyled('error', e.message);
|
||||
inp.disabled = false;
|
||||
if (e.name !== 'AbortError') halAppendStyled('error', e.message);
|
||||
_halFinished(inp);
|
||||
});
|
||||
}
|
||||
|
||||
function _halFinished(inp) {
|
||||
if (inp) { inp.disabled = false; inp.focus(); }
|
||||
_halReader = null;
|
||||
_halAbortController = null;
|
||||
var sendBtn = document.getElementById('hal-send-btn');
|
||||
var stopBtn = document.getElementById('hal-stop-btn');
|
||||
if (sendBtn) sendBtn.style.display = '';
|
||||
if (stopBtn) stopBtn.style.display = 'none';
|
||||
}
|
||||
|
||||
function halStop() {
|
||||
// Abort the fetch stream
|
||||
if (_halAbortController) {
|
||||
_halAbortController.abort();
|
||||
}
|
||||
// Cancel the reader
|
||||
if (_halReader) {
|
||||
try { _halReader.cancel(); } catch(e) {}
|
||||
}
|
||||
halAppendStyled('status', '-- Stopped by user --');
|
||||
_halFinished(document.getElementById('hal-input'));
|
||||
}
|
||||
|
||||
function halAppendStyled(type, text) {
|
||||
var msgs = document.getElementById('hal-messages');
|
||||
if (!msgs) return;
|
||||
var div = document.createElement('div');
|
||||
div.className = 'hal-msg hal-msg-' + type;
|
||||
if (type === 'thought') {
|
||||
div.style.cssText = 'font-style:italic;color:var(--text-muted,#888);font-size:0.8rem';
|
||||
div.textContent = text;
|
||||
} else if (type === 'action') {
|
||||
div.style.cssText = 'font-family:monospace;color:var(--accent,#0af);font-size:0.78rem;background:rgba(0,170,255,0.08);padding:4px 8px;border-radius:4px';
|
||||
if (type === 'action') {
|
||||
div.textContent = '> ' + text;
|
||||
} else if (type === 'result') {
|
||||
div.style.cssText = 'font-family:monospace;color:var(--text-secondary,#aaa);font-size:0.75rem;max-height:100px;overflow-y:auto;white-space:pre-wrap;background:rgba(255,255,255,0.03);padding:4px 8px;border-radius:4px';
|
||||
div.textContent = text;
|
||||
} else if (type === 'status') {
|
||||
div.style.cssText = 'color:var(--text-muted,#666);font-size:0.78rem;font-style:italic';
|
||||
div.textContent = text;
|
||||
} else if (type === 'error') {
|
||||
div.style.cssText = 'color:var(--danger,#f55);font-size:0.82rem';
|
||||
div.textContent = 'Error: ' + text;
|
||||
} else {
|
||||
div.textContent = text;
|
||||
@@ -2242,6 +2303,410 @@ function halClear() {
|
||||
fetch('/api/chat/reset', {method: 'POST'}).catch(function() {});
|
||||
}
|
||||
|
||||
// ── HAL Auto-Analyst ──────────────────────────────────────────────────────────
|
||||
// Call halAnalyze() after any tool displays results. HAL will analyze the output
|
||||
// via the loaded LLM and open the chat panel with findings.
|
||||
//
|
||||
// Usage: halAnalyze('Log Analyzer', logText, 'syslog', 'defense');
|
||||
// halAnalyze('ARP Table', arpOutput, '', 'network');
|
||||
|
||||
var _halAnalyzing = false;
|
||||
var _halFeedbackEnabled = true; // Auto-analysis on tool output
|
||||
|
||||
function halToggleFeedback() {
|
||||
_halFeedbackEnabled = !_halFeedbackEnabled;
|
||||
var btn = document.getElementById('hal-feedback-btn');
|
||||
if (btn) {
|
||||
btn.style.color = _halFeedbackEnabled ? 'var(--accent)' : 'var(--text-muted)';
|
||||
btn.style.borderColor = _halFeedbackEnabled ? 'var(--accent)' : 'var(--text-muted)';
|
||||
btn.title = _halFeedbackEnabled ? 'Auto-analysis ON — click to disable' : 'Auto-analysis OFF — click to enable';
|
||||
}
|
||||
}
|
||||
|
||||
function halAnalyze(toolName, output, context, category) {
|
||||
if (!_halFeedbackEnabled) return; // Feedback disabled by user
|
||||
if (!toolName || !output) return;
|
||||
if (_halAnalyzing) return; // Prevent concurrent analysis
|
||||
|
||||
// Auto-close previous IR when a new scan starts
|
||||
_halAutoCloseActiveIR();
|
||||
_halAnalyzing = true;
|
||||
|
||||
// Truncate very long output for the API call
|
||||
var maxLen = 12000;
|
||||
var sendOutput = output.length > maxLen ? output.substring(0, maxLen) + '\n...[truncated]' : output;
|
||||
|
||||
// Show notification on HAL button with rotating status messages
|
||||
var btn = document.getElementById('hal-toggle-btn');
|
||||
if (btn) {
|
||||
btn.style.animation = 'halPulse 1s ease-in-out infinite';
|
||||
var _halMsgs = [
|
||||
'Analyzing...', 'Processing data...', 'Scanning output...',
|
||||
'Evaluating risks...', 'Checking signatures...', 'Cross-referencing...',
|
||||
'Building assessment...', 'Identifying threats...', 'Mapping vectors...',
|
||||
'Correlating events...', 'Parsing results...', 'Running diagnostics...'
|
||||
];
|
||||
var _halMsgIdx = 0;
|
||||
btn.textContent = _halMsgs[0];
|
||||
btn.style.minWidth = '120px';
|
||||
window._halStatusInterval = setInterval(function() {
|
||||
_halMsgIdx = (_halMsgIdx + 1) % _halMsgs.length;
|
||||
if (btn) btn.textContent = _halMsgs[_halMsgIdx];
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
fetch('/api/hal/analyze', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
tool_name: toolName,
|
||||
output: sendOutput,
|
||||
context: context || '',
|
||||
category: category || 'default'
|
||||
})
|
||||
})
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(d) {
|
||||
_halAnalyzing = false;
|
||||
if (btn) { btn.style.animation = ''; btn.textContent = 'HAL'; btn.style.minWidth = ''; } if (window._halStatusInterval) { clearInterval(window._halStatusInterval); window._halStatusInterval = null; }
|
||||
|
||||
if (!d.available) {
|
||||
// No LLM loaded — skip silently
|
||||
return;
|
||||
}
|
||||
|
||||
// Open HAL panel and show analysis
|
||||
var panel = document.getElementById('hal-panel');
|
||||
if (panel) panel.style.display = 'flex';
|
||||
|
||||
// Risk level badge
|
||||
var riskColors = {
|
||||
clean: 'var(--success,#34c759)',
|
||||
low: '#8ec07c',
|
||||
medium: '#f59e0b',
|
||||
high: '#ff6b35',
|
||||
critical: 'var(--danger,#ff3b30)',
|
||||
unknown: 'var(--text-muted)'
|
||||
};
|
||||
var riskColor = riskColors[d.risk_level] || riskColors.unknown;
|
||||
|
||||
halAppendStyled('status', '─── HAL Analysis: ' + toolName + ' ───');
|
||||
|
||||
// Risk badge
|
||||
var msgs = document.getElementById('hal-messages');
|
||||
if (msgs) {
|
||||
var badge = document.createElement('div');
|
||||
badge.style.cssText = 'display:inline-block;padding:2px 10px;border-radius:3px;font-size:0.75rem;font-weight:700;margin:4px 0;border:1px solid ' + riskColor + ';color:' + riskColor;
|
||||
badge.textContent = 'Risk: ' + (d.risk_level || 'unknown').toUpperCase();
|
||||
msgs.appendChild(badge);
|
||||
}
|
||||
|
||||
// Analysis text — render markdown-style
|
||||
var analysis = d.analysis || 'No analysis available.';
|
||||
var div = document.createElement('div');
|
||||
div.className = 'hal-msg hal-msg-bot';
|
||||
div.style.cssText = 'white-space:pre-wrap;font-size:0.82rem;line-height:1.55';
|
||||
div.textContent = analysis;
|
||||
if (msgs) msgs.appendChild(div);
|
||||
|
||||
// Store analysis for fix/IR extraction
|
||||
window._halLastAnalysis = analysis;
|
||||
window._halLastToolName = toolName;
|
||||
window._halLastRiskLevel = d.risk_level || 'unknown';
|
||||
|
||||
// Action buttons
|
||||
var btnDiv = document.createElement('div');
|
||||
btnDiv.style.cssText = 'margin:8px 0;padding:8px 12px;border:1px solid var(--border);border-radius:6px;background:var(--bg-card);font-size:0.78rem;display:flex;gap:6px;flex-wrap:wrap;align-items:center';
|
||||
|
||||
if (d.has_fixes) {
|
||||
btnDiv.innerHTML += '<button onclick="halAutoFix()" class="btn btn-sm" style="border-color:#f59e0b;color:#f59e0b">Let HAL Fix It</button>';
|
||||
btnDiv.innerHTML += '<button onclick="halFixAndIR()" class="btn btn-sm" style="border-color:var(--accent);color:var(--accent)">Let HAL Fix It + IR</button>';
|
||||
}
|
||||
btnDiv.innerHTML += '<button onclick="halCreateIR()" class="btn btn-sm" style="border-color:var(--text-secondary);color:var(--text-secondary)">Report Only</button>';
|
||||
if (msgs) msgs.appendChild(btnDiv);
|
||||
|
||||
halScroll();
|
||||
})
|
||||
.catch(function(e) {
|
||||
_halAnalyzing = false;
|
||||
if (btn) { btn.style.animation = ''; btn.textContent = 'HAL'; btn.style.minWidth = ''; } if (window._halStatusInterval) { clearInterval(window._halStatusInterval); window._halStatusInterval = null; }
|
||||
});
|
||||
}
|
||||
|
||||
function halCreateIR() {
|
||||
var analysis = window._halLastAnalysis || '';
|
||||
var toolName = window._halLastToolName || 'Unknown';
|
||||
var riskLevel = window._halLastRiskLevel || 'unknown';
|
||||
halAppendStyled('status', 'Creating Investigation Report...');
|
||||
// Truncate analysis to prevent oversized requests
|
||||
var trimmedAnalysis = analysis.length > 50000 ? analysis.substring(0, 50000) + '\n...[truncated]' : analysis;
|
||||
|
||||
fetch('/targets/ir/create', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
title: 'IR: ' + toolName,
|
||||
source: 'HAL Auto-Analyst',
|
||||
scan_type: toolName,
|
||||
analysis: trimmedAnalysis,
|
||||
risk_level: riskLevel,
|
||||
fix_attempted: false,
|
||||
})
|
||||
}).then(function(r) {
|
||||
if (!r.ok) { halAppendStyled('error', 'IR creation failed: HTTP ' + r.status); return Promise.reject(); }
|
||||
var ct = r.headers.get('content-type') || '';
|
||||
if (ct.indexOf('json') === -1) { halAppendStyled('error', 'IR endpoint returned non-JSON (check login session)'); return Promise.reject(); }
|
||||
return r.json();
|
||||
}).then(function(d) {
|
||||
if (!d) return;
|
||||
if (d.ok) {
|
||||
window._halActiveIR = d.ir.id;
|
||||
halAppendStyled('status', 'IR Created: ' + d.ir.id + ' — ' + d.ir.title);
|
||||
var link = document.createElement('div');
|
||||
link.style.cssText = 'font-size:0.78rem;margin:4px 0;padding:8px 10px;background:rgba(0,255,65,0.06);border:1px solid var(--accent);border-radius:4px';
|
||||
link.innerHTML = 'Report ID: <strong style="color:var(--accent)">' + escapeHtml(d.ir.id) + '</strong> — <a href="/targets" style="color:var(--accent)">View in Investigations</a>'
|
||||
+ '<div style="margin-top:6px;display:flex;gap:6px">'
|
||||
+ '<button class="btn btn-sm" style="border-color:var(--accent);color:var(--accent)" onclick="halUpdateIR()">Update IR</button>'
|
||||
+ '<button class="btn btn-sm" style="border-color:var(--text-muted);color:var(--text-muted)" onclick="halCloseIR()">Close IR</button>'
|
||||
+ '</div>';
|
||||
var msgs = document.getElementById('hal-messages');
|
||||
if (msgs) msgs.appendChild(link);
|
||||
halScroll();
|
||||
} else {
|
||||
halAppendStyled('error', 'Failed to create IR: ' + (d.error || 'Unknown'));
|
||||
}
|
||||
}).catch(function(e) { halAppendStyled('error', 'IR creation failed: ' + e.message); });
|
||||
}
|
||||
|
||||
function halFixAndIR() {
|
||||
// Run the fix first, then create an IR with the fix results
|
||||
halAppendStyled('status', 'Running fixes then creating IR...');
|
||||
var analysis = window._halLastAnalysis || '';
|
||||
var toolName = window._halLastToolName || 'Unknown';
|
||||
var riskLevel = window._halLastRiskLevel || 'unknown';
|
||||
|
||||
// Extract and run commands
|
||||
var commands = [];
|
||||
var lines = analysis.split('\n');
|
||||
var inBlock = false;
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
var line = lines[i].trim();
|
||||
if (line.startsWith('```')) { inBlock = !inBlock; continue; }
|
||||
if (inBlock && line && !line.startsWith('#')) { commands.push(line); }
|
||||
else if (!inBlock && (line.startsWith('apt') || line.startsWith('systemctl ') || line.startsWith('ufw ') || line.startsWith('iptables ') || line.startsWith('chmod ') || line.startsWith('chown ') || line.startsWith('sed ') || line.startsWith('adb ') || line.startsWith('fastboot ') || line.startsWith('pm ') || line.startsWith('am ') || line.startsWith('dumpsys ') || line.startsWith('settings put '))) { commands.push(line); }
|
||||
}
|
||||
commands = commands.map(function(c) { return c.replace(/^sudo\s+/, ''); });
|
||||
var seen = {};
|
||||
commands = commands.filter(function(c) { if (seen[c]) return false; seen[c] = true; return true; });
|
||||
|
||||
var fixResults = [];
|
||||
var idx = 0;
|
||||
function runNext() {
|
||||
if (idx >= commands.length) {
|
||||
// All done — create the IR
|
||||
var trimAnalysis = analysis.length > 50000 ? analysis.substring(0, 50000) + '\n...[truncated]' : analysis;
|
||||
fetch('/targets/ir/create', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
title: 'IR: ' + toolName + ' (auto-fixed)',
|
||||
source: 'HAL Auto-Analyst + Auto-Fix',
|
||||
scan_type: toolName,
|
||||
analysis: trimAnalysis,
|
||||
risk_level: riskLevel,
|
||||
fix_attempted: true,
|
||||
fix_results: fixResults.map(function(r) { return r.cmd + ': ' + (r.ok ? 'OK' : 'FAILED') + ' ' + r.output; }).join('\n'),
|
||||
})
|
||||
}).then(function(r) {
|
||||
if (!r.ok) { halAppendStyled('error', 'IR creation failed: HTTP ' + r.status); return null; }
|
||||
var ct = r.headers.get('content-type') || '';
|
||||
if (ct.indexOf('json') === -1) { halAppendStyled('error', 'IR endpoint returned non-JSON'); return null; }
|
||||
return r.json();
|
||||
}).then(function(d) {
|
||||
if (!d) return;
|
||||
if (d.ok) {
|
||||
window._halActiveIR = d.ir.id;
|
||||
halAppendStyled('status', 'IR Created: ' + d.ir.id);
|
||||
var link = document.createElement('div');
|
||||
link.style.cssText = 'font-size:0.78rem;margin:4px 0;padding:8px 10px;background:rgba(0,255,65,0.06);border:1px solid var(--accent);border-radius:4px';
|
||||
link.innerHTML = 'Report: <strong style="color:var(--accent)">' + escapeHtml(d.ir.id) + '</strong> — Fix attempted: ' + commands.length + ' commands — <a href="/targets" style="color:var(--accent)">View</a>'
|
||||
+ '<div style="margin-top:6px;display:flex;gap:6px">'
|
||||
+ '<button class="btn btn-sm" style="border-color:var(--accent);color:var(--accent)" onclick="halUpdateIR()">Update IR</button>'
|
||||
+ '<button class="btn btn-sm" style="border-color:var(--text-muted);color:var(--text-muted)" onclick="halCloseIR()">Close IR</button>'
|
||||
+ '</div>';
|
||||
var msgs = document.getElementById('hal-messages');
|
||||
if (msgs) msgs.appendChild(link);
|
||||
halScroll();
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
var cmd = commands[idx++];
|
||||
halAppendStyled('action', cmd);
|
||||
fetch('/api/hal/fix', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({command: cmd})
|
||||
}).then(function(r) { return r.json(); }).then(function(d) {
|
||||
fixResults.push({cmd: cmd, ok: d.ok, output: (d.output || d.error || '').trim()});
|
||||
if (d.ok) { halAppendStyled('result', d.output || '(success)'); }
|
||||
else { halAppendStyled('error', d.error || d.output || 'Failed'); }
|
||||
halScroll();
|
||||
runNext();
|
||||
}).catch(function(e) {
|
||||
fixResults.push({cmd: cmd, ok: false, output: e.message});
|
||||
halAppendStyled('error', e.message);
|
||||
runNext();
|
||||
});
|
||||
}
|
||||
if (commands.length > 0) {
|
||||
halAppendStyled('status', 'Executing ' + commands.length + ' fix command(s) + creating IR...');
|
||||
runNext();
|
||||
} else {
|
||||
halAppendStyled('status', 'No fix commands found — creating IR with analysis only...');
|
||||
halCreateIR();
|
||||
}
|
||||
}
|
||||
|
||||
// Active IR tracking
|
||||
window._halActiveIR = null;
|
||||
|
||||
function halUpdateIR() {
|
||||
var irId = window._halActiveIR;
|
||||
if (!irId) { halAppendStyled('error', 'No active IR to update.'); return; }
|
||||
var analysis = window._halLastAnalysis || '';
|
||||
halAppendStyled('status', 'Updating IR ' + irId + '...');
|
||||
fetch('/targets/ir/' + irId + '/update', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
analysis: analysis,
|
||||
risk_level: window._halLastRiskLevel || 'unknown',
|
||||
updated_by: 'HAL',
|
||||
})
|
||||
}).then(function(r) { return r.json(); }).then(function(d) {
|
||||
if (d.ok) halAppendStyled('status', 'IR ' + irId + ' updated.');
|
||||
else halAppendStyled('error', 'Update failed: ' + (d.error || ''));
|
||||
}).catch(function(e) { halAppendStyled('error', e.message); });
|
||||
}
|
||||
|
||||
function halCloseIR() {
|
||||
var irId = window._halActiveIR;
|
||||
if (!irId) { halAppendStyled('error', 'No active IR.'); return; }
|
||||
halAppendStyled('status', 'Closing IR ' + irId + '...');
|
||||
fetch('/targets/ir/' + irId + '/update', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({ status: 'closed' })
|
||||
}).then(function(r) { return r.json(); }).then(function(d) {
|
||||
if (d.ok) {
|
||||
halAppendStyled('status', 'IR ' + irId + ' closed.');
|
||||
window._halActiveIR = null;
|
||||
} else {
|
||||
halAppendStyled('error', 'Close failed: ' + (d.error || ''));
|
||||
}
|
||||
}).catch(function(e) { halAppendStyled('error', e.message); });
|
||||
}
|
||||
|
||||
function _halAutoCloseActiveIR() {
|
||||
// Auto-close previous IR when a new scan starts
|
||||
if (window._halActiveIR) {
|
||||
fetch('/targets/ir/' + window._halActiveIR + '/update', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({ status: 'closed', notes: 'Auto-closed: new scan detected' })
|
||||
}).catch(function() {});
|
||||
window._halActiveIR = null;
|
||||
}
|
||||
}
|
||||
|
||||
function halAutoFix() {
|
||||
var analysis = window._halLastAnalysis || '';
|
||||
if (!analysis) { halAppendStyled('error', 'No analysis to extract fixes from.'); return; }
|
||||
|
||||
// Extract commands from code blocks and inline commands
|
||||
var commands = [];
|
||||
var lines = analysis.split('\n');
|
||||
var inBlock = false;
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
var line = lines[i].trim();
|
||||
if (line.startsWith('```')) { inBlock = !inBlock; continue; }
|
||||
if (inBlock && line && !line.startsWith('#')) { commands.push(line); }
|
||||
else if (!inBlock && (line.startsWith('sudo ') || line.startsWith('apt') || line.startsWith('systemctl ') || line.startsWith('ufw ') || line.startsWith('iptables ') || line.startsWith('chmod ') || line.startsWith('chown ') || line.startsWith('adb ') || line.startsWith('fastboot ') || line.startsWith('pm ') || line.startsWith('am ') || line.startsWith('dumpsys ') || line.startsWith('settings put '))) { commands.push(line); }
|
||||
}
|
||||
|
||||
// Clean commands: strip sudo, split && chains, remove shell redirections
|
||||
var expanded = [];
|
||||
commands.forEach(function(cmd) {
|
||||
// Split && chains into individual commands
|
||||
cmd.split('&&').forEach(function(part) {
|
||||
part = part.trim()
|
||||
.replace(/^sudo\s+/, '')
|
||||
.replace(/\s*2>\/dev\/null\s*/g, ' ')
|
||||
.replace(/\s*>\/dev\/null\s*/g, ' ')
|
||||
.replace(/\s*2>&1\s*/g, ' ')
|
||||
.trim();
|
||||
if (part) expanded.push(part);
|
||||
});
|
||||
});
|
||||
commands = expanded;
|
||||
|
||||
// Deduplicate
|
||||
var seen = {};
|
||||
commands = commands.filter(function(cmd) {
|
||||
if (seen[cmd]) return false;
|
||||
seen[cmd] = true;
|
||||
return true;
|
||||
});
|
||||
|
||||
if (commands.length === 0) {
|
||||
halAppendStyled('status', 'No executable commands found in the analysis.');
|
||||
return;
|
||||
}
|
||||
|
||||
halAppendStyled('status', 'Executing ' + commands.length + ' fix command(s) via daemon...');
|
||||
|
||||
// Execute commands sequentially
|
||||
var idx = 0;
|
||||
function runNext() {
|
||||
if (idx >= commands.length) {
|
||||
halAppendStyled('status', '─── Fix complete ───');
|
||||
return;
|
||||
}
|
||||
var cmd = commands[idx++];
|
||||
halAppendStyled('action', cmd);
|
||||
|
||||
fetch('/api/hal/fix', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({command: cmd})
|
||||
})
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(d) {
|
||||
if (d.ok) {
|
||||
halAppendStyled('result', d.output || '(success)');
|
||||
} else {
|
||||
halAppendStyled('error', d.error || d.output || 'Command failed');
|
||||
}
|
||||
halScroll();
|
||||
runNext();
|
||||
})
|
||||
.catch(function(e) {
|
||||
halAppendStyled('error', 'Failed: ' + e.message);
|
||||
runNext();
|
||||
});
|
||||
}
|
||||
runNext();
|
||||
}
|
||||
|
||||
// Check if HAL is available on page load (show/hide notification capability)
|
||||
var _halAvailable = false;
|
||||
(function() {
|
||||
fetch('/api/hal/available').then(function(r) { return r.json(); }).then(function(d) {
|
||||
_halAvailable = d.available || false;
|
||||
}).catch(function() {});
|
||||
})();
|
||||
|
||||
// ── Debug Console ─────────────────────────────────────────────────────────────
|
||||
|
||||
var _dbgEs = null;
|
||||
|
||||
Reference in New Issue
Block a user