Documentation
¶
Overview ¶
Package regexutil provides bounded, DoS-safe wrappers around regexp.Compile for every call path that takes a user- or config-supplied regex pattern.
Threat model ¶
Go's regexp engine is RE2-based and therefore does not suffer classical catastrophic backtracking at match time. Compilation, however, is not guaranteed linear: a pathological pattern (e.g. `(a+)+b`, deeply nested alternation, or simply very long repetition chains) can take measurable wall-clock time inside regexp.Compile and allocate an automaton of many thousands of states. That is sufficient to hang a CLI's update flow or freeze an interactive TUI.
This package applies two defences uniformly:
- A byte-length cap (MaxPatternLength) rejects oversize patterns before any compile work begins.
- A wall-clock timeout on the compile itself (DefaultCompileTimeout) bounds the worst case for anything that slips past the length cap.
Call-site discipline: any regex compiled from a pattern that originates outside the binary (config file, CLI flag, TUI input, HTTP payload, message queue) MUST go through CompileBounded or CompileBoundedTimeout. Literal patterns known at build time can — and should — continue to use regexp.MustCompile directly.
Goroutine leak tradeoff ¶
regexp.Compile is not context-aware, so the timeout path launches the compile in a goroutine and returns on timeout while that goroutine keeps running until the compile finishes (or forever, for truly pathological inputs). This is a bounded leak: the number of distinct pathological patterns a single process ever sees is small; the goroutine holds a single compile's working set; the caller gets their error immediately and can continue. If a future Go version exposes a context-aware compile, migrate.
Logging ¶
Rejection errors never include the offending pattern — only its length and the rejection kind. Logging the pattern would let an attacker fill logs with content of their choosing. Callers that wish to surface the pattern to the operator should do so from a trusted-source context where log amplification is not a concern.
Index ¶
Constants ¶
const DefaultCompileTimeout = 100 * time.Millisecond
DefaultCompileTimeout is the wall-clock timeout applied to a single regexp.Compile call. Compiling a well-behaved 1 KiB pattern takes well under 1 ms on typical hardware; 100 ms is two orders of magnitude above normal and still imperceptible for interactive use.
const MaxPatternLength = 1024
MaxPatternLength is the maximum accepted pattern length, in bytes. Patterns longer than this are rejected without being compiled. Legitimate filename patterns and search queries are short; 1 KiB is generous for the former and ample for the latter.
Variables ¶
var ErrPatternCompileTimeout = errors.New("regex pattern compile timed out")
ErrPatternCompileTimeout is returned when regexp.Compile does not finish within the configured timeout — typically a sign of a pathological pattern.
var ErrPatternInvalid = errors.New("regex pattern is invalid")
ErrPatternInvalid is returned when regexp.Compile fails for a reason other than length or timeout (i.e. syntax errors).
var ErrPatternTooLong = errors.New("regex pattern exceeds maximum length")
ErrPatternTooLong is returned when a pattern exceeds MaxPatternLength. Distinguish with errors.Is.
Functions ¶
func CompileBounded ¶
CompileBounded compiles pattern with a byte-length cap of MaxPatternLength and a wall-clock timeout of whichever of DefaultCompileTimeout or the deadline already present on ctx expires first.
The returned error wraps one of ErrPatternTooLong, ErrPatternCompileTimeout, or ErrPatternInvalid so callers can distinguish the failure mode via errors.Is.
Use this wherever a pattern originates outside the binary: config file, CLI flag, HTTP payload, TUI input. Literal patterns known at build time should keep using regexp.MustCompile.
func CompileBoundedTimeout ¶
CompileBoundedTimeout is a convenience wrapper that applies a caller-supplied timeout via context.WithTimeout on top of context.Background. Use this from call sites that do not naturally carry a context.Context (e.g. a Bubble Tea TUI loop).
The effective timeout is the minimum of timeout and DefaultCompileTimeout, so you cannot accidentally widen the package's wall-clock bound.
Types ¶
This section is empty.