198 lines
5.7 KiB
Python
198 lines
5.7 KiB
Python
from pathlib import Path
|
|
|
|
from applepy.findings import Finding, Severity
|
|
from applepy.reporters.markdown import write_markdown_report
|
|
from applepy.reporters.xlsx import (
|
|
_evidence_chunks_for_excel,
|
|
_xlsx_cell_text,
|
|
write_xlsx_report,
|
|
)
|
|
|
|
|
|
def test_markdown_and_xlsx(tmp_path: Path) -> None:
|
|
f = Finding(
|
|
id="t-1",
|
|
title="Test finding",
|
|
category="Test",
|
|
severity=Severity.INFORMATIONAL,
|
|
description="Desc",
|
|
evidence="Ev",
|
|
worksheet="Test checks",
|
|
mitre_techniques=("T1082",),
|
|
)
|
|
cis = Finding(
|
|
id="cis-099",
|
|
title="CIS tab sample",
|
|
category="Compliance",
|
|
severity=Severity.INFORMATIONAL,
|
|
description="d",
|
|
evidence="e",
|
|
worksheet="CIS",
|
|
)
|
|
md = tmp_path / "r.md"
|
|
xl = tmp_path / "f.xlsx"
|
|
write_markdown_report(md, [f, cis], ["warn1"])
|
|
write_xlsx_report(xl, [f, cis])
|
|
md_text = md.read_text(encoding="utf-8")
|
|
assert md_text.startswith("# ApplePY")
|
|
assert "## Contents" in md_text
|
|
assert "[Executive summary](#executive-summary)" in md_text
|
|
assert "overarching themes" in md_text.lower()
|
|
assert "## Other findings" in md_text or "## Core platform and controls" in md_text
|
|
assert "Detail category:" in md_text
|
|
assert "Granular categories covered here:" in md_text
|
|
assert "#### Evidence" in md_text
|
|
assert "```" in md_text
|
|
assert "CIS macOS worksheet index" in md_text
|
|
assert "cis-099" in md_text
|
|
assert xl.is_file() and xl.stat().st_size > 200
|
|
from openpyxl import load_workbook
|
|
|
|
wb = load_workbook(xl)
|
|
assert wb.sheetnames[0] == "Summary"
|
|
assert wb.sheetnames[1] == "Findings list"
|
|
assert "CIS" in wb.sheetnames
|
|
assert wb["CIS"]["A2"].value == "cis-099"
|
|
fl = wb["Findings list"]
|
|
assert fl["A1"].value == "ID"
|
|
assert fl["E1"].value == "Detail worksheet"
|
|
assert fl.max_row == 1
|
|
# Summary sheet must contain the worksheet index section
|
|
summary_ws = wb["Summary"]
|
|
col_a_vals = [summary_ws.cell(row=r, column=1).value for r in range(1, summary_ws.max_row + 1)]
|
|
assert "Findings list (non-informational)" in col_a_vals
|
|
assert "CIS" in col_a_vals
|
|
|
|
|
|
def test_xlsx_cell_text_strips_control_chars() -> None:
|
|
raw = "a\x00b\x08c"
|
|
assert _xlsx_cell_text(raw) == "a b c"
|
|
|
|
|
|
def test_evidence_chunks_split_for_excel_limit() -> None:
|
|
long_ev = "X" * 40000
|
|
chunks = _evidence_chunks_for_excel(long_ev)
|
|
assert len(chunks) >= 2
|
|
assert all(len(c) <= 32767 for c in chunks)
|
|
assert "".join(chunks) == _xlsx_cell_text(long_ev)
|
|
|
|
|
|
def test_xlsx_sorts_by_severity_descending(tmp_path: Path) -> None:
|
|
from openpyxl import load_workbook
|
|
|
|
lo = Finding(
|
|
id="a-low",
|
|
title="Later",
|
|
category="Test",
|
|
severity=Severity.INFORMATIONAL,
|
|
description="d",
|
|
evidence="e",
|
|
worksheet="Zebra",
|
|
)
|
|
hi = Finding(
|
|
id="z-high",
|
|
title="First",
|
|
category="Test",
|
|
severity=Severity.HIGH,
|
|
description="d",
|
|
evidence="e",
|
|
worksheet="Zebra",
|
|
)
|
|
xl = tmp_path / "sort.xlsx"
|
|
write_xlsx_report(xl, [lo, hi])
|
|
wb = load_workbook(xl)
|
|
assert wb.sheetnames[:2] == ["Summary", "Findings list"]
|
|
fl = wb["Findings list"]
|
|
assert fl.max_row == 2
|
|
assert fl["A2"].value == "z-high"
|
|
assert fl["C2"].value == "high"
|
|
ws = wb["Zebra"]
|
|
assert ws["A2"].value == "z-high"
|
|
assert ws["A3"].value == "a-low"
|
|
|
|
|
|
def test_xlsx_findings_list_includes_non_informational_only(tmp_path: Path) -> None:
|
|
from openpyxl import load_workbook
|
|
|
|
info = Finding(
|
|
id="i-1",
|
|
title="Noise",
|
|
category="Test",
|
|
severity=Severity.INFORMATIONAL,
|
|
description="d",
|
|
evidence="e",
|
|
worksheet="Alpha",
|
|
)
|
|
med = Finding(
|
|
id="m-1",
|
|
title="Material",
|
|
category="Test",
|
|
severity=Severity.MEDIUM,
|
|
description="d",
|
|
evidence="e",
|
|
worksheet="Alpha",
|
|
)
|
|
xl = tmp_path / "fl.xlsx"
|
|
write_xlsx_report(xl, [info, med])
|
|
wb = load_workbook(xl)
|
|
fl = wb["Findings list"]
|
|
assert fl.max_row == 2
|
|
assert fl["A2"].value == "m-1"
|
|
assert fl["E2"].value == "Alpha"
|
|
|
|
|
|
def test_xlsx_severity_row_has_distinct_fill(tmp_path: Path) -> None:
|
|
from openpyxl import load_workbook
|
|
|
|
high_f = Finding(
|
|
id="h-sev",
|
|
title="High item",
|
|
category="Test",
|
|
severity=Severity.HIGH,
|
|
description="d",
|
|
evidence="e",
|
|
worksheet="Alpha",
|
|
)
|
|
low_f = Finding(
|
|
id="l-sev",
|
|
title="Low item",
|
|
category="Test",
|
|
severity=Severity.LOW,
|
|
description="d",
|
|
evidence="e",
|
|
worksheet="Alpha",
|
|
)
|
|
xl = tmp_path / "sevfill.xlsx"
|
|
write_xlsx_report(xl, [high_f, low_f])
|
|
wb = load_workbook(xl)
|
|
ws = wb["Alpha"]
|
|
hi_fill = ws["A2"].fill
|
|
lo_fill = ws["A3"].fill
|
|
assert hi_fill.fill_type == "solid"
|
|
assert lo_fill.fill_type == "solid"
|
|
assert hi_fill.start_color.rgb != lo_fill.start_color.rgb
|
|
fl = wb["Findings list"]
|
|
assert fl["A2"].fill.start_color.rgb == hi_fill.start_color.rgb
|
|
|
|
|
|
def test_xlsx_long_evidence_writes_continuation_rows(tmp_path: Path) -> None:
|
|
from openpyxl import load_workbook
|
|
|
|
long_ev = "Y" * 35000
|
|
f = Finding(
|
|
id="long-1",
|
|
title="Long evidence",
|
|
category="Test",
|
|
severity=Severity.INFORMATIONAL,
|
|
description="d",
|
|
evidence=long_ev,
|
|
worksheet="Evidence sheet",
|
|
)
|
|
xl = tmp_path / "big.xlsx"
|
|
write_xlsx_report(xl, [f])
|
|
wb = load_workbook(xl)
|
|
assert wb.sheetnames[1] == "Findings list"
|
|
assert wb["Findings list"].max_row == 1
|
|
ws = wb["Evidence sheet"]
|
|
assert ws.max_row >= 3
|