@comate/zulu 1.3.3-internal.4 → 1.3.4-beta.1

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 (30) hide show
  1. package/comate-engine/assets/skills/auto-commit-comate/SKILL.md +42 -62
  2. package/comate-engine/assets/skills/auto-commit-comate/references/data_structures.md +69 -75
  3. package/comate-engine/assets/skills/auto-commit-comate/scripts/git_diff_cli.py +5 -0
  4. package/comate-engine/assets/skills/auto-commit-comate/scripts/match_card_cli.py +5 -1
  5. package/comate-engine/assets/skills/code-security-comate/SKILL.md +2 -1
  6. package/comate-engine/assets/skills/code-security-comate/references/vul_repair-go_sql_injection.md +627 -5
  7. package/comate-engine/assets/skills/code-security-comate/references/vul_repair-java_sql_injection.md +545 -21
  8. package/comate-engine/assets/skills/code-security-comate/references/vul_repair-php_sql_injection.md +596 -13
  9. package/comate-engine/assets/skills/code-security-comate/references/vul_repair-python_sql_injection.md +480 -82
  10. package/comate-engine/assets/skills/code-security-comate/scripts/http_client.py +10 -2
  11. package/comate-engine/assets/skills/code-security-comate/scripts/repair_vulnerability.py +12 -10
  12. package/comate-engine/assets/skills/code-security-comate/scripts/report_chat.py +1 -1
  13. package/comate-engine/assets/skills/comate-docs-comate/SKILL.md +70 -105
  14. package/comate-engine/assets/skills/comate-docs-comate/references/doc-map-extended.md +52 -7
  15. package/comate-engine/assets/skills/comate-docs-comate/references/models-and-billing.md +45 -26
  16. package/comate-engine/assets/skills/comate-docs-comate/references/product-overview.md +60 -14
  17. package/comate-engine/assets/skills/create-image-comate/SKILL.md +18 -15
  18. package/comate-engine/assets/skills/get-ugate-token-comate/SKILL.md +14 -12
  19. package/comate-engine/assets/skills/ku-operator-comate/SKILL.md +35 -7
  20. package/comate-engine/assets/skills/ku-operator-comate/examples.md +13 -0
  21. package/comate-engine/assets/skills/ku-operator-comate/scripts/ku_operator.py +35 -18
  22. package/comate-engine/node_modules/@comate/plugin-engine/dist/index.js +1 -1
  23. package/comate-engine/node_modules/@comate/plugin-host/dist/index.js +1 -1
  24. package/comate-engine/node_modules/@comate/plugin-host/dist/main.js +1 -1
  25. package/comate-engine/node_modules/@comate/plugin-shared-internals/dist/index.js +8 -8
  26. package/comate-engine/package.json +0 -1
  27. package/comate-engine/server.js +305 -290
  28. package/dist/bundle/index.js +8 -8
  29. package/package.json +1 -1
  30. package/comate-engine/node_modules/better-sqlite3/build/Release/better_sqlite3.node +0 -0
@@ -26,11 +26,11 @@ func escapeLikeValue(s string) string {
26
26
  // 使用示例:WHERE name LIKE ?
27
27
  db.Query("SELECT * FROM users WHERE name LIKE ?", "%"+escapeLikeValue(keyword)+"%")
28
28
  ```
29
- 4. 表名、列名、视图名称、DataBase 名称等拼接进入SQL 语句时需要进行过滤的方式修复漏洞,过滤使用的正则:`"^[\w\s\[\]"`$.,*]+$"`。
30
- 5. show、create 、drop、grant 语句场景不支持预编译参数方式修复漏洞,需要使用对污点参数进行过滤的方法进行修复,过滤使用的正则:`"^[\w\s\[\]"`$.,*]+$"`。
31
- 6. 对于 limit、SortBy、orderBy 等子句无法使用预编译,需要使用 `"^[\w\s\[\]"`$.,*]+$"` 过滤。
29
+ 4. 表名、列名、视图名称、DataBase 名称等拼接进入SQL 语句时需要进行白名单或者过滤的方式修复漏洞。
30
+ 5. show、create 、drop、grant 语句场景不支持预编译参数方式修复漏洞,需要使用对污点参数进行白名单或者过滤的方法进行修复。
31
+ 6. 对于 limit、SortBy、orderBy 等子句无法使用预编译,需要使用白名单或者正则过滤的方式修复。
32
32
  7. 当前框架支持安全写法时,优先使用框架安全写法,在无法使用安全写法时,才使用过滤等方案进行漏洞修复(注意不可修复场景不需要强行进行过滤修复)。
33
- 8. 几何函数例如ST_GeometryN,第一个参数为列名,不支持预编译,如果污点数据传入,需要过滤 `"^[\w\s\[\]"`$.,*]+$"`,第二个参数支持预编译,使用预编译方式修复。
33
+ 8. 几何函数例如ST_GeometryN,第一个参数为列名,不支持预编译,如果污点数据传入,需要白名单或者正则过滤的方式修复 ,第二个参数支持预编译,使用预编译方式修复。
34
34
  9. SQL 语句拼接 in 的参数时,需要对其中的每个子项进行过滤或者预编译,如果适合修改成预编译,则进行参数个数的?替换,将参数放入查询参数中,优先进行预编译参数绑定。注意事项:(1) 过滤 IN 子项时,如果所有子项都被过滤,必须有兜底逻辑(如返回空结果或返回错误),不能生成空的 `IN ()`,否则会导致 SQL 语法错误;(2) 动态构建 IN 列表时,建议先收集合法项到新切片,再用 `strings.Join` 拼接,避免基于原始索引判断逗号导致的尾随逗号问题;(3) 某些数据库对单条 SQL 的参数数量有限制(如 SQLite 默认 999 个),大量 IN 参数需分批处理。
35
35
 
36
36
  **IN 子句预编译示例:**
@@ -251,6 +251,628 @@ safeParams, err := safeFilterQueryParams(params)
251
251
  if err != nil { return err }
252
252
  result, err := queryData(db, safeParams)
253
253
  ```
254
+ ##### SQLi 正则过滤规范
255
+ ###### 核心决策树
256
+ ```
257
+ 用户输入需要过滤?
258
+
259
+ ├─ 是 SQL 标识符(表名/列名/数据库名/索引名)?
260
+ │ └─ → 正则验证格式 + 白名单验证(双重保障)
261
+
262
+ ├─ 是 SELECT 列表?
263
+ │ └─ → 正则验证格式 + 提取列名后白名单验证
264
+
265
+ ├─ 是 WHERE 条件/IN 子句?
266
+ │ └─ → 使用参数化查询(强烈推荐)
267
+
268
+ ├─ 是 ORDER BY/GROUP BY?
269
+ │ └─ → 正则验证格式 + 提取列名后白名单验证
270
+
271
+ ├─ 是 LIMIT/OFFSET?
272
+ │ └─ → 正则验证纯数字
273
+
274
+ └─ 其他场景
275
+ └─ → 根据具体需求选择白名单或精确正则
276
+ ```
277
+ ---
278
+
279
+ ###### 白名单数据来源与处理策略
280
+ ###### 白名单数据从哪里来?
281
+ |来源|说明|示例|
282
+ |-|-|-|
283
+ |**数据库元数据查询**|从数据库系统表查询实际存在的表/列名|`SELECT column_name FROM information_schema.columns WHERE table_name = 'users'`|
284
+ |**代码静态分析**|扫描代码中定义的实体类、ORM 模型|JPA `@Entity`、MyBatis mapper、SQLAlchemy Model|
285
+ |**配置文件/注解**|从配置文件或注解中提取允许的字段列表|`@AllowedFields({"id", "name", "email"})`|
286
+ |**API 文档/接口定义**|从 Swagger/OpenAPI 等文档中提取字段定义||
287
+
288
+ ###### 白名单不完整时的处理策略
289
+ ```
290
+ 白名单验证失败?
291
+
292
+ ├─ 白名单数据来源明确(如元数据查询、ORM 模型)?
293
+ │ └─ → 拒绝输入,记录日志,提示用户
294
+
295
+ ├─ 白名单数据来源不明确(如硬编码、不完整)?
296
+ │ └─ → 降级为仅正则验证,记录警告日志
297
+
298
+ └─ 无法获取白名单(如动态表名)?
299
+ └─ → 仅正则验证格式,加强输入长度限制
300
+ ```
301
+ **AI 修复决策原则**:
302
+
303
+ 1. **优先获取白名单**:如果代码中有 ORM 模型或实体类定义,优先从中提取字段列表
304
+ 2. **正则兜底**:当白名单获取困难或不完整时,使用严格正则作为安全底线
305
+ 3. **记录决策原因**:在代码注释中说明为什么选择正则而非白名单
306
+
307
+ ---
308
+
309
+ ###### 不同数据库的差异说明
310
+ |数据库|标识符引用符|参数占位符|特殊注意事项|
311
+ |-|-|-|-|
312
+ |**MySQL**|反引号 ```|`?` 或 `:name`|支持反引号包裹标识符,如 `user_name`|
313
+ |**PostgreSQL**|双引号 `"`|`$1, $2, ...` 或 `?`|支持双引号包裹标识符,如 `"user_name"`|
314
+ |**SQL Server**|方括号 `[` `]`|`@param` 或 `?`|支持方括号包裹标识符,如 `[user_name]`|
315
+ |**Oracle**|双引号 `"`|`:name` 或 `?`|标识符默认大写,双引号区分大小写|
316
+ |**SQLite**|双引号 `"` 或方括号 `[` `]`|`?` 或 `:name`|兼容多种引用符|
317
+
318
+ **AI 修复原则**:根据当前使用的数据库类型,选择对应的引用符和参数占位符。
319
+
320
+ ---
321
+
322
+ ###### 选择策略对比
323
+ |场景|推荐策略|原因|
324
+ |-|-|-|
325
+ |SQL 标识符(表名/列名/数据库名/索引名)|正则 + 白名单|格式验证 + 值验证,双重保障|
326
+ |SELECT 列表|正则 + 白名单|允许通配符 `*` 和多列,列名需白名单验证|
327
+ |ORDER BY/GROUP BY|正则 + 白名单|格式验证 + 列名白名单 + 方向关键字(ASC/DESC)|
328
+ |LIMIT/OFFSET|严格正则|只允许数字,不需要白名单|
329
+ |WHERE 条件|**参数化查询**|正则过滤仍有风险,参数化最安全|
330
+ |IN 子句|参数化查询优先|优先参数化,其次严格正则|
331
+
332
+ ---
333
+
334
+ ###### 各场景正则与实现
335
+ ###### 1. SELECT 列名列表 `__SELECT_FILTER__`
336
+ **适用场景**:SELECT 后面的列名列表(如 `SELECT id,name,email FROM users` 或 `SELECT users.id,users.name FROM users`)
337
+
338
+ **正则(通用版,支持引用符)**:
339
+
340
+ ```
341
+ ^(\*|((`[^`]+`|"[^"]+"|\w+\.\w+|\w+)(,\s*(`[^`]+`|"[^"]+"|\w+\.\w+|\w+))*))$
342
+ ```
343
+ **支持格式**:
344
+
345
+ * `*` (通配符)
346
+ * `id` (单列)
347
+ * `users.id` (表名.列名)
348
+ * `user_name` (MySQL 反引号)
349
+ * `"user_name"` (PostgreSQL/Oracle 双引号)
350
+ * `id,name,email` (多列,逗号分隔)
351
+ * `users.id, users.name, users.email` (带表名前缀的多列)
352
+ * `id, name, email` (允许逗号前后空格)
353
+
354
+ **AI 修复指引**:
355
+
356
+ ```go
357
+ import (
358
+ "fmt"
359
+ "regexp"
360
+ "strings"
361
+ )
362
+
363
+ // safeFilterSelect 过滤 SELECT 列名列表
364
+ // allowedColumns 为可选白名单,传入 nil 则仅做正则校验
365
+ func safeFilterSelect(selectStr string, allowedColumns map[string]bool) (string, error) {
366
+ // 1. 先 TrimSpace 去除首尾空白
367
+ selectStr = strings.TrimSpace(selectStr)
368
+ // 2. 正则验证格式(支持引用符)
369
+ regex := `^(\*|(` + "`[^`]+`" + `|"[^"]+"|\w+\.\w+|\w+)(,\s*(` + "`[^`]+`" + `|"[^"]+"|\w+\.\w+|\w+))*))$`
370
+ matched, _ := regexp.MatchString(regex, selectStr)
371
+ if !matched {
372
+ return "", fmt.Errorf("invalid select columns")
373
+ }
374
+ // 3. 如果是通配符 *,直接允许
375
+ if selectStr == "*" {
376
+ return selectStr, nil
377
+ }
378
+ // 4. 提取列名并验证白名单
379
+ parts := regexp.MustCompile(`\s*,\s*`).Split(selectStr, -1)
380
+ for _, part := range parts {
381
+ // 去除引用符
382
+ cleanPart := strings.Trim(part, "`\"")
383
+ // 处理表名.列名格式,提取列名部分
384
+ column := cleanPart
385
+ if idx := strings.LastIndex(cleanPart, "."); idx != -1 {
386
+ column = cleanPart[idx+1:]
387
+ }
388
+ // 白名单验证(如果白名单不为空)
389
+ if allowedColumns != nil && len(allowedColumns) > 0 {
390
+ if !allowedColumns[strings.ToLower(strings.TrimSpace(column))] {
391
+ return "", fmt.Errorf("column not in whitelist: %s", column)
392
+ }
393
+ }
394
+ }
395
+ return selectStr, nil
396
+ }
397
+ ```
398
+ ---
399
+
400
+ ###### 2. SQL 标识符(表名/列名/数据库名/索引名)`__IDENTIFIER_FILTER__`
401
+ **适用场景**:表名、列名、数据库名、索引名、视图名等 SQL 标识符
402
+
403
+ **正则(通用版,支持引用符)**:
404
+
405
+ ```
406
+ ^[A-Za-z_][A-Za-z0-9_]{0,127}$|^`[^`]{1,128}`$|^"[^"]{1,128}"$
407
+ ```
408
+ **支持格式**:
409
+
410
+ * `user` (小写字母开头)
411
+ * `User123` (字母数字)
412
+ * `user_name` (下划线)
413
+ * `_private` (下划线开头,部分数据库支持)
414
+ * `user_name` (MySQL 反引号)
415
+ * `"user_name"` (PostgreSQL/Oracle 双引号)
416
+ * `[user_name]` (SQL Server 方括号)
417
+ * 长度限制:1-128 字符
418
+
419
+ **AI 修复指引**:
420
+
421
+ ```go
422
+ import (
423
+ "fmt"
424
+ "regexp"
425
+ "strings"
426
+ )
427
+
428
+ // safeFilterIdentifier 过滤 SQL 标识符(表名/列名/数据库名/索引名)
429
+ // allowedIdentifiers 为可选白名单,传入 nil 则仅做正则校验
430
+ func safeFilterIdentifier(identifier string, allowedIdentifiers map[string]bool) (string, error) {
431
+ // 1. 先 TrimSpace 去除首尾空白
432
+ identifier = strings.TrimSpace(identifier)
433
+ // 2. 正则验证格式(支持引用符)
434
+ regex := `^[A-Za-z_][A-Za-z0-9_]{0,127}$|^` + "`[^`]{1,128}`" + `$|^"[^"]{1,128}"$|^\[[^\]]{1,128}\]$`
435
+ matched, _ := regexp.MatchString(regex, identifier)
436
+ if !matched {
437
+ return "", fmt.Errorf("invalid identifier")
438
+ }
439
+ // 3. 去除引用符
440
+ cleanIdentifier := strings.Trim(identifier, "`\"[]")
441
+ // 4. 白名单验证(如果白名单不为空)
442
+ if allowedIdentifiers != nil && len(allowedIdentifiers) > 0 {
443
+ if !allowedIdentifiers[strings.ToLower(cleanIdentifier)] {
444
+ return "", fmt.Errorf("identifier not in whitelist: %s", cleanIdentifier)
445
+ }
446
+ }
447
+ return identifier, nil
448
+ }
449
+ ```
450
+ **说明**:
451
+
452
+ * 表名、列名、数据库名、索引名等标识符都遵循相同的命名规范
453
+ * 正则验证格式 + 白名单验证允许的值,双重保障
454
+ * 如果白名单不完整或无法获取,可省略步骤 4,仅使用正则验证
455
+
456
+ ---
457
+
458
+ ###### 3. ORDER BY `__ORDER_FILTER__`
459
+ **适用场景**:ORDER BY 后的字段(如 `ORDER BY name ASC, id DESC` 或 `ORDER BY users.name ASC, users.id DESC`)
460
+
461
+ **正则(通用版,支持引用符)**:
462
+
463
+ ```
464
+ ^((`[^`]+`|"[^"]+"|\w+\.\w+|\w+)(\s+(ASC|DESC))?(,\s*(`[^`]+`|"[^"]+"|\w+\.\w+|\w+)(\s+(ASC|DESC))?)*)$
465
+ ```
466
+ **支持格式**:
467
+
468
+ * `name` (单列)
469
+ * `users.name` (表名.列名)
470
+ * `user_name` (MySQL 反引号)
471
+ * `"user_name"` (PostgreSQL/Oracle 双引号)
472
+ * `name ASC` / `name DESC` (带方向)
473
+ * `users.name ASC` / `users.name DESC` (带表名前缀和方向)
474
+ * `name ASC, id DESC` (多列)
475
+ * `users.name ASC, users.id DESC` (带表名前缀的多列)
476
+
477
+ **AI 修复指引**:
478
+
479
+ ```go
480
+ import (
481
+ "fmt"
482
+ "regexp"
483
+ "strings"
484
+ )
485
+
486
+ // safeFilterOrderBy 过滤 ORDER BY 子句
487
+ // allowedColumns 为可选白名单,传入 nil 则仅做正则校验
488
+ func safeFilterOrderBy(order string, allowedColumns map[string]bool) (string, error) {
489
+ // 1. 正则验证格式(不区分大小写)
490
+ regex := `(?i)^((` + "`[^`]+`" + `|"[^"]+"|\w+\.\w+|\w+)(\s+(ASC|DESC))?(,\s*(` + "`[^`]+`" + `|"[^"]+"|\w+\.\w+|\w+)(\s+(ASC|DESC))?)*)$`
491
+ matched, _ := regexp.MatchString(regex, order)
492
+ if !matched {
493
+ return "", fmt.Errorf("invalid order by clause")
494
+ }
495
+ // 2. 提取列名并验证白名单
496
+ parts := regexp.MustCompile(`\s*,\s*`).Split(order, -1)
497
+ for _, part := range parts {
498
+ // 提取列名部分(处理表名.列名格式)
499
+ columnPart := regexp.MustCompile(`\s+`).Split(part, -1)[0]
500
+ // 去除引用符
501
+ cleanPart := strings.Trim(columnPart, "`\"")
502
+ column := cleanPart
503
+ if idx := strings.LastIndex(cleanPart, "."); idx != -1 {
504
+ column = cleanPart[idx+1:]
505
+ }
506
+ // 白名单验证(如果白名单不为空)
507
+ if allowedColumns != nil && len(allowedColumns) > 0 {
508
+ if !allowedColumns[strings.ToLower(column)] {
509
+ return "", fmt.Errorf("column not in whitelist: %s", column)
510
+ }
511
+ }
512
+ }
513
+ return order, nil
514
+ }
515
+ ```
516
+ ---
517
+
518
+ ###### 4. GROUP BY `__GROUP_FILTER__`
519
+ **适用场景**:GROUP BY 后的字段(如 `GROUP BY name, age` 或 `GROUP BY users.name, users.age`)
520
+
521
+ **正则(通用版,支持引用符)**:
522
+
523
+ ```
524
+ ^((`[^`]+`|"[^"]+"|\w+\.\w+|\w+)(,\s*(`[^`]+`|"[^"]+"|\w+\.\w+|\w+))*)$
525
+ ```
526
+ **支持格式**:
527
+
528
+ * `name` (单列)
529
+ * `users.name` (表名.列名)
530
+ * `user_name` (MySQL 反引号)
531
+ * `"user_name"` (PostgreSQL/Oracle 双引号)
532
+ * `name, age` (多列)
533
+ * `users.name, users.age` (带表名前缀的多列)
534
+
535
+ **AI 修复指引**:
536
+
537
+ ```go
538
+ import (
539
+ "fmt"
540
+ "regexp"
541
+ "strings"
542
+ )
543
+
544
+ // safeFilterGroupBy 过滤 GROUP BY 子句
545
+ // allowedColumns 为可选白名单,传入 nil 则仅做正则校验
546
+ func safeFilterGroupBy(group string, allowedColumns map[string]bool) (string, error) {
547
+ // 1. 正则验证格式
548
+ regex := `^((` + "`[^`]+`" + `|"[^"]+"|\w+\.\w+|\w+)(,\s*(` + "`[^`]+`" + `|"[^"]+"|\w+\.\w+|\w+))*)$`
549
+ matched, _ := regexp.MatchString(regex, group)
550
+ if !matched {
551
+ return "", fmt.Errorf("invalid group by clause")
552
+ }
553
+ // 2. 提取列名并验证白名单
554
+ parts := regexp.MustCompile(`\s*,\s*`).Split(group, -1)
555
+ for _, part := range parts {
556
+ // 去除引用符
557
+ cleanPart := strings.Trim(part, "`\"")
558
+ // 提取列名部分(处理表名.列名格式)
559
+ column := cleanPart
560
+ if idx := strings.LastIndex(cleanPart, "."); idx != -1 {
561
+ column = cleanPart[idx+1:]
562
+ }
563
+ // 白名单验证(如果白名单不为空)
564
+ if allowedColumns != nil && len(allowedColumns) > 0 {
565
+ if !allowedColumns[strings.ToLower(strings.TrimSpace(column))] {
566
+ return "", fmt.Errorf("column not in whitelist: %s", column)
567
+ }
568
+ }
569
+ }
570
+ return group, nil
571
+ }
572
+ ```
573
+ ---
574
+
575
+ ###### 5. LIMIT/OFFSET `__LIMIT_FILTER__`
576
+ **适用场景**:LIMIT、OFFSET 后的数值(如 `LIMIT 10`、`LIMIT 1,10`)
577
+
578
+ **正则**:`^\d+\s*,\s*\d+$|^\d+$`
579
+
580
+ **支持格式**:
581
+
582
+ * `10` (单数字)
583
+ * `1,10` (带偏移量)
584
+ * `1, 10` (允许逗号前后空格)
585
+
586
+ **AI 修复指引**:
587
+
588
+ ```go
589
+ import (
590
+ "fmt"
591
+ "regexp"
592
+ "strings"
593
+ )
594
+
595
+ // safeFilterLimit 过滤 LIMIT/OFFSET 参数
596
+ func safeFilterLimit(limit string) (string, error) {
597
+ // 1. 先 TrimSpace 去除首尾空白
598
+ limit = strings.TrimSpace(limit)
599
+ // 2. 正则验证纯数字
600
+ regex := `^\d+\s*,\s*\d+$|^\d+$`
601
+ matched, _ := regexp.MatchString(regex, limit)
602
+ if matched {
603
+ return limit, nil
604
+ }
605
+ return "", fmt.Errorf("invalid limit value")
606
+ }
607
+ ```
608
+ ---
609
+
610
+ ###### 6. WHERE 条件 `__WHERE_FILTER__`
611
+ **适用场景**:WHERE 子句、条件判断
612
+
613
+ **策略**:**放弃正则,使用参数化查询**
614
+
615
+ **AI 修复指引**:
616
+
617
+ ```go
618
+ import "database/sql"
619
+
620
+ // ❌ 错误:字符串拼接
621
+ sql := fmt.Sprintf("SELECT * FROM users WHERE name = '%s'", name)
622
+
623
+ // ✅ 正确:参数化查询(database/sql 使用 ?)
624
+ rows, err := db.Query("SELECT * FROM users WHERE name = ?", name)
625
+
626
+ // ✅ 正确:参数化查询(PostgreSQL 使用 $1, $2)
627
+ rows, err := db.Query("SELECT * FROM users WHERE name = $1", name)
628
+
629
+ // ✅ 正确:GORM 参数绑定
630
+ db.Where("name = ?", name).First(&user)
631
+ ```
632
+ ---
633
+
634
+ ###### 7. IN 子句 `__IN_FILTER__`
635
+ **适用场景**:IN 后面的值列表(如 `IN (1,2,3)`)
636
+
637
+ **策略**:优先参数化查询,其次严格正则
638
+
639
+ **正则**:
640
+
641
+ * 数字:`^\(\s*\d+(\s*,\s*\d+)*\s*\)$`
642
+ * 字符串:`^\(\s*'[^']*'(\s*,\s*'[^']*')*\s*\)$`
643
+
644
+ **支持格式**:
645
+
646
+ * `(1,2,3)` (数字列表)
647
+ * `('a','b','c')` (字符串列表)
648
+ * `(1, 2, 3)` (允许逗号前后空格)
649
+
650
+ **AI 修复指引**:
651
+
652
+ ```go
653
+ import (
654
+ "fmt"
655
+ "regexp"
656
+ "strings"
657
+ )
658
+
659
+ // ✅ 优先:参数化查询(动态生成占位符)
660
+ func queryByIDs(db *sql.DB, ids []int64) ([]User, error) {
661
+ if len(ids) == 0 {
662
+ return []User{}, nil // 兜底:空列表直接返回空结果
663
+ }
664
+ placeholders := make([]string, len(ids))
665
+ args := make([]interface{}, len(ids))
666
+ for i, id := range ids {
667
+ placeholders[i] = "?"
668
+ args[i] = id
669
+ }
670
+ query := fmt.Sprintf("SELECT * FROM users WHERE id IN (%s)", strings.Join(placeholders, ","))
671
+ rows, err := db.Query(query, args...)
672
+ // ...
673
+ }
674
+
675
+ // ⚠️ 备选:严格正则验证(仅当无法参数化时)
676
+ func safeFilterInClause(inClause string) (string, error) {
677
+ inClause = strings.TrimSpace(inClause)
678
+ regex := `^\(\s*\d+(\s*,\s*\d+)*\s*\)$`
679
+ matched, _ := regexp.MatchString(regex, inClause)
680
+ if matched {
681
+ return inClause, nil
682
+ }
683
+ return "", fmt.Errorf("invalid IN clause")
684
+ }
685
+ ```
686
+ ---
687
+
688
+ ###### 8. 方向关键字 `__DIRECTION_FILTER__`
689
+ **适用场景**:ORDER BY 后的 ASC/DESC
690
+
691
+ **正则**:`^(asc|desc)$`
692
+
693
+ **支持格式**:
694
+
695
+ * `ASC` / `DESC` / `asc` / `desc` (不区分大小写)
696
+
697
+ **AI 修复指引**:
698
+
699
+ ```go
700
+ import (
701
+ "fmt"
702
+ "regexp"
703
+ "strings"
704
+ )
705
+
706
+ // safeFilterDirection 过滤排序方向(ASC/DESC)
707
+ func safeFilterDirection(direction string) (string, error) {
708
+ // 正则验证(不区分大小写)
709
+ regex := `(?i)^(asc|desc)$`
710
+ matched, _ := regexp.MatchString(regex, direction)
711
+ if matched {
712
+ return strings.ToUpper(direction), nil
713
+ }
714
+ return "", fmt.Errorf("invalid direction")
715
+ }
716
+ ```
717
+ ---
718
+
719
+ ###### 9. 纯字母数字标识符 `__W_FILTER__`
720
+ **适用场景**:简单的表名、列名(无特殊字符)
721
+
722
+ **正则**:`^[\w-]+$`
723
+
724
+ **支持格式**:
725
+
726
+ * `user_name` (下划线)
727
+ * `User123` (字母数字)
728
+ * `table-1` (连字符)
729
+
730
+ **AI 修复指引**:
731
+
732
+ ```go
733
+ import (
734
+ "fmt"
735
+ "regexp"
736
+ )
737
+
738
+ // safeFilterSimpleIdentifier 过滤简单标识符(字母数字下划线连字符)
739
+ func safeFilterSimpleIdentifier(identifier string) (string, error) {
740
+ regex := `^[\w-]+$`
741
+ matched, _ := regexp.MatchString(regex, identifier)
742
+ if matched {
743
+ return identifier, nil
744
+ }
745
+ return "", fmt.Errorf("invalid identifier")
746
+ }
747
+ ```
748
+ ---
749
+
750
+ ###### 10. 路径过滤 `__PATH_FILTER__`
751
+ **适用场景**:文件路径、URL 路径
752
+
753
+ **正则**:`^[\w/-]+$`
754
+
755
+ **支持格式**:
756
+
757
+ * `/path/to/file` (绝对路径)
758
+ * `path/to/file` (相对路径)
759
+
760
+ **AI 修复指引**:
761
+
762
+ ```go
763
+ import (
764
+ "fmt"
765
+ "regexp"
766
+ )
767
+
768
+ // safeFilterPath 过滤文件路径/URL路径
769
+ func safeFilterPath(path string) (string, error) {
770
+ regex := `^[\w/-]+$`
771
+ matched, _ := regexp.MatchString(regex, path)
772
+ if matched {
773
+ return path, nil
774
+ }
775
+ return "", fmt.Errorf("invalid path")
776
+ }
777
+ ```
778
+ ---
779
+
780
+ ###### 11. 运算符 `__OP_FILTER__`
781
+ **适用场景**:算术运算符(如 `score+1`)
782
+
783
+ **正则**:`[+\-*/%]`
784
+
785
+ **支持格式**:
786
+
787
+ * `+` `-` `*` `/` `%` (基本算术运算符)
788
+
789
+ **AI 修复指引**:
790
+
791
+ ```go
792
+ import (
793
+ "fmt"
794
+ "regexp"
795
+ )
796
+
797
+ // safeFilterOperator 过滤算术运算符
798
+ func safeFilterOperator(operator string) (string, error) {
799
+ regex := `^[+\-*/%]$`
800
+ matched, _ := regexp.MatchString(regex, operator)
801
+ if matched {
802
+ return operator, nil
803
+ }
804
+ return "", fmt.Errorf("invalid operator")
805
+ }
806
+ ```
807
+ ---
808
+
809
+ ###### 正则速查表
810
+ |过滤器|正则|说明|
811
+ |-|-|-|
812
+ |`__IDENTIFIER_FILTER__`|`` ^[A-Za-z_][A-Za-z0-9_]{0,127}$\|^`[^`]{1,128}`$\|^"[^"]{1,128}"$ ``|SQL 标识符,支持反引号/双引号,1-128字符|
813
+ |`__SELECT_FILTER__`|`` ^(\*\|((`[^`]+`\|"[^"]+"\|\w+\.\w+\|\w+)(,\s*(`[^`]+`\|"[^"]+"\|\w+\.\w+\|\w+))*))$ ``|允许通配符 `*` 和逗号分隔的列名|
814
+ |`__ORDER_FILTER__`|`` (?i)^((`[^`]+`\|"[^"]+"\|\w+\.\w+\|\w+)(\s+(ASC\|DESC))?(,\s*(`[^`]+`\|"[^"]+"\|\w+\.\w+\|\w+)(\s+(ASC\|DESC))?)*)$ ``|列名 + 方向关键字|
815
+ |`__GROUP_FILTER__`|`` ^((`[^`]+`\|"[^"]+"\|\w+\.\w+\|\w+)(,\s*(`[^`]+`\|"[^"]+"\|\w+\.\w+\|\w+))*)$ ``|多列,逗号分隔|
816
+ |`__LIMIT_FILTER__`|`^\d+\s*,\s*\d+$\|^\d+$`|纯数字,允许逗号|
817
+ |`__WHERE_FILTER__`|**参数化查询**|不要用正则|
818
+ |`__IN_FILTER__`|`^\(\s*\d+(\s*,\s*\d+)*\s*\)$`|数字列表|
819
+ |`__DIRECTION_FILTER__`|`(?i)^(asc\|desc)$`|ASC 或 DESC|
820
+ |`__W_FILTER__`|`^[\w-]+$`|字母数字+下划线+连字符|
821
+ |`__PATH_FILTER__`|`^[\w/-]+$`|路径字符|
822
+ |`__OP_FILTER__`|`^[+\-*/%]$`|算术运算符|
823
+
824
+ **Go 语言正则使用示例**:
825
+ ```go
826
+ import "regexp"
827
+
828
+ // =====================================================
829
+ // 重要:Go 正则字符串的两种写法(必须二选一,不能混用)
830
+ // =====================================================
831
+
832
+ // 方式1:原始字符串(推荐)- 反斜杠不需要转义
833
+ regex := `^[\w\s\[\]"` + "`" + `$.,*]+$`
834
+ matched, _ := regexp.MatchString(regex, input)
835
+
836
+ // 方式2:普通字符串 - 反斜杠必须双重转义
837
+ regex := "^[\\w\\s\\[\\]\"`$.,*]+$"
838
+ matched, _ := regexp.MatchString(regex, input)
839
+
840
+ // ❌ 错误写法(会导致编译错误或运行时错误):
841
+ // regex := "^[\w\s]+$" // \w \s 不是有效的 Go 字符串转义!
842
+
843
+ // 使用预编译正则(性能更好,适合多次使用)
844
+ var identifierRegex = regexp.MustCompile(`^[\w-]+$`)
845
+ matched := identifierRegex.MatchString(identifier)
846
+
847
+ // 不区分大小写:在正则开头加 (?i)
848
+ matched, _ := regexp.MatchString(`(?i)^(asc|desc)$`, direction)
849
+ ```
850
+
851
+ **Go 正则转义对照表**:
852
+ |正则元字符|原始字符串写法|普通字符串写法|
853
+ |-|-|-|
854
+ |`\w`|`` `\w` ``|`"\\w"`|
855
+ |`\s`|`` `\s` ``|`"\\s"`|
856
+ |`\d`|`` `\d` ``|`"\\d"`|
857
+ |`\.`|`` `\.` ``|`"\\."`|
858
+ |`\[`|`` `\[` ``|`"\\["`|
859
+ |反引号 `` ` ``|需拼接:`` `a` + "`" + `b` ``|`"\`"`|
860
+
861
+ ---
862
+
863
+ ###### 重要提示
864
+ 1. **所有正则验证前,务必先 `strings.TrimSpace()` 去除首尾空白字符**
865
+ 2. **白名单优先**:列名/表名等固定值优先用白名单
866
+ 3. **白名单不完整时降级**:当白名单获取困难或不完整时,使用严格正则作为安全底线
867
+ 4. **参数化优先**:WHERE 条件、IN 子句优先用参数化查询
868
+ 5. **数据库适配**:根据使用的数据库类型选择对应的引用符和参数占位符
869
+ 6. **最小化改动**:只修复漏洞点,不重构其他代码
870
+ 7. **Go 正则特点**:Go 使用 RE2 引擎,不支持回溯,用 `(?i)` 实现大小写不敏感
871
+ 8. **Go 正则字符串写法(关键)**:
872
+ - 使用原始字符串 `` `...` `` 时,`\w` `\s` 等直接写,不需要转义
873
+ - 使用普通字符串 `"..."` 时,必须写成 `\\w` `\\s`(双重转义)
874
+ - **绝对不能**在普通字符串中写 `"\w"` `"\s"`,这会导致编译错误或运行时错误
875
+
254
876
  ## 各框架SQL注入风险与防护
255
877
  ### 1. 标准库 database/sql
256
878
  #### 风险示例
@@ -396,4 +1018,4 @@ Beego ORM同样存在直接拼接SQL导致注入的风险。
396
1018
  // 安全: 使用参数化查询
397
1019
  o.Raw("SELECT * FROM users WHERE username = ?", username).QueryRow(&user)
398
1020
  ```
399
- ##
1021
+ ##