diff --git a/.jules/palette.md b/.jules/palette.md index ea004e2d..452c3c03 100644 --- a/.jules/palette.md +++ b/.jules/palette.md @@ -81,3 +81,7 @@ ## 2026-08-12 - Skip to Content Accessibility **Learning:** Screen reader and keyboard-only users experience significant friction when forced to navigate through repetitive header controls on every page load. **Action:** Keep a visible-on-focus skip link as the first interactive element, target a programmatically focusable main container, and give the focused link a high-contrast outline. + +## 2026-08-23 - Async Interactive Element Interactions +**Learning:** Adding `aria-busy="true"` explicitly communicates loading states to screen readers, but it does not natively prevent clicks, unlike the `disabled` attribute. During async operations (like loading scan details), users might double-click an interactive element that lacks `pointer-events: none`, causing duplicate API requests or unexpected state bugs. +**Action:** Always map the `[aria-busy="true"]` state directly to CSS with `pointer-events: none` and visual indicators (like `opacity`) to ensure visual parity with the accessibility tree and prevent duplicate user actions. diff --git a/CHANGELOG.d/1031-malicious-shared-skill-threats.md b/CHANGELOG.d/1031-malicious-shared-skill-threats.md deleted file mode 100644 index 28763f23..00000000 --- a/CHANGELOG.d/1031-malicious-shared-skill-threats.md +++ /dev/null @@ -1,3 +0,0 @@ -### Added - -- The threat inventory now registers two observed shared-skill threats: a homoglyph-named prompt-injection skill whose body ordered safety-rule bypass and file-content exfiltration, and an unrendered placeholder template that polluted discovery. Controls point to deny-listed sync with exact alias matching and untrusted-catalog handling (#1031). diff --git a/docs/THREAT_MODEL.md b/docs/THREAT_MODEL.md index b4af3243..8aeba721 100644 --- a/docs/THREAT_MODEL.md +++ b/docs/THREAT_MODEL.md @@ -44,8 +44,6 @@ flowchart LR | deploy exclusions erase evidence | hidden risk | exclusions affect gate only; finding remains visible | | tampered SBOM/report evidence | acquisition/security misstatement | deterministic source/lock provenance and manifest hashes | | autonomous model self-approval | governance bypass | developer/reviewer/merge/release authority separation | -| malicious shared skill (homoglyph name, injected instructions, exfiltration directive) | agent hijack and secret exfiltration | deny-listed skill sync with exact alias matching; catalog/skill text treated as untrusted data, never instructions (#1031) | -| placeholder template published as an installable skill | discovery pollution and broken installs | reject unresolved placeholder names at sync; workspace state kept out of skill roots (#1031) | ## Stored SSRF abuse case diff --git a/scanner/dashboard/console.html b/scanner/dashboard/console.html index 7ec262af..3ecd3729 100644 --- a/scanner/dashboard/console.html +++ b/scanner/dashboard/console.html @@ -46,6 +46,7 @@ .err{color:var(--crit);font-weight:600} code{background:var(--bg);padding:1px 5px;border-radius:4px} .hidden{display:none} + :disabled, [aria-busy="true"] { opacity: 0.7; pointer-events: none; } @@ -135,8 +136,8 @@

AppGuardrail Console

${esc(s.created_at)}${esc(s.repo||"—")}${esc((s.commit||"—").slice(0,10))} ${s.total}${pill(s.deploy_blocking,"var(--crit)")}${pill(s.new_blocking,"var(--high)")}`).join("")||'No scans. POST to /api/v1/scans from CI.'; document.querySelectorAll("tr.scan").forEach(tr=>{ - tr.onclick=()=>detail(tr.dataset.id,tr); - tr.addEventListener('keydown', e => { if(e.key === 'Enter' || e.key === ' ') { e.preventDefault(); detail(tr.dataset.id,tr); } }); + tr.onclick=()=>{if(tr.getAttribute("aria-busy")==="true")return;detail(tr.dataset.id,tr);}; + tr.addEventListener('keydown', e => { if(e.key === 'Enter' || e.key === ' ') { e.preventDefault(); if(tr.getAttribute("aria-busy")==="true")return; detail(tr.dataset.id,tr); } }); }); }catch(e){ $("#msg").classList.remove("hidden");$("#app").classList.add("hidden"); $("#msg").innerHTML=`${esc(e.message)}`; }