mcpField
E
error-pattern-safety

error-pattern-safety

github.com/github开发5.5k61 次安装今天更新
代码仓库

关于 error-pattern-safety

面向 Agent 引擎开发者的错误模式正则安全指南,重点防止 JavaScript 全局正则因零宽匹配陷入循环,并提供规则与测试建议。

主要功能

  1. 01避免零宽正则匹配
  2. 02要求模式包含实际字符
  3. 03检查模式是否匹配空字符串
  4. 04说明 Go 到 JavaScript 的转换
  5. 05提供验证测试建议

适用场景

  1. 01审查新增的引擎错误模式
  2. 02排查 JavaScript 正则循环风险
  3. 03转换 Go 模式以供 JavaScript 验证

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/github/gh-aw --skill error-pattern-safety

安装后在 Claude Code 中输入/error-pattern-safety确认已加载。

详细介绍

错误模式安全指南

在 Agent 引擎中使用这些正则表达式安全规则,防止 JavaScript 无限循环。

问题

使用 JavaScript 全局标志(/pattern/g)时,零宽匹配可能导致无限循环,因为:

  1. 带有 g 标志的 JavaScript regex.exec() 使用 lastIndex 跟踪位置
  2. 模式进行零宽匹配时,lastIndex 不会前进
  3. 同一位置会被反复匹配,从而导致无限循环

危险模式示例

❌ 切勿使用这些模式:

// Pure .* - matches everything including empty string at end
/.*/g

// Single character with * - matches zero or more (including zero)
/a*/g

// Patterns that can match empty string
/(x|y)*/g

安全模式示例

✅ 始终使用此类模式:

// Required prefix before .*
/error.*/gi
/error.*permission.*denied/gi

// Specific structure with required content
/\[(\d{4}-\d{2}-\d{2})\]\s+(ERROR):\s+(.+)/g

// Required characters throughout
/access denied.*user.*not authorized/gi

模式安全规则

  1. 始终要求至少匹配一个字符

    • 需要表示“某些内容”时,使用 .+ 而非 .*
    • 确保模式具有必需的前缀/后缀
  2. 切勿将单独的 .* 用作整个模式

    • 始终与必需文本组合:error.*
    • 切勿只使用 .* 或 .*?
  3. 针对空字符串测试模式

    const regex = /your-pattern/g;
    if (regex.test("")) {
      throw new Error("Pattern matches empty string - DANGEROUS!");
    }
    
  4. 尽可能使用特定锚点

    • 开头:^error.*
    • 结尾:.*error$
    • 单词边界:\berror\b

验证测试

所有错误模式都必须通过以下测试:

Go 测试(pkg/workflow/engine_error_patterns_infinite_loop_test.go)
// Test that pattern doesn't match empty string
func TestPatternSafety(t *testing.T) {
    pattern := "your-pattern"
    regex := regexp.MustCompile(pattern)
    
    if regex.MatchString("") {
        t.Error("Pattern matches empty string!")
    }
}
JavaScript 测试(pkg/workflow/js/validate_errors.test.cjs)
test("should not match empty string", () => {
  const regex = new RegExp("your-pattern", "g");
  expect(regex.test("")).toBe(false);
});

validate_errors.cjs 中的安全机制

validate_errors.cjs 脚本内置了防护措施:

  1. 零宽检测:检查 regex.lastIndex 是否停止前进
  2. 迭代警告:迭代 1000 次时发出警告
  3. 硬性限制:迭代达到 10,000 次时停止,以防程序挂起
// Safety check in validate_errors.cjs
if (regex.lastIndex === lastIndex) {
  core.error(`Infinite loop detected! Pattern: ${pattern.pattern}`);
  break;
}

添加新的错误模式

向引擎添加新的错误模式时:

  1. 编写包含必需内容的模式

    {
        Pattern:      `(?i)error.*permission.*denied`,
        LevelGroup:   0,
        MessageGroup: 0,
        Description:  "Permission denied error",
    }
    
  2. 针对空字符串进行测试

    • 运行:make test-unit
    • 检查:TestAllEnginePatternsSafe
  3. 使用实际日志样本进行测试

    • 确保它能匹配真实错误
    • 确保它不会匹配提示性文本
  4. 记录模式

    • 添加清晰的描述
    • 说明该模式旨在捕获什么

模式转换:从 Go 到 JavaScript

模式会从 Go 转换为 JavaScript:

// Go pattern (case-insensitive flag)
Pattern: `(?i)error.*permission.*denied`

// Converted to JavaScript
new RegExp("error.*permission.*denied", "gi")

由于 JavaScript 使用 i 标志,因此会移除 (?i) 前缀。

当前代码库中的示例

✅ 安全模式
// Requires "error" prefix
Pattern: `(?i)error.*permission.*denied`

// Requires specific timestamp format
Pattern: `(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)\s+\[(ERROR)\]\s+(.+)`

// Requires "access denied" prefix
Pattern: `(?i)access denied.*user.*not authorized`
如何修复不安全模式

如果发现某个模式能匹配空字符串:

之前(不安全):

Pattern: `.*error.*`  // Can match empty at start/end

之后(安全):

Pattern: `error.*`     // Requires "error" at start
// OR
Pattern: `.*error.+`   // Requires "error" and at least one char after
// OR
Pattern: `\berror\b.*` // Requires word "error"

测试清单

提交模式变更前:

  • 运行 make test-unit
  • 确认 TestAllEnginePatternsSafe 通过
  • 确认 TestErrorPatternsNoInfiniteLoopPotential 通过
  • 运行 JavaScript 测试:cd pkg/workflow/js && npm test
  • 验证模式能匹配预期的错误消息
  • 验证模式不会匹配提示性文本

参考资料

评论

支持 Markdown