/* * SysDeck - Integrity Panel (v0.0.10) * Author: Jeremy Anderson (https://dcos.net) * * Calls lynis audit system via cockpit.spawn. Falls back gracefully * when lynis is absent — the trust score becomes null and the panel * shows an install hint. * * Uses the systemd dbus proxy to surface the integrity-scanner service * state without polling. The proxy fires 'changed' when the unit state * transitions; the panel re-fetches the trust score on that signal. */ export async function mount(panel, { bridge, EventBus }) { panel.innerHTML = renderSkeleton(); const trust = await bridge.integrity.trustScore(); panel.innerHTML = `

Integrity Auditor

Lynis system audit

Trust Score

${trust !== null ? `
${trust}/100
` : '

Lynis not installed.

'}

Quick Actions

Scanner Status

lynis${trust !== null ? 'ready' : 'missing'}
rkhunterdeferred
chkrootkitdeferred
`; const runBtn = panel.querySelector('#btn-run-lynis'); if (runBtn) { runBtn.addEventListener('click', async () => { runBtn.disabled = true; runBtn.textContent = 'Running audit…'; try { await bridge.integrity.runLynis(); EventBus.emit('integrity.scan.complete'); mount(panel, { bridge, EventBus }); } catch (err) { runBtn.textContent = 'Run Full Audit'; runBtn.disabled = false; EventBus.emit('integrity.scan.error', { error: err.message }); } }); } // Subscribe to lynis.service state changes via the systemd dbus proxy. // On any transition, re-fetch the trust score so the panel reflects // the most recent audit result without manual refresh. if (panel._unsubscribeUnit) panel._unsubscribeUnit(); try { if (bridge.dbusProxies?.systemd) { panel._unsubscribeUnit = bridge.dbusProxies.systemd.subscribeToUnit( 'lynis.service', () => mount(panel, { bridge, EventBus }), ); } } catch { // systemd proxy unavailable — manual refresh still works. } } const SCORE_COLORS = [ { min: 90, color: 'var(--suite-accent-success)' }, { min: 70, color: 'var(--suite-accent-warn)' }, { min: 0, color: 'var(--suite-accent-danger)' }, ]; function scoreColor(score) { // Step-down: lookup table over if-ladder. First match wins. const entry = SCORE_COLORS.find((band) => score >= band.min); return entry?.color ?? 'var(--suite-accent-danger)'; } function renderSkeleton() { return `
`; }