"""Unit tests for the TenzaiValidationSummary dynamic-section results panel.""" from TenzaiValidationSummary import _severity_counts, build_summary_html FINDINGS = [{"finding": f"Medium finding {i}", "severity": "Medium", "detail": "x"} for i in range(6)] + [ {"finding": f"Low finding {i}", "severity": "Low", "detail": "y"} for i in range(3) ] COMPLETE_FIELDS = { "tenzaiassessmentstatus": "Complete", "tenzaiissuevalidated": "true", "tenzaicreditusage": 48.41, "tenzaireferenceurl": "https://app.tenzai.io/checks/abc", "tenzaifindings": FINDINGS, } def test_severity_counts_tallies_by_level(): counts = _severity_counts(FINDINGS) assert counts["Medium"] == 6 assert counts["Low"] == 3 assert counts["High"] == 0 assert counts["Critical"] == 0 def test_severity_counts_tolerates_emoji_and_case(): counts = _severity_counts([{"severity": "🟠 High"}, {"severity": "critical"}, {"severity": " Low "}]) assert counts["High"] == 1 assert counts["Critical"] == 1 assert counts["Low"] == 1 def test_build_summary_html_complete_validated(): html = build_summary_html(COMPLETE_FIELDS) assert "Tenzai validation" in html assert "Assessment complete" in html # The verdict lives in the "Result" stat cell (Figma has no hero headline). assert ">Result<" in html assert "Exposure Exploit<" not in html # the old Exploit cell is dropped (Figma stat card) # Severity renders as the Figma "Findings severity" badge row. assert "Findings severity" in html assert "48.41 ACU" in html assert "https://app.tenzai.io/checks/abc" in html assert "View in Tenzai" in html # The panel lists each finding (title) under a "Findings confirmed" section. assert "Findings confirmed" in html assert "Medium finding 0" in html assert "Low finding 0" in html # Findings render as expandable
disclosures (no separate grid). assert " (the entry sanitizer strips them). assert " block. assert "SCRIPT_MARKER" in html assert "import socket" in html assert " no 'Findings confirmed' section (that case is handled elsewhere).""" html = build_summary_html({**COMPLETE_FIELDS, "tenzaiissuevalidated": "false", "tenzaifindings": []}) assert "Findings confirmed" not in html def test_build_summary_html_not_validated(): html = build_summary_html({**COMPLETE_FIELDS, "tenzaiissuevalidated": "false"}) assert "Not Exploitable" in html # Must not use the ambiguous "validated" wording in the verdict. assert "Not Validated" not in html def test_build_summary_html_running_shows_status_not_verdict(): html = build_summary_html({"tenzaiassessmentstatus": "Running", "tenzaifindings": []}) assert "Running" in html assert "Exposure Result<" not in html # the stat card only renders on the completed panel def test_running_panel_links_to_live_agent_log(): """While running, the panel links to the live Agent Log when a reference URL is present (ENG-5200).""" url = "https://app.tenzai.io/apps/a/tests/t/log" html = build_summary_html({"tenzaiassessmentstatus": "Running", "tenzaireferenceurl": url, "tenzaifindings": []}) assert "Running" in html assert "follow the live" in html assert "View agent log" in html assert url in html assert 'target="_blank"' in html def test_running_panel_without_reference_has_no_link(): """No reference URL => the plain 'in progress' message and no link.""" html = build_summary_html({"tenzaiassessmentstatus": "Running", "tenzaifindings": []}) assert "Validation in progress" in html assert "View agent log" not in html assert " no Exposure assessment block.""" html = build_summary_html({**COMPLETE_FIELDS, "tenzaiissuevalidated": "false", "tenzaifindings": []}) assert "Exposure assessment" not in html def test_stat_card_cells_match_figma(): """The completed panel renders the Figma stat card: Result | Status | Findings | Started at | Credit usage (a fixed five-cell grid). No hero, no Exploit/Duration/Verdict cells.""" html = build_summary_html(COMPLETE_FIELDS) assert ">Result<" in html assert ">Status<" in html assert ">Findings<" in html assert ">Credit usage<" in html # Dropped by the Figma realignment: assert ">Verdict<" not in html assert ">Exploit<" not in html assert ">Duration<" not in html def test_result_cell_verdicts(): """The Result cell reads Exploitable / Not Exploitable / — for validated True / False / None.""" exploitable = build_summary_html(COMPLETE_FIELDS) assert "Exploitable" in exploitable assert "Not Exploitable" not in exploitable not_exploitable = build_summary_html({**COMPLETE_FIELDS, "tenzaiissuevalidated": "false"}) assert "Not Exploitable" in not_exploitable inconclusive = build_summary_html({**COMPLETE_FIELDS, "tenzaiissuevalidated": "", "tenzaifindings": []}) assert "—" in inconclusive # em-dash — no verdict def test_blocked_result_is_dash_not_not_exploitable(): """A BLOCKED exposure reached no verdict: Result is '—', never 'Not Exploitable' — even though the zero-finding SUCCESS gate makes `validated` False (Figma 88-2970). Status stays literal.""" html = build_summary_html( {**COMPLETE_FIELDS, "tenzaiissuevalidated": "false", "tenzaifindings": [], "tenzaiexposurestatus": "BLOCKED"} ) assert ">Result<" in html assert "—" in html # Result shows the em-dash assert "Not Exploitable" not in html # must NOT read as "assessed and found safe" assert ">Status<" in html assert "Blocked" in html # literal lead status def test_status_cell_shows_literal_lead_status(): """The Status cell shows the lead's LITERAL status (Materialized / Invalidated), not the old Tenzai-side translation (Exploited / Not affected).""" materialized = build_summary_html({**COMPLETE_FIELDS, "tenzaiexposurestatus": "MATERIALIZED"}) assert "Materialized" in materialized assert "Exploited" not in materialized invalidated = build_summary_html({**COMPLETE_FIELDS, "tenzaiissuevalidated": "false", "tenzaiexposurestatus": "INVALIDATED"}) assert "Invalidated" in invalidated assert "Not affected" not in invalidated def test_started_at_cell_formats_iso(): """A stored ISO start timestamp renders in the Figma 'Started at' cell as 'Mon Dth YYYY HH:MM' (UTC).""" html = build_summary_html({**COMPLETE_FIELDS, "tenzaistartedat": "2026-08-17T19:13:46.192631Z"}) assert ">Started at<" in html assert "Aug 17th 2026 19:13" in html def test_stat_card_is_a_fixed_five_cell_grid(): """All five Figma cells always render; absent Started at / Credit usage show '—' so the grid never collapses to fewer columns.""" html = build_summary_html({k: v for k, v in COMPLETE_FIELDS.items() if k != "tenzaicreditusage"}) for label in (">Result<", ">Status<", ">Findings<", ">Started at<", ">Credit usage<"): assert label in html # Started at and Credit usage are both absent here → both render the em-dash placeholder. assert html.count("—") >= 2 def test_started_at_cell_shows_dash_when_absent_or_unparseable(): """No start timestamp (or an unparseable one) => the Started at cell renders '—', not omitted.""" for fields in (COMPLETE_FIELDS, {**COMPLETE_FIELDS, "tenzaistartedat": "not-a-date"}): html = build_summary_html(fields) assert ">Started at<" in html assert "Aug " not in html # no formatted date leaked def test_status_cell_has_no_scan_status_fallback(): """An absent lead status renders '—', never the scan status 'Complete' (literal-status rule).""" html = build_summary_html({**COMPLETE_FIELDS, "tenzaiexposurestatus": ""}) assert ">Status<" in html # The Status cell must not print the assessment status word. assert "Complete<" not in html.split(">Status<", 1)[1][:200] def test_severity_badges_show_all_levels(): """The Figma 'Findings severity' badge row renders (all four levels, including zeros).""" html = build_summary_html(COMPLETE_FIELDS) assert "Findings severity" in html assert "Severity distribution" not in html # the old proportional-bar label is gone _MIXED_FINDINGS = [ {"finding": "SQLi in login", "severity": "High", "detail": "x", "cve": "CVE-2023-44487", "attribution": "own"}, { "finding": "CVE-2024-23897: Arbitrary File Read", "severity": "Critical", "detail": "y", "cve": "CVE-2024-23897", "attribution": "discovered", }, ] _UNATTRIBUTED_FINDINGS = [ { "finding": "CVE-2024-23897: Arbitrary File Read", "severity": "High", "detail": "z", "cve": "CVE-2024-23897", "attribution": "unattributed", }, ] def test_discovered_finding_renders_in_its_own_section(): """A discovered finding lists under 'Discovered during validation' with a Discovered tag, separate from the exposure's own 'Findings confirmed' section.""" html = build_summary_html({**COMPLETE_FIELDS, "tenzaifindings": _MIXED_FINDINGS}) assert "Findings confirmed" in html assert "Discovered during validation" in html assert ">Discovered<" in html # the per-finding tag # The own finding is above the discovered section; the discovered CVE is not in the own section. own_section = html.split("Findings confirmed", 1)[1].split("Discovered during validation", 1)[0] assert "SQLi in login" in own_section assert "CVE-2024-23897" not in own_section def test_unattributed_findings_render_in_unverified_section_and_are_not_counted(): """With no correlated lead, findings render under 'correlation unverified' with an Unverified tag, are NOT counted in the Findings cell (0 own), and are absent from the confirmed section.""" html = build_summary_html({**COMPLETE_FIELDS, "tenzaiissuevalidated": "", "tenzaifindings": _UNATTRIBUTED_FINDINGS}) assert "correlation unverified" in html assert ">Unverified<" in html assert "Findings confirmed" not in html # nothing is "own" findings_cell = html.split(">Findings<", 1)[1][:500] assert ">0" in findings_cell # zero own findings counted def test_findings_count_and_badges_exclude_non_own(): """The Findings cell and severity badges count only the exposure's own findings.""" html = build_summary_html({**COMPLETE_FIELDS, "tenzaifindings": _MIXED_FINDINGS}) # Findings cell reads 1 (one own finding), not 2. The value span follows the label span after a # long inline style, so slice generously past it. findings_cell = html.split(">Findings<", 1)[1][:500] assert ">1" in findings_cell assert ">2" not in findings_cell assert "Findings severity" in html def test_no_extra_sections_when_all_findings_are_own(): """With only own findings, neither the discovered nor the unverified section renders.""" html = build_summary_html(COMPLETE_FIELDS) assert "Findings confirmed" in html assert "Discovered during validation" not in html assert "correlation unverified" not in html def test_reference_and_agent_log_links_render_in_header(): """Reference + agent log are header actions, not strip cells; the agent log derives /findings -> /log.""" fields = {**COMPLETE_FIELDS, "tenzaireferenceurl": "https://app.tenzai.io/apps/a/tests/b/findings"} html = build_summary_html(fields) assert "View in Tenzai" in html assert "View agent log" in html assert "/apps/a/tests/b/log" in html def test_verification_step_payload_renders_as_monospace_block(): """A numbered step ending in ': `payload`' lifts the payload into its own code block.""" rationale = "## Description\n\n1. Probe the host with a payload: `GET /cgi-bin/.%2e/etc/passwd HTTP/1.1`" html = build_summary_html({**COMPLETE_FIELDS, "tenzaiissuerationale": rationale}) assert "GET /cgi-bin/.%2e/etc/passwd HTTP/1.1" in html def test_hard_broken_lines_share_one_paragraph(): """Markdown hard breaks (two trailing spaces) keep related lines in one
-joined paragraph, not one gapped paragraph per line — so header fields render tight like the Tenzai app.""" rationale = ( "## Description\n\n**CVE:** CVE-2024-23897 \n**Reported Severity:** Critical \n**Affected Asset:** http://192.0.2.10:80" ) html = build_summary_html({**COMPLETE_FIELDS, "tenzaiissuerationale": rationale}) # The three fields are one paragraph joined by
, not three separate margin'd divs. assert "CVE-2024-23897
" in html assert "Reported Severity: Critical
" in html def test_long_rationale_keeps_all_verification_steps(): """A full exposure assessment (all 5 verification steps + preconditions) is not truncated, and truncation — when it does occur — lands on a whole-line boundary, never mid-step.""" steps = "\n".join(f"{i}. Verification step number {i} with enough words to add length." for i in range(1, 6)) rationale = f"## Description\n\n**Vulnerability Description:** \nA long narrative. {'x ' * 200}\n\n{steps}" html = build_summary_html({**COMPLETE_FIELDS, "tenzaiissuerationale": rationale}) for i in range(1, 6): assert f"Verification step number {i}" in html def test_header_renders_logo_lockup(): """The compact header renders the Tenzai logo (base64 ) beside the 'Tenzai validation' wordline; both are present.""" from TenzaiValidationSummary import LOGO_DATA_URI html = build_summary_html(COMPLETE_FIELDS) assert "Tenzai validation" in html assert " is stripped by the entry sanitizer. assert "