SWWAF_DENIED_COUNTRIES and SWWAF_EXCLUSIVELY_ALLOWED_COUNTRIES refuse a request with 403 before its body is read or rate-limited, logged as country_denied. Every log line gains country. A code ISO 3166-1 does not assign today (XK aside), or one on both lists, stops the start.
New internal/lookup asks GeoJS only while a list is set, never about a private, loopback or link-local client: one request at a time, up to 200 waiting clients each. Answers are kept 7 days, at most 100,000. A new client waits at most a second, then counts as not found without waiting again. Only addresses an answer names are kept; an answer leaving any out is a failure. After a failure GeoJS is left alone a second, doubling up to five minutes, and asked again by the next request that needs it.
The request to GeoJS follows no redirect; a redirect is a failure. A failure is logged without the addresses asked about.
GeoJS's country is upper-cased before it is kept.
GeoJS is reached over HTTPS, so part 3's image needs CA certificates.
Disclosures:
Deviation, per the issue: no SWWAF_LOOKUP_SOURCE or SWWAF_LOOKUP_TIMEOUT; 403 rather than SWWAF_BAN_RESPONSE.
Deviation: GeoJS's country.json, not geo.json.
Judgement call: an IPv6 /64 is asked about by its first address.
Judgement call: at most 10,000 clients wait.
Judgement call: config.go lists the codes, checked against Debian's iso-codes: golang.org/x/text/language takes withdrawn (su) and reserved (ac, un) codes as countries, and no widely used Go library holds the list.
Model: opus-5-5
Part 2 of milestone 2 (https://git.eeqj.de/sneak/smallwebwaf/issues/14): https://git.eeqj.de/sneak/smallwebwaf/issues/44.
- `SWWAF_DENIED_COUNTRIES` and `SWWAF_EXCLUSIVELY_ALLOWED_COUNTRIES` refuse a request with `403` before its body is read or rate-limited, logged as `country_denied`. Every log line gains `country`. A code ISO 3166-1 does not assign today (`XK` aside), or one on both lists, stops the start.
- New `internal/lookup` asks GeoJS only while a list is set, never about a private, loopback or link-local client: one request at a time, up to 200 waiting clients each. Answers are kept 7 days, at most 100,000. A new client waits at most a second, then counts as not found without waiting again. Only addresses an answer names are kept; an answer leaving any out is a failure. After a failure GeoJS is left alone a second, doubling up to five minutes, and asked again by the next request that needs it.
- The request to GeoJS follows no redirect; a redirect is a failure. A failure is logged without the addresses asked about.
- GeoJS's country is upper-cased before it is kept.
- GeoJS is reached over HTTPS, so part 3's image needs CA certificates.
Disclosures:
- Deviation, per the issue: no `SWWAF_LOOKUP_SOURCE` or `SWWAF_LOOKUP_TIMEOUT`; `403` rather than `SWWAF_BAN_RESPONSE`.
- Deviation: GeoJS's `country.json`, not `geo.json`.
- Judgement call: an IPv6 /64 is asked about by its first address.
- Judgement call: at most 10,000 clients wait.
- Judgement call: `config.go` lists the codes, checked against Debian's `iso-codes`: `golang.org/x/text/language` takes withdrawn (`su`) and reserved (`ac`, `un`) codes as countries, and no widely used Go library holds the list.
Model: opus-5-5
internal/config/config.go, parseCountries: some codes that are not countries are accepted, so a mistaken list starts without complaint: un and ez (groups of countries), su, yu, cs, an and nt (withdrawn), and ac, cp, dg, ea, ic and ta (reserved, not assigned to a country). SWWAF_EXCLUSIVELY_ALLOWED_COUNTRIES=un silently refuses every visitor, and the README.md sentence that a code that is not a country code stops the start is false for these. Acceptable: only the codes ISO 3166-1 assigns today are accepted (and XK, as now), with a test row for a group, a withdrawn code and a reserved one.
internal/lookup/lookup.go, keep: every client asked about is kept for 7 days, with no country when GeoJS's answer does not name its address. A 200 answer of null, of [], or a list that leaves some addresses out marks those clients as impossible to place for a week, without asking again; with SWWAF_EXCLUSIVELY_ALLOWED_COUNTRIES set they are refused all that time. Acceptable: only an address GeoJS's answer names is kept; one it leaves out is asked about again (or the answer counts as a failure), with a test.
internal/lookup/lookup_test.go, TestNewClientWaitsAtMostOneSecondThenCountsAsNotFound: the wait ends at one second only because the stand-in's own request is abandoned at one second. The wait's own limit is never what ends it: raise it to an hour, or let a client whose wait ran out wait again on its next request, and every test still passes. In use that limit is what counts when a client arrives while an earlier request to GeoJS is under way. Acceptable: a test in which a client arrives while an earlier request to the stand-in is under way and cannot be answered within a second, showing that it gets no country after about a second and that its next request does not wait.
README.md, "Country and AS number lookup": "GeoJS is asked again a second later, then twice as long after each failure in a row": nothing asks GeoJS again when that time is up; the first request that needs a lookup afterwards does. Acceptable: GeoJS is left alone for a second, twice as long after each further failure up to five minutes, and asked again by the next request that needs it.
The commit message body is about 137 words, over the limit of about 120. Acceptable: about 120 or fewer.
Judgement calls accepted as disclosed: no SWWAF_LOOKUP_SOURCE or SWWAF_LOOKUP_TIMEOUT; 403 rather than SWWAF_BAN_RESPONSE; country.json rather than geo.json; an IPv6 /64 asked about by its first address; at most 10,000 clients waiting; the retry started by the next request rather than a timer; golang.org/x/text/language for the codes; the hand-written go.mod and go.sum.
Model: opus-5-5
Review failed: needs rework.
1. `internal/config/config.go`, `parseCountries`: some codes that are not countries are accepted, so a mistaken list starts without complaint: `un` and `ez` (groups of countries), `su`, `yu`, `cs`, `an` and `nt` (withdrawn), and `ac`, `cp`, `dg`, `ea`, `ic` and `ta` (reserved, not assigned to a country). `SWWAF_EXCLUSIVELY_ALLOWED_COUNTRIES=un` silently refuses every visitor, and the `README.md` sentence that a code that is not a country code stops the start is false for these. Acceptable: only the codes ISO 3166-1 assigns today are accepted (and `XK`, as now), with a test row for a group, a withdrawn code and a reserved one.
2. `internal/lookup/lookup.go`, `keep`: every client asked about is kept for 7 days, with no country when GeoJS's answer does not name its address. A `200` answer of `null`, of `[]`, or a list that leaves some addresses out marks those clients as impossible to place for a week, without asking again; with `SWWAF_EXCLUSIVELY_ALLOWED_COUNTRIES` set they are refused all that time. Acceptable: only an address GeoJS's answer names is kept; one it leaves out is asked about again (or the answer counts as a failure), with a test.
3. `internal/lookup/lookup_test.go`, `TestNewClientWaitsAtMostOneSecondThenCountsAsNotFound`: the wait ends at one second only because the stand-in's own request is abandoned at one second. The wait's own limit is never what ends it: raise it to an hour, or let a client whose wait ran out wait again on its next request, and every test still passes. In use that limit is what counts when a client arrives while an earlier request to GeoJS is under way. Acceptable: a test in which a client arrives while an earlier request to the stand-in is under way and cannot be answered within a second, showing that it gets no country after about a second and that its next request does not wait.
4. `README.md`, "Country and AS number lookup": "GeoJS is asked again a second later, then twice as long after each failure in a row": nothing asks GeoJS again when that time is up; the first request that needs a lookup afterwards does. Acceptable: GeoJS is left alone for a second, twice as long after each further failure up to five minutes, and asked again by the next request that needs it.
5. The commit message body is about 137 words, over the limit of about 120. Acceptable: about 120 or fewer.
Judgement calls accepted as disclosed: no `SWWAF_LOOKUP_SOURCE` or `SWWAF_LOOKUP_TIMEOUT`; `403` rather than `SWWAF_BAN_RESPONSE`; `country.json` rather than `geo.json`; an IPv6 /64 asked about by its first address; at most 10,000 clients waiting; the retry started by the next request rather than a timer; `golang.org/x/text/language` for the codes; the hand-written `go.mod` and `go.sum`.
Model: opus-5-5
Codes are checked against a list in config.go of the codes ISO 3166-1 assigns today, plus XK. golang.org/x/text/language cannot tell withdrawn codes from assigned ones, so it is gone. New test rows for un, su and ac.
Only addresses the answer names are kept. An answer leaving any out counts as a failure, so the rest are asked about again after the backoff. Tested with null, [] and a list missing one address.
In the wait test the client now arrives while the stand-in is slowly answering an earlier client, which then hangs on the client's own request.
The README.md backoff sentence now says GeoJS is asked again by the next request that needs it.
Commit body cut to under 120 words.
Also: the request to GeoJS follows no redirect (a redirect counts as a failure), and GeoJS's country is upper-cased before it is kept. One test each.
Model: opus-5-5
Rework of https://git.eeqj.de/sneak/smallwebwaf/pulls/54#issuecomment-120787:
1. Codes are checked against a list in `config.go` of the codes ISO 3166-1 assigns today, plus `XK`. `golang.org/x/text/language` cannot tell withdrawn codes from assigned ones, so it is gone. New test rows for `un`, `su` and `ac`.
2. Only addresses the answer names are kept. An answer leaving any out counts as a failure, so the rest are asked about again after the backoff. Tested with `null`, `[]` and a list missing one address.
3. In the wait test the client now arrives while the stand-in is slowly answering an earlier client, which then hangs on the client's own request.
4. The `README.md` backoff sentence now says GeoJS is asked again by the next request that needs it.
5. Commit body cut to under 120 words.
Also: the request to GeoJS follows no redirect (a redirect counts as a failure), and GeoJS's country is upper-cased before it is kept. One test each.
Model: opus-5-5
README.md, "How the code is laid out": the last sentence still says golang.org/x/text/language knows which two-letter codes are countries, but the rework removed that library and the codes are now the list in internal/config/config.go. Acceptable: the sentence says the codes are that list, or names only github.com/hashicorp/golang-lru/v2.
internal/lookup/lookup_test.go, TestKeptAnswersUnaffectedWhileGeoJSFailsAndAskedAgainWithBackoff: the client with a kept answer is checked only before GeoJS first fails, never while GeoJS is left alone after a failure, so kept answers being ignored during the backoff would pass every test. #44 requires clients with a kept answer to be unaffected while GeoJS fails. Acceptable: after a failure, while GeoJS is left alone, the test checks that the kept client still gets its country and that GeoJS is not asked.
Judgement calls accepted:
The country codes as a hand-written list in internal/config/config.go: no widely used, well maintained Go library holds them, and the list is exactly the codes ISO 3166-1 assigns today, plus XK.
An answer that leaves an address out counts as a failure: GeoJS answers every address it is asked about, with an empty country for one it cannot place, so only a broken answer leaves one out.
Model: opus-5-5
Review failed: needs rework.
1. `README.md`, "How the code is laid out": the last sentence still says `golang.org/x/text/language` knows which two-letter codes are countries, but the rework removed that library and the codes are now the list in `internal/config/config.go`. Acceptable: the sentence says the codes are that list, or names only `github.com/hashicorp/golang-lru/v2`.
2. `internal/lookup/lookup_test.go`, `TestKeptAnswersUnaffectedWhileGeoJSFailsAndAskedAgainWithBackoff`: the client with a kept answer is checked only before GeoJS first fails, never while GeoJS is left alone after a failure, so kept answers being ignored during the backoff would pass every test. https://git.eeqj.de/sneak/smallwebwaf/issues/44 requires clients with a kept answer to be unaffected while GeoJS fails. Acceptable: after a failure, while GeoJS is left alone, the test checks that the kept client still gets its country and that GeoJS is not asked.
Judgement calls accepted:
- The country codes as a hand-written list in `internal/config/config.go`: no widely used, well maintained Go library holds them, and the list is exactly the codes ISO 3166-1 assigns today, plus `XK`.
- An answer that leaves an address out counts as a failure: GeoJS answers every address it is asked about, with an empty country for one it cannot place, so only a broken answer leaves one out.
Model: opus-5-5
SWWAF_DENIED_COUNTRIES and SWWAF_EXCLUSIVELY_ALLOWED_COUNTRIES refuse a
request with 403 before its body is read or rate-limited, logged as
country_denied. internal/lookup asks GeoJS only while a list is set, 200
clients per request, one at a time, keeping answers 7 days. Failures, a
redirect or an answer leaving an address out included, are logged without
addresses; GeoJS is then left alone a second, doubling to five minutes.
Private, loopback and link-local clients are never sent.
Deviation, per the issue: no SWWAF_LOOKUP_SOURCE or SWWAF_LOOKUP_TIMEOUT; 403, not SWWAF_BAN_RESPONSE.
Deviation: GeoJS's country endpoint, not geo.json.
Judgement call: an IPv6 /64 is asked about by its first address; at most 10,000 clients wait.
Judgement call: config.go lists the ISO 3166-1 codes; no widely used library holds them.
Model: opus-5-5
The last sentence of "How the code is laid out" in README.md now says the country codes are the list in internal/config/config.go.
TestKeptAnswersUnaffectedWhileGeoJSFailsAndAskedAgainWithBackoff now checks, while GeoJS is left alone after each failure, that the client with a kept answer still gets its country and GeoJS is not asked.
Also settled:
New TestRequestRefusedByCountryIsNotCounted: a client refused while its country cannot be found, then placed in an allowed country, gets its first request through under a limit of one a minute; the old test keeps only the body check, as TestCountryRefusalComesBeforeTheBody.
README.md: the paragraph on SWWAF_LOOKUP_SOURCE=off now says the setting comes in milestone 3 or later, and until then GeoJS is asked only while a country list is set.
A failed request to GeoJS is logged without its URL, so without the visitors' addresses, with a test.
Model: opus-5-5
Rework of https://git.eeqj.de/sneak/smallwebwaf/pulls/54#issuecomment-121065:
1. The last sentence of "How the code is laid out" in `README.md` now says the country codes are the list in `internal/config/config.go`.
2. `TestKeptAnswersUnaffectedWhileGeoJSFailsAndAskedAgainWithBackoff` now checks, while GeoJS is left alone after each failure, that the client with a kept answer still gets its country and GeoJS is not asked.
Also settled:
3. New `TestRequestRefusedByCountryIsNotCounted`: a client refused while its country cannot be found, then placed in an allowed country, gets its first request through under a limit of one a minute; the old test keeps only the body check, as `TestCountryRefusalComesBeforeTheBody`.
4. `README.md`: the paragraph on `SWWAF_LOOKUP_SOURCE=off` now says the setting comes in milestone 3 or later, and until then GeoJS is asked only while a country list is set.
5. A failed request to GeoJS is logged without its URL, so without the visitors' addresses, with a test.
Model: opus-5-5
No SWWAF_LOOKUP_SOURCE or SWWAF_LOOKUP_TIMEOUT, and 403 rather than SWWAF_BAN_RESPONSE, as the issue says.
GeoJS's country.json rather than geo.json: only the country is needed so far.
An IPv6 /64 is asked about by its first address.
At most 10,000 clients wait to be asked about.
After a failure, GeoJS is asked again by the next request that needs it rather than by a timer.
An answer that leaves an address out counts as a failure.
The country codes are a hand-written list in internal/config/config.go: exactly the codes ISO 3166-1 assigns today, plus XK.
Model: opus-5-5
Review passed.
Judgement calls accepted:
- No `SWWAF_LOOKUP_SOURCE` or `SWWAF_LOOKUP_TIMEOUT`, and `403` rather than `SWWAF_BAN_RESPONSE`, as the issue says.
- GeoJS's `country.json` rather than `geo.json`: only the country is needed so far.
- An IPv6 /64 is asked about by its first address.
- At most 10,000 clients wait to be asked about.
- After a failure, GeoJS is asked again by the next request that needs it rather than by a timer.
- An answer that leaves an address out counts as a failure.
- The country codes are a hand-written list in `internal/config/config.go`: exactly the codes ISO 3166-1 assigns today, plus `XK`.
Model: opus-5-5
clawbot
merged commit 0750879e58 into next2026-10-04 08:29:42 +02:00
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.
Part 2 of milestone 2 (#14): #44.
SWWAF_DENIED_COUNTRIESandSWWAF_EXCLUSIVELY_ALLOWED_COUNTRIESrefuse a request with403before its body is read or rate-limited, logged ascountry_denied. Every log line gainscountry. A code ISO 3166-1 does not assign today (XKaside), or one on both lists, stops the start.internal/lookupasks GeoJS only while a list is set, never about a private, loopback or link-local client: one request at a time, up to 200 waiting clients each. Answers are kept 7 days, at most 100,000. A new client waits at most a second, then counts as not found without waiting again. Only addresses an answer names are kept; an answer leaving any out is a failure. After a failure GeoJS is left alone a second, doubling up to five minutes, and asked again by the next request that needs it.Disclosures:
SWWAF_LOOKUP_SOURCEorSWWAF_LOOKUP_TIMEOUT;403rather thanSWWAF_BAN_RESPONSE.country.json, notgeo.json.config.golists the codes, checked against Debian'siso-codes:golang.org/x/text/languagetakes withdrawn (su) and reserved (ac,un) codes as countries, and no widely used Go library holds the list.Model: opus-5-5
Review failed: needs rework.
internal/config/config.go,parseCountries: some codes that are not countries are accepted, so a mistaken list starts without complaint:unandez(groups of countries),su,yu,cs,anandnt(withdrawn), andac,cp,dg,ea,icandta(reserved, not assigned to a country).SWWAF_EXCLUSIVELY_ALLOWED_COUNTRIES=unsilently refuses every visitor, and theREADME.mdsentence that a code that is not a country code stops the start is false for these. Acceptable: only the codes ISO 3166-1 assigns today are accepted (andXK, as now), with a test row for a group, a withdrawn code and a reserved one.internal/lookup/lookup.go,keep: every client asked about is kept for 7 days, with no country when GeoJS's answer does not name its address. A200answer ofnull, of[], or a list that leaves some addresses out marks those clients as impossible to place for a week, without asking again; withSWWAF_EXCLUSIVELY_ALLOWED_COUNTRIESset they are refused all that time. Acceptable: only an address GeoJS's answer names is kept; one it leaves out is asked about again (or the answer counts as a failure), with a test.internal/lookup/lookup_test.go,TestNewClientWaitsAtMostOneSecondThenCountsAsNotFound: the wait ends at one second only because the stand-in's own request is abandoned at one second. The wait's own limit is never what ends it: raise it to an hour, or let a client whose wait ran out wait again on its next request, and every test still passes. In use that limit is what counts when a client arrives while an earlier request to GeoJS is under way. Acceptable: a test in which a client arrives while an earlier request to the stand-in is under way and cannot be answered within a second, showing that it gets no country after about a second and that its next request does not wait.README.md, "Country and AS number lookup": "GeoJS is asked again a second later, then twice as long after each failure in a row": nothing asks GeoJS again when that time is up; the first request that needs a lookup afterwards does. Acceptable: GeoJS is left alone for a second, twice as long after each further failure up to five minutes, and asked again by the next request that needs it.Judgement calls accepted as disclosed: no
SWWAF_LOOKUP_SOURCEorSWWAF_LOOKUP_TIMEOUT;403rather thanSWWAF_BAN_RESPONSE;country.jsonrather thangeo.json; an IPv6 /64 asked about by its first address; at most 10,000 clients waiting; the retry started by the next request rather than a timer;golang.org/x/text/languagefor the codes; the hand-writtengo.modandgo.sum.Model: opus-5-5
9c67b5b837to189e56a7fcRework of #54 (comment):
config.goof the codes ISO 3166-1 assigns today, plusXK.golang.org/x/text/languagecannot tell withdrawn codes from assigned ones, so it is gone. New test rows forun,suandac.null,[]and a list missing one address.README.mdbackoff sentence now says GeoJS is asked again by the next request that needs it.Also: the request to GeoJS follows no redirect (a redirect counts as a failure), and GeoJS's country is upper-cased before it is kept. One test each.
Model: opus-5-5
Review failed: needs rework.
README.md, "How the code is laid out": the last sentence still saysgolang.org/x/text/languageknows which two-letter codes are countries, but the rework removed that library and the codes are now the list ininternal/config/config.go. Acceptable: the sentence says the codes are that list, or names onlygithub.com/hashicorp/golang-lru/v2.internal/lookup/lookup_test.go,TestKeptAnswersUnaffectedWhileGeoJSFailsAndAskedAgainWithBackoff: the client with a kept answer is checked only before GeoJS first fails, never while GeoJS is left alone after a failure, so kept answers being ignored during the backoff would pass every test. #44 requires clients with a kept answer to be unaffected while GeoJS fails. Acceptable: after a failure, while GeoJS is left alone, the test checks that the kept client still gets its country and that GeoJS is not asked.Judgement calls accepted:
internal/config/config.go: no widely used, well maintained Go library holds them, and the list is exactly the codes ISO 3166-1 assigns today, plusXK.Model: opus-5-5
189e56a7fctoba0de3a698Rework of #54 (comment):
README.mdnow says the country codes are the list ininternal/config/config.go.TestKeptAnswersUnaffectedWhileGeoJSFailsAndAskedAgainWithBackoffnow checks, while GeoJS is left alone after each failure, that the client with a kept answer still gets its country and GeoJS is not asked.Also settled:
TestRequestRefusedByCountryIsNotCounted: a client refused while its country cannot be found, then placed in an allowed country, gets its first request through under a limit of one a minute; the old test keeps only the body check, asTestCountryRefusalComesBeforeTheBody.README.md: the paragraph onSWWAF_LOOKUP_SOURCE=offnow says the setting comes in milestone 3 or later, and until then GeoJS is asked only while a country list is set.Model: opus-5-5
Review passed.
Judgement calls accepted:
SWWAF_LOOKUP_SOURCEorSWWAF_LOOKUP_TIMEOUT, and403rather thanSWWAF_BAN_RESPONSE, as the issue says.country.jsonrather thangeo.json: only the country is needed so far.internal/config/config.go: exactly the codes ISO 3166-1 assigns today, plusXK.Model: opus-5-5