fix(rate_limit): cap regex match timeout to prevent fail-open

Description

Rule match regexes are compiled with no timeout, so a match is bounded only by whatever global the host app sets - ~40s in GitLab Rails, unbounded elsewhere. Matching runs per rule per request, and a timeout lands in Evaluator#check's fail-open rescue, so the request ends up not rate limited at all.

Adds Matcher::MATCH_TIMEOUT_SECONDS = 0.005, set per-Regexp rather than via the global Regexp.timeout=.

Why 5ms: against GitLab's real RackAttack patterns on inputs up to 16KB, the worst match is 0.0419ms - ~119x headroom, none timed out. Still 20x tighter than the 0.1s in the issue.

Worth a look: Matcher.build also takes a bare Regexp, previously stored as-is, so the timeout means recompiling it. The expect(m.value).to equal(re) spec is dropped, replaced by four covering the new behaviour: timeout applied, caller's Regexp not mutated, flags/encoding preserved, and an explicit caller timeout respected. source, options, fixed_encoding? and #== all still hold — only object identity changes. Happy to narrow to the { re: ... } path if preferred.

Fail-closed on timeout (issue point 2) is deliberately left out — a pattern that times out for one request times out for all similar ones, so failing closed turns a bypass into a way to 429 an endpoint. Needs its own discussion.

Closes https://gitlab.com/gitlab-com/gl-infra/production-engineering/-/work_items/28882

Merge request reports

Loading
Loading