diff --git a/plugins/llm-security/.claude-plugin/plugin.json b/plugins/llm-security/.claude-plugin/plugin.json index 3bf3ee8..0dfb850 100644 --- a/plugins/llm-security/.claude-plugin/plugin.json +++ b/plugins/llm-security/.claude-plugin/plugin.json @@ -1,5 +1,5 @@ { "name": "llm-security", "description": "Security scanning, auditing, and threat modeling for Claude Code projects. Detects secrets, validates MCP servers, assesses security posture, and generates threat models aligned with OWASP LLM Top 10.", - "version": "7.6.0" + "version": "7.6.1" } diff --git a/plugins/llm-security/CHANGELOG.md b/plugins/llm-security/CHANGELOG.md index a2fd2aa..4a4099c 100644 --- a/plugins/llm-security/CHANGELOG.md +++ b/plugins/llm-security/CHANGELOG.md @@ -6,6 +6,57 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [Unreleased] +## [7.6.1] - 2026-05-06 + +Playground v7.6.0 visuell-patch. Seks bugs fanget under maintainer- +verifisering i nettleser; alle skyldes mismatch mellom DS-klasser og +hvordan playground-rendrere brukte dem (eller manglende DS-implementasjoner +av klasser playground-rendrere antok eksisterte). Ingen scanner- eller +hook-behavior-changes. + +### Fixed + +- **`renderFindingsBlock` brukte `.findings` outer-class** som DS har som + 2-kolonners grid (`grid-template-columns: 360px 1fr`) for list+detail- + panel-layout. Resultat: findings-headeren havnet i venstre 360px- + kolonne og items i 1fr-kolonnen, brutt layout i alle 18 rapporter med + findings. Erstattet med `
` + `

` + + `findings__list > findings__group > findings__group-header + + findings__items` (korrekt DS-mønster). +- **`.report-table` mangler i DS** men brukes i 7+ rendrere (OWASP- + kategorier, Supply chain, Scanner Risk Matrix, Plugin-meta, Permission- + matrise, Live-meter, Siste runs, Godkjenninger, Mitigation roadmap). + Lagt lokal CSS-implementasjon i playground-HTML ` @@ -6818,7 +6841,7 @@ verdict: 'n-a', hero: true, meta: [ - 'Plugin v7.6.0', + 'Plugin v7.6.1', projects.length + ' prosjekt' + (projects.length === 1 ? '' : 'er'), CATALOG.commands.length + ' kommandoer' ], @@ -8820,10 +8843,18 @@ '' ); }).join(''); + // DS .findings outer-class er et 2-kolonners grid (360px list + 1fr detail-panel) — + // playgroundet bruker bare list-delen, så vi wrapper i .findings__list (uten outer + // .findings) for å unngå at headeren ender i venstre 360px-kolonne. v7.6.1 fix. return ( - '
' + - '
' + escapeHtml(label || 'Funn') + ' (' + findings.length + ')
' + - '
' + items + '
' + + '
' + + '

' + escapeHtml(label || 'Funn') + '

' + + '
' + + '
' + + '
' + escapeHtml(label || 'Funn') + ' (' + findings.length + ')
' + + '
' + items + '
' + + '
' + + '
' + '
' ); } @@ -8917,7 +8948,10 @@ function renderRadarSvg(axes) { // axes: [{ name, score (0-5) }] if (!axes || axes.length < 3) return ''; - const size = 280, cx = size / 2, cy = size / 2, r = 105; + // v7.6.1 fix: øk SVG-bredden fra 280 til 380 og r fra 105 til 125 for å gi + // labels mer plass. Bruk text-anchor basert på horisontal-posisjon for å + // unngå at bottom-labels overlapper hverandre ved 6+ akser. + const size = 380, cx = size / 2, cy = size / 2, r = 125; const n = axes.length; const axisRows = axes.map(function (a) { return '
' + escapeHtml(a.name) + '' + escapeHtml(String(a.score || 0)) + '/5
'; @@ -8925,9 +8959,12 @@ const angle = function (i) { return -Math.PI / 2 + (i * 2 * Math.PI / n); }; const labelHtml = axes.map(function (a, i) { const ang = angle(i); - const lx = cx + Math.cos(ang) * (r + 22); - const ly = cy + Math.sin(ang) * (r + 22); - return '' + escapeHtml(a.name) + ''; + const lx = cx + Math.cos(ang) * (r + 28); + const ly = cy + Math.sin(ang) * (r + 28); + // Velg text-anchor basert på posisjon: ankerene til venstre/høyre snur. + const dx = Math.cos(ang); + const anchor = Math.abs(dx) < 0.2 ? 'middle' : (dx > 0 ? 'start' : 'end'); + return '' + escapeHtml(a.name) + ''; }).join(''); const grids = [1, 2, 3, 4, 5].map(function (k) { const rk = (r * k) / 5; @@ -9800,12 +9837,24 @@ if (u === 'FAIL' || u === 'BLOCK' || u === 'NO-GO') return 'critical'; return 'info'; }; + // v7.6.1 fix: sm-card__grade er fast 28×28 px (designet for én A-F-bokstav), så + // "PASS"/"PASS-WITH-NOTES"/"FAIL" ble kuttet til "AS"/"PASS-WITH-..."/"FA". Bytt + // til en bredde-tilpasset status-pill via inline styling (ingen DS-klasse-endring). const cards = lights.map(function (l) { const sev = sevForStatus(l.status); - return '
' + + const pillBg = sev === 'low' ? 'var(--color-severity-low-soft)' + : sev === 'medium' ? 'var(--color-severity-medium-soft)' + : sev === 'critical' ? 'var(--color-severity-critical-soft)' + : 'var(--color-bg-soft)'; + const pillFg = sev === 'low' ? 'var(--color-severity-low-on)' + : sev === 'medium' ? 'var(--color-severity-medium-on)' + : sev === 'critical' ? 'var(--color-severity-critical-on)' + : 'var(--color-text-secondary)'; + const statusPill = '' + escapeHtml(l.status) + ''; + return '
' + '
' + '' + escapeHtml(l.category) + '' + - '' + escapeHtml(l.status) + '' + + statusPill + '
' + (l.notes ? '' + escapeHtml(l.notes) + '' : '') + '
'; @@ -10077,12 +10126,14 @@ for (let prob = 1; prob <= probSize; prob++) { const score = prob * cons; const items = byPC[prob + '_' + cons] || []; + // v7.6.1 fix: bobler er nå '; }).join('') + - (items.length > 3 ? '+' + (items.length - 3) + '' : '') + + (items.length > 3 ? '' : '') + '
' : ''; matrixHtml += '
' + @@ -10368,6 +10419,31 @@ } function attachActionHandlers() { + // v7.6.1 fix: matrix-bobler klikkbare. Klikk scroller til tilsvarende rad + // i Trusler-tabellen og fremhever den kort. Bruker data-threat-id som anker. + document.addEventListener('click', function (ev) { + const bubble = ev.target.closest('.matrix__bubble[data-threat-id]'); + if (!bubble) return; + const threatId = bubble.getAttribute('data-threat-id'); + if (!threatId) return; + // Finn raden i Trusler-tabellen (TM-XXX i første kolonne) + const tables = document.querySelectorAll('table.report-table'); + for (let t = 0; t < tables.length; t++) { + const rows = tables[t].querySelectorAll('tbody tr'); + for (let r = 0; r < rows.length; r++) { + const firstCell = rows[r].querySelector('td'); + if (firstCell && firstCell.textContent.trim() === threatId) { + rows[r].scrollIntoView({ behavior: 'smooth', block: 'center' }); + const orig = rows[r].style.background; + rows[r].style.background = 'var(--color-primary-100, var(--color-bg-soft))'; + rows[r].style.transition = 'background var(--duration-base) var(--ease-default)'; + setTimeout(function () { rows[r].style.background = orig; }, 1600); + return; + } + } + } + }); + document.addEventListener('click', function (ev) { const target = ev.target.closest('[data-action]'); if (!target) return;