mirror of
https://github.com/myronblair/jarvis
synced 2026-07-28 00:34:35 -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:
@@ -430,7 +430,7 @@ if ($action) {
|
||||
|
||||
|
||||
case 'cal_feeds_list':
|
||||
j(JarvisDB::query("SELECT * FROM calendar_feeds ORDER BY source,name") ?? []);
|
||||
j(JarvisDB::query("SELECT id,name,source,ics_url,username,(password != '' AND password IS NOT NULL) AS has_password,active,created_at FROM calendar_feeds ORDER BY source,name") ?? []);
|
||||
|
||||
case 'cal_feed_save':
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
@@ -552,7 +552,7 @@ if ($action) {
|
||||
} else { bad('Unknown cron worker'); }
|
||||
} elseif ($wType === 'daemon' && $wId === 'arc_reactor' && $wAction === 'restart') {
|
||||
shell_exec('systemctl restart jarvis-arc 2>&1');
|
||||
k(['ok'=>true,'msg'=>'Arc Reactor restarting via systemd']);
|
||||
j(['ok'=>true,'msg'=>'Arc Reactor restarting via systemd']);
|
||||
} elseif ($wType === 'daemon' && $wId === 'arc_reactor' && $wAction === 'setup') {
|
||||
$log = '/var/log/jarvis/arc-setup.log';
|
||||
$cmd = implode(' && ', [
|
||||
@@ -567,7 +567,7 @@ if ($action) {
|
||||
'systemctl restart jarvis-arc',
|
||||
]);
|
||||
shell_exec("($cmd) >> " . escapeshellarg($log) . " 2>&1 &");
|
||||
k(['ok'=>true,'msg'=>'Arc Reactor setup started — check ' . $log]);
|
||||
j(['ok'=>true,'msg'=>'Arc Reactor setup started — check ' . $log]);
|
||||
} elseif ($wType === 'agent' && $wAction === 'update_status') {
|
||||
$ag = JarvisDB::single('SELECT version, status FROM registered_agents WHERE agent_id=?', [$wId]);
|
||||
j(['ok'=>true,'version'=>$ag['version']??null,'status'=>$ag['status']??'unknown']);
|
||||
@@ -1255,6 +1255,17 @@ if ($action) {
|
||||
readfile($path);
|
||||
exit;
|
||||
|
||||
case 'docs_download':
|
||||
$path = '/var/www/jarvis-private/INFRASTRUCTURE-REFERENCE.md';
|
||||
if (!file_exists($path)) bad('File not found', 404);
|
||||
header('Content-Type: text/markdown');
|
||||
header('Content-Disposition: attachment; filename="INFRASTRUCTURE-REFERENCE.md"');
|
||||
header('Content-Length: ' . filesize($path));
|
||||
header('X-Accel-Buffering: no');
|
||||
ob_end_clean();
|
||||
readfile($path);
|
||||
exit;
|
||||
|
||||
default: bad('Unknown action');
|
||||
}
|
||||
}
|
||||
@@ -1644,7 +1655,7 @@ select.filter-sel:focus{border-color:var(--cyan)}
|
||||
<div class="card" style="padding:24px;margin:20px 0">
|
||||
<div style="font-size:0.7rem;letter-spacing:2px;color:var(--cyan);margin-bottom:8px">INFRASTRUCTURE REFERENCE</div>
|
||||
<div style="color:var(--text-dim);font-size:0.75rem;margin-bottom:16px">Complete server map, credentials, deployment workflow, service configs, and phone system reference.</div>
|
||||
<a href="downloads/INFRASTRUCTURE-REFERENCE.md" download="INFRASTRUCTURE-REFERENCE.md"
|
||||
<a href="?action=docs_download" download="INFRASTRUCTURE-REFERENCE.md"
|
||||
style="display:inline-block;padding:8px 20px;background:rgba(0,212,255,0.1);border:1px solid var(--cyan);color:var(--cyan);font-size:0.7rem;letter-spacing:2px;text-decoration:none">
|
||||
↓ DOWNLOAD INFRASTRUCTURE-REFERENCE.MD
|
||||
</a>
|
||||
@@ -2171,6 +2182,12 @@ let _alertFilter = 'active';
|
||||
let _modalCb = null;
|
||||
|
||||
function esc(s){ return String(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)}')"). Escaping the quote via esc() alone is NOT enough:
|
||||
// the browser HTML-decodes the attribute before parsing it as JS, so '/" would
|
||||
// just turn back into a literal ' before the JS parser ever sees it. Backslash-escaping
|
||||
// survives that decode step, so JS-escape first, then HTML-escape on top.
|
||||
function escJs(s){ return esc(String(s||'').replace(/\\/g,'\\\\').replace(/'/g,"\\'").replace(/\n/g,'\\n').replace(/\r/g,'\\r')); }
|
||||
function ts(s){ if(!s) return '—'; const d=new Date(s); return d.toLocaleString('en-US',{month:'short',day:'numeric',hour:'2-digit',minute:'2-digit'}); }
|
||||
function ago(s){ if(!s) return '—'; const sec=Math.floor((Date.now()-new Date(s))/1000); if(sec<60) return sec+'s ago'; if(sec<3600) return Math.floor(sec/60)+'m ago'; return Math.floor(sec/3600)+'h ago'; }
|
||||
function fmtUp(s){ const d=Math.floor(s/86400),h=Math.floor((s%86400)/3600),m=Math.floor((s%3600)/60); return (d>0?d+'d ':'')+h+'h '+m+'m'; }
|
||||
@@ -3078,7 +3095,7 @@ function renderNetwork() {
|
||||
<td class="ts">${ago(d.last_seen)}</td>
|
||||
<td><div class="actions-col">
|
||||
<button class="btn btn-xs" onclick="pingDev('${esc(d.ip)}',this)">PING</button>
|
||||
<button class="btn btn-xs btn-yellow" onclick="netModal(${d.id},'${esc(d.ip)}','${esc(d.alias||'')}','${esc(d.device_type||'')}')">NAME</button>
|
||||
<button class="btn btn-xs btn-yellow" onclick="netModal(${d.id},'${escJs(d.ip)}','${escJs(d.alias||'')}','${escJs(d.device_type||'')}')">NAME</button>
|
||||
<button class="btn btn-xs btn-red" onclick="delNet(${d.id},'${esc(name)}')">DEL</button>
|
||||
</div></td>`;
|
||||
}, null, null);
|
||||
@@ -3157,7 +3174,7 @@ async function loadAlerts() {
|
||||
<td class="ts">${ts(a.created_at)}</td>
|
||||
<td><div class="actions-col">
|
||||
${!a.resolved?`<button class="btn btn-xs btn-green" onclick="apiPost('alerts_resolve',{id:${a.id}},()=>{toast('Resolved','ok');loadAlerts()})">RESOLVE</button>`:''}
|
||||
<button class="btn btn-xs btn-yellow" onclick="alertModal(${a.id},'${esc(a.alert_type)}','${esc(a.title)}','${esc(a.message||'')}','${esc(a.severity)}')">EDIT</button>
|
||||
<button class="btn btn-xs btn-yellow" onclick="alertModal(${a.id},'${escJs(a.alert_type)}','${escJs(a.title)}','${escJs(a.message||'')}','${escJs(a.severity)}')">EDIT</button>
|
||||
<button class="btn btn-xs btn-red" onclick="apiPost('alerts_delete',{id:${a.id}},()=>{toast('Deleted','ok');loadAlerts()})">DEL</button>
|
||||
</div></td>`, null, null);
|
||||
}
|
||||
@@ -3267,7 +3284,7 @@ function renderIntents(intents) {
|
||||
<td>${i.active?'<span class="badge badge-green">ON</span>':'<span class="badge badge-dim">OFF</span>'}</td>
|
||||
<td><div class="actions-col">
|
||||
<button class="btn btn-xs" onclick="apiPost('intents_toggle',{id:${i.id}},()=>{toast('Toggled','ok');loadIntents()})">${i.active?'DISABLE':'ENABLE'}</button>
|
||||
<button class="btn btn-xs btn-yellow" onclick='intentModal(${i.id},"${esc(i.intent_name)}","${esc(i.pattern)}",${JSON.stringify(i.response_template||"")},"${esc(i.action_type)}",${i.priority},${i.active})'>EDIT</button>
|
||||
<button class="btn btn-xs btn-yellow" onclick="intentModal(${i.id},'${escJs(i.intent_name)}','${escJs(i.pattern)}','${escJs(i.response_template||'')}','${escJs(i.action_type)}',${i.priority},${i.active})">EDIT</button>
|
||||
<button class="btn btn-xs btn-red" onclick="apiPost('intents_delete',{id:${i.id}},()=>{toast('Deleted','ok');loadIntents()})">DEL</button>
|
||||
</div></td>`, null, null);
|
||||
}
|
||||
@@ -3499,7 +3516,7 @@ async function loadNews() {
|
||||
<div style="font-size:0.75rem">${esc(c.title)}</div>
|
||||
${c.url?`<div style="font-size:0.6rem;color:var(--dim)">${esc(c.url)}</div>`:''}
|
||||
</div>
|
||||
<button class="btn btn-xs btn-yellow" onclick='newsCustomModal(${c.id},"${esc(c.title)}","${esc(c.url||"")}")'>EDIT</button>
|
||||
<button class="btn btn-xs btn-yellow" onclick="newsCustomModal(${c.id},'${escJs(c.title)}','${escJs(c.url||'')}')">EDIT</button>
|
||||
<button class="btn btn-xs btn-red" onclick="apiPost('news_custom_delete',{id:${c.id}},()=>{toast('Deleted','ok');loadNews()})">DEL</button>
|
||||
</div>`).join('');
|
||||
}
|
||||
@@ -4884,7 +4901,7 @@ async function loadCalFeeds() {
|
||||
<td>${ts(f.last_sync)}</td>
|
||||
<td>${f.last_count||0}</td>
|
||||
<td>${f.active?'<span class="badge badge-green">ACTIVE</span>':'<span class="badge badge-red">PAUSED</span>'}</td>
|
||||
<td><button class="btn btn-xs" onclick='calFeedModal(${JSON.stringify(f)})'>EDIT</button>
|
||||
<td><button class="btn btn-xs" onclick="calFeedModal(${esc(JSON.stringify(f))})">EDIT</button>
|
||||
<button class="btn btn-xs btn-red" onclick="calFeedDel(${f.id})">DEL</button></td>
|
||||
</tr>`).join('')}</tbody></table>`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user