-
Notifications
You must be signed in to change notification settings - Fork 111
Expand file tree
/
Copy pathdynamic_tester.py
More file actions
133 lines (107 loc) · 3.92 KB
/
Copy pathdynamic_tester.py
File metadata and controls
133 lines (107 loc) · 3.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
"""
Dynamic testing wrapper.
Runs Docker-isolated exploit tests against confirmed vulnerabilities.
Wraps ``utilities.dynamic_tester.run_dynamic_tests()``.
"""
import json
import os
import shutil
import sys
from core.schemas import DynamicTestStepResult, UsageInfo
from core import tracking
def run_tests(
pipeline_output_path: str,
output_dir: str,
max_retries: int = 3,
repo_path: str | None = None,
) -> DynamicTestStepResult:
"""Run dynamic exploit tests on confirmed vulnerabilities.
Requires Docker to be installed and running.
Args:
pipeline_output_path: Path to ``pipeline_output.json``.
output_dir: Directory for test results.
max_retries: Max retries per finding on error (default 3).
Returns:
DynamicTestStepResult with counts and paths.
Raises:
RuntimeError: If Docker is not available.
FileNotFoundError: If pipeline_output_path doesn't exist.
"""
# Check Docker availability
if not shutil.which("docker"):
raise RuntimeError(
"Docker is required for dynamic testing but was not found. "
"Install Docker and ensure it is running."
)
if not os.path.exists(pipeline_output_path):
raise FileNotFoundError(
f"pipeline_output.json not found: {pipeline_output_path}"
)
os.makedirs(output_dir, exist_ok=True)
# Check how many findings to test
with open(pipeline_output_path, encoding="utf-8") as f:
pipeline_data = json.load(f)
findings = pipeline_data.get("findings", [])
testable = [
f for f in findings
if f.get("stage2_verdict") in ("confirmed", "agreed", "vulnerable")
]
print(f"[Dynamic Test] {len(testable)} testable findings "
f"(out of {len(findings)} total)", file=sys.stderr)
if not testable:
results_path = os.path.join(output_dir, "dynamic_test_results.json")
with open(results_path, "w", encoding="utf-8") as f:
json.dump({"findings_tested": 0, "results": []}, f, indent=2)
return DynamicTestStepResult(
results_json_path=results_path,
findings_tested=0,
usage=tracking.get_usage(),
)
# Import and run
from utilities.dynamic_tester import run_dynamic_tests
print(f"[Dynamic Test] Running with max_retries={max_retries}...",
file=sys.stderr)
results = run_dynamic_tests(
pipeline_output_path,
output_dir,
max_retries=max_retries,
repo_path=repo_path,
)
# Count outcomes
confirmed = 0
not_reproduced = 0
blocked = 0
inconclusive = 0
errors = 0
for r in results:
status = r.get("status", "") if isinstance(r, dict) else getattr(r, "status", "")
if status == "CONFIRMED":
confirmed += 1
elif status == "NOT_REPRODUCED":
not_reproduced += 1
elif status == "BLOCKED":
blocked += 1
elif status == "INCONCLUSIVE":
inconclusive += 1
elif status == "ERROR":
errors += 1
results_json_path = os.path.join(output_dir, "dynamic_test_results.json")
results_md_path = os.path.join(output_dir, "dynamic_test_results.md")
# Check which output files exist (dynamic_tester may write them itself)
if not os.path.exists(results_md_path):
results_md_path = None
tracking.log_usage("Dynamic Test")
print(f"\n[Dynamic Test] Results: {confirmed} confirmed, "
f"{not_reproduced} not reproduced, {blocked} blocked, "
f"{inconclusive} inconclusive, {errors} errors", file=sys.stderr)
return DynamicTestStepResult(
results_json_path=results_json_path,
results_md_path=results_md_path,
findings_tested=len(testable),
confirmed=confirmed,
not_reproduced=not_reproduced,
blocked=blocked,
inconclusive=inconclusive,
errors=errors,
usage=tracking.get_usage(),
)