← back to Security Dashboard
server.js
131 lines
#!/usr/bin/env node
// Single-page LIVE security posture viewer for Steve's Kamatera host.
// Read-only: pulls current state from prod via `ssh my-server` + the local
// secrets registry (digests only, never values). Binds loopback.
const http = require('http');
const fs = require('fs');
const path = require('path');
const { execFile } = require('child_process');
const PORT = parseInt(process.env.PORT || '9768', 10);
const PUBLIC = path.join(__dirname, 'public');
const REGISTRY = `${process.env.HOME}/Projects/secrets-manager/registry.json`;
let cache = { at: 0, data: null };
const CACHE_MS = 25_000;
// --- read-only prod probe (one batched ssh) ---------------------------------
const PROBE = `
CHAT_LOCAL=$(curl -s -o /dev/null -w '%{http_code}' -m6 http://127.0.0.1:9936/ 2>/dev/null || echo 000)
ANALYZE_LOCK=$(curl -s -o /dev/null -w '%{http_code}' -m8 https://chat.designerwallcoverings.com/api/analyze 2>/dev/null || echo 000)
own(){ p=$(ss -ltnp 2>/dev/null | grep ":$1 " | grep -oE 'pid=[0-9]+' | head -1 | cut -d= -f2); ps -o user= -p "\${p:-0}" 2>/dev/null | tr -d ' '; }
AICHAT_OWNER=$(own 9936); VIDEOS_OWNER=$(own 9783); AGENTS_OWNER=$(own 7577)
ADMIN_IPLOCK=$(grep -c 'allow 76.33.146.135' /etc/nginx/sites-available/admin.agentabrams.com 2>/dev/null || echo 0)
PM2_TOTAL=$(pm2 jlist 2>/dev/null | node -e 'try{console.log(JSON.parse(require("fs").readFileSync(0)).length)}catch(e){console.log(0)}')
PM2_NONROOT=$(pm2 jlist 2>/dev/null | node -e 'try{let d=JSON.parse(require("fs").readFileSync(0));console.log(d.filter(p=>p.pm2_env.uid&&String(p.pm2_env.uid)!=="0").length)}catch(e){console.log(0)}')
UFW_DEFAULT=$(iptables -L INPUT -n 2>/dev/null | head -1 | grep -oE 'policy (DROP|ACCEPT)' | awk '{print $2}')
PAKCHOI=$(awk -F: '$3==0 && $1!="root"{print $1}' /etc/passwd 2>/dev/null | head -1)
echo "CHAT_LOCAL=$CHAT_LOCAL"
echo "ANALYZE_LOCK=$ANALYZE_LOCK"
echo "AICHAT_OWNER=$AICHAT_OWNER"
echo "VIDEOS_OWNER=$VIDEOS_OWNER"
echo "AGENTS_OWNER=$AGENTS_OWNER"
echo "ADMIN_IPLOCK=$ADMIN_IPLOCK"
echo "PM2_TOTAL=$PM2_TOTAL"
echo "PM2_NONROOT=$PM2_NONROOT"
echo "UFW_DEFAULT=$UFW_DEFAULT"
echo "UID0_BACKDOOR=\${PAKCHOI:-none}"
`;
// On Mac (dev) we reach prod via the `ssh my-server` alias. When this app is
// itself hosted ON prod (PROBE_LOCAL=1), localhost IS prod — run the probe
// script directly with `bash -s` instead of ssh-ing to ourselves.
const PROBE_LOCAL = process.env.PROBE_LOCAL === '1';
function probeProd() {
return new Promise((resolve) => {
const child = PROBE_LOCAL
? execFile('bash', ['-s'], { timeout: 25_000 }, onDone)
: execFile('ssh', ['my-server', 'bash -s'], { timeout: 25_000 }, onDone);
child.stdin.end(PROBE);
function onDone(err, stdout) {
const kv = {};
(stdout || '').split('\n').forEach((l) => {
const i = l.indexOf('=');
if (i > 0) kv[l.slice(0, i).trim()] = l.slice(i + 1).trim();
});
kv._error = err ? String(err.message || err).slice(0, 120) : null;
resolve(kv);
}
});
}
// --- local secrets registry (digests only, NO values) -----------------------
const TIER1 = [
['CLOUDFLARE_API_TOKEN', 'Cloudflare DNS', 'https://dash.cloudflare.com/profile/api-tokens'],
['GODADDY_API_KEY', 'GoDaddy registrar', 'https://developer.godaddy.com/keys'],
['STRIPE_SECRET_KEY', 'Stripe live payments', 'https://dashboard.stripe.com/apikeys'],
['SHOPIFY_ADMIN_TOKEN', 'Shopify store admin', 'https://admin.shopify.com'],
['OPENAI_API_KEY', 'OpenAI', 'https://platform.openai.com/api-keys'],
['GEMINI_API_KEY', 'Google Gemini', 'https://aistudio.google.com/app/apikey'],
['ELEVENLABS_API_KEY', 'ElevenLabs voice', 'https://elevenlabs.io/app/settings/api-keys'],
];
function readSecrets() {
let reg = {};
try { reg = JSON.parse(fs.readFileSync(REGISTRY, 'utf8')); } catch (e) { /* */ }
const m = reg.secrets || {};
return TIER1.map(([key, label, url]) => {
const e = m[key] || {};
const last4 = (e.digest || '').split(':')[0] || '—';
const rotated = e.last_updated || null;
// pre-breach (<= 2026-05-30) => still needs rotation
const stale = !rotated || rotated.slice(0, 10) <= '2026-05-30';
return { key, label, url, last4, rotated: rotated ? rotated.slice(0, 10) : '—', stale };
});
}
// --- fleet-sweep hardcoded credential leaks (local JSON, digests/last-4 only) ---
function readCodeLeaks() {
try { return JSON.parse(fs.readFileSync(path.join(__dirname, 'data', 'code-leaks.json'), 'utf8')); }
catch (e) { return { leaks: [], malware: null }; }
}
async function status() {
if (Date.now() - cache.at < CACHE_MS && cache.data) return cache.data;
const prod = await probeProd();
const data = { prod, secrets: readSecrets(), codeLeaks: readCodeLeaks(), checkedAt: new Date().toISOString() };
cache = { at: Date.now(), data };
return data;
}
const server = http.createServer(async (req, res) => {
if (req.url === '/api/health') {
res.writeHead(200, { 'Content-Type': 'application/json', 'Cache-Control': 'no-store' });
res.end(JSON.stringify({ status: 'ok', probe: PROBE_LOCAL ? 'local' : 'ssh' }));
return;
}
if (req.url === '/api/status') {
try {
const data = await status();
res.writeHead(200, { 'Content-Type': 'application/json', 'Cache-Control': 'no-store' });
res.end(JSON.stringify(data));
} catch (e) {
res.writeHead(500); res.end(JSON.stringify({ error: String(e) }));
}
return;
}
let f = req.url === '/' ? '/index.html' : req.url.split('?')[0];
const fp = path.join(PUBLIC, path.normalize(f).replace(/^(\.\.[/\\])+/, ''));
if (fp.startsWith(PUBLIC) && fs.existsSync(fp)) {
const ext = path.extname(fp);
res.writeHead(200, { 'Content-Type': ext === '.html' ? 'text/html' : 'text/plain' });
fs.createReadStream(fp).pipe(res);
} else { res.writeHead(404); res.end('not found'); }
});
server.on('error', (e) => {
if (e.code === 'EADDRINUSE') { console.log(`[sec-dash] :${PORT} in use — exiting cleanly`); process.exit(0); }
throw e;
});
server.listen(PORT, '127.0.0.1', () => console.log(`[sec-dash] http://127.0.0.1:${PORT}/`));