opencode-api-security-testing 3.0.8 → 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 (79) hide show
  1. package/agents/api-cyber-supervisor.md +9 -3
  2. package/agents/api-probing-miner.md +10 -2
  3. package/agents/api-resource-specialist.md +44 -35
  4. package/agents/api-vuln-verifier.md +56 -24
  5. package/package.json +1 -1
  6. package/postinstall.mjs +1 -0
  7. package/preuninstall.mjs +43 -32
  8. package/src/index.ts +3 -100
  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
  79. package/src/hooks/directory-agents-injector.ts +0 -106
@@ -1,479 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Browser Automation Tester - 浏览器动态测试引擎
4
- 支持 Playwright/Puppeteer/Selenium 多引擎
5
- """
6
-
7
- import json
8
- import time
9
- import re
10
- import warnings
11
- from typing import Dict, List, Tuple, Optional, Any
12
- from dataclasses import dataclass, field
13
- from enum import Enum
14
-
15
- warnings.filterwarnings("ignore")
16
-
17
-
18
- class BrowserEngine(Enum):
19
- PLAYWRIGHT = "playwright"
20
- PUPPETEER = "puppeteer"
21
- SELENIUM = "selenium"
22
- NONE = "none"
23
-
24
-
25
- @dataclass
26
- class XSSResult:
27
- vuln_type: str
28
- payload: str
29
- location: str
30
- sink: Optional[str] = None
31
- severity: str = "medium"
32
- evidence: str = ""
33
- url: str = ""
34
-
35
-
36
- @dataclass
37
- class BrowserTestConfig:
38
- target_url: str
39
- engine: BrowserEngine = BrowserEngine.NONE
40
- headless: bool = True
41
- timeout: int = 30000
42
- wait_for_selector: Optional[str] = None
43
- screenshot_on_error: bool = False
44
- console_log: bool = False
45
- user_data_dir: Optional[str] = None
46
- proxy: Optional[str] = None
47
-
48
-
49
- class BrowserAutomationTester:
50
- """
51
- 浏览器动态测试引擎
52
-
53
- 支持:
54
- - DOM XSS 检测
55
- - SPA 路由测试
56
- - 表单交互测试
57
- - JavaScript 执行检测
58
- - Cookie/Session 测试
59
- """
60
-
61
- def __init__(self, config: BrowserTestConfig):
62
- self.config = config
63
- self.engine = config.engine
64
- self.results: List[XSSResult] = []
65
- self._browser = None
66
- self._context = None
67
- self._page = None
68
- self._init_engine()
69
-
70
- def _init_engine(self):
71
- """初始化浏览器引擎"""
72
- if self.engine == BrowserEngine.PLAYWRIGHT:
73
- self._init_playwright()
74
- elif self.engine == BrowserEngine.SELENIUM:
75
- self._init_selenium()
76
- elif self.engine == BrowserEngine.PUPPETEER:
77
- self._init_puppeteer()
78
- else:
79
- print("[!] No browser engine available. Install: pip install playwright")
80
-
81
- def _init_playwright(self):
82
- """初始化 Playwright"""
83
- try:
84
- from playwright.sync_api import sync_playwright
85
- self._playwright = sync_playwright().start()
86
- self._browser = self._playwright.chromium.launch(
87
- headless=self.config.headless,
88
- args=["--no-sandbox", "--disable-dev-shm-usage"]
89
- )
90
- self._context = self._browser.new_context(
91
- ignore_https_errors=True,
92
- user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
93
- )
94
- self._page = self._context.new_page()
95
- if self.config.console_log:
96
- self._page.on("console", lambda msg: print(f"[Browser Console] {msg.text}"))
97
- print("[+] Playwright initialized successfully")
98
- except ImportError:
99
- print("[!] Playwright not installed. Run: pip install playwright && playwright install chromium")
100
- self.engine = BrowserEngine.NONE
101
- except Exception as e:
102
- print(f"[!] Playwright init failed: {e}")
103
- self.engine = BrowserEngine.NONE
104
-
105
- def _init_selenium(self):
106
- """初始化 Selenium"""
107
- try:
108
- from selenium import webdriver
109
- from selenium.webdriver.chrome.options import Options
110
- from selenium.webdriver.chrome.service import Service
111
-
112
- options = Options()
113
- if self.config.headless:
114
- options.add_argument("--headless")
115
- options.add_argument("--no-sandbox")
116
- options.add_argument("--disable-dev-shm-usage")
117
- if self.config.proxy:
118
- options.add_argument(f"--proxy-server={self.config.proxy}")
119
-
120
- self._driver = webdriver.Chrome(options=options)
121
- self._driver.set_page_load_timeout(self.config.timeout / 1000)
122
- print("[+] Selenium initialized successfully")
123
- except ImportError:
124
- print("[!] Selenium not installed. Run: pip install selenium")
125
- self.engine = BrowserEngine.NONE
126
- except Exception as e:
127
- print(f"[!] Selenium init failed: {e}")
128
- self.engine = BrowserEngine.NONE
129
-
130
- def _init_puppeteer(self):
131
- """初始化 Puppeteer (通过 pyppeteer)"""
132
- try:
133
- import asyncio
134
- from pyppeteer import launch
135
- asyncio.get_event_loop().run_until_complete(
136
- self._init_puppeteer_async()
137
- )
138
- except ImportError:
139
- print("[!] pyppeteer not installed. Run: pip install pyppeteer")
140
- self.engine = BrowserEngine.NONE
141
- except Exception as e:
142
- print(f"[!] Puppeteer init failed: {e}")
143
- self.engine = BrowserEngine.NONE
144
-
145
- async def _init_puppeteer_async(self):
146
- self._browser = await launch(
147
- headless=self.config.headless,
148
- args=["--no-sandbox", "--disable-dev-shm-usage"]
149
- )
150
- self._page = await self._browser.newPage()
151
- print("[+] Puppeteer initialized successfully")
152
-
153
- def test_dom_xss(self, payloads: List[Dict]) -> List[XSSResult]:
154
- """
155
- 测试 DOM XSS 漏洞
156
-
157
- 检测 sinks: innerHTML, eval, document.write, location.href, etc.
158
- """
159
- if self.engine == BrowserEngine.NONE:
160
- return []
161
-
162
- dom_sinks = {
163
- "innerHTML": r'innerHTML\s*=',
164
- "outerHTML": r'outerHTML\s*=',
165
- "insertAdjacentHTML": r'insertAdjacentHTML',
166
- "document.write": r'document\.write',
167
- "eval": r'eval\(',
168
- "setTimeout": r'setTimeout\s*\(',
169
- "setInterval": r'setInterval\s*\(',
170
- "Function": r'new\s+Function\(',
171
- "location.href": r'location\.href',
172
- "location.hash": r'location\.hash',
173
- "location.search": r'location\.search',
174
- "document.cookie": r'document\.cookie',
175
- "localStorage": r'localStorage\.',
176
- "sessionStorage": r'sessionStorage\.',
177
- }
178
-
179
- xss_payloads = [p for p in payloads if p.get("type") in ["dom", "reflected", "polyglot"]]
180
-
181
- print(f"[*] Testing {len(xss_payloads)} DOM XSS payloads...")
182
-
183
- for payload_data in xss_payloads:
184
- payload = payload_data.get("payload", "")
185
- sink = payload_data.get("sink", "")
186
-
187
- for url_pattern, source in [
188
- (f"{self.config.target_url}?input={payload}", "URL parameter"),
189
- (f"{self.config.target_url}#{payload}", "URL fragment"),
190
- (f"{self.config.target_url}?redirect={payload}", "redirect param"),
191
- ]:
192
- try:
193
- self._navigate_and_check(url_pattern, payload, sink, dom_sinks)
194
- except Exception as e:
195
- print(f"[!] Error testing payload: {e}")
196
-
197
- return self.results
198
-
199
- def _navigate_and_check(self, url: str, payload: str, sink: str, dom_sinks: Dict):
200
- """导航到 URL 并检查 XSS"""
201
- if self.engine == BrowserEngine.PLAYWRIGHT:
202
- self._page.goto(url, timeout=self.config.timeout)
203
- self._page.wait_for_load_state("networkidle", timeout=self.config.timeout)
204
- time.sleep(1)
205
-
206
- content = self._page.content()
207
-
208
- for sink_pattern, sink_name in dom_sinks.items():
209
- if re.search(sink_pattern, content, re.IGNORECASE):
210
- if payload in content or any(x in content for x in ["<script", "<img", "<svg"]):
211
- self.results.append(XSSResult(
212
- vuln_type="DOM XSS",
213
- payload=payload,
214
- location="client-side",
215
- sink=sink_name,
216
- severity="high",
217
- evidence=f"Sink '{sink_name}' found with payload",
218
- url=url
219
- ))
220
- return
221
-
222
- def test_spa_routing(self, routes: List[str]) -> Dict[str, Any]:
223
- """
224
- 测试 SPA 路由安全性
225
-
226
- 1. 测试路由参数中的 XSS
227
- 2. 测试认证绕过
228
- 3. 测试敏感端点访问
229
- """
230
- if self.engine == BrowserEngine.NONE:
231
- return {"error": "No browser engine"}
232
-
233
- results = {
234
- "routes_tested": [],
235
- "xss_found": [],
236
- "auth_bypass": [],
237
- "sensitive_access": []
238
- }
239
-
240
- test_params = ["<script>alert(1)</script>", "' Or '1'='1", "../../../etc/passwd"]
241
-
242
- for route in routes:
243
- full_url = f"{self.config.target_url}{route}"
244
-
245
- try:
246
- if self.engine == BrowserEngine.PLAYWRIGHT:
247
- self._page.goto(full_url, timeout=self.config.timeout)
248
- self._page.wait_for_load_state("networkidle")
249
- time.sleep(1)
250
-
251
- results["routes_tested"].append(route)
252
-
253
- content = self._page.content()
254
- for param in test_params:
255
- if param in content:
256
- results["xss_found"].append({
257
- "route": route,
258
- "payload": param
259
- })
260
-
261
- title = self._page.title()
262
- if "admin" in title.lower() or "dashboard" in title.lower():
263
- results["sensitive_access"].append({
264
- "route": route,
265
- "title": title
266
- })
267
-
268
- except Exception as e:
269
- print(f"[!] Error testing route {route}: {e}")
270
-
271
- return results
272
-
273
- def test_form_interaction(self, form_selectors: List[Dict]) -> List[XSSResult]:
274
- """
275
- 测试表单交互 XSS
276
-
277
- 填写表单并提交,检测存储型 XSS
278
- """
279
- if self.engine == BrowserEngine.NONE:
280
- return []
281
-
282
- results = []
283
-
284
- xss_test_payloads = [
285
- "<script>alert(1)</script>",
286
- "<img src=x onerror=alert(1)>",
287
- "javascript:alert(1)",
288
- "'; alert(1); //"
289
- ]
290
-
291
- for form in form_selectors:
292
- selector = form.get("selector")
293
- fields = form.get("fields", {})
294
-
295
- for payload in xss_test_payloads:
296
- try:
297
- if self.engine == BrowserEngine.PLAYWRIGHT:
298
- self._page.goto(self.config.target_url, timeout=self.config.timeout)
299
- self._page.wait_for_load_state("networkidle")
300
-
301
- for field_name, field_type in fields.items():
302
- if field_type == "text":
303
- self._page.fill(f"input[name='{field_name}']", payload)
304
- elif field_type == "textarea":
305
- self._page.fill(f"textarea[name='{field_name}']", payload)
306
-
307
- if "submit" in fields:
308
- self._page.click(f"button[type='submit'], input[type='submit']")
309
- time.sleep(2)
310
-
311
- content = self._page.content()
312
- if payload in content:
313
- results.append(XSSResult(
314
- vuln_type="Stored XSS",
315
- payload=payload,
316
- location=f"Form: {selector}",
317
- severity="critical",
318
- evidence="Payload persisted after submission"
319
- ))
320
-
321
- except Exception as e:
322
- print(f"[!] Form test error: {e}")
323
-
324
- return results
325
-
326
- def test_javascript_execution(self, payload: str) -> Tuple[bool, str]:
327
- """
328
- 测试 JavaScript 是否被执行
329
-
330
- 使用 console.log 配合 CDP 获取执行结果
331
- """
332
- if self.engine == BrowserEngine.NONE:
333
- return False, "No browser engine"
334
-
335
- test_payloads = [
336
- f"<script>console.log('XSS_TEST_{payload[:20]}')</script>",
337
- f"<img src=x onerror='console.log(\"XSS_TEST_{payload[:20]}\")'>",
338
- f"<svg onload='console.log(\"XSS_TEST_{payload[:20]}\")'>"
339
- ]
340
-
341
- for test_payload in test_payloads:
342
- try:
343
- if self.engine == BrowserEngine.PLAYWRIGHT:
344
- console_messages = []
345
-
346
- def handle_console(msg):
347
- if "XSS_TEST" in msg.text:
348
- console_messages.append(msg.text)
349
-
350
- self._page.on("console", handle_console)
351
-
352
- test_url = f"{self.config.target_url}?input={test_payload}"
353
- self._page.goto(test_url, timeout=self.config.timeout)
354
- self._page.wait_for_load_state("networkidle")
355
- time.sleep(2)
356
-
357
- if console_messages:
358
- return True, f"JS executed: {console_messages}"
359
-
360
- except Exception as e:
361
- continue
362
-
363
- return False, "No JS execution detected"
364
-
365
- def capture_network_requests(self) -> List[Dict]:
366
- """捕获网络请求,用于分析 API 调用"""
367
- if self.engine == BrowserEngine.PLAYWRIGHT:
368
- requests = []
369
-
370
- def handle_request(request):
371
- requests.append({
372
- "url": request.url,
373
- "method": request.method,
374
- "headers": dict(request.headers),
375
- "post_data": request.post_data
376
- })
377
-
378
- self._page.on("request", handle_request)
379
- self._page.reload()
380
- self._page.wait_for_load_state("networkidle")
381
-
382
- return requests
383
- return []
384
-
385
- def get_client_storage(self) -> Dict[str, Dict]:
386
- """获取客户端存储 (localStorage, sessionStorage, cookies)"""
387
- if self.engine == BrowserEngine.PLAYWRIGHT:
388
- return {
389
- "localStorage": self._page.evaluate("() => JSON.stringify(localStorage)"),
390
- "sessionStorage": self._page.evaluate("() => JSON.stringify(sessionStorage)"),
391
- "cookies": [dict(c) for c in self._context.cookies()]
392
- }
393
- return {}
394
-
395
- def close(self):
396
- """关闭浏览器"""
397
- try:
398
- if self._browser:
399
- self._browser.close()
400
- if hasattr(self, '_driver') and self._driver:
401
- self._driver.quit()
402
- except:
403
- pass
404
-
405
-
406
- def auto_detect_engine() -> BrowserEngine:
407
- """自动检测可用的浏览器引擎"""
408
- try:
409
- from playwright.sync_api import sync_playwright
410
- return BrowserEngine.PLAYWRIGHT
411
- except ImportError:
412
- pass
413
-
414
- try:
415
- from selenium import webdriver
416
- return BrowserEngine.SELENIUM
417
- except ImportError:
418
- pass
419
-
420
- return BrowserEngine.NONE
421
-
422
-
423
- def create_tester(target_url: str, headless: bool = True) -> Optional[BrowserAutomationTester]:
424
- """创建浏览器测试器,自动选择可用引擎"""
425
- engine = auto_detect_engine()
426
-
427
- if engine == BrowserEngine.NONE:
428
- print("[!] No browser automation library found.")
429
- print("[*] Install one of:")
430
- print(" - Playwright: pip install playwright && playwright install chromium")
431
- print(" - Selenium: pip install selenium")
432
- return None
433
-
434
- config = BrowserTestConfig(
435
- target_url=target_url,
436
- engine=engine,
437
- headless=headless,
438
- timeout=30000,
439
- console_log=False
440
- )
441
-
442
- return BrowserAutomationTester(config)
443
-
444
-
445
- # CLI interface
446
- if __name__ == "__main__":
447
- import argparse
448
-
449
- parser = argparse.ArgumentParser(description="Browser Automation Security Tester")
450
- parser.add_argument("--target", required=True, help="Target URL")
451
- parser.add_argument("--type", choices=["dom", "spa", "form", "all"], default="all")
452
- parser.add_argument("--payloads", help="Path to payloads JSON file")
453
- parser.add_argument("--routes", nargs="+", help="SPA routes to test")
454
- parser.add_argument("--headless", action="store_true", default=True)
455
-
456
- args = parser.parse_args()
457
-
458
- tester = create_tester(args.target, headless=args.headless)
459
-
460
- if not tester:
461
- exit(1)
462
-
463
- payloads = [
464
- {"id": "xss-001", "payload": "<script>alert(1)</script>", "type": "dom"},
465
- {"id": "xss-002", "payload": "<img src=x onerror=alert(1)>", "type": "reflected"},
466
- ]
467
-
468
- if args.type in ["dom", "all"]:
469
- print("\n[*] Running DOM XSS tests...")
470
- results = tester.test_dom_xss(payloads)
471
- print(f"[+] Found {len(results)} DOM XSS vulnerabilities")
472
-
473
- if args.type in ["spa", "all"]:
474
- routes = args.routes or ["/", "/admin", "/dashboard", "/login"]
475
- print("\n[*] Running SPA routing tests...")
476
- results = tester.test_spa_routing(routes)
477
- print(f"[+] Tested {len(results.get('routes_tested', []))} routes")
478
-
479
- tester.close()