const state = { findings: [], accessMap: [], }; const fileInput = document.getElementById('file'); const uploadBtn = document.getElementById('upload-btn'); const sampleBtn = document.getElementById('sample-btn'); const stats = { findings: document.getElementById('stat-findings'), access: document.getElementById('stat-access'), providers: document.getElementById('stat-providers'), }; const findingsTable = document.getElementById('findings'); const accessTable = document.getElementById('access-map'); const payloadPreview = document.getElementById('payload-preview'); uploadBtn.addEventListener('click', () => fileInput.click()); fileInput.addEventListener('change', () => { if (fileInput.files?.[0]) { loadFile(fileInput.files[0]); } }); sampleBtn.addEventListener('click', async () => { const resp = await fetch('sample-report.json'); const data = await resp.json(); render(normalizePayload(data)); }); async function loadFile(file) { const text = await file.text(); render(parseReport(text)); } function parseReport(text) { try { const parsed = JSON.parse(text); return normalizePayload(parsed); } catch (_) { return parseJsonl(text); } } function parseJsonl(text) { const lines = text.split(/\r?\n/).filter(Boolean); const findings = []; let accessMap = []; lines.forEach((line) => { try { const row = JSON.parse(line); if (row.rule && row.finding) { findings.push(row); } if (row.access_map) { accessMap = accessMap.concat(row.access_map); } } catch (_) { /* ignore */ } }); return { findings, access_map: accessMap }; } function normalizePayload(data) { if (Array.isArray(data)) { return { findings: data, access_map: [] }; } return { findings: data.findings || [], access_map: normalizeAccessMap(data.access_map || []), }; } function render(payload) { state.findings = payload.findings || []; state.accessMap = normalizeAccessMap(payload.access_map || []); const flattened = flattenAccessMap(state.accessMap); stats.findings.textContent = state.findings.length; stats.access.textContent = flattened.length; stats.providers.textContent = new Set(state.accessMap.map((e) => e.provider || '')).size; renderFindings(); renderAccessMap(flattened); payloadPreview.textContent = JSON.stringify({ ...payload, access_map: state.accessMap }, null, 2); } function renderFindings() { const tbody = findingsTable.querySelector('tbody'); tbody.innerHTML = ''; if (state.findings.length === 0) { tbody.innerHTML = '