opencode-api-security-testing 3.0.10 → 3.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +74 -0
- package/SKILL.md +1797 -0
- package/core/advanced_recon.py +788 -0
- package/core/agentic_analyzer.py +445 -0
- package/core/analyzers/api_parser.py +210 -0
- package/core/analyzers/response_analyzer.py +212 -0
- package/core/analyzers/sensitive_finder.py +184 -0
- package/core/api_fuzzer.py +422 -0
- package/core/api_interceptor.py +525 -0
- package/core/api_parser.py +955 -0
- package/core/browser_tester.py +479 -0
- package/core/cloud_storage_tester.py +1330 -0
- package/core/collectors/__init__.py +23 -0
- package/core/collectors/api_path_finder.py +300 -0
- package/core/collectors/browser_collect.py +645 -0
- package/core/collectors/browser_collector.py +411 -0
- package/core/collectors/http_client.py +111 -0
- package/core/collectors/js_collector.py +490 -0
- package/core/collectors/js_parser.py +780 -0
- package/core/collectors/url_collector.py +319 -0
- package/core/context_manager.py +682 -0
- package/core/deep_api_tester_v35.py +844 -0
- package/core/deep_api_tester_v55.py +366 -0
- package/core/dynamic_api_analyzer.py +532 -0
- package/core/http_client.py +179 -0
- package/core/models.py +296 -0
- package/core/orchestrator.py +890 -0
- package/core/prerequisite.py +227 -0
- package/core/reasoning_engine.py +1042 -0
- package/core/response_classifier.py +606 -0
- package/core/runner.py +938 -0
- package/core/scan_engine.py +599 -0
- package/core/skill_executor.py +435 -0
- package/core/skill_executor_v2.py +670 -0
- package/core/skill_executor_v3.py +704 -0
- package/core/smart_analyzer.py +687 -0
- package/core/strategy_pool.py +707 -0
- package/core/testers/auth_tester.py +264 -0
- package/core/testers/idor_tester.py +200 -0
- package/core/testers/sqli_tester.py +211 -0
- package/core/testing_loop.py +655 -0
- package/core/utils/base_path_dict.py +255 -0
- package/core/utils/payload_lib.py +167 -0
- package/core/utils/ssrf_detector.py +220 -0
- package/core/verifiers/vuln_verifier.py +536 -0
- package/package.json +1 -1
- package/references/README.md +72 -0
- package/references/asset-discovery.md +119 -0
- package/references/fuzzing-patterns.md +129 -0
- package/references/graphql-guidance.md +108 -0
- package/references/intake.md +84 -0
- package/references/pua-agent.md +192 -0
- package/references/report-template.md +156 -0
- package/references/rest-guidance.md +76 -0
- package/references/severity-model.md +76 -0
- package/references/test-matrix.md +86 -0
- package/references/validation.md +78 -0
- package/references/vulnerabilities/01-sqli-tests.md +1128 -0
- package/references/vulnerabilities/02-user-enum-tests.md +423 -0
- package/references/vulnerabilities/03-jwt-tests.md +499 -0
- package/references/vulnerabilities/04-idor-tests.md +362 -0
- package/references/vulnerabilities/05-sensitive-data-tests.md +466 -0
- package/references/vulnerabilities/06-biz-logic-tests.md +501 -0
- package/references/vulnerabilities/07-security-config-tests.md +511 -0
- package/references/vulnerabilities/08-brute-force-tests.md +457 -0
- package/references/vulnerabilities/09-vulnerability-chains.md +465 -0
- package/references/vulnerabilities/10-auth-tests.md +537 -0
- package/references/vulnerabilities/11-graphql-tests.md +355 -0
- package/references/vulnerabilities/12-ssrf-tests.md +396 -0
- package/references/vulnerabilities/README.md +148 -0
- package/references/workflows.md +192 -0
|
@@ -0,0 +1,655 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Insight-Driven Testing Loop - 洞察驱动测试循环
|
|
4
|
+
|
|
5
|
+
实现:
|
|
6
|
+
- 观察 → 推理 → 策略 → 执行 → 验证闭环
|
|
7
|
+
- 结果反馈机制
|
|
8
|
+
- 收敛检测
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import time
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
from typing import Dict, List, Set, Optional, Any, Callable, Generator, Tuple
|
|
14
|
+
from dataclasses import dataclass, field
|
|
15
|
+
from enum import Enum
|
|
16
|
+
from collections import defaultdict
|
|
17
|
+
import logging
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class LoopState(Enum):
|
|
23
|
+
"""循环状态"""
|
|
24
|
+
IDLE = "idle"
|
|
25
|
+
RUNNING = "running"
|
|
26
|
+
PAUSED = "paused"
|
|
27
|
+
CONVERGED = "converged"
|
|
28
|
+
BLOCKED = "blocked"
|
|
29
|
+
COMPLETED = "completed"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class TestAction:
|
|
34
|
+
"""测试动作"""
|
|
35
|
+
id: str
|
|
36
|
+
type: str
|
|
37
|
+
target: str
|
|
38
|
+
params: Dict[str, Any] = field(default_factory=dict)
|
|
39
|
+
|
|
40
|
+
priority: int = 0
|
|
41
|
+
timeout: float = 30.0
|
|
42
|
+
expected_outcome: Optional[str] = None
|
|
43
|
+
|
|
44
|
+
executed_at: Optional[datetime] = None
|
|
45
|
+
result: Optional[str] = None
|
|
46
|
+
actual_outcome: Optional[str] = None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass
|
|
50
|
+
class ActionResult:
|
|
51
|
+
"""动作执行结果"""
|
|
52
|
+
action: TestAction
|
|
53
|
+
success: bool
|
|
54
|
+
response_time: float
|
|
55
|
+
|
|
56
|
+
status_code: Optional[int] = None
|
|
57
|
+
content_length: int = 0
|
|
58
|
+
content_preview: str = ""
|
|
59
|
+
actual_outcome: Optional[str] = None
|
|
60
|
+
|
|
61
|
+
deviation: float = 0.0
|
|
62
|
+
new_insights: List[Any] = field(default_factory=list)
|
|
63
|
+
|
|
64
|
+
error: Optional[str] = None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass
|
|
68
|
+
class Validation:
|
|
69
|
+
"""验证结果"""
|
|
70
|
+
is_expected: bool
|
|
71
|
+
deviation: float
|
|
72
|
+
|
|
73
|
+
new_insights: List[Any] = field(default_factory=list)
|
|
74
|
+
strategy_adjustment: Optional[Dict] = None
|
|
75
|
+
|
|
76
|
+
confidence_impact: float = 0.0
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@dataclass
|
|
80
|
+
class LoopProgress:
|
|
81
|
+
"""循环进度"""
|
|
82
|
+
state: LoopState
|
|
83
|
+
|
|
84
|
+
iterations: int = 0
|
|
85
|
+
max_iterations: int = 100
|
|
86
|
+
|
|
87
|
+
actions_executed: int = 0
|
|
88
|
+
actions_total: int = 0
|
|
89
|
+
|
|
90
|
+
insights_generated: int = 0
|
|
91
|
+
vulnerabilities_found: int = 0
|
|
92
|
+
|
|
93
|
+
convergence_score: float = 0.0
|
|
94
|
+
effectiveness: float = 0.0
|
|
95
|
+
|
|
96
|
+
elapsed_time: float = 0.0
|
|
97
|
+
estimated_remaining: float = 0.0
|
|
98
|
+
|
|
99
|
+
blockers: List[str] = field(default_factory=list)
|
|
100
|
+
suggestions: List[str] = field(default_factory=list)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class Validator:
|
|
104
|
+
"""
|
|
105
|
+
验证器
|
|
106
|
+
|
|
107
|
+
职责:
|
|
108
|
+
- 预期对比验证
|
|
109
|
+
- 偏差计算
|
|
110
|
+
- 新洞察生成
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
def __init__(self):
|
|
114
|
+
self.validation_history: List[Validation] = []
|
|
115
|
+
self.expected_outcomes: Dict[str, str] = {}
|
|
116
|
+
|
|
117
|
+
def set_expected_outcome(self, action_id: str, outcome: str):
|
|
118
|
+
"""设置预期结果"""
|
|
119
|
+
self.expected_outcomes[action_id] = outcome
|
|
120
|
+
|
|
121
|
+
def validate_result(self, action: TestAction, result: ActionResult) -> Validation:
|
|
122
|
+
"""
|
|
123
|
+
验证结果是否符合预期
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
action: 执行的动作
|
|
127
|
+
result: 执行结果
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
验证结果
|
|
131
|
+
"""
|
|
132
|
+
expected = self.expected_outcomes.get(action.id, action.expected_outcome)
|
|
133
|
+
|
|
134
|
+
if expected is None:
|
|
135
|
+
return Validation(
|
|
136
|
+
is_expected=True,
|
|
137
|
+
deviation=0.0
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
is_expected = False
|
|
141
|
+
deviation = 1.0
|
|
142
|
+
|
|
143
|
+
if result.error:
|
|
144
|
+
is_expected = expected == 'error'
|
|
145
|
+
deviation = 1.0 if not is_expected else 0.0
|
|
146
|
+
|
|
147
|
+
elif result.status_code:
|
|
148
|
+
expected_codes = self._parse_expected_code(expected)
|
|
149
|
+
is_expected = result.status_code in expected_codes
|
|
150
|
+
|
|
151
|
+
if expected_codes:
|
|
152
|
+
deviation = 0.0 if is_expected else 1.0
|
|
153
|
+
|
|
154
|
+
elif result.content_preview:
|
|
155
|
+
is_expected = self._check_content_match(expected, result.content_preview)
|
|
156
|
+
deviation = 0.0 if is_expected else 0.5
|
|
157
|
+
|
|
158
|
+
validation = Validation(
|
|
159
|
+
is_expected=is_expected,
|
|
160
|
+
deviation=deviation,
|
|
161
|
+
confidence_impact=1.0 - deviation
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
self.validation_history.append(validation)
|
|
165
|
+
|
|
166
|
+
return validation
|
|
167
|
+
|
|
168
|
+
def _parse_expected_code(self, expected: str) -> Set[int]:
|
|
169
|
+
"""解析预期状态码"""
|
|
170
|
+
codes = set()
|
|
171
|
+
|
|
172
|
+
if '2xx' in expected or 'success' in expected.lower():
|
|
173
|
+
codes.update([200, 201, 202, 204])
|
|
174
|
+
elif '3xx' in expected or 'redirect' in expected.lower():
|
|
175
|
+
codes.update([301, 302, 303, 307, 308])
|
|
176
|
+
elif '4xx' in expected or 'client_error' in expected.lower():
|
|
177
|
+
codes.update([400, 401, 403, 404])
|
|
178
|
+
elif '5xx' in expected or 'server_error' in expected.lower():
|
|
179
|
+
codes.update([500, 502, 503])
|
|
180
|
+
else:
|
|
181
|
+
try:
|
|
182
|
+
codes.add(int(expected))
|
|
183
|
+
except ValueError:
|
|
184
|
+
pass
|
|
185
|
+
|
|
186
|
+
return codes
|
|
187
|
+
|
|
188
|
+
def _check_content_match(self, expected: str, content: str) -> bool:
|
|
189
|
+
"""检查内容匹配"""
|
|
190
|
+
content_lower = content.lower()
|
|
191
|
+
expected_lower = expected.lower()
|
|
192
|
+
|
|
193
|
+
if expected_lower in content_lower:
|
|
194
|
+
return True
|
|
195
|
+
|
|
196
|
+
keywords = expected_lower.split()
|
|
197
|
+
return any(kw in content_lower for kw in keywords)
|
|
198
|
+
|
|
199
|
+
def check_convergence(self, recent_validations: List[Validation]) -> Tuple[bool, float]:
|
|
200
|
+
"""
|
|
201
|
+
检查测试是否收敛
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
(是否收敛, 收敛分数 0-1)
|
|
205
|
+
"""
|
|
206
|
+
if len(recent_validations) < 3:
|
|
207
|
+
return False, 0.0
|
|
208
|
+
|
|
209
|
+
recent = recent_validations[-10:]
|
|
210
|
+
|
|
211
|
+
deviations = [v.deviation for v in recent]
|
|
212
|
+
avg_deviation = sum(deviations) / len(deviations)
|
|
213
|
+
|
|
214
|
+
deviation_trend = 0.0
|
|
215
|
+
if len(recent) >= 5:
|
|
216
|
+
first_half = sum(deviations[:len(deviations)//2]) / (len(deviations)//2)
|
|
217
|
+
second_half = sum(deviations[len(deviations)//2:]) / (len(deviations) - len(deviations)//2)
|
|
218
|
+
deviation_trend = first_half - second_half
|
|
219
|
+
|
|
220
|
+
convergence_score = 1.0 - avg_deviation
|
|
221
|
+
|
|
222
|
+
if deviation_trend > 0.1:
|
|
223
|
+
convergence_score *= 0.8
|
|
224
|
+
|
|
225
|
+
is_converged = convergence_score > 0.8 and len(recent_validations) >= 5
|
|
226
|
+
|
|
227
|
+
return is_converged, convergence_score
|
|
228
|
+
|
|
229
|
+
def get_false_negative_risk(self) -> float:
|
|
230
|
+
"""
|
|
231
|
+
评估假阴性风险
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
风险分数 0-1 (越高风险越大)
|
|
235
|
+
"""
|
|
236
|
+
if len(self.validation_history) < 3:
|
|
237
|
+
return 0.3
|
|
238
|
+
|
|
239
|
+
recent = self.validation_history[-10:]
|
|
240
|
+
|
|
241
|
+
high_deviation_count = sum(1 for v in recent if v.deviation > 0.5)
|
|
242
|
+
high_deviation_ratio = high_deviation_count / len(recent)
|
|
243
|
+
|
|
244
|
+
consistent_no_finding = all(v.is_expected and v.deviation < 0.1 for v in recent[-5:])
|
|
245
|
+
|
|
246
|
+
risk = high_deviation_ratio * 0.5
|
|
247
|
+
|
|
248
|
+
if consistent_no_finding:
|
|
249
|
+
risk += 0.3
|
|
250
|
+
|
|
251
|
+
return min(risk, 1.0)
|
|
252
|
+
|
|
253
|
+
def generate_validation_report(self) -> Dict:
|
|
254
|
+
"""生成验证报告"""
|
|
255
|
+
recent = self.validation_history[-20:] if self.validation_history else []
|
|
256
|
+
|
|
257
|
+
if not recent:
|
|
258
|
+
return {
|
|
259
|
+
'total_validations': 0,
|
|
260
|
+
'convergence_score': 0.0,
|
|
261
|
+
'false_negative_risk': 0.3,
|
|
262
|
+
'recommendation': 'insufficient_data'
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
is_converged, convergence_score = self.check_convergence(recent)
|
|
266
|
+
fn_risk = self.get_false_negative_risk()
|
|
267
|
+
|
|
268
|
+
recommendations = []
|
|
269
|
+
if fn_risk > 0.5:
|
|
270
|
+
recommendations.append("假阴性风险较高,建议扩大测试范围")
|
|
271
|
+
if convergence_score < 0.5:
|
|
272
|
+
recommendations.append("收敛度低,可能存在遗漏")
|
|
273
|
+
if is_converged:
|
|
274
|
+
recommendations.append("测试已收敛,可以生成报告")
|
|
275
|
+
|
|
276
|
+
return {
|
|
277
|
+
'total_validations': len(self.validation_history),
|
|
278
|
+
'recent_validations': len(recent),
|
|
279
|
+
'expected_count': sum(1 for v in recent if v.is_expected),
|
|
280
|
+
'unexpected_count': sum(1 for v in recent if not v.is_expected),
|
|
281
|
+
'convergence_score': convergence_score,
|
|
282
|
+
'is_converged': is_converged,
|
|
283
|
+
'false_negative_risk': fn_risk,
|
|
284
|
+
'recommendations': recommendations
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
class InsightDrivenLoop:
|
|
289
|
+
"""
|
|
290
|
+
洞察驱动测试循环
|
|
291
|
+
|
|
292
|
+
实现:观察 → 推理 → 策略 → 执行 → 验证 闭环
|
|
293
|
+
"""
|
|
294
|
+
|
|
295
|
+
def __init__(
|
|
296
|
+
self,
|
|
297
|
+
reasoner: Any,
|
|
298
|
+
strategist: Any,
|
|
299
|
+
context_manager: Any,
|
|
300
|
+
executor: Optional[Callable] = None
|
|
301
|
+
):
|
|
302
|
+
self.reasoner = reasoner
|
|
303
|
+
self.strategist = strategist
|
|
304
|
+
self.context_manager = context_manager
|
|
305
|
+
self.executor = executor
|
|
306
|
+
|
|
307
|
+
self.validator = Validator()
|
|
308
|
+
|
|
309
|
+
self.state = LoopState.IDLE
|
|
310
|
+
self.progress = LoopProgress(state=LoopState.IDLE)
|
|
311
|
+
|
|
312
|
+
self.action_queue: List[TestAction] = []
|
|
313
|
+
self.execution_history: List[ActionResult] = []
|
|
314
|
+
|
|
315
|
+
self.callbacks: Dict[str, List[Callable]] = defaultdict(list)
|
|
316
|
+
|
|
317
|
+
def on(self, event: str, callback: Callable):
|
|
318
|
+
"""注册回调"""
|
|
319
|
+
self.callbacks[event].append(callback)
|
|
320
|
+
|
|
321
|
+
def _emit(self, event: str, data: Any):
|
|
322
|
+
"""触发回调"""
|
|
323
|
+
for callback in self.callbacks.get(event, []):
|
|
324
|
+
try:
|
|
325
|
+
callback(data)
|
|
326
|
+
except Exception as e:
|
|
327
|
+
logger.warning(f"Callback error ({event}): {e}")
|
|
328
|
+
|
|
329
|
+
def add_action(self, action: TestAction):
|
|
330
|
+
"""添加测试动作"""
|
|
331
|
+
self.action_queue.append(action)
|
|
332
|
+
self.action_queue.sort(key=lambda a: a.priority, reverse=True)
|
|
333
|
+
self.progress.actions_total += 1
|
|
334
|
+
|
|
335
|
+
def add_actions_batch(self, actions: List[TestAction]):
|
|
336
|
+
"""批量添加测试动作"""
|
|
337
|
+
for action in actions:
|
|
338
|
+
self.add_action(action)
|
|
339
|
+
|
|
340
|
+
def run(
|
|
341
|
+
self,
|
|
342
|
+
max_iterations: int = 100,
|
|
343
|
+
max_duration: float = 3600.0,
|
|
344
|
+
convergence_threshold: float = 0.8
|
|
345
|
+
) -> Dict:
|
|
346
|
+
"""
|
|
347
|
+
运行测试循环
|
|
348
|
+
|
|
349
|
+
Args:
|
|
350
|
+
max_iterations: 最大迭代次数
|
|
351
|
+
max_duration: 最大运行时长(秒)
|
|
352
|
+
convergence_threshold: 收敛阈值
|
|
353
|
+
|
|
354
|
+
Returns:
|
|
355
|
+
测试结果
|
|
356
|
+
"""
|
|
357
|
+
self.state = LoopState.RUNNING
|
|
358
|
+
self.progress = LoopProgress(
|
|
359
|
+
state=LoopState.RUNNING,
|
|
360
|
+
max_iterations=max_iterations
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
start_time = time.time()
|
|
364
|
+
|
|
365
|
+
logger.info("Starting insight-driven testing loop")
|
|
366
|
+
|
|
367
|
+
try:
|
|
368
|
+
while self.state == LoopState.RUNNING:
|
|
369
|
+
if self._should_terminate(max_iterations, max_duration):
|
|
370
|
+
break
|
|
371
|
+
|
|
372
|
+
self._iterate()
|
|
373
|
+
|
|
374
|
+
self.progress.elapsed_time = time.time() - start_time
|
|
375
|
+
|
|
376
|
+
self._emit('iteration', self.progress)
|
|
377
|
+
|
|
378
|
+
except Exception as e:
|
|
379
|
+
logger.error(f"Loop error: {e}")
|
|
380
|
+
self.state = LoopState.BLOCKED
|
|
381
|
+
self.progress.blockers.append(str(e))
|
|
382
|
+
|
|
383
|
+
finally:
|
|
384
|
+
score = 0.0
|
|
385
|
+
if self.state == LoopState.RUNNING:
|
|
386
|
+
is_converged, score = self.validator.check_convergence(
|
|
387
|
+
self.validator.validation_history[-10:]
|
|
388
|
+
)
|
|
389
|
+
if is_converged:
|
|
390
|
+
self.state = LoopState.CONVERGED
|
|
391
|
+
else:
|
|
392
|
+
self.state = LoopState.COMPLETED
|
|
393
|
+
|
|
394
|
+
self.progress.state = self.state
|
|
395
|
+
self.progress.convergence_score = score
|
|
396
|
+
|
|
397
|
+
return self._generate_report()
|
|
398
|
+
|
|
399
|
+
def _should_terminate(self, max_iterations: int, max_duration: float) -> bool:
|
|
400
|
+
"""检查是否应该终止"""
|
|
401
|
+
if self.progress.iterations >= max_iterations:
|
|
402
|
+
logger.info("Max iterations reached")
|
|
403
|
+
return True
|
|
404
|
+
|
|
405
|
+
if self.progress.elapsed_time >= max_duration:
|
|
406
|
+
logger.info("Max duration reached")
|
|
407
|
+
return True
|
|
408
|
+
|
|
409
|
+
if self.progress.blockers:
|
|
410
|
+
logger.warning(f"Blocked: {self.progress.blockers}")
|
|
411
|
+
return True
|
|
412
|
+
|
|
413
|
+
is_converged, _ = self.validator.check_convergence(
|
|
414
|
+
self.validator.validation_history[-5:]
|
|
415
|
+
)
|
|
416
|
+
if is_converged:
|
|
417
|
+
logger.info("Convergence achieved")
|
|
418
|
+
return True
|
|
419
|
+
|
|
420
|
+
return False
|
|
421
|
+
|
|
422
|
+
def _iterate(self):
|
|
423
|
+
"""执行一次迭代"""
|
|
424
|
+
self.progress.iterations += 1
|
|
425
|
+
|
|
426
|
+
if not self.action_queue:
|
|
427
|
+
if self.progress.insights_generated > 0:
|
|
428
|
+
self._generate_more_actions()
|
|
429
|
+
else:
|
|
430
|
+
self.state = LoopState.COMPLETED
|
|
431
|
+
return
|
|
432
|
+
|
|
433
|
+
action = self.action_queue.pop(0)
|
|
434
|
+
|
|
435
|
+
result = self._execute_action(action)
|
|
436
|
+
|
|
437
|
+
self.execution_history.append(result)
|
|
438
|
+
self.progress.actions_executed += 1
|
|
439
|
+
|
|
440
|
+
validation = self.validator.validate_result(action, result)
|
|
441
|
+
|
|
442
|
+
if not validation.is_expected:
|
|
443
|
+
self._handle_unexpected_result(action, result, validation)
|
|
444
|
+
|
|
445
|
+
insights = self._process_insights(result, validation)
|
|
446
|
+
|
|
447
|
+
self.progress.insights_generated += len(insights)
|
|
448
|
+
|
|
449
|
+
self._update_strategy(insights, result)
|
|
450
|
+
|
|
451
|
+
self._emit('action_completed', {
|
|
452
|
+
'action': action,
|
|
453
|
+
'result': result,
|
|
454
|
+
'validation': validation,
|
|
455
|
+
'insights': insights
|
|
456
|
+
})
|
|
457
|
+
|
|
458
|
+
def _execute_action(self, action: TestAction) -> ActionResult:
|
|
459
|
+
"""执行动作"""
|
|
460
|
+
action.executed_at = datetime.now()
|
|
461
|
+
|
|
462
|
+
if self.executor:
|
|
463
|
+
try:
|
|
464
|
+
response = self.executor(action)
|
|
465
|
+
|
|
466
|
+
return ActionResult(
|
|
467
|
+
action=action,
|
|
468
|
+
success=True,
|
|
469
|
+
response_time=response.get('time', 0.0),
|
|
470
|
+
status_code=response.get('status', 0),
|
|
471
|
+
content_length=len(response.get('content', '')),
|
|
472
|
+
content_preview=response.get('content', '')[:200]
|
|
473
|
+
)
|
|
474
|
+
|
|
475
|
+
except Exception as e:
|
|
476
|
+
return ActionResult(
|
|
477
|
+
action=action,
|
|
478
|
+
success=False,
|
|
479
|
+
response_time=0.0,
|
|
480
|
+
error=str(e)
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
return ActionResult(
|
|
484
|
+
action=action,
|
|
485
|
+
success=False,
|
|
486
|
+
response_time=0.0,
|
|
487
|
+
error="No executor configured"
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
def _process_insights(self, result: ActionResult, validation: Validation) -> List[Any]:
|
|
491
|
+
"""处理洞察"""
|
|
492
|
+
insights = list(validation.new_insights)
|
|
493
|
+
|
|
494
|
+
if result.content_preview:
|
|
495
|
+
response_data = {
|
|
496
|
+
'url': result.action.target,
|
|
497
|
+
'method': result.action.type,
|
|
498
|
+
'status_code': result.status_code or 0,
|
|
499
|
+
'content_type': '',
|
|
500
|
+
'content': result.content_preview,
|
|
501
|
+
'response_time': result.response_time
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
new_insights = self.reasoner.observe_and_reason(response_data)
|
|
505
|
+
insights.extend(new_insights)
|
|
506
|
+
|
|
507
|
+
return insights
|
|
508
|
+
|
|
509
|
+
def _handle_unexpected_result(
|
|
510
|
+
self,
|
|
511
|
+
action: TestAction,
|
|
512
|
+
result: ActionResult,
|
|
513
|
+
validation: Validation
|
|
514
|
+
):
|
|
515
|
+
"""处理意外结果"""
|
|
516
|
+
logger.info(f"Unexpected result for {action.type}: {result.actual_outcome}")
|
|
517
|
+
|
|
518
|
+
if validation.deviation > 0.5:
|
|
519
|
+
self.progress.suggestions.append(
|
|
520
|
+
f"高偏差动作: {action.type} -> 考虑调整策略"
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
def _generate_more_actions(self):
|
|
524
|
+
"""生成更多测试动作"""
|
|
525
|
+
if self.progress.insights_generated == 0:
|
|
526
|
+
self.state = LoopState.COMPLETED
|
|
527
|
+
return
|
|
528
|
+
|
|
529
|
+
context = {
|
|
530
|
+
'insights': self.reasoner.insight_store.get_active(),
|
|
531
|
+
'endpoints': self.context_manager.context.discovered_endpoints,
|
|
532
|
+
'phase': self.context_manager.context.current_phase.value
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
strategy = self.strategist.get_best_strategy_for_context(context)
|
|
536
|
+
|
|
537
|
+
for action_def in strategy.actions:
|
|
538
|
+
action = TestAction(
|
|
539
|
+
id=f"generated_{int(time.time() * 1000)}",
|
|
540
|
+
type=action_def.type,
|
|
541
|
+
target="",
|
|
542
|
+
params=action_def.params,
|
|
543
|
+
priority=action_def.priority,
|
|
544
|
+
timeout=action_def.timeout
|
|
545
|
+
)
|
|
546
|
+
self.add_action(action)
|
|
547
|
+
|
|
548
|
+
def _update_strategy(self, insights: List[Any], result: ActionResult):
|
|
549
|
+
"""更新策略"""
|
|
550
|
+
if not insights:
|
|
551
|
+
return
|
|
552
|
+
|
|
553
|
+
context = {
|
|
554
|
+
'insights': insights,
|
|
555
|
+
'result_success': result.success
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
try:
|
|
559
|
+
self.strategist.record_execution(
|
|
560
|
+
self.strategist.current_plan.primary_strategy.id if self.strategist.current_plan else 'unknown',
|
|
561
|
+
{
|
|
562
|
+
'effectiveness': 1.0 - result.deviation if hasattr(result, 'deviation') else 0.5,
|
|
563
|
+
'vulnerabilities_found': self.progress.vulnerabilities_found
|
|
564
|
+
}
|
|
565
|
+
)
|
|
566
|
+
except Exception as e:
|
|
567
|
+
logger.warning(f"Strategy update error: {e}")
|
|
568
|
+
|
|
569
|
+
def pause(self):
|
|
570
|
+
"""暂停循环"""
|
|
571
|
+
self.state = LoopState.PAUSED
|
|
572
|
+
self.progress.state = LoopState.PAUSED
|
|
573
|
+
|
|
574
|
+
def resume(self):
|
|
575
|
+
"""恢复循环"""
|
|
576
|
+
if self.state == LoopState.PAUSED:
|
|
577
|
+
self.state = LoopState.RUNNING
|
|
578
|
+
|
|
579
|
+
def stop(self):
|
|
580
|
+
"""停止循环"""
|
|
581
|
+
self.state = LoopState.COMPLETED
|
|
582
|
+
self.progress.state = LoopState.COMPLETED
|
|
583
|
+
|
|
584
|
+
def get_progress(self) -> LoopProgress:
|
|
585
|
+
"""获取进度"""
|
|
586
|
+
return self.progress
|
|
587
|
+
|
|
588
|
+
def _generate_report(self) -> Dict:
|
|
589
|
+
"""生成报告"""
|
|
590
|
+
validation_report = self.validator.generate_validation_report()
|
|
591
|
+
|
|
592
|
+
return {
|
|
593
|
+
'state': self.state.value,
|
|
594
|
+
'progress': {
|
|
595
|
+
'iterations': self.progress.iterations,
|
|
596
|
+
'actions_executed': self.progress.actions_executed,
|
|
597
|
+
'actions_total': self.progress.actions_total,
|
|
598
|
+
'insights_generated': self.progress.insights_generated,
|
|
599
|
+
'vulnerabilities_found': self.progress.vulnerabilities_found,
|
|
600
|
+
'elapsed_time': self.progress.elapsed_time,
|
|
601
|
+
'convergence_score': self.progress.convergence_score
|
|
602
|
+
},
|
|
603
|
+
'validation': validation_report,
|
|
604
|
+
'blockers': self.progress.blockers,
|
|
605
|
+
'suggestions': self.progress.suggestions
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
def create_test_loop(
|
|
610
|
+
reasoner: Any,
|
|
611
|
+
strategist: Any,
|
|
612
|
+
context_manager: Any,
|
|
613
|
+
executor: Optional[Callable] = None
|
|
614
|
+
) -> InsightDrivenLoop:
|
|
615
|
+
"""创建测试循环工厂函数"""
|
|
616
|
+
return InsightDrivenLoop(reasoner, strategist, context_manager, executor)
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
if __name__ == "__main__":
|
|
620
|
+
from core.reasoning_engine import create_reasoner
|
|
621
|
+
from core.strategy_pool import create_strategist
|
|
622
|
+
from core.context_manager import create_context_manager
|
|
623
|
+
|
|
624
|
+
reasoner = create_reasoner()
|
|
625
|
+
strategist = create_strategist()
|
|
626
|
+
context_manager = create_context_manager("http://example.com")
|
|
627
|
+
|
|
628
|
+
loop = create_test_loop(reasoner, strategist, context_manager)
|
|
629
|
+
|
|
630
|
+
loop.add_action(TestAction(
|
|
631
|
+
id="test_1",
|
|
632
|
+
type="GET",
|
|
633
|
+
target="http://example.com/api",
|
|
634
|
+
priority=10,
|
|
635
|
+
expected_outcome="2xx"
|
|
636
|
+
))
|
|
637
|
+
|
|
638
|
+
loop.add_action(TestAction(
|
|
639
|
+
id="test_2",
|
|
640
|
+
type="GET",
|
|
641
|
+
target="http://example.com/login",
|
|
642
|
+
priority=8,
|
|
643
|
+
expected_outcome="html"
|
|
644
|
+
))
|
|
645
|
+
|
|
646
|
+
print("Test loop initialized")
|
|
647
|
+
print(f"Actions in queue: {len(loop.action_queue)}")
|
|
648
|
+
|
|
649
|
+
report = loop.run(max_iterations=2)
|
|
650
|
+
|
|
651
|
+
print("\nLoop Report:")
|
|
652
|
+
print(f"State: {report['state']}")
|
|
653
|
+
print(f"Iterations: {report['progress']['iterations']}")
|
|
654
|
+
print(f"Actions executed: {report['progress']['actions_executed']}")
|
|
655
|
+
print(f"Validation: {report['validation']}")
|