audit-labs/audit-report
Turn audit-tools evidence packages into control-mapped, auditor-ready reports.
clone: git clone https://gitbay.org/audit-labs/audit-report.git
v0.1.0: tests/test_reporters.py · raw
1"""Tests for the report renderers and the CLI entry point."""
2
3import json
4from pathlib import Path
5
6import pytest
7
8from audit_report import __version__, reporters
9from audit_report.cli import main
10from audit_report.engine import evaluate
11from audit_report.loader import load_package
12from audit_report.rules import load_ruleset
13
14FIXTURES = Path(__file__).parent / "fixtures"
15RULESETS = Path("audit_report/rulesets")
16
17
18def _report(pkg_name="aws_audit_acme_2026-01-01", ruleset="aws.yaml"):
19 pkg = load_package(FIXTURES / pkg_name)
20 rs = load_ruleset(RULESETS / ruleset)
21 findings = evaluate(pkg, rs)
22 return reporters.build_report(pkg, findings, rs)
23
24
25def test_markdown_render_contains_sections():
26 md = reporters.render(_report(), "md")
27 assert "# Evidence Report — acme" in md
28 assert "## Control coverage" in md
29 assert "## Findings" in md
30 assert "aws.network.no-open-ssh" in md
31
32
33def test_html_render_is_self_contained():
34 html = reporters.render(_report(), "html")
35 assert html.startswith("<!doctype html>")
36 assert "<style>" in html
37 # No external resource references.
38 assert "http://" not in html and "https://" not in html
39 assert "src=" not in html
40
41
42def test_json_render_roundtrips():
43 data = json.loads(reporters.render(_report(), "json"))
44 assert data["platform"] == "aws"
45 assert data["subject"] == "acme"
46 ids = {f["id"]: f["status"] for f in data["findings"]}
47 assert ids["aws.iam.console-mfa"] == "fail"
48
49
50def test_json_stamps_tool_and_ruleset_provenance():
51 data = json.loads(reporters.render(_report(), "json"))
52 assert data["tool"] == {"name": "audit-report", "version": __version__}
53 rs = data["ruleset"]
54 assert rs["name"] == "AWS ITGC ruleset"
55 assert rs["platform"] == "aws"
56 assert rs["version"] == "2026.08.0"
57 assert len(rs["sha256"]) == 64 # full SHA-256 hex digest of the ruleset file
58
59
60def test_markdown_shows_ruleset_provenance():
61 md = reporters.render(_report(), "md")
62 assert "**Tool:** audit-report" in md
63 assert "sha256:" in md
64
65
66def test_html_escapes_evidence(tmp_path):
67 pkg_dir = tmp_path / "aws_audit_x_2026-01-01"
68 pkg_dir.mkdir()
69 (pkg_dir / "open_security_groups.csv").write_text(
70 "region,group_id,group_name,protocol,from_port,to_port,open_to\n"
71 "us-east-1,sg-1,<script>x</script>,tcp,22,22,0.0.0.0/0\n",
72 encoding="utf-8",
73 )
74 pkg = load_package(pkg_dir)
75 report = reporters.build_report(pkg, evaluate(pkg, load_ruleset(RULESETS / "aws.yaml")))
76 html = reporters.render(report, "html")
77 assert "<script>x</script>" not in html
78 assert "<script>" in html
79
80
81def test_unknown_format_raises():
82 with pytest.raises(ValueError, match="unknown format"):
83 reporters.render(_report(), "pdf")
84
85
86def test_cli_writes_files_and_exit_code(tmp_path):
87 out = tmp_path / "out"
88 code = main(
89 [
90 str(FIXTURES / "aws_audit_acme_2026-01-01"),
91 "--format",
92 "md,html,json",
93 "--out",
94 str(out),
95 "--fail-on",
96 "high",
97 ]
98 )
99 assert (out / "report.md").exists()
100 assert (out / "report.html").exists()
101 assert (out / "report.json").exists()
102 # The AWS fixture has high-severity failures (open SSH), so exit is non-zero.
103 assert code == 1
104
105
106def test_cli_fail_on_none_is_zero(tmp_path):
107 code = main(
108 [str(FIXTURES / "aws_audit_acme_2026-01-01"), "--format", "json", "--out", str(tmp_path)]
109 )
110 assert code == 0
111
112
113def test_cli_missing_package_returns_2():
114 assert main([str(FIXTURES / "nope")]) == 2