A path rule matches if its regex matches the path as sent or that path percent-decoded once, as a uri rule already did. The query is cut off before the path is decoded, so a %3F stays in the path.
A path in SWWAF_TRAP_PATHS matches the path as sent or decoded once, so /%77p-login.php now matches /wp-login.php. The ban's notes keep the path as sent.
SPEC.md ("Rule files", path target) and the README's rule file and trap path text say so. The README's /%77p-login.php example used to say the opposite.
Not visible in the diff
decodeOnce is exported as DecodeOnce so the trap path check can use it. The path and uri targets are now constants, because the linter flagged the third "path" literal.
A trap path holding a ? still stops the start. Only the reason given for it changed, since a decoded path can now hold a ? sent as %3F.
A malformed escape in the path, such as /%zz.env, never reaches a path rule or a trap path, because Go's HTTP server answers such a request 400 itself. No test covers it.
The cases that must not match (/%252eenv, /%2577p-login.php) pass with or without the change. Each sits in a test that fails without it.
Judgement call, from the brief: trap paths are decoded as path rules are, against the README's earlier example.
Model: opus-5-5
Builds https://git.eeqj.de/sneak/smallwebwaf/issues/85 as the brief in https://git.eeqj.de/sneak/smallwebwaf/issues/85#issuecomment-139822 settles it.
## What changed
- A `path` rule matches if its regex matches the path as sent or that path percent-decoded once, as a `uri` rule already did. The query is cut off before the path is decoded, so a `%3F` stays in the path.
- A path in `SWWAF_TRAP_PATHS` matches the path as sent or decoded once, so `/%77p-login.php` now matches `/wp-login.php`. The ban's notes keep the path as sent.
- `SPEC.md` ("Rule files", `path` target) and the README's rule file and trap path text say so. The README's `/%77p-login.php` example used to say the opposite.
## Not visible in the diff
- `decodeOnce` is exported as `DecodeOnce` so the trap path check can use it. The `path` and `uri` targets are now constants, because the linter flagged the third `"path"` literal.
- A trap path holding a `?` still stops the start. Only the reason given for it changed, since a decoded path can now hold a `?` sent as `%3F`.
- A malformed escape in the path, such as `/%zz.env`, never reaches a path rule or a trap path, because Go's HTTP server answers such a request `400` itself. No test covers it.
- The cases that must not match (`/%252eenv`, `/%2577p-login.php`) pass with or without the change. Each sits in a test that fails without it.
Judgement call, from the brief: trap paths are decoded as path rules are, against the README's earlier example.
Model: opus-5-5
A path rule matched only the path as the client sent it, so /%2eenv got
past the default env-file rule while an app that decodes the path
serves /.env. A path rule now matches the path as sent or decoded once,
as a uri rule already did, and so does a path in SWWAF_TRAP_PATHS:
/%77p-login.php matches /wp-login.php. The query is cut off before the
path is decoded, and the path is decoded only once, so /%252eenv is not
/.env. A ban's notes keep the path as sent. SPEC.md and README.md say
so.
Model: opus-5-5
PASS: a path rule and a path in SWWAF_TRAP_PATHS now match the path as sent or decoded once, with the query cut off before decoding and the ban's notes keeping the path as sent, and SPEC.md and README.md say so, as #85 and its brief ask.
Judgement call: I accept that no test covers a broken escape such as /%zz.env, since Go's HTTP server answers such a request 400 before any check runs.
Model: opus-5-5
PASS: a `path` rule and a path in `SWWAF_TRAP_PATHS` now match the path as sent or decoded once, with the query cut off before decoding and the ban's notes keeping the path as sent, and `SPEC.md` and `README.md` say so, as https://git.eeqj.de/sneak/smallwebwaf/issues/85 and its brief ask.
Judgement call: I accept that no test covers a broken escape such as `/%zz.env`, since Go's HTTP server answers such a request `400` before any check runs.
Model: opus-5-5
clawbot
merged commit 27f0007b54 into next2026-10-14 18:20:32 +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.
Builds #85 as the brief in #85 (comment) settles it.
What changed
pathrule matches if its regex matches the path as sent or that path percent-decoded once, as aurirule already did. The query is cut off before the path is decoded, so a%3Fstays in the path.SWWAF_TRAP_PATHSmatches the path as sent or decoded once, so/%77p-login.phpnow matches/wp-login.php. The ban's notes keep the path as sent.SPEC.md("Rule files",pathtarget) and the README's rule file and trap path text say so. The README's/%77p-login.phpexample used to say the opposite.Not visible in the diff
decodeOnceis exported asDecodeOnceso the trap path check can use it. Thepathanduritargets are now constants, because the linter flagged the third"path"literal.?still stops the start. Only the reason given for it changed, since a decoded path can now hold a?sent as%3F./%zz.env, never reaches a path rule or a trap path, because Go's HTTP server answers such a request400itself. No test covers it./%252eenv,/%2577p-login.php) pass with or without the change. Each sits in a test that fails without it.Judgement call, from the brief: trap paths are decoded as path rules are, against the README's earlier example.
Model: opus-5-5
PASS: a
pathrule and a path inSWWAF_TRAP_PATHSnow match the path as sent or decoded once, with the query cut off before decoding and the ban's notes keeping the path as sent, andSPEC.mdandREADME.mdsay so, as #85 and its brief ask.Judgement call: I accept that no test covers a broken escape such as
/%zz.env, since Go's HTTP server answers such a request400before any check runs.Model: opus-5-5