Merge claude/1
This commit is contained in:
commit
b94cda0ffe
@ -88,6 +88,35 @@
|
||||
.updater-cve-pkg { color: rgba(var(--text-rgb), 0.6); }
|
||||
.updater-cve-fix { margin-left: auto; color: rgba(var(--page-verify-rgb), 0.9); font-size: 0.76rem; }
|
||||
|
||||
/* Count pill next to the "Security" detail heading (e.g. "Security 28"). */
|
||||
.updater-cve-count {
|
||||
display: inline-block; vertical-align: middle; margin-left: 4px;
|
||||
font-size: 0.68rem; font-weight: 700; line-height: 1; padding: 3px 8px;
|
||||
border-radius: 999px;
|
||||
background: rgba(var(--page-verify-rgb, var(--accent-rgb)), 0.16);
|
||||
color: rgb(var(--page-verify-rgb, var(--accent-rgb)));
|
||||
}
|
||||
|
||||
/* A long CVE list stays compact: once past a handful of rows it caps its height
|
||||
and scrolls internally rather than pushing the whole page down. The first row
|
||||
already carries a top border, so no seam is needed at the box edge. */
|
||||
.updater-cve-scroll.is-scrollable {
|
||||
max-height: 260px; overflow-y: auto;
|
||||
padding-right: 8px; margin-right: -4px;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(var(--text-rgb), 0.28) transparent;
|
||||
/* Bottom-only fade hints there's more below; the top stays crisp so the
|
||||
worst-severity rows (sorted first) are never dimmed. */
|
||||
-webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 18px), transparent 100%);
|
||||
mask-image: linear-gradient(to bottom, #000 calc(100% - 18px), transparent 100%);
|
||||
}
|
||||
.updater-cve-scroll.is-scrollable::-webkit-scrollbar { width: 8px; }
|
||||
.updater-cve-scroll.is-scrollable::-webkit-scrollbar-track { background: transparent; }
|
||||
.updater-cve-scroll.is-scrollable::-webkit-scrollbar-thumb {
|
||||
background: rgba(var(--text-rgb), 0.22); border-radius: 999px;
|
||||
}
|
||||
.updater-cve-scroll.is-scrollable::-webkit-scrollbar-thumb:hover { background: rgba(var(--text-rgb), 0.34); }
|
||||
|
||||
/* ---- Buttons ---- */
|
||||
.updater-btn {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
|
||||
@ -183,6 +183,27 @@ class UpdaterPage {
|
||||
return null;
|
||||
}
|
||||
|
||||
sevRank(s) {
|
||||
const r = { critical: 0, high: 1, medium: 2, low: 3 };
|
||||
return r[(s || '').toLowerCase()] ?? 4;
|
||||
}
|
||||
|
||||
// One CVE list, shared by the standalone Security tab and the per-app expander.
|
||||
// Sorted worst-first (so the most severe are visible before any scroll) and
|
||||
// wrapped in a height-capped scroll box once the list is long, so an app with
|
||||
// dozens of CVEs (e.g. 28) stays compact instead of pushing the page down.
|
||||
renderCveList(cves) {
|
||||
const list = [...(cves || [])].sort((x, y) => this.sevRank(x.severity) - this.sevRank(y.severity));
|
||||
const items = list.map(c => `
|
||||
<div class="updater-cve sev-${(c.severity || 'low').toLowerCase()}">
|
||||
<span class="updater-cve-sev">${this.escape((c.severity || '').toUpperCase())}</span>
|
||||
<a class="updater-cve-id" href="${this.escape(c.url || ('https://nvd.nist.gov/vuln/detail/' + (c.id || '')))}" target="_blank" rel="noopener">${this.escape(c.id || 'CVE')}</a>
|
||||
<span class="updater-cve-pkg">${this.escape(c.package || '')}</span>
|
||||
${c.fixed_in ? `<span class="updater-cve-fix">fixed in ${this.escape(c.fixed_in)}</span>` : ''}
|
||||
</div>`).join('');
|
||||
return `<div class="updater-cve-scroll${list.length > 6 ? ' is-scrollable' : ''}">${items}</div>`;
|
||||
}
|
||||
|
||||
// The CVE scanner's live state, stamped on cves.json by the updater generator:
|
||||
// 'ready' — Trivy's vulnerability DB is present; results are real
|
||||
// 'db_updating' — Trivy is installed but still downloading its DB (no
|
||||
@ -417,16 +438,9 @@ class UpdaterPage {
|
||||
// manual Check), so the message alone is the right button-free empty UI.
|
||||
if (!this.cves) return this.empty('No vulnerability scan yet — one runs automatically within a couple of minutes.');
|
||||
if (!withCves.length) return this.empty('No known vulnerabilities in your installed apps. 🎉');
|
||||
const blocks = withCves.map(a => {
|
||||
const items = (a.cves || []).map(c => `
|
||||
<div class="updater-cve sev-${(c.severity || 'low').toLowerCase()}">
|
||||
<span class="updater-cve-sev">${this.escape((c.severity || '').toUpperCase())}</span>
|
||||
<a class="updater-cve-id" href="${this.escape(c.url || ('https://nvd.nist.gov/vuln/detail/' + (c.id || '')))}" target="_blank" rel="noopener">${this.escape(c.id || 'CVE')}</a>
|
||||
<span class="updater-cve-pkg">${this.escape(c.package || '')}</span>
|
||||
${c.fixed_in ? `<span class="updater-cve-fix">fixed in ${this.escape(c.fixed_in)}</span>` : ''}
|
||||
</div>`).join('');
|
||||
return `<div class="updater-cve-app"><div class="updater-cve-app-name">${this.escape(a.displayName)} <span class="updater-badge sev-${a.worstSeverity}">${(a.cves || []).length}</span></div>${items}</div>`;
|
||||
}).join('');
|
||||
const blocks = withCves.map(a =>
|
||||
`<div class="updater-cve-app"><div class="updater-cve-app-name">${this.escape(a.displayName)} <span class="updater-badge sev-${a.worstSeverity}">${(a.cves || []).length}</span></div>${this.renderCveList(a.cves)}</div>`
|
||||
).join('');
|
||||
return `<div class="updater-list">${blocks}</div>`;
|
||||
}
|
||||
|
||||
@ -480,15 +494,9 @@ class UpdaterPage {
|
||||
<div class="updater-detail-row">${badge} <span class="updater-row-ver">${cur}${avail ? ` <span class="updater-arrow">→</span> <strong>${avail}</strong>` : ''}</span></div></div>`;
|
||||
}
|
||||
const cves = a.cves || [];
|
||||
const cveItems = cves.map((c) => `
|
||||
<div class="updater-cve sev-${(c.severity || 'low').toLowerCase()}">
|
||||
<span class="updater-cve-sev">${this.escape((c.severity || '').toUpperCase())}</span>
|
||||
<a class="updater-cve-id" href="${this.escape(c.url || ('https://nvd.nist.gov/vuln/detail/' + (c.id || '')))}" target="_blank" rel="noopener">${this.escape(c.id || 'CVE')}</a>
|
||||
<span class="updater-cve-pkg">${this.escape(c.package || '')}</span>
|
||||
${c.fixed_in ? `<span class="updater-cve-fix">fixed in ${this.escape(c.fixed_in)}</span>` : ''}
|
||||
</div>`).join('');
|
||||
const security = `<div class="updater-detail-section"><h4>Security</h4>${
|
||||
cves.length ? cveItems : '<p class="updater-detail-empty">No known CVEs. 🎉</p>'}</div>`;
|
||||
const security = `<div class="updater-detail-section"><h4>Security${
|
||||
cves.length ? ` <span class="updater-cve-count">${cves.length}</span>` : ''}</h4>${
|
||||
cves.length ? this.renderCveList(cves) : '<p class="updater-detail-empty">No known CVEs. 🎉</p>'}</div>`;
|
||||
|
||||
// A rollback target exists if a snapshot field is present (future-proofing)
|
||||
// OR — the data the generator actually emits today — this app has a prior
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user