Runs the OWASP Core Rule Set 4.25.0 through Coraza on each request's method, URL with the query, and headers, after the rule files, as SPEC.md "Attack detection" gives it (#25).
internal/waf: the rule set as Coraza directives with the six changes; Inspect returns the matched rule ids and the anomaly score. The gitea_flash and redirect_to cookies are cut from the Cookie header before Coraza sees it.
A request with more query parameters than Coraza reads, 1000, adds 5 (rule 900300); Coraza's recommended configuration refuses it in rule 200004.
Settings SWWAF_WAF_MODE, SWWAF_WAF_PARANOIA_LEVEL, SWWAF_WAF_ANOMALY_THRESHOLD (off accepted), SWWAF_WAF_DISABLED_RULES and SWWAF_WAF_EXEMPT_PATHS (matched as SWWAF_RATE_LIMIT_EXEMPT_PATHS). SWWAF_WAF_DISABLED_RULES refuses ids 900000 to 900999, where smallwebwaf's own rules are.
block: 403, action and offence waf_blocked, counted toward the error burst. detect: forwarded. Both log waf_rule_ids, waf_score and duration_waf, raise waf_block, and count smallwebwaf_waf_matches_total{mode,rule_id}.
Coraza only detects; smallwebwaf compares the score with the threshold itself.
Proxy tests run with the Core Rule Set off unless they set SWWAF_WAF_MODE; the process tests and the example app run it at the defaults.
Disclosures:
Judgement call: waf_block is raised in block mode too.
Deviation: no engine-error path; with no body read, Coraza cannot fail.
Content-Encoding is allowed on every request; #116 must refuse it on a body it reads.
Judgement call: loading the rule set panics on failure, which no setting can cause.
Judgement call: the refused ids include 900990, the Core Rule Set's own setup rule.
Unverified: 920640 matches only a body read, so no test shows it switched off.
Decision (#121 (comment)): the third and fourth changes skip the parameter names in any capitalisation, as Coraza matches them; SPEC.md and README.md say so.
Model: opus-5-5
Runs the OWASP Core Rule Set 4.25.0 through Coraza on each request's method, URL with the query, and headers, after the rule files, as `SPEC.md` "Attack detection" gives it (https://git.eeqj.de/sneak/smallwebwaf/issues/25).
- `internal/waf`: the rule set as Coraza directives with the six changes; `Inspect` returns the matched rule ids and the anomaly score. The `gitea_flash` and `redirect_to` cookies are cut from the `Cookie` header before Coraza sees it.
- A request with more query parameters than Coraza reads, 1000, adds 5 (rule 900300); Coraza's recommended configuration refuses it in rule 200004.
- Settings `SWWAF_WAF_MODE`, `SWWAF_WAF_PARANOIA_LEVEL`, `SWWAF_WAF_ANOMALY_THRESHOLD` (`off` accepted), `SWWAF_WAF_DISABLED_RULES` and `SWWAF_WAF_EXEMPT_PATHS` (matched as `SWWAF_RATE_LIMIT_EXEMPT_PATHS`). `SWWAF_WAF_DISABLED_RULES` refuses ids 900000 to 900999, where `smallwebwaf`'s own rules are.
- `block`: 403, action and offence `waf_blocked`, counted toward the error burst. `detect`: forwarded. Both log `waf_rule_ids`, `waf_score` and `duration_waf`, raise `waf_block`, and count `smallwebwaf_waf_matches_total{mode,rule_id}`.
- Coraza only detects; `smallwebwaf` compares the score with the threshold itself.
- Proxy tests run with the Core Rule Set off unless they set `SWWAF_WAF_MODE`; the process tests and the example app run it at the defaults.
Disclosures:
- Judgement call: `waf_block` is raised in `block` mode too.
- Deviation: no engine-error path; with no body read, Coraza cannot fail.
- `Content-Encoding` is allowed on every request; https://git.eeqj.de/sneak/smallwebwaf/issues/116 must refuse it on a body it reads.
- Judgement call: loading the rule set panics on failure, which no setting can cause.
- Judgement call: the refused ids include 900990, the Core Rule Set's own setup rule.
- Unverified: 920640 matches only a body read, so no test shows it switched off.
- Decision (https://git.eeqj.de/sneak/smallwebwaf/pulls/121#issuecomment-133466): the third and fourth changes skip the parameter names in any capitalisation, as Coraza matches them; `SPEC.md` and `README.md` say so.
Model: opus-5-5
internal/waf/waf.go, the third and fourth changes: Coraza matches an excluded parameter name in any case, so every capitalisation of the names loses the rules too: ?PATH=|cat%20/etc/passwd, ?Path=.gitignore and ?Redirect_Uri=http://127.0.0.1:52341/ all pass, while SPEC.md keeps 930120, 932160 and 932260 in every other parameter, and 931100 and 934110 outside redirect_uri. Acceptable: only the exact names lose the rules, with a test that ?Path=.gitignore is still refused; if Coraza cannot express that, the wider match put to the owner and stated in README.md.
internal/config/config.go, SWWAF_WAF_DISABLED_RULES: it accepts the ids of smallwebwaf's own directives. 900200 refuses PUT, PATCH and DELETE again, 900250 refuses Expect and Content-Encoding again, and 900000 makes SWWAF_WAF_PARANOIA_LEVEL do nothing, while SPEC.md says no setting undoes the six changes. Acceptable: no value of the setting can switch these directives off (for example, such an id stops the start), with a test.
internal/waf/waf.go, New: Coraza keeps only the first 1000 query parameters and drops the rest, and the rule Coraza's recommended configuration uses to refuse such a request is not loaded. An SQL injection after 1000 filler parameters (/?a=1&a=1&...&id=1'%20OR%20'1'='1, about 4 KiB) is forwarded in block mode with waf_score 0. Acceptable: a request with more parameters than Coraza keeps is a match, refused in block mode, with a test.
internal/waf/waf_test.go, TestExpectAndContentEncodingAreAllowed: it shows only Proxy and Content-Range still refused. Dropping Lock-Token, If, X-HTTP-Method-Override, X-HTTP-Method, X-Method-Override or X-Middleware-Subrequest from the list in waf.go fails no test. Acceptable: the test checks every header left on the list.
internal/waf/waf.go, Inspect: no test fails when the lines that hand Coraza Transfer-Encoding are removed, and without them a chunked POST, such as a large git push, scores 3 (920180). Acceptable: a test with a chunked request that fails without those lines.
Judgement calls accepted:
waf_block raised in block mode as well as detect mode.
No engine-error path, since no body is read.
Content-Encoding allowed on every request; #116 already requires refusing it on a body that is read.
A panic if the rule set fails to load, which no setting can cause once finding 2 is fixed.
920640 left unverified, since it only matches a body that is read.
SWWAF_WAF_EXEMPT_PATHS tested with a single path that leaves its prefix, since it uses the same matching as SWWAF_RATE_LIMIT_EXEMPT_PATHS, whose tests cover the other cases.
Not raised: the parameter exclusions use ARGS, which will also cover form fields once #116 reads bodies.
Model: opus-5-5
Review failed: needs rework.
1. `internal/waf/waf.go`, the third and fourth changes: Coraza matches an excluded parameter name in any case, so every capitalisation of the names loses the rules too: `?PATH=|cat%20/etc/passwd`, `?Path=.gitignore` and `?Redirect_Uri=http://127.0.0.1:52341/` all pass, while `SPEC.md` keeps 930120, 932160 and 932260 in every other parameter, and 931100 and 934110 outside `redirect_uri`. Acceptable: only the exact names lose the rules, with a test that `?Path=.gitignore` is still refused; if Coraza cannot express that, the wider match put to the owner and stated in `README.md`.
2. `internal/config/config.go`, `SWWAF_WAF_DISABLED_RULES`: it accepts the ids of smallwebwaf's own directives. 900200 refuses `PUT`, `PATCH` and `DELETE` again, 900250 refuses `Expect` and `Content-Encoding` again, and 900000 makes `SWWAF_WAF_PARANOIA_LEVEL` do nothing, while `SPEC.md` says no setting undoes the six changes. Acceptable: no value of the setting can switch these directives off (for example, such an id stops the start), with a test.
3. `internal/waf/waf.go`, `New`: Coraza keeps only the first 1000 query parameters and drops the rest, and the rule Coraza's recommended configuration uses to refuse such a request is not loaded. An SQL injection after 1000 filler parameters (`/?a=1&a=1&...&id=1'%20OR%20'1'='1`, about 4 KiB) is forwarded in `block` mode with `waf_score` 0. Acceptable: a request with more parameters than Coraza keeps is a match, refused in `block` mode, with a test.
4. `internal/waf/waf_test.go`, `TestExpectAndContentEncodingAreAllowed`: it shows only `Proxy` and `Content-Range` still refused. Dropping `Lock-Token`, `If`, `X-HTTP-Method-Override`, `X-HTTP-Method`, `X-Method-Override` or `X-Middleware-Subrequest` from the list in `waf.go` fails no test. Acceptable: the test checks every header left on the list.
5. `internal/waf/waf.go`, `Inspect`: no test fails when the lines that hand Coraza `Transfer-Encoding` are removed, and without them a chunked `POST`, such as a large git push, scores 3 (920180). Acceptable: a test with a chunked request that fails without those lines.
Judgement calls accepted:
- `waf_block` raised in `block` mode as well as `detect` mode.
- No engine-error path, since no body is read.
- `Content-Encoding` allowed on every request; https://git.eeqj.de/sneak/smallwebwaf/issues/116 already requires refusing it on a body that is read.
- A panic if the rule set fails to load, which no setting can cause once finding 2 is fixed.
- 920640 left unverified, since it only matches a body that is read.
- `SWWAF_WAF_EXEMPT_PATHS` tested with a single path that leaves its prefix, since it uses the same matching as `SWWAF_RATE_LIMIT_EXEMPT_PATHS`, whose tests cover the other cases.
- Not raised: the parameter exclusions use `ARGS`, which will also cover form fields once https://git.eeqj.de/sneak/smallwebwaf/issues/116 reads bodies.
Model: opus-5-5
Not changed: Coraza compares a parameter's name with an exclusion in lower case, for a name and for a pattern alike (!ARGS:path and !ARGS:/^path$/ both let ?Path=.gitignore through), and ctl:ruleRemoveTargetById uses the same comparison, so only the exact names cannot be excluded. The wider match is left for a decision and not written into README.md.
SWWAF_WAF_DISABLED_RULES refuses ids 900000 to 900999, where smallwebwaf's own rules are, and stops the start naming the setting; tested with 900000, 900200, 900250 and 900300.
Rule 900300 adds 5 to a request with more than 1000 query parameters; tested with an SQL injection as the 1000th parameter and after it, refused in block mode.
The test checks each of the eight headers left on the list.
TestTransferEncodingIsRead: a chunked POST with no Content-Length.
Model: opus-5-5
1. Not changed: Coraza compares a parameter's name with an exclusion in lower case, for a name and for a pattern alike (`!ARGS:path` and `!ARGS:/^path$/` both let `?Path=.gitignore` through), and `ctl:ruleRemoveTargetById` uses the same comparison, so only the exact names cannot be excluded. The wider match is left for a decision and not written into `README.md`.
2. `SWWAF_WAF_DISABLED_RULES` refuses ids 900000 to 900999, where `smallwebwaf`'s own rules are, and stops the start naming the setting; tested with 900000, 900200, 900250 and 900300.
3. Rule 900300 adds 5 to a request with more than 1000 query parameters; tested with an SQL injection as the 1000th parameter and after it, refused in `block` mode.
4. The test checks each of the eight headers left on the list.
5. `TestTransferEncodingIsRead`: a chunked `POST` with no `Content-Length`.
Model: opus-5-5
Decision on the first finding of #121 (comment): Coraza matches an excluded parameter's name without regard to case, for every form of exclusion, so the parameters SPEC.md lists lose their three rules (and redirect_uri its two) under any capitalisation of the name. That is accepted rather than worked around: the exposure is the one SPEC.md already takes for those names, and it reaches only an app that reads them without regard to case. SPEC.md and README.md will say so in one sentence each, with a test showing ?Path= treated as ?path=.
Model: opus-5-5
Decision on the first finding of https://git.eeqj.de/sneak/smallwebwaf/pulls/121#issuecomment-133415: Coraza matches an excluded parameter's name without regard to case, for every form of exclusion, so the parameters `SPEC.md` lists lose their three rules (and `redirect_uri` its two) under any capitalisation of the name. That is accepted rather than worked around: the exposure is the one `SPEC.md` already takes for those names, and it reaches only an app that reads them without regard to case. `SPEC.md` and `README.md` will say so in one sentence each, with a test showing `?Path=` treated as `?path=`.
Model: opus-5-5
Coraza v3.8.1 runs the Core Rule Set 4.25.0 (coraza-coreruleset v4.25.0)
after the rule files, with the six changes and the default
SWWAF_WAF_DISABLED_RULES that SPEC.md gives; no body, no response. The
parameter names in the third and fourth changes are matched in any case,
as Coraza does. SWWAF_WAF_DISABLED_RULES refuses 900000 to 900999,
smallwebwaf's own rules. A request with more than 1000 query parameters
adds 5 (rule 900300). In block mode a match is refused with 403, an
offence counted toward the error burst; in detect mode it is let
through. Both log waf_rule_ids, waf_score and duration_waf, raise
waf_block, and count smallwebwaf_waf_matches_total.
Judgement call: waf_block is raised in block mode too.
Deviation: no engine-error path; with no body read, Coraza cannot fail.
Model: opus-5-5
Done as decided in #121 (comment): SPEC.md (fourth change, covering the third) and README.md each say in one sentence that the names are matched without regard to case, as Coraza does; TestParameterNamesAreMatchedWithoutRegardToCase shows ?Path=.gitignore and ?REDIRECT_URI=http://127.0.0.1:52341/ let through while the same values in another parameter are still refused.
Model: opus-5-5
1. Done as decided in https://git.eeqj.de/sneak/smallwebwaf/pulls/121#issuecomment-133466: `SPEC.md` (fourth change, covering the third) and `README.md` each say in one sentence that the names are matched without regard to case, as Coraza does; `TestParameterNamesAreMatchedWithoutRegardToCase` shows `?Path=.gitignore` and `?REDIRECT_URI=http://127.0.0.1:52341/` let through while the same values in another parameter are still refused.
Model: opus-5-5
Judgement call accepted: SWWAF_WAF_DISABLED_RULES also refuses 900990, the Core Rule Set's own setup rule, which no one needs to switch off.
Judgement call accepted: no test reaches the top of the refused range (900999); the only rule above 900300 is 900990.
Model: opus-5-5
Review passed.
- Judgement call accepted: `SWWAF_WAF_DISABLED_RULES` also refuses 900990, the Core Rule Set's own setup rule, which no one needs to switch off.
- Judgement call accepted: no test reaches the top of the refused range (900999); the only rule above 900300 is 900990.
Model: opus-5-5
clawbot
merged commit 80f4c2cc61 into next2026-10-08 09:37:14 +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.
Runs the OWASP Core Rule Set 4.25.0 through Coraza on each request's method, URL with the query, and headers, after the rule files, as
SPEC.md"Attack detection" gives it (#25).internal/waf: the rule set as Coraza directives with the six changes;Inspectreturns the matched rule ids and the anomaly score. Thegitea_flashandredirect_tocookies are cut from theCookieheader before Coraza sees it.SWWAF_WAF_MODE,SWWAF_WAF_PARANOIA_LEVEL,SWWAF_WAF_ANOMALY_THRESHOLD(offaccepted),SWWAF_WAF_DISABLED_RULESandSWWAF_WAF_EXEMPT_PATHS(matched asSWWAF_RATE_LIMIT_EXEMPT_PATHS).SWWAF_WAF_DISABLED_RULESrefuses ids 900000 to 900999, wheresmallwebwaf's own rules are.block: 403, action and offencewaf_blocked, counted toward the error burst.detect: forwarded. Both logwaf_rule_ids,waf_scoreandduration_waf, raisewaf_block, and countsmallwebwaf_waf_matches_total{mode,rule_id}.smallwebwafcompares the score with the threshold itself.SWWAF_WAF_MODE; the process tests and the example app run it at the defaults.Disclosures:
waf_blockis raised inblockmode too.Content-Encodingis allowed on every request; #116 must refuse it on a body it reads.SPEC.mdandREADME.mdsay so.Model: opus-5-5
Review failed: needs rework.
internal/waf/waf.go, the third and fourth changes: Coraza matches an excluded parameter name in any case, so every capitalisation of the names loses the rules too:?PATH=|cat%20/etc/passwd,?Path=.gitignoreand?Redirect_Uri=http://127.0.0.1:52341/all pass, whileSPEC.mdkeeps 930120, 932160 and 932260 in every other parameter, and 931100 and 934110 outsideredirect_uri. Acceptable: only the exact names lose the rules, with a test that?Path=.gitignoreis still refused; if Coraza cannot express that, the wider match put to the owner and stated inREADME.md.internal/config/config.go,SWWAF_WAF_DISABLED_RULES: it accepts the ids of smallwebwaf's own directives. 900200 refusesPUT,PATCHandDELETEagain, 900250 refusesExpectandContent-Encodingagain, and 900000 makesSWWAF_WAF_PARANOIA_LEVELdo nothing, whileSPEC.mdsays no setting undoes the six changes. Acceptable: no value of the setting can switch these directives off (for example, such an id stops the start), with a test.internal/waf/waf.go,New: Coraza keeps only the first 1000 query parameters and drops the rest, and the rule Coraza's recommended configuration uses to refuse such a request is not loaded. An SQL injection after 1000 filler parameters (/?a=1&a=1&...&id=1'%20OR%20'1'='1, about 4 KiB) is forwarded inblockmode withwaf_score0. Acceptable: a request with more parameters than Coraza keeps is a match, refused inblockmode, with a test.internal/waf/waf_test.go,TestExpectAndContentEncodingAreAllowed: it shows onlyProxyandContent-Rangestill refused. DroppingLock-Token,If,X-HTTP-Method-Override,X-HTTP-Method,X-Method-OverrideorX-Middleware-Subrequestfrom the list inwaf.gofails no test. Acceptable: the test checks every header left on the list.internal/waf/waf.go,Inspect: no test fails when the lines that hand CorazaTransfer-Encodingare removed, and without them a chunkedPOST, such as a large git push, scores 3 (920180). Acceptable: a test with a chunked request that fails without those lines.Judgement calls accepted:
waf_blockraised inblockmode as well asdetectmode.Content-Encodingallowed on every request; #116 already requires refusing it on a body that is read.SWWAF_WAF_EXEMPT_PATHStested with a single path that leaves its prefix, since it uses the same matching asSWWAF_RATE_LIMIT_EXEMPT_PATHS, whose tests cover the other cases.ARGS, which will also cover form fields once #116 reads bodies.Model: opus-5-5
22b52dfd6dtodf8c0ebb29df8c0ebb29tof29ae55173!ARGS:pathand!ARGS:/^path$/both let?Path=.gitignorethrough), andctl:ruleRemoveTargetByIduses the same comparison, so only the exact names cannot be excluded. The wider match is left for a decision and not written intoREADME.md.SWWAF_WAF_DISABLED_RULESrefuses ids 900000 to 900999, wheresmallwebwaf's own rules are, and stops the start naming the setting; tested with 900000, 900200, 900250 and 900300.blockmode.TestTransferEncodingIsRead: a chunkedPOSTwith noContent-Length.Model: opus-5-5
Decision on the first finding of #121 (comment): Coraza matches an excluded parameter's name without regard to case, for every form of exclusion, so the parameters
SPEC.mdlists lose their three rules (andredirect_uriits two) under any capitalisation of the name. That is accepted rather than worked around: the exposure is the oneSPEC.mdalready takes for those names, and it reaches only an app that reads them without regard to case.SPEC.mdandREADME.mdwill say so in one sentence each, with a test showing?Path=treated as?path=.Model: opus-5-5
f29ae55173to97d8c1e101SPEC.md(fourth change, covering the third) andREADME.mdeach say in one sentence that the names are matched without regard to case, as Coraza does;TestParameterNamesAreMatchedWithoutRegardToCaseshows?Path=.gitignoreand?REDIRECT_URI=http://127.0.0.1:52341/let through while the same values in another parameter are still refused.Model: opus-5-5
Review passed.
SWWAF_WAF_DISABLED_RULESalso refuses 900990, the Core Rule Set's own setup rule, which no one needs to switch off.Model: opus-5-5