tests/test_scanner_python.py
2.8 KB · 92 lines · python Raw
1 """Scanner tests for Python fixtures."""
2
3 from __future__ import annotations
4
5 import os
6
7 from pqc_lint.scanner import Scanner
8
9
10 def _write(path, content):
11 path.parent.mkdir(parents=True, exist_ok=True)
12 path.write_text(content, encoding="utf-8")
13
14
15 def test_scanner_detects_rsa_generate(scan_tmpdir, sample_python_rsa):
16 p = scan_tmpdir / "rsa_sample.py"
17 _write(p, sample_python_rsa)
18 report = Scanner().scan_path(str(scan_tmpdir))
19 ids = {f.rule_id for f in report.findings}
20 assert "PQC001" in ids
21
22
23 def test_scanner_detects_ecdsa(scan_tmpdir, sample_python_ecdsa):
24 p = scan_tmpdir / "ec_sample.py"
25 _write(p, sample_python_ecdsa)
26 report = Scanner().scan_path(str(scan_tmpdir))
27 ids = {f.rule_id for f in report.findings}
28 assert "PQC002" in ids
29
30
31 def test_scanner_detects_ed25519(scan_tmpdir):
32 p = scan_tmpdir / "ed.py"
33 _write(p, (
34 "from cryptography.hazmat.primitives.asymmetric import ed25519\n"
35 "k = ed25519.Ed25519PrivateKey.generate()\n"
36 ))
37 report = Scanner().scan_path(str(scan_tmpdir))
38 ids = {f.rule_id for f in report.findings}
39 assert "PQC003" in ids
40
41
42 def test_scanner_detects_md5(scan_tmpdir):
43 p = scan_tmpdir / "h.py"
44 _write(p, "import hashlib\nd = hashlib.md5(b'x').hexdigest()\n")
45 report = Scanner().scan_path(str(scan_tmpdir))
46 ids = {f.rule_id for f in report.findings}
47 assert "PQC301" in ids
48
49
50 def test_scanner_clean_file_no_findings(scan_tmpdir, sample_python_clean):
51 p = scan_tmpdir / "clean.py"
52 _write(p, sample_python_clean)
53 report = Scanner().scan_path(str(scan_tmpdir))
54 assert report.findings == []
55 assert report.files_scanned == 1
56
57
58 def test_scanner_excludes_directories(scan_tmpdir, sample_python_rsa):
59 node = scan_tmpdir / "node_modules" / "evil.py"
60 _write(node, sample_python_rsa)
61 report = Scanner().scan_path(str(scan_tmpdir))
62 # File inside node_modules should not produce findings
63 paths = {f.file for f in report.findings}
64 assert not any("node_modules" in p for p in paths)
65
66
67 def test_scanner_respects_language_filter(scan_tmpdir, sample_python_rsa):
68 p = scan_tmpdir / "x.py"
69 _write(p, sample_python_rsa)
70 report = Scanner(languages=("go",)).scan_path(str(scan_tmpdir))
71 assert report.findings == []
72
73
74 def test_scanner_handles_nonexistent_file():
75 scanner = Scanner()
76 findings = scanner.scan_file("/totally/not/a/real/path.py")
77 assert findings == []
78
79
80 def test_scanner_skips_huge_files(scan_tmpdir):
81 p = scan_tmpdir / "big.py"
82 # Write >2MB of data. The pattern IS inside, but file should be skipped.
83 content = (
84 "import cryptography\n"
85 + ("x = 1" + " " * 20 + "\n") * 100_000
86 + "rsa.generate_private_key()\n"
87 )
88 _write(p, content)
89 assert os.path.getsize(p) > 2 * 1024 * 1024
90 report = Scanner().scan_path(str(scan_tmpdir))
91 assert report.findings == []
92