gitlab-labkit 4.1.1 → 4.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/labkit/rate_limit/matcher.rb +39 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 65cd128c823d6b57133cffb5e0b4314af6ca3e18f0cf23c38c874e58a77bcdad
|
|
4
|
+
data.tar.gz: cbdd5aa4acc9250e07fb2332257e15c6712cf6a6c82389031d159f565a857dff
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7c93fda74c06cd0d76c4aa37315a98433b24e94e3b8f6a4a67ff5e2f579dfb01b25b3f716b90806e175fada5bb5d7669adc3daf3483872f142a026a141f09675
|
|
7
|
+
data.tar.gz: 4e2ca4d166e927065772c3b87892c4bc0b5152e4c77eba4ec407bce5d7901e5cc136a7748e7063a777401c116c2f57414b8fb1d8ecdeea17b671107d9a73a2d7
|
|
@@ -24,10 +24,21 @@ module Labkit
|
|
|
24
24
|
MAX_REGEX_SOURCE_LENGTH = 200
|
|
25
25
|
ERROR_INSPECT_LIMIT = 80
|
|
26
26
|
|
|
27
|
+
# Wall-clock budget for a single #match? call. Without it a match is
|
|
28
|
+
# bounded only by whatever global the host sets (40s in GitLab Rails,
|
|
29
|
+
# unbounded elsewhere), and a timeout reaches Evaluator's fail-open
|
|
30
|
+
# rescue, so the request goes unlimited.
|
|
31
|
+
#
|
|
32
|
+
# 5ms is ~119x the slowest real match measured against GitLab's
|
|
33
|
+
# RackAttack path patterns on inputs up to 16KB (worst: 0.0419ms, none
|
|
34
|
+
# timed out). Re-measure if a rule ever needs nested quantifiers or
|
|
35
|
+
# backreferences, which are what make backtracking exponential.
|
|
36
|
+
MATCH_TIMEOUT_SECONDS = 0.005
|
|
37
|
+
|
|
27
38
|
def self.build(input)
|
|
28
39
|
case input
|
|
29
40
|
when Regexp
|
|
30
|
-
new(type: :re, value: input)
|
|
41
|
+
new(type: :re, value: with_match_timeout(input))
|
|
31
42
|
when Hash
|
|
32
43
|
from_hash(input)
|
|
33
44
|
when Array
|
|
@@ -67,7 +78,7 @@ module Labkit
|
|
|
67
78
|
end
|
|
68
79
|
|
|
69
80
|
begin
|
|
70
|
-
new(type: :re, value:
|
|
81
|
+
new(type: :re, value: with_match_timeout(source))
|
|
71
82
|
rescue RegexpError, TypeError => e
|
|
72
83
|
raise ArgumentError,
|
|
73
84
|
"rate-limit match value {re: #{truncate_for_error(source)}} failed to compile: #{e.message}"
|
|
@@ -76,6 +87,32 @@ module Labkit
|
|
|
76
87
|
end
|
|
77
88
|
private_class_method :compile
|
|
78
89
|
|
|
90
|
+
# Compiles +source+ (a String pattern or an existing Regexp) into a
|
|
91
|
+
# Regexp bounded by MATCH_TIMEOUT_SECONDS.
|
|
92
|
+
#
|
|
93
|
+
# The timeout is set per-Regexp rather than via the global
|
|
94
|
+
# +Regexp.timeout=+: labkit is a library, and a global would silently
|
|
95
|
+
# change regex behaviour throughout the host application, including
|
|
96
|
+
# code unrelated to rate limiting.
|
|
97
|
+
#
|
|
98
|
+
# A Regexp that already carries its own timeout is returned untouched -
|
|
99
|
+
# an explicit choice by the rule author wins over our default.
|
|
100
|
+
#
|
|
101
|
+
# Recompiling an existing Regexp preserves its source and options
|
|
102
|
+
# (including the fixed-encoding flags) and therefore +#==+; only object
|
|
103
|
+
# identity changes.
|
|
104
|
+
def self.with_match_timeout(source)
|
|
105
|
+
return source if source.is_a?(Regexp) && source.timeout
|
|
106
|
+
|
|
107
|
+
if source.is_a?(Regexp)
|
|
108
|
+
Regexp.new(source.source, source.options, timeout: MATCH_TIMEOUT_SECONDS)
|
|
109
|
+
else
|
|
110
|
+
Regexp.new(source, timeout: MATCH_TIMEOUT_SECONDS)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
private_class_method :with_match_timeout
|
|
115
|
+
|
|
79
116
|
def self.truncate_for_error(value)
|
|
80
117
|
s = value.inspect
|
|
81
118
|
s.length > ERROR_INSPECT_LIMIT ? "#{s[0, ERROR_INSPECT_LIMIT]}...(truncated)" : s
|