--- title: SBOM --- ```js const API = "http://127.0.0.1:8000"; ``` ```js // Fetch SBOM data on load let _entries = [], _report = {groups: [], copyleft_direct_count: 0}, _repos = []; try { [_entries, _report, _repos] = await Promise.all([ fetch(`${API}/sbom/`).then(r => r.ok ? r.json() : []), fetch(`${API}/sbom/report/licences/`).then(r => r.ok ? r.json() : {groups:[], copyleft_direct_count: 0}), fetch(`${API}/repos/`).then(r => r.ok ? r.json() : []), ]); } catch {} ``` ```js const entries = _entries ?? []; const report = _report ?? {groups: [], copyleft_direct_count: 0}; const repos = _repos ?? []; const groups = report.groups ?? []; const riskCount = report.copyleft_direct_count ?? 0; ``` # SBOM ## Licence Risk ```js const riskBadge = riskCount === 0 ? html`✓ No copyleft in direct prod deps` : html`⚠ ${riskCount} direct prod dep(s) with copyleft licence`; display(html`

Total Packages

${entries.length}

Repos Scanned

${new Set(entries.map(e => e.repo_id)).size}

Licence Risk

${riskCount}

${riskBadge}

Unique Licences

${groups.length}

`); ``` ## Licence Distribution ```js import * as Plot from "npm:@observablehq/plot"; if (groups.length === 0) { display(html`

No SBOM data ingested yet. Run make ingest-sbom REPO=<slug>.

`); } else { const plotData = groups.slice(0, 15).map(g => ({ licence: g.license_spdx ?? "(unknown)", count: g.count, copyleft: g.is_copyleft, })); display(Plot.plot({ x: {label: "Packages"}, y: {label: null, domain: plotData.map(d => d.licence)}, color: {domain: [false, true], range: ["steelblue", "#e53935"], legend: true, tickFormat: d => d ? "Copyleft" : "Permissive"}, marks: [ Plot.barX(plotData, {y: "licence", x: "count", fill: "copyleft", tip: true}), Plot.ruleX([0]), ], marginLeft: 130, height: Math.max(80, plotData.length * 30 + 50), width: 600, })); } ``` ## Copyleft Risk Detail ```js const copyleftGroups = groups.filter(g => g.is_copyleft); if (copyleftGroups.length === 0) { display(html`

✓ No copyleft packages found.

`); } else { display(html`
${copyleftGroups.map(g => html`
${g.license_spdx ?? "unknown"} ${g.count} package(s) ${g.repos.join(", ")}
`)}
`); } ``` ## Package Table ```js // Filters const ecoFilter = Inputs.select(["all", "python", "node", "rust", "go", "java", "other"], {label: "Ecosystem", value: "all"}); const directOnly = Inputs.toggle({label: "Direct deps only", value: false}); const prodOnly = Inputs.toggle({label: "Prod deps only (no dev)", value: false}); display(html`
${ecoFilter}${directOnly}${prodOnly}
`); ``` ```js // Build repo_id → slug lookup const repoById = Object.fromEntries(_repos.map(r => [r.id, r.slug])); const filteredEntries = entries.filter(e => (ecoFilter.value === "all" || e.ecosystem === ecoFilter.value) && (!directOnly.value || e.is_direct) && (!prodOnly.value || !e.is_dev) ); display(Inputs.table(filteredEntries.map(e => ({ Package: e.package_name, Version: e.package_version ?? "—", Ecosystem: e.ecosystem, Licence: e.license_spdx ?? "—", Repo: repoById[e.repo_id] ?? e.repo_id?.slice(0, 8) ?? "—", Direct: e.is_direct ? "✓" : "", Dev: e.is_dev ? "✓" : "", })), {maxWidth: 900})); ```