This commit is contained in:
Samir Hanna Verza 2026-10-14 20:05:23 +00:00 • committed by GitHub
commit a3f17127c8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 68 additions and 0 deletions

View file

@ -570,6 +570,16 @@ def _finding_flowables(
meta_bits = []
if vuln.get("cvss") is not None:
meta_bits.append(f"<b>CVSS</b> {_esc(vuln.get('cvss'))}")
confidence = str(vuln.get("confidence") or "").strip().title()
meta_bits.extend(
f"<b>{label}</b> {_esc(value)}"
for label, value in (
("Confidence", confidence),
("CWE", vuln.get("cwe")),
("CVE", vuln.get("cve")),
)
if value
)
meta_bits.extend(
f"<b>{key.title()}</b> {_esc(vuln.get(key))}"
for key in ("target", "endpoint", "method")
@ -592,6 +602,15 @@ def _finding_flowables(
poc_script = _strip_code_fence(vuln.get("poc_script_code"))
story.extend(_field_block(styles, "PoC script", poc_script, code=True))
story.extend(_field_block(styles, "Evidence", vuln.get("evidence"), code=True))
# What a reader needs to weigh the finding, as in the per-finding markdown report.
story.extend(_field_block(styles, "Counterevidence", vuln.get("counterevidence")))
story.extend(_field_block(styles, "Confidence rationale", vuln.get("confidence_rationale")))
story.extend(
_field_block(
styles, "What would change this severity", vuln.get("severity_change_conditions")
)
)
story.extend(_field_block(styles, "Assumptions", vuln.get("assumptions")))
remediation = vuln.get("remediation_steps")
if isinstance(remediation, list):

View file

@ -77,6 +77,55 @@ def test_generate_report_pdf_has_pdf_header(tmp_path: Path) -> None:
assert len(pdf) > 1000
def test_pdf_shows_what_a_reader_needs_to_weigh_a_finding(tmp_path: Path) -> None:
run_dir = _make_run(tmp_path)
vulns = json.loads((run_dir / "vulnerabilities.json").read_text(encoding="utf-8"))
vulns[0].update(
{
"confidence": "high",
"cwe": "CWE-89",
"cve": "CVE-2024-0001",
"counterevidence": "No WAF in front of the endpoint rewrote the payload.",
"confidence_rationale": "The injected sleep delayed the response three times.",
"severity_change_conditions": "Lower to high if the database user is read-only.",
"assumptions": "The staging database mirrors production.",
}
)
(run_dir / "vulnerabilities.json").write_text(json.dumps(vulns), encoding="utf-8")
text = " ".join(_pdf_text(generate_report_pdf(run_dir)).split())
for expected in (
"Confidence High",
"CWE CWE-89",
"CVE CVE-2024-0001",
"COUNTEREVIDENCE",
"No WAF in front of the endpoint rewrote the payload.",
"CONFIDENCE RATIONALE",
"The injected sleep delayed the response three times.",
"WHAT WOULD CHANGE THIS SEVERITY",
"Lower to high if the database user is read-only.",
"ASSUMPTIONS",
"The staging database mirrors production.",
):
assert expected in text
def test_pdf_leaves_out_the_trust_fields_a_finding_does_not_have(tmp_path: Path) -> None:
text = " ".join(_pdf_text(generate_report_pdf(_make_run(tmp_path))).split())
assert "SQL Injection" in text
for label in (
"Confidence",
"CWE",
"COUNTEREVIDENCE",
"CONFIDENCE RATIONALE",
"WHAT WOULD CHANGE THIS SEVERITY",
"ASSUMPTIONS",
):
assert label not in text
def test_generate_password_is_long_and_random() -> None:
first = generate_password()
second = generate_password()