fix(llm-security): playground v7.6.1 — visuelle bugs i v7.6.0
Seks bugs fanget av maintainer ved manuell verifisering i nettleser etter v7.6.0-release. Alle skyldes mismatch mellom DS-klasser og hvordan playground-rendrere brukte dem, eller manglende DS-implementasjoner av klasser playground-rendrere antok eksisterte. Fixes: - renderFindingsBlock brukte .findings outer-class som DS har som 2-kolonners grid (360px list + 1fr detail-panel) — headeren havnet i venstre kolonne, items i høyre, brutt layout i alle 18 rapporter med findings. Erstattet med .report-meta + h4 + findings__list > findings__group + findings__group-header + findings__items (korrekt DS-mønster, kun list-delen). - .report-table manglet helt i DS men brukes i 7+ rendrere (OWASP, Supply chain, Scanner Risk Matrix, Plugin-meta, Permission-matrise, Live-meter, Siste runs, Godkjenninger, Mitigation roadmap). Lagt lokal CSS-implementasjon i playground-HTML style-blokk: border- collapse, zebra-hover, header-styling. Komplementerer DS-tokens uten å modifisere vendor. - renderPreDeploy traffic-lights brukte .sm-card__grade som er fast 28x28 px (én A-F-bokstav) — kuttet PASS til AS og PASS-WITH-NOTES til PASS-WITH-... i alle traffic-light-cards. Erstattet med bredde-tilpasset status-pill via inline styling (severity-soft + on tokens). - Threat-model matrix-bobler ikke klikkbare. Erstattet span med button type=button data-threat-id + aria-label. Click-handler scroller til tilsvarende rad i Trusler-tabellen og fremhever den i 1.6 sek. - Radar-labels overlappet ved 6+ akser fordi alle brukte text-anchor=middle. Økt SVG-størrelse 280 → 380, radius 105 → 125. Bytter text-anchor fra middle til start/end basert på horisontal- posisjon. - recommendation-card__body tekstoverflyt på lange single-line tekster (vilkår, owner-tags, dato). Lagt overflow-wrap: anywhere; word-break: break-word i lokal style-blokk. Verifisering: - 4/4 fix-spesifikke smoke-tester passerer - 18/18 renderere produserer fortsatt komplett HTML mot dft-komplett-demo (regresjons-test) - Filendring playground.html 10677 → 10753 linjer (+76 netto) Versjonsbump v7.6.0 → v7.6.1 (patch — bugfix-only, ingen scanner- eller hook-atferdsendringer): - plugins/llm-security/.claude-plugin/plugin.json - plugins/llm-security/package.json - plugins/llm-security/README.md (badge) - plugins/llm-security/CHANGELOG.md ([7.6.1] entry) - plugins/llm-security/playground/llm-security-playground.html (footer) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
f006143fb8
commit
f9b555aa64
5 changed files with 142 additions and 15 deletions
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 `<section class="report-meta">` + `<h4>` +
|
||||
`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 `<style>`-blokk:
|
||||
border-collapse, zebra-hover, header-styling, td-padding. Komplementerer
|
||||
DS-tokens uten å modifisere vendor.
|
||||
- **`renderPreDeploy` traffic-lights brukte `.sm-card__grade`** som er
|
||||
fast `28×28 px` (designet for én A-F-bokstav) — kuttet "PASS" til "AS"
|
||||
og "PASS-WITH-NOTES" til "PASS-WITH-..." i alle traffic-light-cards.
|
||||
Erstattet med en bredde-tilpasset status-pill via inline styling
|
||||
(severity-soft + on tokens).
|
||||
- **Threat-model matrix-bobler ikke klikkbare** — `<span>` uten event-
|
||||
handler. Erstattet med `<button type="button" data-threat-id>` +
|
||||
`aria-label`. Lagt til click-handler som scroller til tilsvarende rad
|
||||
i Trusler-tabellen og fremhever den i 1.6 sek.
|
||||
- **Radar-labels overlappet** ved 6+ akser fordi alle brukte
|
||||
`text-anchor="middle"` med samme radius-offset. Økt SVG-størrelse fra
|
||||
280×280 til 380×380, radius fra 105 til 125. Bytter `text-anchor` fra
|
||||
`middle` til `start`/`end` basert på horisontal-posisjon (Math.cos(ang)
|
||||
> 0.2 / < -0.2 / mellom).
|
||||
- **`recommendation-card__body` tekstoverflyt** — lange single-line
|
||||
tekster (vilkår, owner-tags, dato) ble klippet av container. Lagt
|
||||
`overflow-wrap: anywhere; word-break: break-word` i lokal `<style>`-
|
||||
blokk.
|
||||
|
||||
### Verification
|
||||
|
||||
- 4/4 fix-spesifikke smoke-tester passerer (`findings__list`,
|
||||
`data-threat-id`-button, `viewBox="0 0 380 380"`, ingen `sm-card__grade
|
||||
data-grade` i pre-deploy).
|
||||
- 18/18 renderere produserer fortsatt komplett HTML-output mot
|
||||
`dft-komplett-demo` (regresjons-test).
|
||||
- Filendring: playground.html 10677 → 10753 linjer (+76 netto).
|
||||
|
||||
## [7.6.0] - 2026-05-06
|
||||
|
||||
Playground Tier 3-referanse-case. v7.6.0 hever playgroundet
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
*AI-generated: all code produced by Claude Code through dialog-driven development. [Full disclosure →](../../README.md#ai-generated-code-disclosure)*
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "llm-security",
|
||||
"version": "7.6.0",
|
||||
"version": "7.6.1",
|
||||
"description": "Security scanning, auditing, and threat modeling for Claude Code projects",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
|
|
|
|||
|
|
@ -138,6 +138,29 @@
|
|||
overstyrer). Gjengis av renderVerdictPill() med data-verdict-mapping. */
|
||||
/* v7.6.0 fase 4: lokal form-progress-stegblokk fjernet — DS Tier 3 sup
|
||||
leverer .form-progress__steps + .fp-step + .fp-step__num/__name. */
|
||||
|
||||
/* v7.6.1 fix: .report-table — DS har ikke implementert denne klassen, men
|
||||
playground-rendrere bruker den i 7+ rapporter (OWASP-kategorier, Supply
|
||||
chain, Scanner Risk Matrix, Plugin-meta, Permission-matrise, Live-meter,
|
||||
Siste runs, Godkjenninger, Mitigation roadmap). Lokal styling som
|
||||
komplementerer DS-tokens. */
|
||||
.report-table { width: 100%; border-collapse: collapse; margin: var(--space-3) 0; font-size: var(--font-size-sm); }
|
||||
.report-table th { text-align: left; padding: 8px 12px; border-bottom: 2px solid var(--color-border-moderate); background: var(--color-bg-soft); font-weight: var(--font-weight-semibold); color: var(--color-text-secondary); text-transform: uppercase; font-size: 11px; letter-spacing: 0.04em; }
|
||||
.report-table td { padding: 8px 12px; border-bottom: 1px solid var(--color-border-subtle); vertical-align: top; color: var(--color-text-primary); }
|
||||
.report-table tr:last-child td { border-bottom: none; }
|
||||
.report-table tbody tr:hover { background: var(--color-bg-soft); }
|
||||
.report-table code { font-family: var(--font-family-mono); font-size: 12px; background: var(--color-surface-sunken); padding: 1px 6px; border-radius: var(--radius-sm); }
|
||||
|
||||
/* v7.6.1 fix: recommendation-card body kan inneholde lange single-line
|
||||
tekster (vilkår, owner-tags, dato). Tving word-wrap så grid-celle
|
||||
(auto + 1fr) ikke skubber innhold utenfor viewport. */
|
||||
.recommendation-card__body { overflow-wrap: anywhere; word-break: break-word; }
|
||||
|
||||
/* v7.6.1 fix: matrix-bobler skal være klikkbare. DS har hover på cellene,
|
||||
men bobler er <span> uten cursor. Gjør bubble til cursor:pointer + focus. */
|
||||
.matrix__bubble { cursor: pointer; transition: transform var(--duration-fast) var(--ease-default); }
|
||||
.matrix__bubble:hover { transform: scale(1.15); }
|
||||
.matrix__bubble:focus-visible { outline: 2px solid var(--color-primary-500); outline-offset: 2px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
|
@ -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 @@
|
|||
'</div>'
|
||||
);
|
||||
}).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 (
|
||||
'<section class="findings">' +
|
||||
'<div class="findings__group-header">' + escapeHtml(label || 'Funn') + ' (' + findings.length + ')</div>' +
|
||||
'<div class="findings__items">' + items + '</div>' +
|
||||
'<section class="report-meta">' +
|
||||
'<h4>' + escapeHtml(label || 'Funn') + '</h4>' +
|
||||
'<div class="findings__list" style="max-height: none;">' +
|
||||
'<div class="findings__group">' +
|
||||
'<div class="findings__group-header">' + escapeHtml(label || 'Funn') + ' (' + findings.length + ')</div>' +
|
||||
'<div class="findings__items">' + items + '</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</section>'
|
||||
);
|
||||
}
|
||||
|
|
@ -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 '<div class="radar__score-row"><span>' + escapeHtml(a.name) + '</span><strong>' + escapeHtml(String(a.score || 0)) + '/5</strong></div>';
|
||||
|
|
@ -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 '<text class="radar__label" x="' + lx.toFixed(1) + '" y="' + ly.toFixed(1) + '" text-anchor="middle" dominant-baseline="middle">' + escapeHtml(a.name) + '</text>';
|
||||
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 '<text class="radar__label" x="' + lx.toFixed(1) + '" y="' + ly.toFixed(1) + '" text-anchor="' + anchor + '" dominant-baseline="middle">' + escapeHtml(a.name) + '</text>';
|
||||
}).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 '<div class="sm-card" data-severity="' + escapeAttr(sev) + '" style="border-left: 3px solid var(--color-' + sev + '); padding-left: var(--space-3);">' +
|
||||
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 = '<span style="font-family: var(--font-family-mono); font-size: 11px; font-weight: var(--font-weight-bold); letter-spacing: 0.04em; padding: 3px 8px; border-radius: var(--radius-sm); background: ' + pillBg + '; color: ' + pillFg + '; white-space: nowrap;">' + escapeHtml(l.status) + '</span>';
|
||||
return '<div class="sm-card" data-severity="' + escapeAttr(sev) + '" style="border-left: 3px solid var(--color-severity-' + (sev === 'low' ? 'low' : sev === 'medium' ? 'medium' : sev === 'critical' ? 'critical' : 'low') + '); padding-left: var(--space-3);">' +
|
||||
'<div class="sm-card__header">' +
|
||||
'<span class="sm-card__name">' + escapeHtml(l.category) + '</span>' +
|
||||
'<span class="sm-card__grade" data-grade="' + (sev === 'low' ? 'A' : sev === 'medium' ? 'C' : sev === 'critical' ? 'F' : '?') + '">' + escapeHtml(l.status) + '</span>' +
|
||||
statusPill +
|
||||
'</div>' +
|
||||
(l.notes ? '<span class="sm-card__status">' + escapeHtml(l.notes) + '</span>' : '') +
|
||||
'</div>';
|
||||
|
|
@ -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å <button> så de er klikkbare og fokuserbare.
|
||||
// data-threat-id lar event-handler senere mappe til detalj-modal.
|
||||
const bubblesHtml = items.length
|
||||
? '<div class="matrix__cell-bubbles">' +
|
||||
items.slice(0, 3).map(function (it, i) {
|
||||
return '<span class="matrix__bubble" title="' + escapeAttr(it.label || '') + '">' + (i + 1) + '</span>';
|
||||
return '<button type="button" class="matrix__bubble" data-threat-id="' + escapeAttr(it.id || it.label || '') + '" title="' + escapeAttr(it.label || '') + '" aria-label="Trussel: ' + escapeAttr(it.label || it.id || '') + '">' + (i + 1) + '</button>';
|
||||
}).join('') +
|
||||
(items.length > 3 ? '<span class="matrix__bubble matrix__bubble--count">+' + (items.length - 3) + '</span>' : '') +
|
||||
(items.length > 3 ? '<button type="button" class="matrix__bubble matrix__bubble--count" aria-label="' + (items.length - 3) + ' flere trusler">+' + (items.length - 3) + '</button>' : '') +
|
||||
'</div>'
|
||||
: '';
|
||||
matrixHtml += '<div class="matrix__cell" data-score="' + score + '">' +
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue