关于 error-pattern-safety
面向 Agent 引擎开发者的错误模式正则安全指南,重点防止 JavaScript 全局正则因零宽匹配陷入循环,并提供规则与测试建议。
主要功能
- 01避免零宽正则匹配
- 02要求模式包含实际字符
- 03检查模式是否匹配空字符串
- 04说明 Go 到 JavaScript 的转换
- 05提供验证测试建议
适用场景
- 01审查新增的引擎错误模式
- 02排查 JavaScript 正则循环风险
- 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)时,零宽匹配可能导致无限循环,因为:
- 带有
g标志的 JavaScriptregex.exec()使用lastIndex跟踪位置 - 模式进行零宽匹配时,
lastIndex不会前进 - 同一位置会被反复匹配,从而导致无限循环
危险模式示例
❌ 切勿使用这些模式:
// 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
模式安全规则
-
始终要求至少匹配一个字符
- 需要表示“某些内容”时,使用
.+而非.* - 确保模式具有必需的前缀/后缀
- 需要表示“某些内容”时,使用
-
切勿将单独的
.*用作整个模式- 始终与必需文本组合:
error.* - 切勿只使用
.*或.*?
- 始终与必需文本组合:
-
针对空字符串测试模式
const regex = /your-pattern/g; if (regex.test("")) { throw new Error("Pattern matches empty string - DANGEROUS!"); } -
尽可能使用特定锚点
- 开头:
^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 脚本内置了防护措施:
- 零宽检测:检查
regex.lastIndex是否停止前进 - 迭代警告:迭代 1000 次时发出警告
- 硬性限制:迭代达到 10,000 次时停止,以防程序挂起
// Safety check in validate_errors.cjs
if (regex.lastIndex === lastIndex) {
core.error(`Infinite loop detected! Pattern: ${pattern.pattern}`);
break;
}
添加新的错误模式
向引擎添加新的错误模式时:
-
编写包含必需内容的模式
{ Pattern: `(?i)error.*permission.*denied`, LevelGroup: 0, MessageGroup: 0, Description: "Permission denied error", } -
针对空字符串进行测试
- 运行:
make test-unit - 检查:
TestAllEnginePatternsSafe
- 运行:
-
使用实际日志样本进行测试
- 确保它能匹配真实错误
- 确保它不会匹配提示性文本
-
记录模式
- 添加清晰的描述
- 说明该模式旨在捕获什么
模式转换:从 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 - 验证模式能匹配预期的错误消息
- 验证模式不会匹配提示性文本
参考资料
- Go 正则表达式语法:https://pkg.go.dev/regexp/syntax
- JavaScript 正则表达式:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
- 测试文件:
pkg/workflow/engine_error_patterns_infinite_loop_test.gopkg/workflow/js/validate_errors.test.cjspkg/workflow/error_pattern_tuning_test.go
评论
支持 Markdown