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,179 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- HTTP Client - 同步/异步 HTTP 客户端
4
- """
5
-
6
- import asyncio
7
- import time
8
- from typing import Dict, Optional, Any, Callable
9
- from dataclasses import dataclass
10
- import requests
11
- from requests.adapters import HTTPAdapter
12
- from urllib3.util.retry import Retry
13
-
14
-
15
- @dataclass
16
- class HTTPClientConfig:
17
- """HTTP 客户端配置"""
18
- max_concurrent: int = 50
19
- max_retries: int = 3
20
- timeout: int = 30
21
- proxy: Optional[str] = None
22
- verify_ssl: bool = True
23
- retry_backoff: float = 0.5
24
-
25
-
26
- class HTTPClient:
27
- """
28
- HTTP 客户端
29
-
30
- 功能:
31
- - 同步/异步请求
32
- - 并发控制
33
- - 重试机制
34
- - 代理支持
35
- """
36
-
37
- def __init__(self, config: Optional[HTTPClientConfig] = None):
38
- self.config = config or HTTPClientConfig()
39
- self.session = self._create_session()
40
- self._semaphore: Optional[asyncio.Semaphore] = None
41
-
42
- def _create_session(self) -> requests.Session:
43
- """创建 requests session"""
44
- session = requests.Session()
45
-
46
- retry_strategy = Retry(
47
- total=self.config.max_retries,
48
- backoff_factor=self.config.retry_backoff,
49
- status_forcelist=[429, 500, 502, 503, 504],
50
- )
51
-
52
- adapter = HTTPAdapter(max_retries=retry_strategy, pool_maxsize=self.config.max_concurrent)
53
- session.mount("http://", adapter)
54
- session.mount("https://", adapter)
55
-
56
- session.headers.update({
57
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
58
- 'Accept': '*/*',
59
- 'Accept-Encoding': 'gzip, deflate',
60
- 'Connection': 'keep-alive',
61
- })
62
-
63
- if self.config.proxy:
64
- session.proxies = {
65
- 'http': self.config.proxy,
66
- 'https': self.config.proxy,
67
- }
68
-
69
- return session
70
-
71
- def get(self, url: str, **kwargs) -> requests.Response:
72
- """GET 请求"""
73
- kwargs.setdefault('timeout', self.config.timeout)
74
- kwargs.setdefault('verify', self.config.verify_ssl)
75
- return self.session.get(url, **kwargs)
76
-
77
- def post(self, url: str, **kwargs) -> requests.Response:
78
- """POST 请求"""
79
- kwargs.setdefault('timeout', self.config.timeout)
80
- kwargs.setdefault('verify', self.config.verify_ssl)
81
- return self.session.post(url, **kwargs)
82
-
83
- def put(self, url: str, **kwargs) -> requests.Response:
84
- """PUT 请求"""
85
- kwargs.setdefault('timeout', self.config.timeout)
86
- kwargs.setdefault('verify', self.config.verify_ssl)
87
- return self.session.put(url, **kwargs)
88
-
89
- def delete(self, url: str, **kwargs) -> requests.Response:
90
- """DELETE 请求"""
91
- kwargs.setdefault('timeout', self.config.timeout)
92
- kwargs.setdefault('verify', self.config.verify_ssl)
93
- return self.session.delete(url, **kwargs)
94
-
95
- def request(self, method: str, url: str, **kwargs) -> requests.Response:
96
- """通用请求"""
97
- kwargs.setdefault('timeout', self.config.timeout)
98
- kwargs.setdefault('verify', self.config.verify_ssl)
99
- return self.session.request(method, url, **kwargs)
100
-
101
- def batch_request(self, urls: list, method: str = 'GET', **kwargs) -> Dict[str, requests.Response]:
102
- """批量请求"""
103
- import concurrent.futures
104
-
105
- results = {}
106
-
107
- def fetch(url):
108
- try:
109
- resp = self.request(method, url, **kwargs)
110
- return url, resp
111
- except Exception as e:
112
- return url, None
113
-
114
- with concurrent.futures.ThreadPoolExecutor(max_workers=self.config.max_concurrent) as executor:
115
- futures = {executor.submit(fetch, url): url for url in urls}
116
- for future in concurrent.futures.as_completed(futures):
117
- url, resp = future.result()
118
- results[url] = resp
119
-
120
- return results
121
-
122
-
123
- class AsyncHTTPClient:
124
- """
125
- 异步 HTTP 客户端
126
- """
127
-
128
- def __init__(self, config: Optional[HTTPClientConfig] = None):
129
- self.config = config or HTTPClientConfig()
130
- self._session: Optional[aiohttp.ClientSession] = None
131
- self._semaphore = asyncio.Semaphore(self.config.max_concurrent)
132
-
133
- async def _get_session(self) -> 'aiohttp.ClientSession':
134
- """获取或创建 session"""
135
- if self._session is None or self._session.closed:
136
- timeout = aiohttp.ClientTimeout(total=self.config.timeout)
137
- self._session = aiohttp.ClientSession(
138
- timeout=timeout,
139
- headers={
140
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
141
- }
142
- )
143
- return self._session
144
-
145
- async def get(self, url: str, **kwargs) -> 'aiohttp.ClientResponse':
146
- """GET 请求"""
147
- session = await self._get_session()
148
- async with self._semaphore:
149
- return await session.get(url, **kwargs)
150
-
151
- async def post(self, url: str, **kwargs) -> 'aiohttp.ClientResponse':
152
- """POST 请求"""
153
- session = await self._get_session()
154
- async with self._semaphore:
155
- return await session.post(url, **kwargs)
156
-
157
- async def request(self, method: str, url: str, **kwargs) -> 'aiohttp.ClientResponse':
158
- """通用请求"""
159
- session = await self._get_session()
160
- async with self._semaphore:
161
- return await session.request(method, url, **kwargs)
162
-
163
- async def batch_request(self, urls: list, method: str = 'GET', **kwargs) -> Dict[str, Any]:
164
- """批量请求"""
165
- tasks = [self.request(method, url, **kwargs) for url in urls]
166
- responses = await asyncio.gather(*tasks, return_exceptions=True)
167
- return dict(zip(urls, responses))
168
-
169
- async def close(self):
170
- """关闭 session"""
171
- if self._session and not self._session.closed:
172
- await self._session.close()
173
-
174
-
175
- try:
176
- import aiohttp
177
- HAS_AIOHTTP = True
178
- except ImportError:
179
- HAS_AIOHTTP = False
package/core/models.py DELETED
@@ -1,296 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Data Models - 数据模型
4
- """
5
-
6
- from dataclasses import dataclass, field, asdict
7
- from typing import Dict, List, Optional, Set, Any
8
- from datetime import datetime
9
- from enum import Enum
10
-
11
-
12
- class Severity(Enum):
13
- """漏洞严重等级"""
14
- CRITICAL = "critical"
15
- HIGH = "high"
16
- MEDIUM = "medium"
17
- LOW = "low"
18
- INFO = "info"
19
-
20
-
21
- class EndpointSource(Enum):
22
- """端点来源"""
23
- JS_PARSER = "js_parser"
24
- AST_ANALYZER = "ast_analyzer"
25
- FUZZ_API = "fuzz_api"
26
- SWAGGER = "swagger"
27
- BROWSER = "browser"
28
- MANUAL = "manual"
29
-
30
-
31
- @dataclass
32
- class APIEndpoint:
33
- """
34
- API 端点模型
35
-
36
- 属性:
37
- - path: API 路径
38
- - method: HTTP 方法
39
- - parameters: 参数集合
40
- - source: 端点来源
41
- - full_url: 完整 URL
42
- - score: 评分
43
- - is_high_value: 是否高价值目标
44
- - status_code: HTTP 状态码
45
- - content_length: 响应内容长度
46
- """
47
- path: str
48
- method: str = "GET"
49
- parameters: Set[str] = field(default_factory=set)
50
- source: str = "unknown"
51
- full_url: str = ""
52
- score: int = 0
53
- is_high_value: bool = False
54
- status_code: int = 0
55
- content_length: int = 0
56
- headers: Dict[str, str] = field(default_factory=dict)
57
- tags: Set[str] = field(default_factory=set)
58
-
59
- def to_dict(self) -> Dict:
60
- return {
61
- 'path': self.path,
62
- 'method': self.method,
63
- 'parameters': list(self.parameters),
64
- 'source': self.source,
65
- 'full_url': self.full_url,
66
- 'score': self.score,
67
- 'is_high_value': self.is_high_value,
68
- 'status_code': self.status_code,
69
- 'content_length': self.content_length,
70
- 'tags': list(self.tags)
71
- }
72
-
73
- @property
74
- def endpoint_id(self) -> str:
75
- """端点唯一标识"""
76
- return f"{self.method}:{self.path}"
77
-
78
-
79
- @dataclass
80
- class Vulnerability:
81
- """
82
- 漏洞模型
83
-
84
- 属性:
85
- - vuln_type: 漏洞类型
86
- - severity: 严重等级
87
- - endpoint: 关联端点
88
- - method: HTTP 方法
89
- - payload: 攻击载荷
90
- - evidence: 证据
91
- """
92
- vuln_type: str
93
- severity: Severity = Severity.MEDIUM
94
- endpoint: str = ""
95
- method: str = "GET"
96
- payload: str = ""
97
- evidence: str = ""
98
- status_code: int = 0
99
- req_headers: Dict[str, str] = field(default_factory=dict)
100
- resp_headers: Dict[str, str] = field(default_factory=dict)
101
- resp_content: str = ""
102
- timestamp: str = field(default_factory=lambda: datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
103
-
104
- def to_dict(self) -> Dict:
105
- return {
106
- 'type': self.vuln_type,
107
- 'severity': self.severity.value,
108
- 'endpoint': self.endpoint,
109
- 'method': self.method,
110
- 'payload': self.payload,
111
- 'evidence': self.evidence,
112
- 'status_code': self.status_code,
113
- 'timestamp': self.timestamp
114
- }
115
-
116
-
117
- @dataclass
118
- class SensitiveData:
119
- """
120
- 敏感信息模型
121
- """
122
- data_type: str
123
- severity: Severity = Severity.MEDIUM
124
- endpoint: str = ""
125
- value: str = ""
126
- matched_pattern: str = ""
127
- context: str = ""
128
-
129
- def to_dict(self) -> Dict:
130
- return {
131
- 'data_type': self.data_type,
132
- 'severity': self.severity.value,
133
- 'endpoint': self.endpoint,
134
- 'value': self.value[:50] + "..." if len(self.value) > 50 else self.value,
135
- 'matched_pattern': self.matched_pattern
136
- }
137
-
138
-
139
- @dataclass
140
- class ScanResult:
141
- """
142
- 扫描结果模型
143
-
144
- 属性:
145
- - target_url: 目标 URL
146
- - start_time: 开始时间
147
- - end_time: 结束时间
148
- - status: 扫描状态
149
- - total_apis: 总 API 数
150
- - alive_apis: 存活 API 数
151
- - high_value_apis: 高价值 API 数
152
- - api_endpoints: API 端点列表
153
- - vulnerabilities: 漏洞列表
154
- - sensitive_data: 敏感信息列表
155
- - collector_data: 采集阶段数据
156
- - errors: 错误列表
157
- """
158
- target_url: str
159
- start_time: str = ""
160
- end_time: str = ""
161
- status: str = "pending"
162
- total_apis: int = 0
163
- alive_apis: int = 0
164
- high_value_apis: int = 0
165
- api_endpoints: List[APIEndpoint] = field(default_factory=list)
166
- vulnerabilities: List[Vulnerability] = field(default_factory=list)
167
- sensitive_data: List[SensitiveData] = field(default_factory=list)
168
- collector_data: Dict[str, Any] = field(default_factory=dict)
169
- errors: List[str] = field(default_factory=list)
170
-
171
- def to_dict(self) -> Dict:
172
- return {
173
- 'target_url': self.target_url,
174
- 'start_time': self.start_time,
175
- 'end_time': self.end_time,
176
- 'status': self.status,
177
- 'summary': {
178
- 'total_apis': self.total_apis,
179
- 'alive_apis': self.alive_apis,
180
- 'high_value_apis': self.high_value_apis,
181
- 'vulnerabilities': {
182
- 'critical': len([v for v in self.vulnerabilities if v.severity == Severity.CRITICAL]),
183
- 'high': len([v for v in self.vulnerabilities if v.severity == Severity.HIGH]),
184
- 'medium': len([v for v in self.vulnerabilities if v.severity == Severity.MEDIUM]),
185
- 'low': len([v for v in self.vulnerabilities if v.severity == Severity.LOW]),
186
- },
187
- 'sensitive_data': len(self.sensitive_data)
188
- },
189
- 'api_endpoints': [ep.to_dict() for ep in self.api_endpoints],
190
- 'vulnerabilities': [v.to_dict() for v in self.vulnerabilities],
191
- 'sensitive_data': [s.to_dict() for s in self.sensitive_data],
192
- 'errors': self.errors
193
- }
194
-
195
- def add_vulnerability(self, vuln: Vulnerability):
196
- """添加漏洞"""
197
- self.vulnerabilities.append(vuln)
198
-
199
- def add_endpoint(self, endpoint: APIEndpoint):
200
- """添加端点"""
201
- self.api_endpoints.append(endpoint)
202
-
203
- @property
204
- def vuln_count_by_severity(self) -> Dict[str, int]:
205
- """按严重等级统计漏洞"""
206
- return {
207
- 'critical': len([v for v in self.vulnerabilities if v.severity == Severity.CRITICAL]),
208
- 'high': len([v for v in self.vulnerabilities if v.severity == Severity.HIGH]),
209
- 'medium': len([v for v in self.vulnerabilities if v.severity == Severity.MEDIUM]),
210
- 'low': len([v for v in self.vulnerabilities if v.severity == Severity.LOW]),
211
- }
212
-
213
- @property
214
- def total_vulnerabilities(self) -> int:
215
- """漏洞总数"""
216
- return len(self.vulnerabilities)
217
-
218
-
219
- @dataclass
220
- class JSFile:
221
- """
222
- JS 文件模型
223
- """
224
- url: str
225
- content_hash: str = ""
226
- content: str = ""
227
- endpoints: List[Dict] = field(default_factory=list)
228
- parameter_names: Set[str] = field(default_factory=set)
229
- routes: List[str] = field(default_factory=list)
230
- env_configs: Dict[str, str] = field(default_factory=dict)
231
- is_alive: bool = False
232
- size: int = 0
233
-
234
- def to_dict(self) -> Dict:
235
- return {
236
- 'url': self.url,
237
- 'content_hash': self.content_hash,
238
- 'endpoints': self.endpoints,
239
- 'parameters': list(self.parameter_names),
240
- 'routes': self.routes,
241
- 'env_configs': self.env_configs,
242
- 'is_alive': self.is_alive,
243
- 'size': self.size
244
- }
245
-
246
-
247
- @dataclass
248
- class APIFindResult:
249
- """
250
- API 发现结果
251
- """
252
- path: str
253
- method: str = "GET"
254
- source_type: str = ""
255
- url_type: str = ""
256
- parameters: Set[str] = field(default_factory=set)
257
- confidence: float = 1.0
258
-
259
- def to_dict(self) -> Dict:
260
- return {
261
- 'path': self.path,
262
- 'method': self.method,
263
- 'source_type': self.source_type,
264
- 'url_type': self.url_type,
265
- 'parameters': list(self.parameters),
266
- 'confidence': self.confidence
267
- }
268
-
269
-
270
- @dataclass
271
- class ProxyResult:
272
- """
273
- 代理扫描结果
274
- """
275
- method: str
276
- url: str
277
- path: str
278
- headers: Dict[str, str]
279
- body: str = ""
280
- status_code: int = 0
281
- response_headers: Dict[str, str] = field(default_factory=dict)
282
- response_body: str = ""
283
- timestamp: str = field(default_factory=lambda: datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
284
-
285
- def to_dict(self) -> Dict:
286
- return {
287
- 'method': self.method,
288
- 'url': self.url,
289
- 'path': self.path,
290
- 'headers': self.headers,
291
- 'body': self.body[:500] if self.body else '',
292
- 'status_code': self.status_code,
293
- 'response_headers': self.response_headers,
294
- 'response_body': self.response_body[:500] if self.response_body else '',
295
- 'timestamp': self.timestamp
296
- }