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,212 +0,0 @@
1
- """
2
- 响应类型分析 - 识别响应是真实API还是WAF/路由/错误页
3
- 输入: {response, expected_type}
4
- 输出: {type, is_suspicious, suspicious_reasons, sensitive_fields, is_valid_json, parsed_json}
5
- """
6
-
7
- import re
8
- import json
9
-
10
-
11
- def response_analyzer(config):
12
- """
13
- 分析HTTP响应类型和内容
14
-
15
- 输入:
16
- response: {
17
- status: number,
18
- headers: dict,
19
- body: string,
20
- content_type: string
21
- }
22
- expected_type?: "json" | "html" | "any"
23
-
24
- 输出:
25
- type: "json" | "html" | "empty" | "redirect"
26
- is_suspicious: boolean
27
- suspicious_reasons: string[]
28
- sensitive_fields: string[]
29
- is_valid_json: boolean
30
- parsed_json?: object
31
- """
32
- response = config.get('response', {})
33
- expected_type = config.get('expected_type', 'any')
34
-
35
- status = response.get('status', 0)
36
- body = response.get('body', '')
37
- headers = response.get('headers', {})
38
- content_type = response.get('content_type', '') or headers.get('Content-Type', '')
39
-
40
- result = {
41
- 'type': 'unknown',
42
- 'is_suspicious': False,
43
- 'suspicious_reasons': [],
44
- 'sensitive_fields': [],
45
- 'is_valid_json': False,
46
- 'parsed_json': None
47
- }
48
-
49
- # 判断响应类型
50
- if status in [301, 302, 303, 307, 308]:
51
- result['type'] = 'redirect'
52
- result['is_suspicious'] = True
53
- result['suspicious_reasons'].append('redirect')
54
- return result
55
-
56
- body_lower = body.lower()
57
- body_len = len(body)
58
-
59
- # 检查是否是HTML
60
- is_html = (
61
- '<!doctype html>' in body_lower or
62
- '<html' in body_lower or
63
- 'text/html' in content_type.lower()
64
- )
65
-
66
- # 检查是否包含DOCTYPE
67
- hasdoctype = '<!doctype' in body_lower
68
-
69
- # 检查是否是JSON
70
- is_json = False
71
- parsed_json = None
72
-
73
- if 'application/json' in content_type.lower():
74
- try:
75
- parsed_json = json.loads(body)
76
- is_json = True
77
- result['is_valid_json'] = True
78
- result['parsed_json'] = parsed_json
79
- except:
80
- pass
81
-
82
- # 也尝试直接解析body
83
- if not is_json and body.strip().startswith('{'):
84
- try:
85
- parsed_json = json.loads(body)
86
- is_json = True
87
- result['is_valid_json'] = True
88
- result['parsed_json'] = parsed_json
89
- except:
90
- pass
91
-
92
- # 分类响应类型
93
- if body_len < 50:
94
- result['type'] = 'empty'
95
- if status == 200:
96
- result['is_suspicious'] = True
97
- result['suspicious_reasons'].append('empty_response')
98
- elif is_html:
99
- result['type'] = 'html'
100
- # HTML可能是WAF、SPA路由或错误页
101
- if 'waf' in body_lower or '安全' in body_lower or '拦截' in body_lower:
102
- result['suspicious_reasons'].append('waf_block')
103
- if 'not found' in body_lower or '404' in body_lower:
104
- result['suspicious_reasons'].append('not_found')
105
- if hasdoctype and 'vue' in body_lower or 'react' in body_lower:
106
- result['suspicious_reasons'].append('spa_route')
107
- elif is_json:
108
- result['type'] = 'json'
109
- else:
110
- result['type'] = 'other'
111
-
112
- # 检查敏感字段
113
- sensitive_patterns = {
114
- 'password': r'password["\']?\s*[:=]\s*["\']([^"\']+)',
115
- 'token': r'(?:token|Token|TOKEN)["\']?\s*[:=]\s*["\']([^"\']{10,})',
116
- 'secret': r'secret["\']?\s*[:=]\s*["\']([^"\']+)',
117
- 'api_key': r'api[_-]?key["\']?\s*[:=]\s*["\']([^"\']+)',
118
- 'jwt': r'eyJ[A-Za-z0-9-_]+\.eyJ[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+',
119
- }
120
-
121
- if parsed_json:
122
- body_str = json.dumps(parsed_json)
123
- else:
124
- body_str = body
125
-
126
- for field_name, pattern in sensitive_patterns.items():
127
- matches = re.findall(pattern, body_str, re.I)
128
- if matches:
129
- result['sensitive_fields'].append({
130
- 'field': field_name,
131
- 'count': len(matches),
132
- 'preview': matches[0][:50] if matches else ''
133
- })
134
-
135
- # 判断是否可疑
136
- if 'waf_block' in result['suspicious_reasons']:
137
- result['is_suspicious'] = True
138
- if 'spa_route' in result['suspicious_reasons']:
139
- result['is_suspicious'] = True
140
- if result['type'] == 'html' and expected_type == 'json':
141
- result['is_suspicious'] = True
142
-
143
- return result
144
-
145
-
146
- def compare_responses(baseline, test):
147
- """
148
- 对比两个响应的差异
149
-
150
- 输入:
151
- baseline: response - 正常响应
152
- test: response - 测试响应
153
-
154
- 输出:
155
- identical: boolean
156
- differences: Array<{field, baseline_value, test_value, significance}>
157
- """
158
- differences = []
159
-
160
- # 比较状态码
161
- if baseline.get('status') != test.get('status'):
162
- differences.append({
163
- 'field': 'status',
164
- 'baseline_value': baseline.get('status'),
165
- 'test_value': test.get('status'),
166
- 'significance': 'high'
167
- })
168
-
169
- # 比较响应长度
170
- baseline_len = len(baseline.get('body', ''))
171
- test_len = len(test.get('body', ''))
172
-
173
- if baseline_len != test_len:
174
- diff_ratio = abs(baseline_len - test_len) / max(baseline_len, test_len)
175
- significance = 'high' if diff_ratio > 0.5 else 'low'
176
- differences.append({
177
- 'field': 'body_length',
178
- 'baseline_value': baseline_len,
179
- 'test_value': test_len,
180
- 'significance': significance
181
- })
182
-
183
- # 比较响应类型
184
- baseline_type = response_analyzer({'response': baseline}).get('type')
185
- test_type = response_analyzer({'response': test}).get('type')
186
-
187
- if baseline_type != test_type:
188
- differences.append({
189
- 'field': 'response_type',
190
- 'baseline_value': baseline_type,
191
- 'test_value': test_type,
192
- 'significance': 'high'
193
- })
194
-
195
- identical = len(differences) == 0
196
-
197
- return {
198
- 'identical': identical,
199
- 'differences': differences
200
- }
201
-
202
-
203
- if __name__ == '__main__':
204
- # 测试
205
- result = response_analyzer({
206
- 'response': {
207
- 'status': 200,
208
- 'headers': {'Content-Type': 'application/json'},
209
- 'body': '{"code": 200, "data": {"userId": 1}}'
210
- }
211
- })
212
- print(f"Type: {result['type']}, Suspicious: {result['is_suspicious']}")
@@ -1,184 +0,0 @@
1
- """
2
- 敏感信息发现 - 从响应/源码中提取敏感信息
3
- 输入: {content, check_fields}
4
- 输出: {found: [{field, value, position}], severity}
5
- """
6
-
7
- import re
8
- import json
9
-
10
-
11
- def sensitive_finder(config):
12
- """
13
- 发现敏感信息
14
-
15
- 输入:
16
- content: string - 响应body或JS内容
17
- check_fields?: string[] - 自定义敏感字段
18
-
19
- 输出:
20
- found: Array<{field, value, position}>
21
- severity: "high" | "medium" | "low"
22
- """
23
- content = str(content)
24
- check_fields = config.get('check_fields', [])
25
-
26
- # 默认敏感字段
27
- default_fields = [
28
- 'password', 'passwd', 'pwd',
29
- 'token', 'access_token', 'refresh_token',
30
- 'secret', 'secret_key', 'app_secret',
31
- 'api_key', 'apikey', 'api_secret',
32
- 'private_key',
33
- 'aws_access_key', 'aws_secret_key',
34
- 'phone', 'mobile',
35
- 'email',
36
- 'id_card', 'idcard', '身份证',
37
- 'balance', 'account', '余额'
38
- ]
39
-
40
- # 合并字段
41
- all_fields = set(default_fields + check_fields)
42
-
43
- found = []
44
-
45
- # 通用敏感信息模式
46
- patterns = {
47
- 'password': [
48
- r'password["\']?\s*[:=]\s*["\']([^"\']{1,100})["\']',
49
- r'"pwd"\s*:\s*"([^"]+)"',
50
- r'"passwd"\s*:\s*"([^"]+)"',
51
- ],
52
- 'token': [
53
- r'(?:token|Token|TOKEN)["\']?\s*[:=]\s*["\']([^"\']{10,200})["\']',
54
- r'Bearer\s+([a-zA-Z0-9\-_\.]+)',
55
- ],
56
- 'jwt': [
57
- r'eyJ[A-Za-z0-9-_]+\.eyJ[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+',
58
- ],
59
- 'phone': [
60
- r'1[3-9]\d{9}',
61
- r'\d{3}-?\d{4}-?\d{4}',
62
- ],
63
- 'email': [
64
- r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}',
65
- ],
66
- 'secret': [
67
- r'secret["\']?\s*[:=]\s*["\']([^"\']{1,100})["\']',
68
- r'appSecret["\']?\s*[:=]\s*["\']([^"\']{1,100})["\']',
69
- ],
70
- 'api_key': [
71
- r'apiKey["\']?\s*[:=]\s*["\']([^"\']{1,100})["\']',
72
- r'api[_-]?key["\']?\s*[:=]\s*["\']([^"\']{1,100})["\']',
73
- ],
74
- 'aws_key': [
75
- r'AKIA[0-9A-Z]{16}',
76
- ],
77
- 'private_key': [
78
- r'-----BEGIN[ A-Z]*PRIVATE KEY-----',
79
- ]
80
- }
81
-
82
- # 搜索敏感信息
83
- for field_name, field_patterns in patterns.items():
84
- for pattern in field_patterns:
85
- matches = re.finditer(pattern, content, re.I)
86
- for match in matches:
87
- value = match.group(0)
88
- # 过滤掉太长的值
89
- if len(value) > 200:
90
- continue
91
- # 过滤掉测试数据
92
- if is_test_data(value):
93
- continue
94
- found.append({
95
- 'field': field_name,
96
- 'value': value[:100],
97
- 'position': match.start()
98
- })
99
-
100
- # 搜索用户定义的字段
101
- for field in all_fields:
102
- if field.lower() in ['password', 'token', 'secret', 'api_key']:
103
- continue # 已处理
104
- pattern = rf'{field}["\']?\s*[:=]\s*["\']([^"\']{{1,100}})["\']'
105
- matches = re.finditer(pattern, content, re.I)
106
- for match in matches:
107
- value = match.group(1)
108
- if len(value) > 100:
109
- continue
110
- if is_test_data(value):
111
- continue
112
- found.append({
113
- 'field': field,
114
- 'value': value[:50],
115
- 'position': match.start()
116
- })
117
-
118
- # 判断严重性
119
- severity = 'low'
120
- high_severity = ['password', 'token', 'jwt', 'secret', 'api_key', 'aws_key', 'private_key']
121
- medium_severity = ['phone', 'email', 'id_card']
122
-
123
- found_fields = [f['field'].lower() for f in found]
124
- if any(f in found_fields for f in high_severity):
125
- severity = 'high'
126
- elif any(f in found_fields for f in medium_severity):
127
- severity = 'medium'
128
-
129
- return {
130
- 'found': found,
131
- 'severity': severity,
132
- 'count': len(found)
133
- }
134
-
135
-
136
- def is_test_data(value):
137
- """判断是否是测试数据"""
138
- test_patterns = [
139
- 'test', 'TEST', 'Test',
140
- 'xxx', 'xxx.xxx',
141
- 'null', 'undefined',
142
- 'example', 'sample',
143
- 'placeholder'
144
- ]
145
- value_lower = value.lower()
146
- return any(t in value_lower for t in test_patterns)
147
-
148
-
149
- def extract_secrets_from_json(data, path=''):
150
- """从JSON中递归提取敏感信息"""
151
- secrets = []
152
-
153
- if isinstance(data, dict):
154
- for key, value in data.items():
155
- current_path = f"{path}.{key}" if path else key
156
-
157
- sensitive_keys = ['password', 'token', 'secret', 'key', 'api', 'credential']
158
- if any(s in key.lower() for s in sensitive_keys):
159
- if isinstance(value, str) and len(value) > 0:
160
- secrets.append({
161
- 'path': current_path,
162
- 'value': value[:50],
163
- 'key': key
164
- })
165
-
166
- if isinstance(value, (dict, list)):
167
- secrets.extend(extract_secrets_from_json(value, current_path))
168
-
169
- elif isinstance(data, list):
170
- for i, item in enumerate(data):
171
- current_path = f"{path}[{i}]"
172
- if isinstance(item, (dict, list)):
173
- secrets.extend(extract_secrets_from_json(item, current_path))
174
-
175
- return secrets
176
-
177
-
178
- if __name__ == '__main__':
179
- # 测试
180
- result = sensitive_finder({
181
- 'content': '{"token": "eyJhbGciOiJIUzI1NiJ9", "password": "admin123"}',
182
- 'check_fields': ['custom_field']
183
- })
184
- print(f"Found: {result['count']}, Severity: {result['severity']}")