A Python port of the Invisible Internet Project (I2P)
1"""Tests for tools/security/check_results.py."""
2from __future__ import annotations
3
4import json
5import sys
6from pathlib import Path
7
8import pytest
9
10# Add tools/security to path so we can import check_results
11sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "tools" / "security"))
12import check_results
13
14
15class TestCheckBandit:
16 def test_missing_file(self, tmp_path: Path) -> None:
17 result = check_results.check_bandit(tmp_path / "nonexistent.json")
18 assert result == []
19
20 def test_empty_results(self, tmp_path: Path) -> None:
21 f = tmp_path / "bandit.json"
22 f.write_text(json.dumps({"results": []}))
23 assert check_results.check_bandit(f) == []
24
25 def test_low_severity_ignored(self, tmp_path: Path) -> None:
26 f = tmp_path / "bandit.json"
27 f.write_text(json.dumps({"results": [
28 {"issue_severity": "LOW", "issue_confidence": "HIGH",
29 "issue_text": "test", "filename": "x.py", "line_number": 1}
30 ]}))
31 assert check_results.check_bandit(f) == []
32
33 def test_high_severity_high_confidence(self, tmp_path: Path) -> None:
34 finding = {"issue_severity": "HIGH", "issue_confidence": "HIGH",
35 "issue_text": "shell=True", "filename": "x.py", "line_number": 5}
36 f = tmp_path / "bandit.json"
37 f.write_text(json.dumps({"results": [finding]}))
38 result = check_results.check_bandit(f)
39 assert len(result) == 1
40 assert result[0]["issue_text"] == "shell=True"
41
42 def test_high_severity_low_confidence_ignored(self, tmp_path: Path) -> None:
43 f = tmp_path / "bandit.json"
44 f.write_text(json.dumps({"results": [
45 {"issue_severity": "HIGH", "issue_confidence": "LOW",
46 "issue_text": "test", "filename": "x.py", "line_number": 1}
47 ]}))
48 assert check_results.check_bandit(f) == []
49
50 def test_critical_severity(self, tmp_path: Path) -> None:
51 f = tmp_path / "bandit.json"
52 f.write_text(json.dumps({"results": [
53 {"issue_severity": "CRITICAL", "issue_confidence": "MEDIUM",
54 "issue_text": "eval", "filename": "y.py", "line_number": 10}
55 ]}))
56 result = check_results.check_bandit(f)
57 assert len(result) == 1
58
59
60class TestCheckSemgrep:
61 def test_missing_file(self, tmp_path: Path) -> None:
62 assert check_results.check_semgrep(tmp_path / "nope.json") == []
63
64 def test_empty_results(self, tmp_path: Path) -> None:
65 f = tmp_path / "semgrep.json"
66 f.write_text(json.dumps({"results": []}))
67 assert check_results.check_semgrep(f) == []
68
69 def test_warning_ignored(self, tmp_path: Path) -> None:
70 f = tmp_path / "semgrep.json"
71 f.write_text(json.dumps({"results": [
72 {"check_id": "test", "path": "x.py",
73 "start": {"line": 1}, "extra": {"severity": "WARNING"}}
74 ]}))
75 assert check_results.check_semgrep(f) == []
76
77 def test_error_captured(self, tmp_path: Path) -> None:
78 f = tmp_path / "semgrep.json"
79 f.write_text(json.dumps({"results": [
80 {"check_id": "no-eval", "path": "x.py",
81 "start": {"line": 3}, "extra": {"severity": "ERROR", "message": "no eval"}}
82 ]}))
83 result = check_results.check_semgrep(f)
84 assert len(result) == 1
85
86
87class TestCheckPipAudit:
88 def test_missing_file(self, tmp_path: Path) -> None:
89 assert check_results.check_pip_audit(tmp_path / "nope.json") == []
90
91 def test_no_vulns(self, tmp_path: Path) -> None:
92 f = tmp_path / "audit.json"
93 f.write_text(json.dumps({"dependencies": [
94 {"name": "cryptography", "version": "42.0", "vulns": []}
95 ]}))
96 assert check_results.check_pip_audit(f) == []
97
98 def test_vuln_found(self, tmp_path: Path) -> None:
99 f = tmp_path / "audit.json"
100 f.write_text(json.dumps({"dependencies": [
101 {"name": "requests", "version": "2.28.0", "vulns": [
102 {"id": "CVE-2023-1234", "description": "bad thing"}
103 ]}
104 ]}))
105 result = check_results.check_pip_audit(f)
106 assert len(result) == 1
107 assert result[0]["name"] == "requests"
108
109 def test_build_tools_excluded(self, tmp_path: Path) -> None:
110 f = tmp_path / "audit.json"
111 f.write_text(json.dumps({"dependencies": [
112 {"name": "pip", "version": "24.0", "vulns": [
113 {"id": "CVE-2025-8869", "description": "tar issue"}
114 ]},
115 {"name": "setuptools", "version": "69.0", "vulns": [
116 {"id": "CVE-2024-5678", "description": "another issue"}
117 ]},
118 {"name": "wheel", "version": "0.45", "vulns": [
119 {"id": "CVE-2026-2049", "description": "path traversal"}
120 ]},
121 ]}))
122 assert check_results.check_pip_audit(f) == []
123
124
125class TestMain:
126 def test_all_clean(self, tmp_path: Path) -> None:
127 bandit_f = tmp_path / "bandit.json"
128 bandit_f.write_text(json.dumps({"results": []}))
129 audit_f = tmp_path / "audit.json"
130 audit_f.write_text(json.dumps({"dependencies": []}))
131
132 sys.argv = ["check_results.py",
133 "--bandit", str(bandit_f),
134 "--pip-audit", str(audit_f)]
135 assert check_results.main() == 0
136
137 def test_failure_on_high(self, tmp_path: Path) -> None:
138 bandit_f = tmp_path / "bandit.json"
139 bandit_f.write_text(json.dumps({"results": [
140 {"issue_severity": "HIGH", "issue_confidence": "HIGH",
141 "issue_text": "bad", "filename": "x.py", "line_number": 1}
142 ]}))
143
144 sys.argv = ["check_results.py", "--bandit", str(bandit_f)]
145 assert check_results.main() == 1