File size: 11,295 Bytes
6ccf48e f42ab02 6ccf48e f42ab02 f1bdfc3 6ccf48e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | <!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Arx · Devseis Endpoint Auditor</title>
<meta name="description" content="Read-only endpoint audit for ISO 27001, GDPR and the EU AI Act. Stage 1, step 1: collectors, evidence format and synthetic data.">
<style>
:root {
--bg: #f7f8fb; --card: #ffffff; --text: #16181d; --muted: #5b6270; --line: #e3e6ec;
--red: #ED2939; --blue: #00A1DE; --ok: #1a7f37; --bad: #c4242f; --na: #6e7781; --pending: #9a6700; --err: #8250df;
--code-bg: #0f1218; --code-text: #e6edf3;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--bg: #0d1117; --card: #161b22; --text: #e6edf3; --muted: #9da7b3; --line: #2b313a;
--ok: #3fb950; --bad: #ff6b6b; --na: #8b949e; --pending: #d29922; --err: #b392f0; --code-bg: #0a0d12;
}
}
* { box-sizing: border-box; }
body { margin: 0; background: var(--bg); color: var(--text); font: 16px/1.55 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; }
main { max-width: 1040px; margin: 0 auto; padding: 32px 16px 64px; }
.tag { display: inline-block; font-size: 12px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--red); border: 1px solid var(--red); border-radius: 999px; padding: 2px 10px; }
h1 { font-size: clamp(28px, 5vw, 40px); line-height: 1.15; margin: 14px 0 8px; }
h2 { font-size: 20px; margin: 36px 0 12px; }
p.lead { color: var(--muted); font-size: 18px; max-width: 760px; margin: 0; }
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px; margin-top: 20px; }
.card { background: var(--card); border: 1px solid var(--line); border-radius: 12px; padding: 16px; }
.card b { display: block; font-size: 15px; margin-bottom: 4px; }
.card span { color: var(--muted); font-size: 14px; }
.tabs { display: flex; gap: 8px; flex-wrap: wrap; margin: 12px 0; }
.tabs button { font: inherit; font-size: 14px; padding: 6px 14px; border-radius: 999px; border: 1px solid var(--line); background: var(--card); color: var(--text); cursor: pointer; }
.tabs button[aria-selected="true"] { background: var(--blue); border-color: var(--blue); color: #fff; }
.counts { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
.pill { font-size: 13px; font-weight: 600; padding: 3px 10px; border-radius: 999px; background: var(--card); border: 1px solid var(--line); }
.s-Compliant { color: var(--ok); } .s-Non-Compliant { color: var(--bad); } .s-NotApplicable { color: var(--na); }
.s-Pending { color: var(--pending); } .s-Error { color: var(--err); }
.split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr); gap: 12px; }
@media (max-width: 760px) { .split { grid-template-columns: 1fr; } }
pre { margin: 0; background: var(--code-bg); color: var(--code-text); border-radius: 12px; padding: 14px; font: 12.5px/1.45 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; overflow: auto; max-height: 560px; }
table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.table-wrap { background: var(--card); border: 1px solid var(--line); border-radius: 12px; overflow: auto; max-height: 560px; }
th, td { text-align: left; padding: 7px 10px; border-bottom: 1px solid var(--line); vertical-align: top; }
th { position: sticky; top: 0; background: var(--card); font-size: 12px; text-transform: uppercase; letter-spacing: .03em; color: var(--muted); }
td.ref { color: var(--muted); font-size: 12.5px; }
code { font: 13px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; background: var(--card); border: 1px solid var(--line); border-radius: 6px; padding: 1px 6px; }
.note { border-left: 4px solid var(--red); background: var(--card); padding: 12px 16px; border-radius: 0 12px 12px 0; color: var(--muted); }
ol li { margin: 4px 0; }
a { color: var(--blue); }
footer { margin-top: 48px; color: var(--muted); font-size: 14px; }
</style>
</head>
<body>
<main>
<span class="tag">Open source · stage 1</span>
<h1>Arx <span style="font-weight:400;color:var(--muted)">· Devseis Endpoint Auditor</span></h1>
<p class="lead">A read-only audit of Windows, Linux, macOS, iPhone and Android devices against a security baseline, GDPR technical controls, EU AI Act controls and organisational controls, written up by a small AI auditor model that runs on the device. <a href="index.html">ask the assistant</a>, <a href="phone.html">check your phone</a> or <a href="download.html">download the Mac app</a>.</p>
<div class="grid">
<div class="card"><b>Read-only collectors</b><span>PowerShell, Linux shell and macOS shell scripts. They read settings and never change the system.</span></div>
<div class="card"><b>One evidence format</b><span>Same JSON for every OS, joined to a catalog with ISO 27001, GDPR and AI Act references.</span></div>
<div class="card"><b>Synthetic data</b><span>Realistic evidence with the same rules, so the auditor model can be built before real data exists.</span></div>
<div class="card"><b>Runs locally</b><span>Evidence stays on the device. Nothing is sent anywhere.</span></div>
<div class="card"><b>Offline vulnerability matching</b><span>Installed software is matched against a signed OSV/NVD/CISA KEV bundle by exact version rules.</span></div>
<div class="card"><b>Phones too</b><span>A browser check for iPhone and Android: what the browser can detect plus guided questions, each result labelled detected or self-reported.</span></div>
</div>
<h2>Sample audit (synthetic)</h2>
<div class="tabs" role="tablist" id="tabs"></div>
<div class="counts" id="counts"></div>
<div class="split">
<pre id="report" aria-label="Text report">Loading…</pre>
<div class="table-wrap">
<table>
<thead><tr><th>Check</th><th>Status</th><th>Found</th><th>References</th></tr></thead>
<tbody id="rows"></tbody>
</table>
</div>
</div>
<h2>Roadmap</h2>
<ol>
<li><b>Collectors, evidence format, catalog (74 checks), synthetic data</b> — done</li>
<li><b>Control library and fix guidance</b> in Devseis's own wording — done; expert review welcome</li>
<li><b>Training data</b> v0.3: 39,500 grounded examples for Windows, Linux, macOS, iPhone and Android — done</li>
<li><b>Offline vulnerability matching</b> (OSV, NVD, CISA KEV, EPSS), signed bundle — done</li>
<li><b>Desktop app</b> (Electron + WebLLM) and <b>phone check</b> (this Space) — done, with the base model</li>
<li>Fine-tune Qwen2.5-0.5B-Instruct on this Mac (CPU, LoRA) — in progress</li>
<li>Evaluate on held-out data, publish the scores and the model</li>
<li>Installers per OS; tests on real Windows and Linux machines</li>
</ol>
<p class="note">Not a certification. ISO 27001 certification needs an accredited auditor, and much of GDPR and AI Act compliance is organisational. The tool collects evidence and flags gaps. See the <a href="README.md">README</a> for how to run the collectors.</p>
<footer>Open source by Devseis: code Apache-2.0, data CC BY 4.0 · <a href="https://huggingface.co/datasets/Devseis/endpoint-auditor-synthetic">training data</a> · <a href="https://huggingface.co/datasets/Devseis/endpoint-auditor-vulndb">vulnerability data</a> · <a href="CITATION.cff">cite</a></footer>
</main>
<script>
const SAMPLES = Object.fromEntries(['windows', 'linux', 'macos', 'ios', 'android'].map(os => [os, `synthetic/samples/synthetic-${os}-42000126.json`]));
const LABELS = { windows: 'Windows', linux: 'Linux', macos: 'macOS', ios: 'iPhone', android: 'Android' };
const LAYOUT = {
windows: { title: 'WINDOWS ENDPOINT SECURITY BASELINE - AUDIT', results: 'COMPLIANCE RESULTS', end: 'END OF READ-ONLY AUDIT', blank: true, extra: ['Build', 'build'] },
linux: { title: 'LINUX ENDPOINT SECURITY BASELINE - AUDIT', results: 'LINUX ENDPOINT SECURITY BASELINE - RESULTS', end: 'END OF LINUX ENDPOINT SECURITY BASELINE AUDIT', blank: false, extra: ['Kernel', 'kernel'] },
macos: { title: 'macOS ENDPOINT SECURITY BASELINE - AUDIT', results: 'COMPLIANCE RESULTS', end: 'END OF AUDIT', blank: false, extra: ['Build', 'build'] },
ios: { title: 'iPHONE SECURITY CHECK (BROWSER)', results: 'COMPLIANCE RESULTS', end: 'END OF PHONE CHECK', blank: false, extra: ['Browser', 'browser'] },
android: { title: 'ANDROID SECURITY CHECK (BROWSER)', results: 'COMPLIANCE RESULTS', end: 'END OF PHONE CHECK', blank: false, extra: ['Browser', 'browser'] },
};
const SECTIONS = [['baseline', null], ['gdpr', 'GDPR TECHNICAL CONTROLS'], ['aiact', 'EU AI ACT CONTROLS'], ['org', 'ORGANISATIONAL CONTROLS']];
let catalog = null;
function render(rec) {
const L = LAYOUT[rec.host.os_family], line = '='.repeat(60), pad = s => (s + ' '.repeat(16)).slice(0, 16);
const out = [line, ' ' + L.title, line,
pad('Hostname') + ': ' + rec.host.hostname, pad('Operating System') + ': ' + rec.host.os_name,
pad('OS Version') + ': ' + rec.host.os_version, pad(L.extra[0]) + ': ' + (rec.host[L.extra[1]] || ''),
pad('Serial Number') + ': ' + (rec.host.serial_number || ''), pad('Execution User') + ': ' + rec.audit.execution_user,
pad('Audit Date') + ': ' + rec.audit.started_at.replace('T', ' ').slice(0, 19), pad('Audit Mode') + ': ' + rec.audit.mode, line];
for (const [id, title] of SECTIONS) {
if (L.blank) out.push('');
out.push(line, ' ' + (title || L.results), line);
for (const r of rec.results) if (r.section === id) out.push(r.name, r.status + (r.evidence ? ` (${r.evidence})` : ''));
}
out.push(line, ' ' + L.end, line);
return out.join('\n');
}
function refs(id) {
const c = catalog && catalog.checks.find(x => x.id === id);
if (!c) return '';
return [...c.controls.iso27001.map(x => 'ISO ' + x), ...c.controls.gdpr.map(x => 'GDPR ' + x), ...c.controls.ai_act.map(x => 'AI Act ' + x)].join(' · ');
}
function esc(s) { const d = document.createElement('div'); d.textContent = s; return d.innerHTML; }
async function show(os) {
document.querySelectorAll('#tabs button').forEach(b => b.setAttribute('aria-selected', String(b.dataset.os === os)));
try {
const rec = await (await fetch(SAMPLES[os])).json();
document.getElementById('report').textContent = render(rec);
document.getElementById('counts').innerHTML = Object.entries(rec.summary)
.map(([k, v]) => `<span class="pill s-${k}">${v} ${k}</span>`).join('');
document.getElementById('rows').innerHTML = rec.results.map(r =>
`<tr><td>${esc(r.name)}</td><td class="s-${r.status}"><b>${r.status}</b></td><td>${esc(r.actual)}</td><td class="ref">${esc(refs(r.id))}</td></tr>`).join('');
} catch (e) {
document.getElementById('report').textContent = 'Could not load the sample: ' + e.message;
}
}
(async () => {
try { catalog = await (await fetch('catalog/checks.json')).json(); } catch (e) { catalog = null; }
const tabs = document.getElementById('tabs');
for (const os of Object.keys(SAMPLES)) {
const b = document.createElement('button');
b.textContent = LABELS[os]; b.dataset.os = os; b.setAttribute('role', 'tab');
b.addEventListener('click', () => show(os));
tabs.appendChild(b);
}
show('windows');
})();
</script>
</body>
</html>
|