opencode-api-security-testing 2.1.0 → 2.1.1

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,707 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Strategy Pool - 策略池系统
4
+
5
+ 动态策略管理:
6
+ - 预定义策略库(8种策略)
7
+ - 策略选择算法
8
+ - 策略适应性调整
9
+ - 策略有效性评估
10
+ """
11
+
12
+ import time
13
+ import json
14
+ from datetime import datetime
15
+ from typing import Dict, List, Set, Optional, Any, Callable
16
+ from dataclasses import dataclass, field
17
+ from enum import Enum
18
+ from collections import defaultdict
19
+ import logging
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ class StrategyState(Enum):
25
+ """策略状态"""
26
+ IDLE = "idle"
27
+ ACTIVE = "active"
28
+ PAUSED = "paused"
29
+ COMPLETED = "completed"
30
+ FAILED = "failed"
31
+
32
+
33
+ @dataclass
34
+ class Condition:
35
+ """策略激活条件"""
36
+ type: str # 'insight_type', 'tech_stack', 'network_status', 'endpoint_score'
37
+ operator: str # 'equals', 'contains', 'greater_than', 'less_than', 'in'
38
+ value: Any
39
+
40
+ def evaluate(self, context: 'StrategyContext') -> bool:
41
+ """评估条件是否满足"""
42
+ if self.type == 'insight_type':
43
+ insight_types = context.get('insight_types', [])
44
+ if self.operator == 'contains':
45
+ return self.value in insight_types
46
+ elif self.operator == 'in':
47
+ return self.value in insight_types
48
+
49
+ elif self.type == 'tech_stack':
50
+ tech_stack = context.get('tech_stack', {})
51
+ if self.operator == 'contains':
52
+ return self.value in tech_stack
53
+
54
+ elif self.type == 'network_status':
55
+ status = context.get('network_status', 'normal')
56
+ if self.operator == 'equals':
57
+ return status == self.value
58
+ elif self.operator == 'in':
59
+ return status in self.value
60
+
61
+ elif self.type == 'endpoint_score':
62
+ score = context.get('endpoint_score', 0)
63
+ if self.operator == 'greater_than':
64
+ return score > self.value
65
+ elif self.operator == 'greater_equal':
66
+ return score >= self.value
67
+ elif self.operator == 'equals':
68
+ return score == self.value
69
+
70
+ elif self.type == 'endpoint_type':
71
+ endpoint_path = context.get('endpoint_path', '').lower()
72
+ if self.operator == 'contains':
73
+ return self.value in endpoint_path
74
+
75
+ elif self.type == 'waf_detected':
76
+ waf = context.get('waf_detected')
77
+ if self.operator == 'equals':
78
+ return waf == self.value
79
+ elif self.operator == 'is_not_none':
80
+ return waf is not None
81
+
82
+ elif self.type == 'is_spa':
83
+ is_spa = context.get('is_spa', False)
84
+ return is_spa == self.value
85
+
86
+ elif self.type == 'has_internal_ips':
87
+ internal_ips = context.get('internal_ips', set())
88
+ return len(internal_ips) > 0 if self.value else len(internal_ips) == 0
89
+
90
+ return False
91
+
92
+
93
+ @dataclass
94
+ class Action:
95
+ """策略动作"""
96
+ type: str # 'test_sqli', 'test_xss', 'fuzz_path', etc.
97
+ params: Dict[str, Any] = field(default_factory=dict)
98
+ priority: int = 0
99
+ timeout: float = 30.0
100
+ retry_on_failure: int = 1
101
+
102
+ def to_dict(self) -> Dict:
103
+ return {
104
+ 'type': self.type,
105
+ 'params': self.params,
106
+ 'priority': self.priority,
107
+ 'timeout': self.timeout,
108
+ 'retry_on_failure': self.retry_on_failure
109
+ }
110
+
111
+
112
+ @dataclass
113
+ class StrategyMetrics:
114
+ """策略指标"""
115
+ success_count: int = 0
116
+ failure_count: int = 0
117
+ total_executions: int = 0
118
+ avg_effectiveness: float = 0.0
119
+ last_execution_time: Optional[datetime] = None
120
+ last_success_time: Optional[datetime] = None
121
+
122
+ def record_execution(self, success: bool, effectiveness: float = 0.0):
123
+ """记录执行结果"""
124
+ self.total_executions += 1
125
+ if success:
126
+ self.success_count += 1
127
+ self.last_success_time = datetime.now()
128
+ else:
129
+ self.failure_count += 1
130
+
131
+ if self.total_executions > 0:
132
+ self.avg_effectiveness = (
133
+ (self.avg_effectiveness * (self.total_executions - 1) + effectiveness)
134
+ / self.total_executions
135
+ )
136
+
137
+ self.last_execution_time = datetime.now()
138
+
139
+ def to_dict(self) -> Dict:
140
+ return {
141
+ 'success_count': self.success_count,
142
+ 'failure_count': self.failure_count,
143
+ 'total_executions': self.total_executions,
144
+ 'avg_effectiveness': self.avg_effectiveness,
145
+ 'last_execution_time': self.last_execution_time.isoformat() if self.last_execution_time else None,
146
+ 'success_rate': self.success_count / max(self.total_executions, 1)
147
+ }
148
+
149
+
150
+ @dataclass
151
+ class Strategy:
152
+ """策略"""
153
+ id: str
154
+ name: str
155
+ description: str
156
+
157
+ activation_conditions: List[Condition] = field(default_factory=list)
158
+ priority: int = 0
159
+
160
+ actions: List[Action] = field(default_factory=list)
161
+ execution_order: str = "sequential" # 'sequential', 'parallel', 'adaptive'
162
+
163
+ exit_on: List[str] = field(default_factory=list) # 'all_complete', 'vuln_found', 'blocked'
164
+ max_duration: float = 300.0
165
+ max_iterations: int = 100
166
+
167
+ is_adaptive: bool = False
168
+ adaptation_threshold: float = 0.3
169
+
170
+ state: StrategyState = StrategyState.IDLE
171
+ metrics: StrategyMetrics = field(default_factory=StrategyMetrics)
172
+
173
+ config: Dict[str, Any] = field(default_factory=dict)
174
+
175
+ def is_active(self) -> bool:
176
+ return self.state == StrategyState.ACTIVE
177
+
178
+ def can_activate(self, context: 'StrategyContext') -> bool:
179
+ """检查是否可以激活"""
180
+ if not self.activation_conditions:
181
+ return True
182
+
183
+ for cond in self.activation_conditions:
184
+ if not cond.evaluate(context):
185
+ return False
186
+
187
+ return True
188
+
189
+ def should_exit(self, exit_reason: str, vulns_found: int = 0) -> bool:
190
+ """检查是否应该退出"""
191
+ if exit_reason in self.exit_on:
192
+ return True
193
+
194
+ if 'vuln_found' in self.exit_on and vulns_found > 0:
195
+ return True
196
+
197
+ return False
198
+
199
+ def to_dict(self) -> Dict:
200
+ return {
201
+ 'id': self.id,
202
+ 'name': self.name,
203
+ 'description': self.description,
204
+ 'priority': self.priority,
205
+ 'state': self.state.value,
206
+ 'metrics': self.metrics.to_dict(),
207
+ 'actions': [a.to_dict() for a in self.actions],
208
+ 'config': self.config
209
+ }
210
+
211
+
212
+ class StrategyContext(Dict):
213
+ """策略上下文(字典-like)"""
214
+
215
+ def get(self, key: str, default: Any = None) -> Any:
216
+ try:
217
+ return self[key]
218
+ except KeyError:
219
+ return default
220
+
221
+
222
+ @dataclass
223
+ class StrategyPlan:
224
+ """策略计划"""
225
+ primary_strategy: Strategy
226
+ fallback_strategy: Optional[Strategy] = None
227
+
228
+ execution_order: List[str] = field(default_factory=list)
229
+ adaptations: Dict[str, Any] = field(default_factory=dict)
230
+
231
+ estimated_duration: float = 0.0
232
+ estimated_actions: int = 0
233
+
234
+
235
+ class StrategyPool:
236
+ """
237
+ 策略池
238
+
239
+ 包含预定义策略库和策略选择逻辑
240
+ """
241
+
242
+ def __init__(self):
243
+ self.strategies: Dict[str, Strategy] = {}
244
+ self._register_default_strategies()
245
+
246
+ def _register_default_strategies(self):
247
+ """注册默认策略"""
248
+ self.register_strategy(self._create_default_strategy())
249
+ self.register_strategy(self._create_waf_bypass_strategy())
250
+ self.register_strategy(self._create_spa_fallback_strategy())
251
+ self.register_strategy(self._create_internal_address_strategy())
252
+ self.register_strategy(self._create_high_value_endpoint_strategy())
253
+ self.register_strategy(self._create_auth_testing_strategy())
254
+ self.register_strategy(self._create_rate_limited_strategy())
255
+ self.register_strategy(self._create_sensitive_operation_strategy())
256
+
257
+ def register_strategy(self, strategy: Strategy):
258
+ """注册策略"""
259
+ self.strategies[strategy.id] = strategy
260
+ logger.debug(f"Registered strategy: {strategy.id}")
261
+
262
+ def get_strategy(self, strategy_id: str) -> Optional[Strategy]:
263
+ """获取策略"""
264
+ return self.strategies.get(strategy_id)
265
+
266
+ def get_all_strategies(self) -> List[Strategy]:
267
+ """获取所有策略"""
268
+ return list(self.strategies.values())
269
+
270
+ def select_strategy(self, context: StrategyContext, insights: List[Any] = None) -> Strategy:
271
+ """
272
+ 根据上下文选择最佳策略
273
+
274
+ 选择逻辑:
275
+ 1. 检查所有可激活策略
276
+ 2. 按优先级排序
277
+ 3. 返回最高优先级策略
278
+ """
279
+ if insights is None:
280
+ insights = []
281
+
282
+ context['insight_types'] = [i.type for i in insights] if hasattr(insights, '__iter__') else []
283
+
284
+ activatable = []
285
+ for strategy in self.strategies.values():
286
+ if strategy.can_activate(context):
287
+ activatable.append((strategy.priority, strategy.id, strategy))
288
+
289
+ if not activatable:
290
+ return self.strategies['default']
291
+
292
+ activatable.sort(key=lambda x: x[0], reverse=True)
293
+
294
+ selected = activatable[0][2]
295
+ selected.state = StrategyState.ACTIVE
296
+
297
+ logger.info(f"Selected strategy: {selected.id} ({selected.name})")
298
+
299
+ return selected
300
+
301
+ def select_multiple(self, context: StrategyContext, max_strategies: int = 3) -> List[Strategy]:
302
+ """选择多个策略"""
303
+ activatable = []
304
+ for strategy in self.strategies.values():
305
+ if strategy.can_activate(context):
306
+ activatable.append((strategy.priority, id(strategy), strategy))
307
+
308
+ if not activatable:
309
+ return [self.strategies['default']]
310
+
311
+ activatable.sort(key=lambda x: x[0], reverse=True)
312
+
313
+ return [s[2] for s in activatable[:max_strategies]]
314
+
315
+ def create_plan(self, context: StrategyContext, insights: List[Any] = None) -> StrategyPlan:
316
+ """创建策略计划"""
317
+ primary = self.select_strategy(context, insights)
318
+
319
+ estimated_actions = len(primary.actions)
320
+ estimated_duration = sum(a.timeout for a in primary.actions)
321
+
322
+ return StrategyPlan(
323
+ primary_strategy=primary,
324
+ fallback_strategy=self.strategies.get('default'),
325
+ execution_order=[primary.id],
326
+ estimated_duration=estimated_duration,
327
+ estimated_actions=estimated_actions
328
+ )
329
+
330
+ def adapt_strategy(self, strategy: Strategy, feedback: Dict) -> Strategy:
331
+ """根据反馈调整策略"""
332
+ if not strategy.is_adaptive:
333
+ return strategy
334
+
335
+ effectiveness = feedback.get('effectiveness', 0.0)
336
+
337
+ if effectiveness < strategy.adaptation_threshold:
338
+ for action in strategy.actions:
339
+ if action.type == 'test_sqli' or action.type == 'test_xss':
340
+ action.params['use_bypass'] = True
341
+ action.params['obfuscation_level'] = 'high'
342
+
343
+ return strategy
344
+
345
+ def record_outcome(self, strategy_id: str, success: bool, effectiveness: float = 0.0):
346
+ """记录策略执行结果"""
347
+ strategy = self.strategies.get(strategy_id)
348
+ if strategy:
349
+ strategy.metrics.record_execution(success, effectiveness)
350
+
351
+ if success:
352
+ logger.info(f"Strategy {strategy_id} succeeded (effectiveness: {effectiveness:.2f})")
353
+ else:
354
+ logger.warning(f"Strategy {strategy_id} failed")
355
+
356
+ def _create_default_strategy(self) -> Strategy:
357
+ """创建默认策略"""
358
+ return Strategy(
359
+ id='default',
360
+ name='默认测试策略',
361
+ description='适用于大多数目标的默认测试策略',
362
+ priority=0,
363
+ actions=[
364
+ Action(type='discover_endpoints', priority=10),
365
+ Action(type='test_sqli', priority=8),
366
+ Action(type='test_xss', priority=8),
367
+ Action(type='test_auth', priority=6),
368
+ ],
369
+ execution_order='sequential',
370
+ exit_on=['all_complete', 'vuln_found', 'blocked'],
371
+ config={
372
+ 'rate_limit': 10,
373
+ 'payload_set': 'standard',
374
+ 'depth': 'normal'
375
+ }
376
+ )
377
+
378
+ def _create_waf_bypass_strategy(self) -> Strategy:
379
+ """创建 WAF 绕过策略"""
380
+ return Strategy(
381
+ id='waf_detected',
382
+ name='WAF 绕过策略',
383
+ description='当检测到 WAF 时使用的绕过策略',
384
+ priority=10,
385
+ activation_conditions=[
386
+ Condition(type='waf_detected', operator='is_not_none', value=None)
387
+ ],
388
+ actions=[
389
+ Action(type='test_sqli_bypass', priority=10, params={'obfuscation_level': 'high'}),
390
+ Action(type='test_xss_bypass', priority=10, params={'obfuscation_level': 'high'}),
391
+ Action(type='test_obfuscated', priority=8),
392
+ ],
393
+ execution_order='sequential',
394
+ exit_on=['all_complete', 'vuln_found', 'blocked'],
395
+ is_adaptive=True,
396
+ adaptation_threshold=0.3,
397
+ config={
398
+ 'rate_limit': 2,
399
+ 'payload_set': 'waf_bypass',
400
+ 'depth': 'normal'
401
+ }
402
+ )
403
+
404
+ def _create_spa_fallback_strategy(self) -> Strategy:
405
+ """创建 SPA Fallback 策略"""
406
+ return Strategy(
407
+ id='spa_fallback',
408
+ name='SPA 深度分析策略',
409
+ description='检测到 SPA fallback 行为时,深度分析 JS',
410
+ priority=20,
411
+ activation_conditions=[
412
+ Condition(type='is_spa', operator='equals', value=True)
413
+ ],
414
+ actions=[
415
+ Action(type='extract_js', priority=10),
416
+ Action(type='analyze_webpack', priority=9),
417
+ Action(type='find_api_urls', priority=9),
418
+ Action(type='test_from_js', priority=8),
419
+ Action(type='test_backend_direct', priority=7),
420
+ ],
421
+ execution_order='sequential',
422
+ exit_on=['all_complete', 'api_found', 'timeout'],
423
+ config={
424
+ 'rate_limit': 5,
425
+ 'js_depth': 3,
426
+ 'focus': 'reconnaissance'
427
+ }
428
+ )
429
+
430
+ def _create_internal_address_strategy(self) -> Strategy:
431
+ """创建内网地址策略"""
432
+ return Strategy(
433
+ id='internal_address',
434
+ name='内网代理策略',
435
+ description='发现内网地址时提示配置代理',
436
+ priority=30,
437
+ activation_conditions=[
438
+ Condition(type='has_internal_ips', operator='equals', value=True)
439
+ ],
440
+ actions=[
441
+ Action(type='mark_unreachable', priority=10),
442
+ Action(type='extract_testable_apis', priority=9),
443
+ Action(type='suggest_proxy', priority=8),
444
+ ],
445
+ execution_order='sequential',
446
+ exit_on=['all_complete'],
447
+ config={
448
+ 'rate_limit': 0,
449
+ 'requires_user_action': True
450
+ }
451
+ )
452
+
453
+ def _create_high_value_endpoint_strategy(self) -> Strategy:
454
+ """创建高价值端点策略"""
455
+ return Strategy(
456
+ id='high_value_endpoint',
457
+ name='高价值端点深度测试',
458
+ description='对高评分端点进行深度测试',
459
+ priority=15,
460
+ activation_conditions=[
461
+ Condition(type='endpoint_score', operator='greater_than', value=7)
462
+ ],
463
+ actions=[
464
+ Action(type='full_test_suite', priority=10),
465
+ Action(type='test_edge_cases', priority=9),
466
+ Action(type='bypass_auth', priority=8),
467
+ Action(type='test_idor', priority=7),
468
+ ],
469
+ execution_order='sequential',
470
+ exit_on=['all_complete', 'vuln_found'],
471
+ is_adaptive=True,
472
+ config={
473
+ 'rate_limit': 5,
474
+ 'depth': 'maximum',
475
+ 'timeout_multiplier': 2.0
476
+ }
477
+ )
478
+
479
+ def _create_auth_testing_strategy(self) -> Strategy:
480
+ """创建认证测试策略"""
481
+ return Strategy(
482
+ id='auth_testing',
483
+ name='认证安全专项',
484
+ description='针对认证接口的专项测试',
485
+ priority=25,
486
+ activation_conditions=[
487
+ Condition(type='endpoint_type', operator='contains', value='auth'),
488
+ Condition(type='endpoint_type', operator='contains', value='login'),
489
+ ],
490
+ actions=[
491
+ Action(type='test_default_creds', priority=10),
492
+ Action(type='test_auth_bypass', priority=9),
493
+ Action(type='test_jwt', priority=9),
494
+ Action(type='test_session', priority=8),
495
+ ],
496
+ execution_order='sequential',
497
+ exit_on=['all_complete', 'vuln_found', 'locked'],
498
+ is_adaptive=True,
499
+ config={
500
+ 'rate_limit': 3,
501
+ 'safety_mode': True,
502
+ 'max_attempts': 5
503
+ }
504
+ )
505
+
506
+ def _create_rate_limited_strategy(self) -> Strategy:
507
+ """创建限速自适应策略"""
508
+ return Strategy(
509
+ id='rate_limited',
510
+ name='限速自适应策略',
511
+ description='检测到限速时自动降速',
512
+ priority=40,
513
+ activation_conditions=[
514
+ Condition(type='network_status', operator='equals', value='rate_limited')
515
+ ],
516
+ actions=[
517
+ Action(type='reduce_rate', priority=10),
518
+ Action(type='rotate_user_agent', priority=9),
519
+ Action(type='use_proxy', priority=8),
520
+ ],
521
+ execution_order='sequential',
522
+ exit_on=['rate_restored', 'blocked'],
523
+ config={
524
+ 'rate_limit': 1,
525
+ 'cooldown': 60,
526
+ 'backoff_factor': 0.5
527
+ }
528
+ )
529
+
530
+ def _create_sensitive_operation_strategy(self) -> Strategy:
531
+ """创建敏感操作策略"""
532
+ return Strategy(
533
+ id='sensitive_operation',
534
+ name='敏感操作安全策略',
535
+ description='测试敏感操作时使用最小化原则',
536
+ priority=35,
537
+ activation_conditions=[
538
+ Condition(type='endpoint_type', operator='contains', value='pay'),
539
+ Condition(type='endpoint_type', operator='contains', value='transfer'),
540
+ ],
541
+ actions=[
542
+ Action(type='minimal_testing', priority=10),
543
+ Action(type='simulate_normal_usage', priority=9),
544
+ Action(type='avoid_destructive', priority=8),
545
+ ],
546
+ execution_order='sequential',
547
+ exit_on=['all_complete'],
548
+ config={
549
+ 'rate_limit': 1,
550
+ 'safety_mode': True,
551
+ 'require_confirmation': True
552
+ }
553
+ )
554
+
555
+ def get_summary(self) -> Dict:
556
+ """获取策略池摘要"""
557
+ return {
558
+ 'total_strategies': len(self.strategies),
559
+ 'strategies': {
560
+ k: v.to_dict() for k, v in self.strategies.items()
561
+ }
562
+ }
563
+
564
+
565
+ class Strategist:
566
+ """
567
+ 策略师
568
+
569
+ 负责:
570
+ - 创建策略计划
571
+ - 评估策略有效性
572
+ - 管理策略执行
573
+ """
574
+
575
+ def __init__(self, strategy_pool: Optional[StrategyPool] = None):
576
+ self.strategy_pool = strategy_pool or StrategyPool()
577
+ self.current_plan: Optional[StrategyPlan] = None
578
+ self.execution_history: List[Dict] = []
579
+
580
+ def create_strategy_plan(self, context: Dict, insights: List[Any] = None) -> StrategyPlan:
581
+ """创建策略计划"""
582
+ strategy_context = StrategyContext(context)
583
+ self.current_plan = self.strategy_pool.create_plan(strategy_context, insights)
584
+
585
+ return self.current_plan
586
+
587
+ def evaluate_effectiveness(self, strategy: Strategy, results: Dict) -> float:
588
+ """
589
+ 评估策略有效性
590
+
591
+ 基于:
592
+ - 发现漏洞数量
593
+ - 测试覆盖率
594
+ - 执行时间
595
+ - 误报率
596
+ """
597
+ vuln_count = results.get('vulnerabilities_found', 0)
598
+ coverage = results.get('coverage', 0.0)
599
+ execution_time = results.get('execution_time', 0.0)
600
+ false_positive_rate = results.get('false_positive_rate', 0.0)
601
+
602
+ vuln_weight = 0.4
603
+ coverage_weight = 0.3
604
+ time_weight = 0.1
605
+ accuracy_weight = 0.2
606
+
607
+ vuln_score = min(vuln_count / 10.0, 1.0)
608
+ coverage_score = coverage
609
+ time_score = 1.0 if execution_time < 300 else 0.5
610
+ accuracy_score = 1.0 - false_positive_rate
611
+
612
+ effectiveness = (
613
+ vuln_score * vuln_weight +
614
+ coverage_score * coverage_weight +
615
+ time_score * time_weight +
616
+ accuracy_score * accuracy_weight
617
+ )
618
+
619
+ return effectiveness
620
+
621
+ def should_switch_strategy(self, current: Strategy, new: Strategy, feedback: Dict) -> bool:
622
+ """判断是否应该切换策略"""
623
+ effectiveness = feedback.get('effectiveness', 0.0)
624
+
625
+ if effectiveness < current.adaptation_threshold:
626
+ return True
627
+
628
+ if current.state == StrategyState.FAILED:
629
+ return True
630
+
631
+ if current.should_exit(feedback.get('exit_reason', '')):
632
+ return True
633
+
634
+ return new.priority > current.priority
635
+
636
+ def record_execution(self, strategy_id: str, results: Dict):
637
+ """记录策略执行"""
638
+ effectiveness = self.evaluate_effectiveness(
639
+ self.strategy_pool.get_strategy(strategy_id) or Strategy(id=strategy_id, name=strategy_id, description=""),
640
+ results
641
+ )
642
+
643
+ success = effectiveness > 0.3
644
+
645
+ self.strategy_pool.record_outcome(strategy_id, success, effectiveness)
646
+
647
+ self.execution_history.append({
648
+ 'strategy_id': strategy_id,
649
+ 'effectiveness': effectiveness,
650
+ 'success': success,
651
+ 'timestamp': datetime.now().isoformat(),
652
+ 'results': results
653
+ })
654
+
655
+ def get_best_strategy_for_context(self, context: StrategyContext) -> Strategy:
656
+ """获取最佳策略"""
657
+ return self.strategy_pool.select_strategy(context)
658
+
659
+ def suggest_alternatives(self, context: StrategyContext) -> List[Strategy]:
660
+ """建议备选策略"""
661
+ return self.strategy_pool.select_multiple(context, max_strategies=3)
662
+
663
+
664
+ def create_strategy_pool() -> StrategyPool:
665
+ """创建策略池工厂函数"""
666
+ return StrategyPool()
667
+
668
+
669
+ def create_strategist(strategy_pool: Optional[StrategyPool] = None) -> Strategist:
670
+ """创建策略师工厂函数"""
671
+ return Strategist(strategy_pool)
672
+
673
+
674
+ if __name__ == "__main__":
675
+ pool = create_strategy_pool()
676
+
677
+ context = StrategyContext({
678
+ 'is_spa': True,
679
+ 'waf_detected': 'aliyun',
680
+ 'network_status': 'normal',
681
+ 'endpoint_score': 8,
682
+ 'tech_stack': {'vue', 'spring'},
683
+ 'insight_types': ['spa_fallback', 'waf_detected']
684
+ })
685
+
686
+ print("=" * 60)
687
+ print("Strategy Pool Demo")
688
+ print("=" * 60)
689
+
690
+ selected = pool.select_strategy(context)
691
+ print(f"\nSelected: {selected.name} ({selected.id})")
692
+ print(f"Priority: {selected.priority}")
693
+ print(f"Actions: {[a.type for a in selected.actions]}")
694
+ print(f"Config: {selected.config}")
695
+
696
+ print("\n" + "-" * 60)
697
+ print("Alternative strategies for this context:")
698
+ alternatives = pool.select_multiple(context, max_strategies=3)
699
+ for i, s in enumerate(alternatives, 1):
700
+ print(f" {i}. {s.name} (priority: {s.priority})")
701
+
702
+ print("\n" + "-" * 60)
703
+ print("Strategy Pool Summary:")
704
+ summary = pool.get_summary()
705
+ print(f"Total strategies: {summary['total_strategies']}")
706
+ for sid, sdata in summary['strategies'].items():
707
+ print(f" - {sid}: {sdata['name']} (state: {sdata['state']})")