internal/proxy/request.go, checkClient: now is read before lookUp, which can wait up to SWWAF_LOOKUP_TIMEOUT for a new client's first answer from GeoJS while a country list, a biased threshold or SWWAF_ADD_LOOKUP_HEADERS is set. limitBroken(now) then counts the request at that earlier time. Buckets.Add in internal/ratelimit/ratelimit.go takes a request dated more than a second before the bucket under way began for a clock set back, and empties that window's buckets.
Repro, with SWWAF_DENIED_COUNTRIES=kp, SWWAF_LOOKUP_TIMEOUT=1h, a stand-in for GeoJS that holds its answer, and a test clock:
At 00:00:58 the client's first request arrives and waits for GeoJS.
At 00:01:00.5 its second arrives and waits too.
GeoJS answers, and both go on, in either order.
At 00:01:01 a third request comes.
The third request's minute count should be about 2.98 (1 × 59/60 + 2). When the second request is counted before the first, the first empties the minute's buckets and the count is 1.98: the second request is lost. A test doing this 12 times got 1.98 in 6.
README, request log, counts: "the client's requests in the minute, the hour and the day as the rate limits count them, this request included: in each window, those in the bucket under way and a share of those in the bucket before". The comment on Buckets.Add keeps emptying the buckets for "the clock was set back"; here it was not.
The two requests must arrive more than a second apart across a minute boundary while both wait, so at the default SWWAF_LOOKUP_TIMEOUT of 1s this needs the process to be held up; a longer timeout makes it easy. Untested: reading now after lookUp should avoid it.
Severity: minor.
Model: opus-5-5
`internal/proxy/request.go`, `checkClient`: `now` is read before `lookUp`, which can wait up to `SWWAF_LOOKUP_TIMEOUT` for a new client's first answer from GeoJS while a country list, a biased threshold or `SWWAF_ADD_LOOKUP_HEADERS` is set. `limitBroken(now)` then counts the request at that earlier time. `Buckets.Add` in `internal/ratelimit/ratelimit.go` takes a request dated more than a second before the bucket under way began for a clock set back, and empties that window's buckets.
Repro, with `SWWAF_DENIED_COUNTRIES=kp`, `SWWAF_LOOKUP_TIMEOUT=1h`, a stand-in for GeoJS that holds its answer, and a test clock:
1. At 00:00:58 the client's first request arrives and waits for GeoJS.
2. At 00:01:00.5 its second arrives and waits too.
3. GeoJS answers, and both go on, in either order.
4. At 00:01:01 a third request comes.
The third request's minute count should be about 2.98 (1 × 59/60 + 2). When the second request is counted before the first, the first empties the minute's buckets and the count is 1.98: the second request is lost. A test doing this 12 times got 1.98 in 6.
README, request log, `counts`: "the client's requests in the minute, the hour and the day as the rate limits count them, this request included: in each window, those in the bucket under way and a share of those in the bucket before". The comment on `Buckets.Add` keeps emptying the buckets for "the clock was set back"; here it was not.
The two requests must arrive more than a second apart across a minute boundary while both wait, so at the default `SWWAF_LOOKUP_TIMEOUT` of `1s` this needs the process to be held up; a longer timeout makes it easy. Untested: reading `now` after `lookUp` should avoid it.
Severity: minor.
Model: opus-5-5
Fixed in #133: the checks after the lookup read the time again, so a request that waited for GeoJS is counted for the rate limits when it goes on, and no longer starts the minute afresh over another request of its client.
Model: opus-5-5
Fixed in https://git.eeqj.de/sneak/smallwebwaf/pulls/133: the checks after the lookup read the time again, so a request that waited for GeoJS is counted for the rate limits when it goes on, and no longer starts the minute afresh over another request of its client.
Model: opus-5-5
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
internal/proxy/request.go,checkClient:nowis read beforelookUp, which can wait up toSWWAF_LOOKUP_TIMEOUTfor a new client's first answer from GeoJS while a country list, a biased threshold orSWWAF_ADD_LOOKUP_HEADERSis set.limitBroken(now)then counts the request at that earlier time.Buckets.Addininternal/ratelimit/ratelimit.gotakes a request dated more than a second before the bucket under way began for a clock set back, and empties that window's buckets.Repro, with
SWWAF_DENIED_COUNTRIES=kp,SWWAF_LOOKUP_TIMEOUT=1h, a stand-in for GeoJS that holds its answer, and a test clock:The third request's minute count should be about 2.98 (1 × 59/60 + 2). When the second request is counted before the first, the first empties the minute's buckets and the count is 1.98: the second request is lost. A test doing this 12 times got 1.98 in 6.
README, request log,
counts: "the client's requests in the minute, the hour and the day as the rate limits count them, this request included: in each window, those in the bucket under way and a share of those in the bucket before". The comment onBuckets.Addkeeps emptying the buckets for "the clock was set back"; here it was not.The two requests must arrive more than a second apart across a minute boundary while both wait, so at the default
SWWAF_LOOKUP_TIMEOUTof1sthis needs the process to be held up; a longer timeout makes it easy. Untested: readingnowafterlookUpshould avoid it.Severity: minor.
Model: opus-5-5
Fixed in #133: the checks after the lookup read the time again, so a request that waited for GeoJS is counted for the rate limits when it goes on, and no longer starts the minute afresh over another request of its client.
Model: opus-5-5