mirror of
https://github.com/myronblair/jarvis
synced 2026-07-28 08:43:00 -05:00
Fix code review findings: unauthenticated infra doc + backup file exposure, onclick-attribute XSS breakouts (admin panel and front-end), plaintext calendar passwords, k()->j() typo, wildcard CORS, session cookie hardening (HttpOnly/SameSite)
This commit is contained in:
@@ -1,4 +1,14 @@
|
||||
// ── GLOBALS ──────────────────────────────────────────────────────────
|
||||
function escHtml(s) {
|
||||
return String(s == null ? '' : s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
||||
}
|
||||
// For values embedded inside a single-quoted JS string literal within an HTML attribute
|
||||
// (e.g. onclick="fn('${escJs(x)}')"). escHtml() alone isn't enough there: the browser
|
||||
// HTML-decodes the attribute before parsing it as JS, so an encoded quote would just
|
||||
// turn back into a literal ' before the JS parser ever sees it.
|
||||
function escJs(s) {
|
||||
return escHtml(String(s == null ? '' : s).replace(/\\/g,'\\\\').replace(/'/g,"\\'").replace(/\n/g,'\\n').replace(/\r/g,'\\r'));
|
||||
}
|
||||
let sessionToken = '';
|
||||
let sessionUser = '';
|
||||
let sessionId = 'session_' + Date.now();
|
||||
@@ -596,15 +606,15 @@ function renderNetworkStatus(n) {
|
||||
agent_id: d.agent_id, hostname: d.name};
|
||||
const lat = d.latency_ms ? ' · ' + d.latency_ms + 'ms' : '';
|
||||
const badge = d.source === 'agent'
|
||||
? `<span style="font-size:0.53rem;color:var(--cyan);letter-spacing:1px;margin-left:4px">${(d.agent_type||'AGENT').toUpperCase()}</span>` : '';
|
||||
? `<span style="font-size:0.53rem;color:var(--cyan);letter-spacing:1px;margin-left:4px">${escHtml((d.agent_type||'AGENT').toUpperCase())}</span>` : '';
|
||||
const del = d.deletable
|
||||
? `<button onclick="deleteNetworkDevice('${d.ip}',event)" style="background:none;border:none;color:var(--red);cursor:pointer;font-size:0.9rem;padding:0 2px;opacity:0.5;flex-shrink:0" title="Remove">×</button>` : '';
|
||||
? `<button onclick="deleteNetworkDevice('${escJs(d.ip)}',event)" style="background:none;border:none;color:var(--red);cursor:pointer;font-size:0.9rem;padding:0 2px;opacity:0.5;flex-shrink:0" title="Remove">×</button>` : '';
|
||||
const bl = d.source === 'agent' ? 'border-left:2px solid ' + (alive ? 'var(--green)' : 'var(--red)') + ';' : '';
|
||||
return `<div class="device-item" data-ctx-key="${ctxKey}" onclick="selectContext('${ctxKey}')" style="${bl}display:flex;align-items:center">
|
||||
return `<div class="device-item" data-ctx-key="${ctxKey}" onclick="selectContext('${escJs(ctxKey)}')" style="${bl}display:flex;align-items:center">
|
||||
<div class="device-status ${alive?'on':'off'}" style="flex-shrink:0"></div>
|
||||
<div class="device-info" style="flex:1;min-width:0">
|
||||
<div class="device-name" style="display:flex;align-items:center">${d.name||d.ip}${badge}</div>
|
||||
<div class="device-ip">${d.ip||''}${lat}</div>
|
||||
<div class="device-name" style="display:flex;align-items:center">${escHtml(d.name||d.ip)}${badge}</div>
|
||||
<div class="device-ip">${escHtml(d.ip||'')}${lat}</div>
|
||||
</div>${del}
|
||||
</div>`;
|
||||
}
|
||||
@@ -684,7 +694,7 @@ async function loadProxmox() {
|
||||
type_label:vm.type||'qemu', uptime:vm.uptime||0};
|
||||
return `<div class="vm-card" data-ctx-key="${ctxKey}" onclick="selectContext('${ctxKey}')" title="Click to ask Jarvis about this VM">
|
||||
<div class="vm-header">
|
||||
<span class="vm-name">${vm.name}</span>
|
||||
<span class="vm-name">${escHtml(vm.name)}</span>
|
||||
<span style="color:${statusColor};font-size:0.65rem">● ${(vm.status||'').toUpperCase()}</span>
|
||||
</div>
|
||||
<div class="vm-metrics">
|
||||
@@ -1031,10 +1041,11 @@ async function loadNews() {
|
||||
const ctxKey = 'news_' + (cat + '_' + a.title).replace(/[^a-z0-9]/gi,'').slice(0,30);
|
||||
_panelCtx[ctxKey] = {type:'news', label:a.title,
|
||||
title:a.title, source:a.source, pub:a.pub||'', category:cat};
|
||||
const titleDisplay = a.title.length > 90 ? a.title.slice(0,87)+'…' : a.title;
|
||||
html += `<div class="news-item" data-ctx-key="${ctxKey}" onclick="selectContext('${ctxKey}')" title="Click to ask Jarvis about this story">
|
||||
<div class="news-source">${a.source}</div>
|
||||
<div class="news-title">${a.title.length > 90 ? a.title.slice(0,87)+'…' : a.title}</div>
|
||||
${a.pub ? '<div class="news-time">' + a.pub + '</div>' : ''}
|
||||
<div class="news-source">${escHtml(a.source)}</div>
|
||||
<div class="news-title">${escHtml(titleDisplay)}</div>
|
||||
${a.pub ? '<div class="news-time">' + escHtml(a.pub) + '</div>' : ''}
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,8 +401,8 @@ function renderAgentsTab(agents, metrics) {
|
||||
style="flex-direction:column;align-items:stretch;border-left:3px solid ${alive ? 'var(--green)' : 'var(--red)'}">
|
||||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">
|
||||
<div style="width:8px;height:8px;border-radius:50%;background:${alive ? 'var(--green)' : 'var(--red)'};box-shadow:${alive ? '0 0 6px var(--green)' : 'none'};flex-shrink:0"></div>
|
||||
<span style="font-family:var(--font-mono);font-size:0.72rem;color:var(--text);flex:1">${ag.hostname}</span>
|
||||
<span style="font-size:0.58rem;color:var(--text-dim)">${ag.agent_type.toUpperCase()} · ${ag.ip_address}</span>
|
||||
<span style="font-family:var(--font-mono);font-size:0.72rem;color:var(--text);flex:1">${escHtml(ag.hostname)}</span>
|
||||
<span style="font-size:0.58rem;color:var(--text-dim)">${escHtml(ag.agent_type.toUpperCase())} · ${escHtml(ag.ip_address)}</span>
|
||||
<span style="font-size:0.58rem;color:${alive ? 'var(--green)' : 'var(--red)'};">${alive ? 'ONLINE' : 'OFFLINE'}</span>
|
||||
</div>
|
||||
${alive ? `<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px;margin-bottom:4px">
|
||||
@@ -425,8 +425,8 @@ function renderAgentsTab(agents, metrics) {
|
||||
${svcs ? `<div style="font-size:0.58rem">${svcs}</div>` : ''}
|
||||
</div>
|
||||
${alive ? `<div style="display:flex;gap:5px;margin-top:6px">
|
||||
<button onclick="event.stopPropagation();agentScreenshot('${ag.hostname}')" style="flex:1;background:rgba(0,212,255,0.06);border:1px solid var(--panel-border);border-radius:3px;padding:3px 6px;color:var(--cyan);font-family:var(--font-display);font-size:0.48rem;letter-spacing:1px;cursor:pointer">◈ SCREENSHOT</button>
|
||||
<button onclick="event.stopPropagation();agentSysinfo('${ag.hostname}')" style="flex:1;background:rgba(0,212,255,0.06);border:1px solid var(--panel-border);border-radius:3px;padding:3px 6px;color:var(--text-dim);font-family:var(--font-display);font-size:0.48rem;letter-spacing:1px;cursor:pointer">⚡ SYSINFO</button>
|
||||
<button onclick="event.stopPropagation();agentScreenshot('${escJs(ag.hostname)}')" style="flex:1;background:rgba(0,212,255,0.06);border:1px solid var(--panel-border);border-radius:3px;padding:3px 6px;color:var(--cyan);font-family:var(--font-display);font-size:0.48rem;letter-spacing:1px;cursor:pointer">◈ SCREENSHOT</button>
|
||||
<button onclick="event.stopPropagation();agentSysinfo('${escJs(ag.hostname)}')" style="flex:1;background:rgba(0,212,255,0.06);border:1px solid var(--panel-border);border-radius:3px;padding:3px 6px;color:var(--text-dim);font-family:var(--font-display);font-size:0.48rem;letter-spacing:1px;cursor:pointer">⚡ SYSINFO</button>
|
||||
</div>` : ''}
|
||||
</div>`;
|
||||
}).join('');
|
||||
|
||||
Reference in New Issue
Block a user