opencode-api-security-testing 3.0.9 → 3.0.10

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 (78) hide show
  1. package/agents/api-cyber-supervisor.md +22 -19
  2. package/agents/api-probing-miner.md +34 -10
  3. package/agents/api-resource-specialist.md +49 -20
  4. package/agents/api-vuln-verifier.md +69 -18
  5. package/package.json +1 -1
  6. package/postinstall.mjs +1 -0
  7. package/preuninstall.mjs +43 -32
  8. package/src/index.ts +6 -3
  9. package/README.md +0 -74
  10. package/SKILL.md +0 -1797
  11. package/core/advanced_recon.py +0 -788
  12. package/core/agentic_analyzer.py +0 -445
  13. package/core/analyzers/api_parser.py +0 -210
  14. package/core/analyzers/response_analyzer.py +0 -212
  15. package/core/analyzers/sensitive_finder.py +0 -184
  16. package/core/api_fuzzer.py +0 -422
  17. package/core/api_interceptor.py +0 -525
  18. package/core/api_parser.py +0 -955
  19. package/core/browser_tester.py +0 -479
  20. package/core/cloud_storage_tester.py +0 -1330
  21. package/core/collectors/__init__.py +0 -23
  22. package/core/collectors/api_path_finder.py +0 -300
  23. package/core/collectors/browser_collect.py +0 -645
  24. package/core/collectors/browser_collector.py +0 -411
  25. package/core/collectors/http_client.py +0 -111
  26. package/core/collectors/js_collector.py +0 -490
  27. package/core/collectors/js_parser.py +0 -780
  28. package/core/collectors/url_collector.py +0 -319
  29. package/core/context_manager.py +0 -682
  30. package/core/deep_api_tester_v35.py +0 -844
  31. package/core/deep_api_tester_v55.py +0 -366
  32. package/core/dynamic_api_analyzer.py +0 -532
  33. package/core/http_client.py +0 -179
  34. package/core/models.py +0 -296
  35. package/core/orchestrator.py +0 -890
  36. package/core/prerequisite.py +0 -227
  37. package/core/reasoning_engine.py +0 -1042
  38. package/core/response_classifier.py +0 -606
  39. package/core/runner.py +0 -938
  40. package/core/scan_engine.py +0 -599
  41. package/core/skill_executor.py +0 -435
  42. package/core/skill_executor_v2.py +0 -670
  43. package/core/skill_executor_v3.py +0 -704
  44. package/core/smart_analyzer.py +0 -687
  45. package/core/strategy_pool.py +0 -707
  46. package/core/testers/auth_tester.py +0 -264
  47. package/core/testers/idor_tester.py +0 -200
  48. package/core/testers/sqli_tester.py +0 -211
  49. package/core/testing_loop.py +0 -655
  50. package/core/utils/base_path_dict.py +0 -255
  51. package/core/utils/payload_lib.py +0 -167
  52. package/core/utils/ssrf_detector.py +0 -220
  53. package/core/verifiers/vuln_verifier.py +0 -536
  54. package/references/README.md +0 -72
  55. package/references/asset-discovery.md +0 -119
  56. package/references/fuzzing-patterns.md +0 -129
  57. package/references/graphql-guidance.md +0 -108
  58. package/references/intake.md +0 -84
  59. package/references/pua-agent.md +0 -192
  60. package/references/report-template.md +0 -156
  61. package/references/rest-guidance.md +0 -76
  62. package/references/severity-model.md +0 -76
  63. package/references/test-matrix.md +0 -86
  64. package/references/validation.md +0 -78
  65. package/references/vulnerabilities/01-sqli-tests.md +0 -1128
  66. package/references/vulnerabilities/02-user-enum-tests.md +0 -423
  67. package/references/vulnerabilities/03-jwt-tests.md +0 -499
  68. package/references/vulnerabilities/04-idor-tests.md +0 -362
  69. package/references/vulnerabilities/05-sensitive-data-tests.md +0 -466
  70. package/references/vulnerabilities/06-biz-logic-tests.md +0 -501
  71. package/references/vulnerabilities/07-security-config-tests.md +0 -511
  72. package/references/vulnerabilities/08-brute-force-tests.md +0 -457
  73. package/references/vulnerabilities/09-vulnerability-chains.md +0 -465
  74. package/references/vulnerabilities/10-auth-tests.md +0 -537
  75. package/references/vulnerabilities/11-graphql-tests.md +0 -355
  76. package/references/vulnerabilities/12-ssrf-tests.md +0 -396
  77. package/references/vulnerabilities/README.md +0 -148
  78. package/references/workflows.md +0 -192
@@ -1,670 +0,0 @@
1
- """
2
- SKILL 执行器 v2.0 - 配置驱动执行
3
-
4
- 根据 SKILL.md v2.0 定义的 execution_config:
5
- 1. 解析配置驱动的执行流程
6
- 2. 综合判断 API 存在性(静态 + 动态)
7
- 3. 端点合并(静态 + 动态 + Hook)
8
- 4. API前缀多源提取
9
- 5. 认证端点优先测试
10
- """
11
-
12
- import sys
13
- sys.path.insert(0, '/workspace/skill-play/API-Security-Testing-Optimized')
14
-
15
- from core.prerequisite import prerequisite_check
16
- from core.api_parser import APIEndpointParser
17
- from core.cloud_storage_tester import CloudStorageTester
18
-
19
-
20
- class SKILLExecutorV2:
21
- """SKILL 执行器 v2.0 - 配置驱动"""
22
-
23
- def __init__(self, target: str):
24
- self.target = target
25
- self.session = None
26
- self.playwright_available = False
27
- self.browser_type = None
28
-
29
- # 资产发现结果
30
- self.parser = None
31
- self.js_files = []
32
- self.static_endpoints = []
33
- self.dynamic_endpoints = []
34
- self.hooked_endpoints = []
35
- self.parent_paths = {}
36
-
37
- # v2.0 新增:API前缀多源提取
38
- self.api_prefix = None
39
- self.api_prefix_sources = {}
40
-
41
- # 测试结果
42
- self.vulnerabilities = []
43
- self.cloud_findings = []
44
-
45
- # 决策状态
46
- self.site_type = None
47
- self.has_real_api = False
48
- self.has_dynamic_endpoints = False
49
- self.html_fallback_all = False
50
-
51
- def run(self):
52
- """执行 SKILL 流程 v2.0"""
53
- print("=" * 70)
54
- print(" API Security Testing Skill v2.0 - 配置驱动执行")
55
- print("=" * 70)
56
- print(f" 目标: {self.target}")
57
- print()
58
-
59
- # ========== 阶段 0: 前置检查 ==========
60
- print("[阶段 0] 前置检查")
61
- print("-" * 50)
62
- self._check_prerequisites()
63
-
64
- # ========== 阶段 1: 资产发现 ==========
65
- print("\n[阶段 1] 资产发现")
66
- print("-" * 50)
67
-
68
- # 1.1 静态分析
69
- print("\n[1.1] 静态分析")
70
- self._static_analysis()
71
-
72
- # 1.2 站点类型检测
73
- print("\n[1.2] 站点类型检测")
74
- self._detect_site_type()
75
-
76
- # 1.3 父路径探测
77
- print("\n[1.3] 父路径探测")
78
- self._probe_parent_paths()
79
-
80
- # 1.4 动态分析 (条件执行)
81
- print("\n[1.4] 动态分析")
82
- if self.site_type in ['modern_spa', 'jquery_spa'] and self.playwright_available:
83
- self._dynamic_analysis()
84
- else:
85
- print(" [跳过] 非SPA或Playwright不可用")
86
-
87
- # 1.5 API Hook (条件执行)
88
- print("\n[1.5] API Hook")
89
- if self.playwright_available and (self.has_real_api or self.has_dynamic_endpoints):
90
- self._api_hook()
91
- else:
92
- print(" [跳过] 无API或Playwright不可用")
93
-
94
- # ========== 阶段 2: 漏洞分析 ==========
95
- print("\n[阶段 2] 漏洞分析")
96
- print("-" * 50)
97
-
98
- # v2.0 修复:综合判断
99
- self._update_decision_state()
100
-
101
- if self.has_real_api or self.has_dynamic_endpoints:
102
- self._vulnerability_testing()
103
- else:
104
- if self.html_fallback_all:
105
- print(" [SKIP] nginx fallback,报告问题")
106
- self._report_nginx_fallback()
107
- else:
108
- print(" [继续] 无API但非fallback,执行测试")
109
- self._vulnerability_testing()
110
-
111
- # ========== 阶段 3: 云存储测试 ==========
112
- print("\n[阶段 3] 云存储测试")
113
- print("-" * 50)
114
- self._cloud_storage_test()
115
-
116
- # ========== 阶段 4: 报告 ==========
117
- print("\n[阶段 4] 报告")
118
- print("-" * 50)
119
- self._generate_report()
120
-
121
- return self._get_result()
122
-
123
- def _check_prerequisites(self):
124
- """前置检查"""
125
- self.playwright_available, self.browser_type, can_proceed = prerequisite_check()
126
-
127
- if can_proceed:
128
- import requests
129
- self.session = requests.Session()
130
- self.session.headers.update({'User-Agent': 'Mozilla/5.0'})
131
- print(" [OK] 前置检查通过")
132
- else:
133
- print(" [FAIL] 前置检查失败")
134
-
135
- def _static_analysis(self):
136
- """静态分析"""
137
- self.parser = APIEndpointParser(self.target, self.session)
138
-
139
- self.js_files = self.parser.discover_js_files()
140
- print(f" JS 文件: {len(self.js_files)}")
141
-
142
- self.static_endpoints = self.parser.parse_js_files(self.js_files)
143
- print(f" 静态端点: {len(self.static_endpoints)}")
144
-
145
- for ep in self.static_endpoints[:5]:
146
- print(f" {ep.method} {ep.path}")
147
-
148
- def _detect_site_type(self):
149
- """判断站点类型"""
150
- html = self.session.get(self.target, timeout=10).text.lower()
151
-
152
- frontend = 'Unknown'
153
- ui = 'Unknown'
154
-
155
- if 'vue' in html:
156
- frontend = 'Vue.js'
157
- if 'react' in html:
158
- frontend = 'React'
159
- if 'angular' in html:
160
- frontend = 'Angular'
161
- if 'jquery' in html:
162
- frontend = 'jQuery'
163
-
164
- if 'element-ui' in html or 'element ui' in html:
165
- ui = 'ElementUI'
166
- if 'ant-design' in html:
167
- ui = 'Ant Design'
168
-
169
- if len(self.js_files) == 0:
170
- self.site_type = 'pure_html'
171
- elif frontend == 'Unknown' and len(self.js_files) > 0:
172
- self.site_type = 'modern_spa'
173
- frontend = 'Vue.js/React (推断)'
174
- elif frontend in ['Vue.js', 'React', 'Angular']:
175
- self.site_type = 'modern_spa'
176
- elif frontend == 'jQuery':
177
- self.site_type = 'jquery_spa'
178
- else:
179
- self.site_type = 'unknown'
180
-
181
- print(f" 站点类型: {self.site_type}")
182
- print(f" 前端框架: {frontend}")
183
- print(f" UI 框架: {ui}")
184
-
185
- def _probe_parent_paths(self):
186
- """父路径探测"""
187
- self.parent_paths = self.parser.probe_parent_paths()
188
-
189
- json_api_count = sum(1 for p in self.parent_paths.values() if p.get('is_api'))
190
- html_fallback_count = len(self.parent_paths) - json_api_count
191
-
192
- print(f" 父路径: {len(self.parent_paths)}")
193
- print(f" JSON API: {json_api_count}")
194
- print(f" HTML fallback: {html_fallback_count}")
195
-
196
- # v2.0: 从父路径提取API前缀
197
- for p in self.parent_paths.values():
198
- if p.get('prefix'):
199
- self.api_prefix_sources['parent_paths'] = p.get('prefix')
200
- self.api_prefix = p.get('prefix')
201
- print(f" [API Prefix] from parent_paths: {self.api_prefix}")
202
- break
203
-
204
- # v2.0: 判断 has_real_api
205
- if json_api_count > 0:
206
- self.has_real_api = True
207
- print(f" [OK] 父路径发现真实 API")
208
- else:
209
- self.has_real_api = False
210
-
211
- def _dynamic_analysis(self):
212
- """动态分析 v2.0"""
213
- print(" [动态分析] 启动 Playwright...")
214
- try:
215
- from core.dynamic_api_analyzer import DynamicAPIAnalyzer
216
- from urllib.parse import urlparse
217
-
218
- analyzer = DynamicAPIAnalyzer(self.target)
219
- results = analyzer.analyze_full()
220
-
221
- count = len(results.get('endpoints', []))
222
- print(f" [动态分析] 发现 {count} 个端点")
223
-
224
- self.dynamic_endpoints = results.get('endpoints', [])
225
-
226
- # v2.0: 从动态端点URL自动提取API前缀
227
- target_host = urlparse(self.target).netloc
228
- for ep in self.dynamic_endpoints:
229
- ep_path = ep.get('path', '')
230
- if ep_path.startswith('http'):
231
- ep_host = urlparse(ep_path).netloc
232
- ep_path_only = urlparse(ep_path).path
233
- if ep_host == target_host and ep_path_only.startswith('/'):
234
- # 提取API前缀:通常是 /xxx/xxx 格式
235
- parts = ep_path_only.strip('/').split('/')
236
- if len(parts) >= 2:
237
- potential_prefix = '/' + '/'.join(parts[:2])
238
- if potential_prefix not in ['/login', '/logout', '/static', '/js', '/css']:
239
- self.api_prefix_sources['dynamic'] = potential_prefix
240
- self.api_prefix = potential_prefix
241
- print(f" [API Prefix] from dynamic: {self.api_prefix}")
242
- break
243
-
244
- for ep in self.dynamic_endpoints[:5]:
245
- print(f" {ep.get('method', 'GET')} {ep.get('path')}")
246
-
247
- # v2.0: 更新动态端点状态
248
- if count > 0:
249
- self.has_dynamic_endpoints = True
250
- print(f" [OK] 动态分析发现真实 API")
251
-
252
- except Exception as e:
253
- print(f" [动态分析] 失败: {e}")
254
-
255
- def _api_hook(self):
256
- """API Hook"""
257
- print(" [API Hook] 启动 Hook...")
258
- try:
259
- from core.api_interceptor import APIInterceptor
260
-
261
- interceptor = APIInterceptor(self.target)
262
- results = interceptor.hook_all_apis()
263
-
264
- count = len(results.get('endpoints', []))
265
- print(f" [API Hook] 捕获 {count} 个 API 调用")
266
-
267
- self.hooked_endpoints = results.get('endpoints', [])
268
-
269
- except Exception as e:
270
- print(f" [API Hook] 失败: {e}")
271
-
272
- def _update_decision_state(self):
273
- """v2.0: 更新决策状态 - 综合判断"""
274
- json_api_count = sum(1 for p in self.parent_paths.values() if p.get('is_api'))
275
- dynamic_count = len(self.dynamic_endpoints)
276
-
277
- print("\n[决策] 综合判断 API 存在性")
278
- print(f" - 父路径 JSON API: {json_api_count}")
279
- print(f" - 动态端点: {dynamic_count}")
280
-
281
- # v2.0 核心修复:综合判断
282
- # has_real_api: 任意来源有API即认为有真实API
283
- self.has_real_api = json_api_count > 0 or dynamic_count > 0
284
- self.has_dynamic_endpoints = dynamic_count > 0
285
-
286
- # v2.0: 只有当父路径存在但全返回HTML,且无动态端点时才是真正的fallback
287
- # 父路径探测失败但有动态端点,说明API是动态加载的,不是nginx fallback
288
-
289
- # v2.0: html_fallback_all 判断
290
- # 只有当没有任何API发现时才认为是fallback
291
- self.html_fallback_all = (
292
- len(self.parent_paths) > 0 and
293
- json_api_count == 0 and
294
- dynamic_count == 0
295
- )
296
-
297
- print(f" has_real_api: {self.has_real_api}")
298
- print(f" has_dynamic_endpoints: {self.has_dynamic_endpoints}")
299
- print(f" html_fallback_all: {self.html_fallback_all}")
300
-
301
- def _vulnerability_testing(self):
302
- """漏洞测试 v2.0 - 集成API Fuzzer进行深度测试"""
303
-
304
- print(" [漏洞测试] 启动深度渗透测试...")
305
-
306
- # 导入fuzzer
307
- try:
308
- from core.api_fuzzer import APIfuzzer, auto_fuzz
309
- fuzzer_available = True
310
- except ImportError:
311
- fuzzer_available = False
312
- print(" [警告] API Fuzzer模块不可用,使用基础测试")
313
-
314
- # v2.0: 合并所有端点
315
- all_endpoints = self._merge_all_endpoints()
316
-
317
- # v2.0: 按优先级排序
318
- sorted_endpoints = self._sort_by_priority(all_endpoints)
319
-
320
- print(f" [漏洞测试] 总端点: {len(sorted_endpoints)}")
321
-
322
- # 打印认证端点详情
323
- auth_count = 0
324
- for ep in sorted_endpoints:
325
- if self._is_auth_endpoint(ep['path']):
326
- auth_count += 1
327
- print(f" [漏洞测试] 认证端点: {auth_count}")
328
-
329
- # ========== 深度Fuzzing测试 ==========
330
- if fuzzer_available:
331
- print("\n [深度测试] 执行API Fuzzing...")
332
- self._run_deep_fuzz_test(sorted_endpoints)
333
-
334
- # ========== 基础漏洞测试 ==========
335
- print("\n [基础测试] SQL注入检测...")
336
- self._test_sql_injection(sorted_endpoints)
337
-
338
- # ========== 未授权访问测试 ==========
339
- print("\n [基础测试] 未授权访问检测...")
340
- self._test_unauthorized_access(sorted_endpoints)
341
-
342
- print(f"\n 发现漏洞: {len(self.vulnerabilities)}")
343
-
344
- # 打印漏洞摘要
345
- if self.vulnerabilities:
346
- print("\n [漏洞摘要]")
347
- for v in self.vulnerabilities[:10]:
348
- print(f" [{v['severity']}] {v['type']} - {v.get('endpoint', 'N/A')}")
349
-
350
- def _run_deep_fuzz_test(self, endpoints):
351
- """深度Fuzz测试"""
352
- from core.api_fuzzer import APIfuzzer
353
-
354
- fuzzer = APIfuzzer(session=self.session)
355
-
356
- # 提取路径列表
357
- api_paths = []
358
- for ep in endpoints:
359
- path = ep['path']
360
- if path.startswith('http'):
361
- from urllib.parse import urlparse
362
- path = urlparse(path).path
363
- api_paths.append(path)
364
-
365
- # 生成fuzz目标
366
- fuzz_targets = fuzzer.generate_parent_fuzz_targets(api_paths, max_per_parent=30)
367
- print(f" [Fuzz] 生成 {len(fuzz_targets)} 个测试目标")
368
-
369
- # 执行fuzz
370
- base_url = self.target.split('?')[0].rstrip('/')
371
- results = fuzzer.fuzz_paths(base_url, fuzz_targets[:100], timeout=3.0)
372
-
373
- # 分析结果
374
- alive = fuzzer.get_alive_endpoints()
375
- high_value = fuzzer.get_high_value_endpoints()
376
-
377
- print(f" [Fuzz] 存活端点: {len(alive)}")
378
- print(f" [Fuzz] 高价值端点: {len(high_value)}")
379
-
380
- # 发现新端点
381
- for r in high_value[:5]:
382
- print(f" [发现] {r.method} {r.path} -> {r.status_code}")
383
-
384
- def _test_sql_injection(self, endpoints):
385
- """SQL注入测试"""
386
- sqli_payloads = [
387
- "' OR '1'='1",
388
- "' OR '1'='1' --",
389
- "' OR '1'='1' #",
390
- "1' OR '1'='1",
391
- "' OR ''='",
392
- ]
393
-
394
- test_params = ['id', 'page', 'pageNum', 'pageSize', 'userId', 'id']
395
-
396
- for ep in endpoints[:20]:
397
- path = ep['path']
398
- method = ep.get('method', 'GET')
399
- priority = ep.get('priority', 'low')
400
-
401
- # 构建URL - 正确添加API前缀
402
- if path.startswith('http'):
403
- from urllib.parse import urlparse, parse_qs
404
- parsed = urlparse(path)
405
- base = f"{parsed.scheme}://{parsed.netloc}{parsed.path}"
406
- existing_params = parse_qs(parsed.query)
407
- else:
408
- base = self.target.split('?')[0].rstrip('/')
409
- # v2.0: 如果静态端点没有前缀,但有已知的api_prefix,添加前缀
410
- path_to_add = path
411
- if self.api_prefix and not path.startswith('/personnelWeb'):
412
- # 从 /personnelWeb/auth 提取 /personnelWeb
413
- prefix_base = '/' + self.api_prefix.split('/')[1]
414
- if path.startswith('/users') or path.startswith('/system') or path.startswith('/menu'):
415
- path_to_add = prefix_base + path
416
- print(f" [修正] {path} -> {path_to_add}")
417
- base = base + path_to_add
418
- existing_params = {}
419
-
420
- # 添加测试参数
421
- for param in test_params:
422
- if param not in existing_params:
423
- test_url = f"{base}?{param}="
424
- break
425
- else:
426
- continue
427
-
428
- for payload in sqli_payloads[:2]:
429
- try:
430
- if method == 'POST':
431
- r = self.session.post(
432
- base,
433
- json={param: payload for param in test_params},
434
- timeout=5
435
- )
436
- else:
437
- r = self.session.get(test_url + payload, timeout=5)
438
-
439
- ct = r.headers.get('Content-Type', '').lower()
440
-
441
- # 检查SQL错误
442
- text_lower = r.text.lower()
443
- sql_patterns = [
444
- 'sql syntax', 'sql error', 'mysql', 'oracle',
445
- 'sqlite', 'sqlstate', 'postgresql', 'syntax error',
446
- 'microsoft sql', 'odbc', 'ora-', 'pgsql'
447
- ]
448
- if any(p in text_lower for p in sql_patterns):
449
- self.vulnerabilities.append({
450
- 'type': 'SQL Injection',
451
- 'severity': 'CRITICAL',
452
- 'endpoint': path,
453
- 'param': param,
454
- 'payload': payload,
455
- 'priority': priority,
456
- 'evidence': 'SQL error detected'
457
- })
458
- print(f" [!] SQL注入: {path} ({param}={payload})")
459
- break
460
-
461
- # 检查异常响应
462
- if r.status_code == 500 and 'error' in text_lower:
463
- self.vulnerabilities.append({
464
- 'type': 'Potential SQL Injection',
465
- 'severity': 'MEDIUM',
466
- 'endpoint': path,
467
- 'param': param,
468
- 'payload': payload,
469
- 'priority': priority,
470
- 'evidence': f'Server error {r.status_code}'
471
- })
472
-
473
- except:
474
- pass
475
-
476
- def _test_unauthorized_access(self, endpoints):
477
- """未授权访问测试"""
478
- sensitive_patterns = [
479
- '/admin', '/user/list', '/user/export', '/config',
480
- '/system', '/manage', '/dashboard', '/api/users'
481
- ]
482
-
483
- for ep in endpoints:
484
- path = ep['path']
485
- method = ep.get('method', 'GET')
486
-
487
- # 检查敏感路径
488
- if not any(p in path.lower() for p in sensitive_patterns):
489
- continue
490
-
491
- # 构建URL
492
- if path.startswith('http'):
493
- from urllib.parse import urlparse
494
- url = f"{urlparse(path).scheme}://{urlparse(path).netloc}{urlparse(path).path}"
495
- else:
496
- url = self.target.split('?')[0].rstrip('/') + path
497
-
498
- try:
499
- # 不带认证信息访问
500
- if method == 'POST':
501
- r = self.session.post(url, json={}, timeout=5)
502
- else:
503
- r = self.session.get(url, timeout=5)
504
-
505
- # 检查是否返回敏感数据(未授权访问成功)
506
- if r.status_code == 200:
507
- text_lower = r.text.lower()
508
- if any(k in text_lower for k in ['user', 'admin', 'password', 'email', 'phone']):
509
- self.vulnerabilities.append({
510
- 'type': 'Unauthorized Access',
511
- 'severity': 'HIGH',
512
- 'endpoint': path,
513
- 'method': method,
514
- 'evidence': f'Sensitive data exposed without auth'
515
- })
516
- print(f" [!] 未授权访问: {method} {path}")
517
-
518
- # 401/403 -> 需要认证(正常)
519
- # 200 + 无敏感数据 -> 可能不需要认证
520
-
521
- except:
522
- pass
523
-
524
- def _merge_all_endpoints(self):
525
- """v2.0: 合并所有端点"""
526
- all_endpoints = []
527
- seen = set()
528
-
529
- # 静态端点
530
- for ep in self.static_endpoints:
531
- key = f"{ep.method}:{ep.path}"
532
- if key not in seen:
533
- seen.add(key)
534
- all_endpoints.append({
535
- 'path': ep.path,
536
- 'method': ep.method,
537
- 'source': 'static'
538
- })
539
-
540
- # 动态端点 (v2.0 新增)
541
- for ep in self.dynamic_endpoints:
542
- key = f"{ep.get('method', 'GET')}:{ep.get('path', '')}"
543
- if key not in seen and ep.get('path'):
544
- seen.add(key)
545
- all_endpoints.append({
546
- 'path': ep.get('path'),
547
- 'method': ep.get('method', 'GET'),
548
- 'source': 'dynamic'
549
- })
550
-
551
- # Hook 端点
552
- for ep in self.hooked_endpoints:
553
- key = f"{ep.get('method', 'GET')}:{ep.get('path', '')}"
554
- if key not in seen and ep.get('path'):
555
- seen.add(key)
556
- all_endpoints.append({
557
- 'path': ep.get('path'),
558
- 'method': ep.get('method', 'GET'),
559
- 'source': 'hooked'
560
- })
561
-
562
- return all_endpoints
563
-
564
- def _is_auth_endpoint(self, path):
565
- """v2.0: 判断是否为认证端点"""
566
- auth_patterns = [
567
- '/auth/', '/login', '/oauth/', '/user/login',
568
- '/api/user/login', '/api/auth/', '/token', '/sso'
569
- ]
570
- path_lower = path.lower()
571
- return any(p in path_lower for p in auth_patterns)
572
-
573
- def _sort_by_priority(self, endpoints):
574
- """v2.0: 按优先级排序端点"""
575
- priority_map = {'high': 0, 'medium': 1, 'low': 2}
576
-
577
- def get_priority(ep):
578
- if self._is_auth_endpoint(ep['path']):
579
- return 'high'
580
- if '/api/' in ep['path'] or '/prod-api/' in ep['path']:
581
- return 'medium'
582
- return 'low'
583
-
584
- for ep in endpoints:
585
- ep['priority'] = get_priority(ep)
586
-
587
- return sorted(endpoints, key=lambda x: priority_map.get(x.get('priority'), 2))
588
-
589
- def _report_nginx_fallback(self):
590
- """报告 nginx fallback 问题"""
591
- self.vulnerabilities.append({
592
- 'type': 'Backend API Unreachable / nginx fallback',
593
- 'severity': 'HIGH',
594
- 'endpoint': 'Multiple paths',
595
- 'evidence': '父路径全部返回HTML,且无动态端点'
596
- })
597
- print(f" 添加问题: nginx fallback")
598
-
599
- def _cloud_storage_test(self):
600
- """云存储测试"""
601
- tester = CloudStorageTester(self.target)
602
- tester.session = self.session
603
- findings, storage_url = tester.full_test(self.target)
604
-
605
- print(f" 云存储: {storage_url}")
606
- print(f" 发现: {len(findings)}")
607
-
608
- self.cloud_findings = findings
609
-
610
- def _generate_report(self):
611
- """生成报告"""
612
- json_api_count = sum(1 for p in self.parent_paths.values() if p.get('is_api'))
613
-
614
- print("\n" + "=" * 50)
615
- print(" 测试完成 v2.0")
616
- print("=" * 50)
617
- print(f" 站点类型: {self.site_type}")
618
- print(f" 静态端点: {len(self.static_endpoints)}")
619
- print(f" 动态端点: {len(self.dynamic_endpoints)}")
620
- print(f" Hook 端点: {len(self.hooked_endpoints)}")
621
- print(f" JSON API: {json_api_count}")
622
- print(f" API Prefix: {self.api_prefix}")
623
- print(f" 漏洞: {len(self.vulnerabilities)}")
624
- print(f" 云存储: {len(self.cloud_findings)}")
625
-
626
- if self.api_prefix_sources:
627
- print(f" API Prefix来源: {self.api_prefix_sources}")
628
-
629
- def _get_result(self):
630
- """获取结果"""
631
- json_api_count = sum(1 for p in self.parent_paths.values() if p.get('is_api'))
632
-
633
- return {
634
- 'target': self.target,
635
- 'site_type': self.site_type,
636
- 'endpoints': {
637
- 'static': len(self.static_endpoints),
638
- 'dynamic': len(self.dynamic_endpoints),
639
- 'hooked': len(self.hooked_endpoints),
640
- 'parent_paths': len(self.parent_paths),
641
- 'json_api': json_api_count,
642
- 'merged': len(self._merge_all_endpoints()),
643
- },
644
- 'api_prefix': self.api_prefix,
645
- 'api_prefix_sources': self.api_prefix_sources,
646
- 'decision': {
647
- 'has_real_api': self.has_real_api,
648
- 'has_dynamic_endpoints': self.has_dynamic_endpoints,
649
- 'html_fallback_all': self.html_fallback_all,
650
- },
651
- 'vulnerabilities': self.vulnerabilities,
652
- 'cloud_findings': self.cloud_findings,
653
- }
654
-
655
-
656
- if __name__ == "__main__":
657
- import sys
658
-
659
- if len(sys.argv) > 1:
660
- target = sys.argv[1]
661
- else:
662
- print("Usage: python skill_executor_v2.py <target_url>")
663
- print("Example: python skill_executor_v2.py http://example.com")
664
- sys.exit(1)
665
-
666
- executor = SKILLExecutorV2(target)
667
- result = executor.run()
668
-
669
- print("\n\n结果:")
670
- print(result)