fix: escape regex special chars in pattern matcher
Fixes #1521. When hook matcher patterns contained regex special characters like parentheses, the pattern-matcher would throw 'SyntaxError: Invalid regular expression: unmatched parentheses' because these characters were not escaped before constructing the RegExp. The fix escapes all regex special characters (.+?^${}()|[\]\) EXCEPT the asterisk (*) which is intentionally converted to .* for glob-style matching. Add comprehensive test suite for pattern-matcher covering: - Exact matching (case-insensitive) - Wildcard matching (glob-style *) - Pipe-separated patterns - All regex special characters (parentheses, brackets, etc.) - Edge cases (empty matcher, complex patterns)
This commit is contained in:
@@ -1,5 +1,14 @@
|
||||
import type { ClaudeHooksConfig, HookMatcher } from "../hooks/claude-code-hooks/types"
|
||||
|
||||
/**
|
||||
* Escape all regex special characters EXCEPT asterisk (*).
|
||||
* Asterisk is preserved for glob-to-regex conversion.
|
||||
*/
|
||||
function escapeRegexExceptAsterisk(str: string): string {
|
||||
// Escape all regex special chars except * (which we convert to .* for glob matching)
|
||||
return str.replace(/[.+?^${}()|[\]\\]/g, "\\$&")
|
||||
}
|
||||
|
||||
export function matchesToolMatcher(toolName: string, matcher: string): boolean {
|
||||
if (!matcher) {
|
||||
return true
|
||||
@@ -7,7 +16,9 @@ export function matchesToolMatcher(toolName: string, matcher: string): boolean {
|
||||
const patterns = matcher.split("|").map((p) => p.trim())
|
||||
return patterns.some((p) => {
|
||||
if (p.includes("*")) {
|
||||
const regex = new RegExp(`^${p.replace(/\*/g, ".*")}$`, "i")
|
||||
// First escape regex special chars (except *), then convert * to .*
|
||||
const escaped = escapeRegexExceptAsterisk(p)
|
||||
const regex = new RegExp(`^${escaped.replace(/\*/g, ".*")}$`, "i")
|
||||
return regex.test(toolName)
|
||||
}
|
||||
return p.toLowerCase() === toolName.toLowerCase()
|
||||
|
||||
Reference in New Issue
Block a user