Default rule file: a probe sent percent-encoded gets past its path rules #85

Closed
opened 2026-10-06 18:16:27 +02:00 by clawbot · 2 comments
Collaborator

SPEC.md "Rule files" defines the path target as the URL path as received, before any decoding, and every probe rule in the default file (and in the 50-gitea.rules example) uses path. So a client sending /%2eenv is not matched by env-file, while an app that decodes the path, as Go's server does, serves /.env. Found in review of #83, which builds the spec as written.

Options:

  1. path is matched both as received and once percent-decoded, as uri already is. No new target; a rule written for the sent form still matches it.
  2. The default file's probe rules use uri instead of path. uri holds the query too, so each regex ending in $ must also allow a query after the path.
  3. Leave it; the Core Rule Set, a later stage, catches some encoded probes.

Recommendation: 1. Whichever you pick lands as its own change after #83; nothing waits on it.

Model: opus-5-5

`SPEC.md` "Rule files" defines the `path` target as the URL path as received, before any decoding, and every probe rule in the default file (and in the `50-gitea.rules` example) uses `path`. So a client sending `/%2eenv` is not matched by `env-file`, while an app that decodes the path, as Go's server does, serves `/.env`. Found in review of https://git.eeqj.de/sneak/smallwebwaf/pulls/83, which builds the spec as written. Options: 1. `path` is matched both as received and once percent-decoded, as `uri` already is. No new target; a rule written for the sent form still matches it. 2. The default file's probe rules use `uri` instead of `path`. `uri` holds the query too, so each regex ending in `$` must also allow a query after the path. 3. Leave it; the Core Rule Set, a later stage, catches some encoded probes. Recommendation: 1. Whichever you pick lands as its own change after https://git.eeqj.de/sneak/smallwebwaf/pulls/83; nothing waits on it. Model: opus-5-5
sneak was assigned by clawbot 2026-10-06 18:16:27 +02:00
Author
Collaborator

Settled by the standing rulings, so it no longer waits on sneak: building option 1. It is small and easy to undo, neither other option matters more, and SPEC.md already says why uri is matched once decoded: "so an encoded probe cannot slip past". The path target should do the same.

What to do (one PR to next)

  • A path rule matches if its regex matches the path as sent (rules.Path) or that path decoded once (decodeOnce, as uri does). Decode after cutting off the query, so a %3F cannot move the split. Decode once only: /%252eenv is not /.env.
  • SWWAF_TRAP_PATHS is matched against "the path a path rule sees", so it follows: a trap path matches the path as sent or decoded once. /%77p-login.php now matches /wp-login.php. The ban's notes keep the path as sent. This is a judgement call, disclosed here: the README's example says the opposite and is changed with it.
  • SPEC.md "Rule files" (path target) and the README's rule file and trap path text say the new behaviour. Change only the sentences this affects.
  • Tests: /%2eenv is caught by the default env-file rule; /%77p-login.php is caught as a trap path; /%252eenv and a broken escape such as /%zz.env behave as the brief says. Each new test must fail with the change taken out.

Definition of done: make check passes, and a reviewer who did not write the PR passes it.

Model: opus-5-5

Settled by the standing rulings, so it no longer waits on sneak: building option 1. It is small and easy to undo, neither other option matters more, and `SPEC.md` already says why `uri` is matched once decoded: "so an encoded probe cannot slip past". The `path` target should do the same. ## What to do (one PR to `next`) - A `path` rule matches if its regex matches the path as sent (`rules.Path`) or that path decoded once (`decodeOnce`, as `uri` does). Decode after cutting off the query, so a `%3F` cannot move the split. Decode once only: `/%252eenv` is not `/.env`. - `SWWAF_TRAP_PATHS` is matched against "the path a `path` rule sees", so it follows: a trap path matches the path as sent or decoded once. `/%77p-login.php` now matches `/wp-login.php`. The ban's notes keep the path as sent. This is a judgement call, disclosed here: the README's example says the opposite and is changed with it. - `SPEC.md` "Rule files" (`path` target) and the README's rule file and trap path text say the new behaviour. Change only the sentences this affects. - Tests: `/%2eenv` is caught by the default `env-file` rule; `/%77p-login.php` is caught as a trap path; `/%252eenv` and a broken escape such as `/%zz.env` behave as the brief says. Each new test must fail with the change taken out. Definition of done: `make check` passes, and a reviewer who did not write the PR passes it. Model: opus-5-5
sneak was unassigned by clawbot 2026-10-14 17:26:24 +02:00
clawbot self-assigned this 2026-10-14 17:26:24 +02:00
Author
Collaborator

Built in #125, as the brief above says. A path rule and a path in SWWAF_TRAP_PATHS now match the path as sent or decoded once, and the ban's notes keep the path as sent. SPEC.md and the README say so.

One thing the brief could not get: a malformed escape such as /%zz.env never reaches a path rule, because Go's HTTP server answers it 400 itself. There is no test for it.

Model: opus-5-5

Built in https://git.eeqj.de/sneak/smallwebwaf/pulls/125, as the brief above says. A `path` rule and a path in `SWWAF_TRAP_PATHS` now match the path as sent or decoded once, and the ban's notes keep the path as sent. `SPEC.md` and the README say so. One thing the brief could not get: a malformed escape such as `/%zz.env` never reaches a path rule, because Go's HTTP server answers it `400` itself. There is no test for it. Model: opus-5-5
Sign in to join this conversation.