diff --git a/tools/approval.py b/tools/approval.py index aac0406d5..5521ab5bc 100644 --- a/tools/approval.py +++ b/tools/approval.py @@ -164,6 +164,18 @@ HARDLINE_PATTERNS = [ (_CMDPOS + r'telinit\s+[06]\b', "telinit 0/6 (shutdown/reboot)"), ] +# Pre-compiled variant used by the hot-path matcher. Building these at module +# load eliminates the ~2.6 ms cold-cache re.compile fan-out on the first +# terminal() call per process (12 HARDLINE + 47 DANGEROUS patterns, each +# potentially evicted from Python's 512-entry ``re._cache`` by unrelated +# regex work elsewhere in the agent). DANGEROUS_PATTERNS_COMPILED is built +# at the end of this module after DANGEROUS_PATTERNS is defined. +_RE_FLAGS = re.IGNORECASE | re.DOTALL +HARDLINE_PATTERNS_COMPILED = [ + (re.compile(pattern, _RE_FLAGS), description) + for pattern, description in HARDLINE_PATTERNS +] + def detect_hardline_command(command: str) -> tuple: """Check if a command matches the unconditional hardline blocklist. @@ -172,8 +184,8 @@ def detect_hardline_command(command: str) -> tuple: (is_hardline, description) or (False, None) """ normalized = _normalize_command_for_detection(command).lower() - for pattern, description in HARDLINE_PATTERNS: - if re.search(pattern, normalized, re.IGNORECASE | re.DOTALL): + for pattern_re, description in HARDLINE_PATTERNS_COMPILED: + if pattern_re.search(normalized): return (True, description) return (False, None) @@ -267,6 +279,13 @@ DANGEROUS_PATTERNS = [ ] +# Pre-compiled variant (same rationale as HARDLINE_PATTERNS_COMPILED above). +DANGEROUS_PATTERNS_COMPILED = [ + (re.compile(pattern, _RE_FLAGS), description) + for pattern, description in DANGEROUS_PATTERNS +] + + def _legacy_pattern_key(pattern: str) -> str: """Reproduce the old regex-derived approval key for backwards compatibility.""" return pattern.split(r'\b')[1] if r'\b' in pattern else pattern[:20] @@ -319,8 +338,8 @@ def detect_dangerous_command(command: str) -> tuple: (is_dangerous, pattern_key, description) or (False, None, None) """ command_lower = _normalize_command_for_detection(command).lower() - for pattern, description in DANGEROUS_PATTERNS: - if re.search(pattern, command_lower, re.IGNORECASE | re.DOTALL): + for pattern_re, description in DANGEROUS_PATTERNS_COMPILED: + if pattern_re.search(command_lower): pattern_key = description return (True, pattern_key, description) return (False, None, None)