From bfd137fd3b63eae032f593c62e6b9ea0ad8f563c Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Wed, 19 Aug 2026 19:05:12 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20=EC=99=B8=EB=B6=80=20?= =?UTF-8?q?=EB=A7=81=ED=81=AC=20=EC=83=88=20=EC=B0=BD=20=EC=97=B4=EB=A6=BC?= =?UTF-8?q?=20=EC=A0=91=EA=B7=BC=EC=84=B1=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scanner/dashboard/index.html | 2 +- tests/test_dashboard_status_contracts.py | 36 ++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/scanner/dashboard/index.html b/scanner/dashboard/index.html index 132bc31b..8df0a0ee 100644 --- a/scanner/dashboard/index.html +++ b/scanner/dashboard/index.html @@ -281,7 +281,7 @@

Dashboard

function openDetail(f){ lastFocus = document.activeElement; const s = String(f.severity||'INFO').toUpperCase(); - const refs = (f.references||[]).map(r=>`${esc(r)}`).join('
'); + const refs = (f.references||[]).map(r=>`${esc(r)}`).join('
'); const owasp = (f.owasp||[]).join(', '); const cwe = (f.cwe||[]).join(', '); const d = document.getElementById('detail'); diff --git a/tests/test_dashboard_status_contracts.py b/tests/test_dashboard_status_contracts.py index a390688c..435ba369 100644 --- a/tests/test_dashboard_status_contracts.py +++ b/tests/test_dashboard_status_contracts.py @@ -2,6 +2,7 @@ from __future__ import annotations +import re from html.parser import HTMLParser from scanner.cli.appguardrail import dashboard_index_path @@ -70,8 +71,7 @@ def test_dashboard_escapes_double_quotes_with_complete_html_entity() -> None: html = _dashboard_html() expected_mapping = ( - "{'&':'&','<':'<','>':'>','\"':'"'," - "\"'\":''','`':'`'}" + "{'&':'&','<':'<','>':'>','\"':'"'," "\"'\":''','`':'`'}" ) assert expected_mapping in html @@ -90,3 +90,35 @@ def test_dashboard_distinguishes_unloaded_and_clean_scan_states() -> None: assert "Clean scan · 0 findings · deploy gate clear" in html assert "Load a different findings.json file" in html assert "🎉 Clean Scan" not in html + + +class _AnchorAttributeParser(HTMLParser): + def __init__(self) -> None: + super().__init__() + self.anchor_attrs: dict[str, str | None] = {} + + def handle_starttag( + self, + tag: str, + attrs: list[tuple[str, str | None]], + ) -> None: + if tag == "a": + self.anchor_attrs = dict(attrs) + + +def test_dashboard_external_links_warn_screen_readers() -> None: + """External links opening in new tabs must have accessible labels warning of context switch.""" + html = _dashboard_html() + + # Extract the literal anchor tag from the JS code + match = re.search( + r"const refs = [^`]+`(]+>)\$\{esc\(r\)\}`", html, re.DOTALL + ) + assert match is not None + anchor_html = match.group(1) + "" + + parser = _AnchorAttributeParser() + parser.feed(anchor_html) + + assert parser.anchor_attrs.get("target") == "_blank" + assert parser.anchor_attrs.get("aria-label") == "${esc(r)} (opens in a new tab)"