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:
SsSnake
2026-03-24 06:59:06 -07:00
parent 1092689f45
commit da53899f66
382 changed files with 15277 additions and 493964 deletions

View File

@@ -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 ─────────────────────────────────────── */

View File

@@ -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;