opencode-api-security-testing 2.1.0 → 2.1.2

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.
Files changed (61) hide show
  1. package/SKILL.md +1797 -0
  2. package/core/advanced_recon.py +788 -0
  3. package/core/agentic_analyzer.py +445 -0
  4. package/core/analyzers/api_parser.py +210 -0
  5. package/core/analyzers/response_analyzer.py +212 -0
  6. package/core/analyzers/sensitive_finder.py +184 -0
  7. package/core/api_fuzzer.py +422 -0
  8. package/core/api_interceptor.py +525 -0
  9. package/core/api_parser.py +955 -0
  10. package/core/browser_tester.py +479 -0
  11. package/core/cloud_storage_tester.py +1330 -0
  12. package/core/collectors/__init__.py +23 -0
  13. package/core/collectors/api_path_finder.py +300 -0
  14. package/core/collectors/browser_collect.py +645 -0
  15. package/core/collectors/browser_collector.py +411 -0
  16. package/core/collectors/http_client.py +111 -0
  17. package/core/collectors/js_collector.py +490 -0
  18. package/core/collectors/js_parser.py +780 -0
  19. package/core/collectors/url_collector.py +319 -0
  20. package/core/context_manager.py +682 -0
  21. package/core/deep_api_tester_v35.py +844 -0
  22. package/core/deep_api_tester_v55.py +366 -0
  23. package/core/dynamic_api_analyzer.py +532 -0
  24. package/core/http_client.py +179 -0
  25. package/core/models.py +296 -0
  26. package/core/orchestrator.py +890 -0
  27. package/core/prerequisite.py +227 -0
  28. package/core/reasoning_engine.py +1042 -0
  29. package/core/response_classifier.py +606 -0
  30. package/core/runner.py +938 -0
  31. package/core/scan_engine.py +599 -0
  32. package/core/skill_executor.py +435 -0
  33. package/core/skill_executor_v2.py +670 -0
  34. package/core/skill_executor_v3.py +704 -0
  35. package/core/smart_analyzer.py +687 -0
  36. package/core/strategy_pool.py +707 -0
  37. package/core/testers/auth_tester.py +264 -0
  38. package/core/testers/idor_tester.py +200 -0
  39. package/core/testers/sqli_tester.py +211 -0
  40. package/core/testing_loop.py +655 -0
  41. package/core/utils/base_path_dict.py +255 -0
  42. package/core/utils/payload_lib.py +167 -0
  43. package/core/utils/ssrf_detector.py +220 -0
  44. package/core/verifiers/vuln_verifier.py +536 -0
  45. package/package.json +17 -13
  46. package/references/asset-discovery.md +119 -612
  47. package/references/graphql-guidance.md +65 -641
  48. package/references/intake.md +84 -0
  49. package/references/report-template.md +131 -38
  50. package/references/rest-guidance.md +55 -526
  51. package/references/severity-model.md +52 -264
  52. package/references/test-matrix.md +65 -263
  53. package/references/validation.md +53 -400
  54. package/scripts/postinstall.js +46 -0
  55. package/agents/cyber-supervisor.md +0 -55
  56. package/agents/probing-miner.md +0 -42
  57. package/agents/resource-specialist.md +0 -31
  58. package/commands/api-security-testing-scan.md +0 -59
  59. package/commands/api-security-testing-test.md +0 -49
  60. package/commands/api-security-testing.md +0 -72
  61. package/tsconfig.json +0 -17
@@ -0,0 +1,890 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Agentic Orchestrator - 智能编排器 v3.0
4
+
5
+ 集成组件:
6
+ - ReasoningEngine: 多层级推理引擎
7
+ - ContextManager: 上下文管理器
8
+ - StrategyPool: 策略池系统
9
+ - InsightDrivenLoop: 洞察驱动测试循环
10
+
11
+ 核心理念:
12
+ 1. 每个模块输出后,理解其含义
13
+ 2. 根据当前状态,动态调整策略
14
+ 3. 不是按顺序执行,而是按需执行
15
+ 4. 失败时理解原因,而不是简单地继续
16
+ """
17
+
18
+ import re
19
+ import time
20
+ import json
21
+ from datetime import datetime
22
+ from typing import Dict, List, Set, Tuple, Optional, Any, Callable
23
+ from dataclasses import dataclass, field, asdict
24
+ from enum import Enum
25
+ import logging
26
+
27
+ try:
28
+ import requests
29
+ HAS_REQUESTS = True
30
+ except ImportError:
31
+ HAS_REQUESTS = False
32
+
33
+ try:
34
+ from .reasoning_engine import (
35
+ Reasoner, Insight, InsightType, UnderstandingLevel,
36
+ Observation, Finding, InsightStore, create_reasoner
37
+ )
38
+ from .context_manager import (
39
+ ContextManager, GlobalContext, TechStackContext,
40
+ NetworkContext, SecurityContext, ContentContext,
41
+ Endpoint, TestPhase, RateLimitStatus, ExposureLevel,
42
+ DataClassification, create_context_manager
43
+ )
44
+ from .strategy_pool import (
45
+ StrategyPool, Strategy, Strategist, StrategyContext,
46
+ Action, Condition, StrategyState, create_strategy_pool, create_strategist
47
+ )
48
+ from .testing_loop import (
49
+ InsightDrivenLoop, Validator, LoopState, TestAction,
50
+ ActionResult, Validation, LoopProgress, create_test_loop
51
+ )
52
+ except ImportError:
53
+ from reasoning_engine import (
54
+ Reasoner, Insight, InsightType, UnderstandingLevel,
55
+ Observation, Finding, InsightStore, create_reasoner
56
+ )
57
+ from context_manager import (
58
+ ContextManager, GlobalContext, TechStackContext,
59
+ NetworkContext, SecurityContext, ContentContext,
60
+ Endpoint, TestPhase, RateLimitStatus, ExposureLevel,
61
+ DataClassification, create_context_manager
62
+ )
63
+ from strategy_pool import (
64
+ StrategyPool, Strategy, Strategist, StrategyContext,
65
+ Action, Condition, StrategyState, create_strategy_pool, create_strategist
66
+ )
67
+ from testing_loop import (
68
+ InsightDrivenLoop, Validator, LoopState, TestAction,
69
+ ActionResult, Validation, LoopProgress, create_test_loop
70
+ )
71
+
72
+
73
+ logger = logging.getLogger(__name__)
74
+
75
+
76
+ class StageStatus(Enum):
77
+ """阶段状态"""
78
+ PENDING = "pending"
79
+ RUNNING = "running"
80
+ COMPLETED = "completed"
81
+ FAILED = "failed"
82
+ SKIPPED = "skipped"
83
+ ADAPTED = "adapted"
84
+
85
+
86
+ @dataclass
87
+ class StageResult:
88
+ """阶段结果"""
89
+ name: str
90
+ status: StageStatus
91
+ duration: float = 0.0
92
+
93
+ data: Any = None
94
+
95
+ insights: List[Dict] = field(default_factory=list)
96
+
97
+ problems: List[str] = field(default_factory=list)
98
+
99
+ suggestions: List[str] = field(default_factory=list)
100
+
101
+ next_stages: List[str] = field(default_factory=list)
102
+
103
+ def summary(self) -> str:
104
+ return f"{self.name}: {self.status.value} ({self.duration:.1f}s)"
105
+
106
+ def to_dict(self) -> Dict:
107
+ return {
108
+ 'name': self.name,
109
+ 'status': self.status.value,
110
+ 'duration': self.duration,
111
+ 'insights': self.insights,
112
+ 'problems': self.problems,
113
+ 'suggestions': self.suggestions,
114
+ 'next_stages': self.next_stages
115
+ }
116
+
117
+
118
+ class EnhancedAgenticOrchestrator:
119
+ """
120
+ 增强型 Agentic Orchestrator v3.0
121
+
122
+ 集成组件:
123
+ - ReasoningEngine: 多层级推理引擎
124
+ - ContextManager: 上下文管理器
125
+ - StrategyPool: 策略池系统
126
+ - InsightDrivenLoop: 洞察驱动测试循环
127
+ """
128
+
129
+ def __init__(self, target: str, session: 'requests.Session' = None):
130
+ self.target = target
131
+ self.session = session or (requests.Session() if HAS_REQUESTS else None)
132
+
133
+ self.reasoner = create_reasoner()
134
+ self.context_manager = create_context_manager(target)
135
+ self.strategy_pool = create_strategy_pool()
136
+ self.strategist = create_strategist(self.strategy_pool)
137
+ self.testing_loop: Optional[InsightDrivenLoop] = None
138
+
139
+ self.stage_results: Dict[str, StageResult] = {}
140
+
141
+ self.callbacks: Dict[str, List[Callable]] = {
142
+ 'stage_start': [],
143
+ 'stage_complete': [],
144
+ 'insight_generated': [],
145
+ 'strategy_changed': [],
146
+ 'blocker_detected': [],
147
+ 'vulnerability_found': [],
148
+ }
149
+
150
+ self._configure_callbacks()
151
+
152
+ def _configure_callbacks(self):
153
+ """配置内部回调"""
154
+
155
+ def on_insight(insight_data):
156
+ logger.info(f"Insight generated: {insight_data.get('content', '')[:50]}")
157
+
158
+ def on_blocker(blocker_data):
159
+ logger.warning(f"Blocker detected: {blocker_data}")
160
+
161
+ def on_vuln(vuln_data):
162
+ logger.info(f"Vulnerability found: {vuln_data}")
163
+
164
+ self.callbacks['insight_generated'].append(on_insight)
165
+ self.callbacks['blocker_detected'].append(on_blocker)
166
+ self.callbacks['vulnerability_found'].append(on_vuln)
167
+
168
+ def on(self, event: str, callback: Callable):
169
+ """注册事件回调"""
170
+ if event in self.callbacks:
171
+ self.callbacks[event].append(callback)
172
+
173
+ def _emit(self, event: str, data: Any):
174
+ """触发事件"""
175
+ for callback in self.callbacks.get(event, []):
176
+ try:
177
+ callback(data)
178
+ except Exception as e:
179
+ logger.warning(f"Callback error for {event}: {e}")
180
+
181
+ def execute(
182
+ self,
183
+ max_iterations: int = 100,
184
+ max_duration: float = 3600.0,
185
+ enable_fuzzing: bool = True,
186
+ enable_testing: bool = True
187
+ ) -> Dict:
188
+ """
189
+ 执行增强型编排
190
+
191
+ Args:
192
+ max_iterations: 最大迭代次数
193
+ max_duration: 最大运行时长(秒)
194
+ enable_fuzzing: 是否启用 fuzzing
195
+ enable_testing: 是否启用漏洞测试
196
+ """
197
+ print("=" * 70)
198
+ print(" Enhanced Agentic Security Testing v3.0")
199
+ print("=" * 70)
200
+ print(f"Target: {self.target}")
201
+ print(f"Components: Reasoner + ContextManager + StrategyPool + TestingLoop")
202
+ print("=" * 70)
203
+
204
+ start_time = time.time()
205
+
206
+ self.context_manager.set_phase(TestPhase.RECON)
207
+
208
+ self._stage_reconnaissance()
209
+
210
+ self._stage_context_analysis()
211
+
212
+ if self._has_blockers():
213
+ self._handle_blockers()
214
+ if self._should_abort():
215
+ return self._generate_report(time.time() - start_time, "blocked")
216
+
217
+ self._stage_discovery()
218
+
219
+ self._stage_reasoning()
220
+
221
+ if enable_fuzzing and not self._has_blockers():
222
+ self._stage_fuzzing()
223
+ else:
224
+ self.stage_results['fuzzing'] = StageResult(
225
+ name="fuzzing",
226
+ status=StageStatus.SKIPPED,
227
+ suggestions=["后端不可达,跳过 fuzzing"] if self._has_blockers() else []
228
+ )
229
+
230
+ if enable_testing and not self._has_blockers():
231
+ self._stage_testing()
232
+ else:
233
+ self.stage_results['testing'] = StageResult(
234
+ name="testing",
235
+ status=StageStatus.SKIPPED,
236
+ suggestions=["后端不可达,跳过测试"] if self._has_blockers() else []
237
+ )
238
+
239
+ duration = time.time() - start_time
240
+
241
+ return self._generate_report(duration)
242
+
243
+ def _stage_reconnaissance(self):
244
+ """阶段 1: 侦察"""
245
+ print("\n[*] Phase 1: 侦察")
246
+
247
+ self._emit('stage_start', {'stage': 'reconnaissance'})
248
+ start = time.time()
249
+
250
+ test_urls = [
251
+ self.target,
252
+ f"{self.target}/login",
253
+ f"{self.target}/admin",
254
+ f"{self.target}/api",
255
+ f"{self.target}/api-docs",
256
+ f"{self.target}/swagger.json",
257
+ ]
258
+
259
+ for url in test_urls:
260
+ try:
261
+ resp = self.session.get(url, timeout=10, allow_redirects=True)
262
+
263
+ response_data = {
264
+ 'url': url,
265
+ 'method': 'GET',
266
+ 'status_code': resp.status_code,
267
+ 'content_type': resp.headers.get('Content-Type', ''),
268
+ 'content': resp.text[:1000],
269
+ 'headers': dict(resp.headers),
270
+ 'response_time': resp.elapsed.total_seconds(),
271
+ 'source': 'recon'
272
+ }
273
+
274
+ insights = self.reasoner.observe_and_reason(response_data)
275
+
276
+ for insight in insights:
277
+ self._process_insight(insight)
278
+
279
+ self.context_manager.update_network_status(True)
280
+ self.context_manager.update_network_status(
281
+ True,
282
+ reason=f"{resp.status_code} from {url}"
283
+ )
284
+
285
+ if 'tech_fingerprints' in response_data:
286
+ pass
287
+
288
+ except Exception as e:
289
+ logger.warning(f"Recon error for {url}: {e}")
290
+ self.context_manager.update_network_status(False, reason=str(e))
291
+
292
+ duration = time.time() - start
293
+
294
+ insights_data = [i.to_dict() for i in self.reasoner.insight_store.get_active()]
295
+
296
+ self.stage_results['reconnaissance'] = StageResult(
297
+ name="reconnaissance",
298
+ status=StageStatus.COMPLETED,
299
+ duration=duration,
300
+ insights=insights_data
301
+ )
302
+
303
+ print(f" 完成 ({duration:.1f}s)")
304
+ print(f" 观察数: {len(self.reasoner.get_observations())}")
305
+ print(f" 洞察数: {len(insights_data)}")
306
+
307
+ self._emit('stage_complete', {'stage': 'reconnaissance', 'duration': duration})
308
+
309
+ def _stage_context_analysis(self):
310
+ """阶段 2: 上下文分析"""
311
+ print("\n[*] Phase 2: 上下文分析")
312
+
313
+ self._emit('stage_start', {'stage': 'context_analysis'})
314
+ start = time.time()
315
+
316
+ for obs in self.reasoner.get_observations()[-10:]:
317
+ if obs.tech_fingerprints:
318
+ self.context_manager.update_tech_stack(obs.tech_fingerprints)
319
+
320
+ if obs.spa_indicators:
321
+ self.context_manager.set_spa_mode(True)
322
+
323
+ if obs.api_indicators:
324
+ for url in obs.url:
325
+ if 'swagger' in url.lower() or 'api-docs' in url.lower():
326
+ self.context_manager.add_swagger_url(url)
327
+
328
+ if obs.security_indicators:
329
+ if any('error' in ind for ind in obs.security_indicators):
330
+ self.context_manager.add_error_leak('|'.join(obs.security_indicators))
331
+
332
+ duration = time.time() - start
333
+
334
+ context_summary = self.context_manager.get_summary()
335
+
336
+ self.stage_results['context_analysis'] = StageResult(
337
+ name="context_analysis",
338
+ status=StageStatus.COMPLETED,
339
+ duration=duration,
340
+ data=context_summary,
341
+ insights=[{
342
+ 'type': 'context_summary',
343
+ 'content': f"技术栈: {context_summary.get('tech_stack', {})}"
344
+ }]
345
+ )
346
+
347
+ print(f" 完成 ({duration:.1f}s)")
348
+ print(f" 技术栈: {context_summary.get('tech_stack', {})}")
349
+ print(f" SPA: {context_summary.get('content', {}).get('is_spa', False)}")
350
+
351
+ self._emit('stage_complete', {'stage': 'context_analysis'})
352
+
353
+ def _discover_js_files(self) -> List[str]:
354
+ """发现目标站点的 JS 文件"""
355
+ import re
356
+
357
+ js_files = []
358
+
359
+ try:
360
+ # 获取首页 HTML
361
+ resp = self.session.get(self.target, timeout=10)
362
+ html = resp.text
363
+
364
+ # 提取所有 JS 文件 URL
365
+ patterns = [
366
+ r'<script[^>]+src=["\']([^"\']+\.js[^"\']*)["\']',
367
+ r'src=["\']([^"\']+\.js[^"\']*)["\']',
368
+ ]
369
+
370
+ for pattern in patterns:
371
+ matches = re.findall(pattern, html)
372
+ for match in matches:
373
+ if match.startswith('//'):
374
+ js_url = 'https:' + match
375
+ elif match.startswith('/'):
376
+ js_url = self.target.rstrip('/') + match
377
+ elif match.startswith('http'):
378
+ js_url = match
379
+ else:
380
+ continue
381
+
382
+ # 只添加本地 JS 文件
383
+ if self.target.replace('http://', '').replace('https://', '').split('/')[0] in js_url:
384
+ if js_url not in js_files:
385
+ js_files.append(js_url)
386
+
387
+ # 尝试常见 JS 路径
388
+ common_js_paths = [
389
+ '/static/js/app.js',
390
+ '/static/js/main.js',
391
+ '/js/app.js',
392
+ '/js/main.js',
393
+ '/assets/js/app.js',
394
+ ]
395
+
396
+ for path in common_js_paths:
397
+ url = self.target.rstrip('/') + path
398
+ try:
399
+ resp = self.session.head(url, timeout=5)
400
+ if resp.status_code == 200:
401
+ if url not in js_files:
402
+ js_files.append(url)
403
+ except:
404
+ pass
405
+
406
+ except Exception as e:
407
+ logger.warning(f"JS 文件发现失败: {e}")
408
+
409
+ return js_files
410
+
411
+ def _stage_discovery(self):
412
+ """阶段 3: API 发现 (整合 V35JSAnalyzer)"""
413
+ print("\n[*] Phase 3: API 发现")
414
+
415
+ self._emit('stage_start', {'stage': 'discovery'})
416
+ start = time.time()
417
+
418
+ discovered_paths = set()
419
+
420
+ # 方式 1: 从 reasoning engine 获取 (原有逻辑)
421
+ for obs in self.reasoner.get_observations():
422
+ if obs.api_indicators:
423
+ discovered_paths.add(obs.url)
424
+
425
+ # 方式 2: Swagger/API文档发现 (原有逻辑)
426
+ if self.context_manager.context.content.swagger_urls:
427
+ for url in self.context_manager.context.content.swagger_urls:
428
+ endpoint = Endpoint(
429
+ path=url,
430
+ method='GET',
431
+ source='swagger',
432
+ score=8,
433
+ is_high_value=True
434
+ )
435
+ self.context_manager.add_discovered_endpoint(endpoint)
436
+
437
+ # 方式 3: V35JSAnalyzer JS 文件分析 (新增整合)
438
+ print(" [V35JSAnalyzer] 开始 JS 文件分析...")
439
+ try:
440
+ # 延迟导入避免循环依赖
441
+ from core.deep_api_tester_v55 import V35JSAnalyzer
442
+
443
+ js_analyzer = V35JSAnalyzer(self.target, self.session)
444
+
445
+ # 发现 JS 文件
446
+ js_files = self._discover_js_files()
447
+ print(f" [V35JSAnalyzer] 发现 {len(js_files)} 个 JS 文件")
448
+
449
+ # 分析每个 JS 文件
450
+ js_endpoints = []
451
+ for js_url in js_files:
452
+ result = js_analyzer.analyze_js(js_url)
453
+ js_endpoints.extend(result['endpoints'])
454
+ if result['endpoints']:
455
+ print(f" [V35JSAnalyzer] {js_url.split('/')[-1]}: 发现 {len(result['endpoints'])} 个端点")
456
+
457
+ # 去重并添加到 context
458
+ seen = set()
459
+ for ep in js_endpoints:
460
+ # 标准化路径
461
+ path = ep.url.replace(self.target.rstrip('/'), '')
462
+ key = f'{ep.method}:{path}'
463
+ if key not in seen and len(path) > 1:
464
+ seen.add(key)
465
+ endpoint = Endpoint(
466
+ path=path,
467
+ method=ep.method,
468
+ source=f'v35js_{ep.discovered_by}',
469
+ score=7,
470
+ is_high_value=True
471
+ )
472
+ self.context_manager.add_discovered_endpoint(endpoint)
473
+ discovered_paths.add(path)
474
+
475
+ print(f" [V35JSAnalyzer] 共发现 {len(js_endpoints)} 个 API 端点")
476
+
477
+ except Exception as e:
478
+ logger.warning(f"V35JSAnalyzer 分析失败: {e}")
479
+ print(f" [WARN] V35JSAnalyzer 分析失败: {e}")
480
+
481
+ # 方式 4: 基于上下文的智能猜测 (新增)
482
+ if self.context_manager.context.content.is_spa:
483
+ print(" [智能猜测] SPA 模式,猜测常见 API 路径...")
484
+ common_api_paths = [
485
+ '/api/users', '/api/user', '/api/admin', '/api/auth',
486
+ '/api/login', '/api/logout', '/api/profile', '/api/settings',
487
+ '/auth/login', '/auth/logout', '/auth/token',
488
+ '/user/info', '/user/list', '/user/profile',
489
+ '/admin/users', '/admin/config', '/admin/settings',
490
+ ]
491
+ for path in common_api_paths:
492
+ if path not in discovered_paths:
493
+ try:
494
+ resp = self.session.get(
495
+ self.target.rstrip('/') + path,
496
+ timeout=5
497
+ )
498
+ if resp.status_code == 200:
499
+ ct = resp.headers.get('Content-Type', '')
500
+ if 'json' in ct.lower() or '{' in resp.text[:100]:
501
+ endpoint = Endpoint(
502
+ path=path,
503
+ method='GET',
504
+ source='intelligent_guess',
505
+ score=6,
506
+ is_high_value=True
507
+ )
508
+ self.context_manager.add_discovered_endpoint(endpoint)
509
+ discovered_paths.add(path)
510
+ print(f" [发现] {path}")
511
+ except:
512
+ pass
513
+
514
+ duration = time.time() - start
515
+
516
+ self.stage_results['discovery'] = StageResult(
517
+ name="discovery",
518
+ status=StageStatus.COMPLETED,
519
+ duration=duration,
520
+ data={
521
+ 'total_endpoints': len(self.context_manager.context.discovered_endpoints),
522
+ 'high_value': len(self.context_manager.get_high_value_endpoints())
523
+ }
524
+ )
525
+
526
+ print(f" 完成 ({duration:.1f}s)")
527
+ print(f" 发现端点: {len(self.context_manager.context.discovered_endpoints)}")
528
+
529
+ self._emit('stage_complete', {'stage': 'discovery'})
530
+
531
+ def _stage_reasoning(self):
532
+ """阶段 4: 推理分析"""
533
+ print("\n[*] Phase 4: 推理分析")
534
+
535
+ self._emit('stage_start', {'stage': 'reasoning'})
536
+ start = time.time()
537
+
538
+ insights = self.reasoner.insight_store.get_active()
539
+
540
+ for insight in insights:
541
+ if insight.type == InsightType.BLOCKER:
542
+ self._emit('blocker_detected', insight.to_dict())
543
+
544
+ if insight.findings:
545
+ for finding in insight.findings:
546
+ if 'strategy' in finding.strategy:
547
+ pass
548
+
549
+ context = StrategyContext({
550
+ 'insights': insights,
551
+ 'tech_stack': self.context_manager.context.tech_stack.to_dict(),
552
+ 'network_status': self.context_manager.context.network.rate_limit_status.value,
553
+ 'is_spa': self.context_manager.context.content.is_spa,
554
+ 'has_internal_ips': bool(self.context_manager.context.content.internal_ips),
555
+ 'waf_detected': self.context_manager.context.tech_stack.waf
556
+ })
557
+
558
+ selected_strategy = self.strategy_pool.select_strategy(context, insights)
559
+
560
+ self.strategist.current_plan = self.strategist.create_strategy_plan(context, insights)
561
+
562
+ duration = time.time() - start
563
+
564
+ self.stage_results['reasoning'] = StageResult(
565
+ name="reasoning",
566
+ status=StageStatus.COMPLETED,
567
+ duration=duration,
568
+ data={
569
+ 'strategy_selected': selected_strategy.id,
570
+ 'strategy_name': selected_strategy.name,
571
+ 'insights_count': len(insights)
572
+ },
573
+ insights=[i.to_dict() for i in insights]
574
+ )
575
+
576
+ print(f" 完成 ({duration:.1f}s)")
577
+ print(f" 选择策略: {selected_strategy.name} ({selected_strategy.id})")
578
+
579
+ self._emit('stage_complete', {'stage': 'reasoning'})
580
+
581
+ def _stage_fuzzing(self):
582
+ """阶段 5: 智能 Fuzzing"""
583
+ print("\n[*] Phase 5: 智能 Fuzzing")
584
+
585
+ self._emit('stage_start', {'stage': 'fuzzing'})
586
+ start = time.time()
587
+
588
+ print(" 使用增强型推理引擎进行智能 Fuzzing")
589
+
590
+ fuzz_targets = []
591
+
592
+ for endpoint in self.context_manager.get_high_value_endpoints():
593
+ full_url = f"{self.target}{endpoint.path}"
594
+ fuzz_targets.append({
595
+ 'url': full_url,
596
+ 'method': endpoint.method,
597
+ 'score': endpoint.score
598
+ })
599
+
600
+ fuzz_results = []
601
+
602
+ for target in fuzz_targets[:20]:
603
+ try:
604
+ resp = self.session.get(
605
+ target['url'],
606
+ timeout=5,
607
+ allow_redirects=False
608
+ )
609
+
610
+ fuzz_results.append({
611
+ 'url': target['url'],
612
+ 'status': resp.status_code,
613
+ 'length': len(resp.content)
614
+ })
615
+
616
+ response_data = {
617
+ 'url': target['url'],
618
+ 'method': 'GET',
619
+ 'status_code': resp.status_code,
620
+ 'content_type': resp.headers.get('Content-Type', ''),
621
+ 'content': resp.text[:500],
622
+ 'response_time': resp.elapsed.total_seconds(),
623
+ 'source': 'fuzzing'
624
+ }
625
+
626
+ insights = self.reasoner.observe_and_reason(response_data)
627
+ for insight in insights:
628
+ self._process_insight(insight)
629
+
630
+ except Exception as e:
631
+ logger.debug(f"Fuzz error for {target['url']}: {e}")
632
+
633
+ duration = time.time() - start
634
+
635
+ alive_count = sum(1 for r in fuzz_results if r['status'] < 500)
636
+
637
+ self.stage_results['fuzzing'] = StageResult(
638
+ name="fuzzing",
639
+ status=StageStatus.COMPLETED,
640
+ duration=duration,
641
+ data={
642
+ 'targets_tested': len(fuzz_targets),
643
+ 'alive': alive_count,
644
+ 'results': fuzz_results[:10]
645
+ }
646
+ )
647
+
648
+ print(f" 完成 ({duration:.1f}s)")
649
+ print(f" 测试目标: {len(fuzz_targets)}, 存活: {alive_count}")
650
+
651
+ self._emit('stage_complete', {'stage': 'fuzzing'})
652
+
653
+ def _stage_testing(self):
654
+ """阶段 6: 漏洞测试"""
655
+ print("\n[*] Phase 6: 漏洞测试")
656
+
657
+ self._emit('stage_start', {'stage': 'testing'})
658
+ start = time.time()
659
+
660
+ print(" 使用洞察驱动循环进行漏洞测试")
661
+
662
+ if self.testing_loop is None:
663
+ self.testing_loop = create_test_loop(
664
+ self.reasoner,
665
+ self.strategist,
666
+ self.context_manager,
667
+ executor=self._create_executor()
668
+ )
669
+
670
+ for endpoint in self.context_manager.get_high_value_endpoints()[:5]:
671
+ self.testing_loop.add_action(TestAction(
672
+ id=f"test_{endpoint.path}",
673
+ type='GET',
674
+ target=f"{self.target}{endpoint.path}",
675
+ priority=endpoint.score,
676
+ expected_outcome='2xx'
677
+ ))
678
+
679
+ report = self.testing_loop.run(max_iterations=20)
680
+
681
+ duration = time.time() - start
682
+
683
+ self.stage_results['testing'] = StageResult(
684
+ name="testing",
685
+ status=StageStatus.COMPLETED if report['state'] != 'blocked' else StageStatus.FAILED,
686
+ duration=duration,
687
+ data=report,
688
+ insights=[report]
689
+ )
690
+
691
+ print(f" 完成 ({duration:.1f}s)")
692
+ print(f" 状态: {report['state']}")
693
+ print(f" 漏洞发现: {report['progress'].get('vulnerabilities_found', 0)}")
694
+
695
+ self._emit('stage_complete', {'stage': 'testing'})
696
+
697
+ def _create_executor(self) -> Callable:
698
+ """创建执行器"""
699
+ def execute(action: TestAction) -> Dict:
700
+ try:
701
+ resp = self.session.request(
702
+ action.type,
703
+ action.target,
704
+ timeout=action.timeout,
705
+ allow_redirects=False
706
+ )
707
+
708
+ return {
709
+ 'status': resp.status_code,
710
+ 'content': resp.text[:500],
711
+ 'time': resp.elapsed.total_seconds(),
712
+ 'headers': dict(resp.headers)
713
+ }
714
+ except Exception as e:
715
+ return {
716
+ 'status': 0,
717
+ 'content': '',
718
+ 'time': 0,
719
+ 'error': str(e)
720
+ }
721
+
722
+ return execute
723
+
724
+ def _process_insight(self, insight: Insight):
725
+ """处理洞察"""
726
+ self._emit('insight_generated', insight.to_dict())
727
+
728
+ if insight.type == InsightType.BLOCKER:
729
+ self._emit('blocker_detected', insight.to_dict())
730
+
731
+ if insight.type.value == 'opportunity' and 'swagger' in insight.content.lower():
732
+ for finding in insight.findings:
733
+ if finding.evidence:
734
+ self.context_manager.add_swagger_url(finding.evidence[0])
735
+
736
+ def _has_blockers(self) -> bool:
737
+ """是否有阻碍因素"""
738
+ blockers = self.reasoner.insight_store.get_by_type(InsightType.BLOCKER)
739
+ return len(blockers) > 0
740
+
741
+ def _should_abort(self) -> bool:
742
+ """是否应该中止"""
743
+ if self.context_manager.is_rate_limited():
744
+ return True
745
+
746
+ internal_ips = self.context_manager.get_internal_addresses()
747
+ if internal_ips and not self.context_manager.context.network.requires_proxy:
748
+ return True
749
+
750
+ return False
751
+
752
+ def _handle_blockers(self):
753
+ """处理阻碍因素"""
754
+ blockers = self.reasoner.insight_store.get_by_type(InsightType.BLOCKER)
755
+
756
+ print(f"\n[!] 发现 {len(blockers)} 个阻碍因素:")
757
+ for blocker in blockers:
758
+ print(f" - {blocker.content}")
759
+
760
+ for finding in blocker.findings:
761
+ if finding.strategy:
762
+ print(f" 建议: {finding.strategy[:100]}...")
763
+
764
+ def _generate_report(self, duration: float, early_termination: Optional[str] = None) -> Dict:
765
+ """生成报告"""
766
+ print("\n" + "=" * 70)
767
+ print(" Enhanced Agentic Analysis Report v3.0")
768
+ print("=" * 70)
769
+
770
+ print(f"\n执行时间: {duration:.1f}s")
771
+ print(f"目标: {self.target}")
772
+
773
+ if early_termination:
774
+ print(f"提前终止: {early_termination}")
775
+
776
+ print(f"\n组件状态:")
777
+ print(f" - Reasoner: {len(self.reasoner.insight_store.get_active())} 活跃洞察")
778
+ print(f" - ContextManager: {len(self.context_manager.context.discovered_endpoints)} 端点")
779
+ print(f" - StrategyPool: {len(self.strategy_pool.get_all_strategies())} 策略")
780
+
781
+ insights = self.reasoner.insight_store.get_active()
782
+
783
+ if insights:
784
+ print(f"\n洞察 ({len(insights)} 个):")
785
+ for i, insight in enumerate(insights[:10], 1):
786
+ icon = {
787
+ InsightType.OBSERVATION: "[O]",
788
+ InsightType.PATTERN: "[P]",
789
+ InsightType.INFERENCE: "[I]",
790
+ InsightType.BLOCKER: "[X]",
791
+ InsightType.OPPORTUNITY: "[*]",
792
+ InsightType.STRATEGY_CHANGE: "[S]",
793
+ }.get(insight.type, "[-]")
794
+
795
+ print(f" {icon} {insight.content[:70]}...")
796
+
797
+ blockers = self.reasoner.insight_store.get_by_type(InsightType.BLOCKER)
798
+ if blockers:
799
+ print(f"\n阻碍因素 ({len(blockers)}):")
800
+ for b in blockers:
801
+ print(f" [X] {b.content}")
802
+
803
+ opportunities = self.reasoner.insight_store.get_by_type(InsightType.OPPORTUNITY)
804
+ if opportunities:
805
+ print(f"\n机会 ({len(opportunities)}):")
806
+ for o in opportunities:
807
+ print(f" [*] {o.content}")
808
+
809
+ print(f"\n上下文摘要:")
810
+ summary = self.context_manager.get_summary()
811
+ print(f" 技术栈: {summary.get('tech_stack', {})}")
812
+ print(f" 网络: {summary.get('network', {})}")
813
+ print(f" SPA: {summary.get('content', {}).get('is_spa', False)}")
814
+ print(f" API文档: {summary.get('content', {}).get('has_api_docs', False)}")
815
+
816
+ print("\n" + "=" * 70)
817
+
818
+ return {
819
+ 'target': self.target,
820
+ 'duration': duration,
821
+ 'early_termination': early_termination,
822
+ 'components': {
823
+ 'reasoner': {
824
+ 'total_insights': len(self.reasoner.insight_store.insights),
825
+ 'active_insights': len(self.reasoner.insight_store.get_active())
826
+ },
827
+ 'context_manager': summary,
828
+ 'strategy_pool': {
829
+ 'strategies': len(self.strategy_pool.get_all_strategies()),
830
+ 'selected': self.strategist.current_plan.primary_strategy.id if self.strategist.current_plan else None
831
+ }
832
+ },
833
+ 'stage_results': {k: v.to_dict() for k, v in self.stage_results.items()},
834
+ 'insights': [i.to_dict() for i in insights],
835
+ 'blockers': [b.to_dict() for b in blockers],
836
+ 'opportunities': [o.to_dict() for o in opportunities]
837
+ }
838
+
839
+ def get_context(self) -> Dict:
840
+ """获取当前上下文"""
841
+ return self.context_manager.export_context()
842
+
843
+ def get_insights(self) -> List[Dict]:
844
+ """获取所有洞察"""
845
+ return [i.to_dict() for i in self.reasoner.insight_store.insights]
846
+
847
+ def save_state(self, filepath: str):
848
+ """保存状态"""
849
+ state = {
850
+ 'target': self.target,
851
+ 'context': self.context_manager.export_context(),
852
+ 'insights': self.get_insights(),
853
+ 'stage_results': {k: v.to_dict() for k, v in self.stage_results.items()}
854
+ }
855
+ with open(filepath, 'w') as f:
856
+ json.dump(state, f, indent=2, default=str)
857
+
858
+ @classmethod
859
+ def load_state(cls, filepath: str) -> 'EnhancedAgenticOrchestrator':
860
+ """加载状态"""
861
+ with open(filepath, 'r') as f:
862
+ state = json.load(f)
863
+
864
+ orchestrator = cls(state['target'])
865
+
866
+ return orchestrator
867
+
868
+
869
+ def run_enhanced_agentic_test(
870
+ target: str,
871
+ max_iterations: int = 100,
872
+ max_duration: float = 3600.0
873
+ ) -> Dict:
874
+ """运行增强型 Agentic 测试"""
875
+ orchestrator = EnhancedAgenticOrchestrator(target)
876
+ return orchestrator.execute(
877
+ max_iterations=max_iterations,
878
+ max_duration=max_duration
879
+ )
880
+
881
+
882
+ if __name__ == "__main__":
883
+ import sys
884
+
885
+ target = sys.argv[1] if len(sys.argv) > 1 else "http://example.com"
886
+
887
+ result = run_enhanced_agentic_test(target, max_iterations=20)
888
+
889
+ print("\n[JSON Output]")
890
+ print(json.dumps(result, indent=2, default=str))