HarperFast / HarperFast/harper-pro
WAF path and query matching decode to different depths, so an escaped payload normalizes on one and not the other
- Dominant language
- JavaScript
- Stars
- 3
- Forks
- 0
- Avg merge
- 1d 21h
- Merged PRs (30d)
- 80
Description
Path matching and query matching canonicalize to different decode depths, so the same escaped payload is normalized on one surface and not the other.
`canonicalizePath` in `waf/matcher.ts` decodes iteratively, up to two passes:
```
for (let pass = 0; pass < 2; pass++) { ... next = decodeURIComponent(decoded); ... }
```
The query-string parser in the same file decodes each name and value exactly once.
So a value arriving as `%2527` becomes `%27` for query matching and `'` for path matching. A rule written against the decoded form matches in a path and misses in a query. An application that itself decodes twice — or a framework that does — sees `'`, which is the form the rule was written for.
This is only a mismatch, not a claim that a single decode is wrong: it is the *asymmetry* that is the defect, because a rule author has no way to know which surface they are on. `canonicalizePath`'s own doc comment states the intent for the path side — "so encoding- and traversal-based evasions ... can't slip past a path rule" — and the query side does not meet it.
**Suggested fix:** share one canonicalization routine between the two, with the same bounded iteration count, and document the decode depth as part of the rule-authoring contract so a rule author knows what form to write against. Whichever depth is chosen, both surfaces should use it.
Found by cross-model review while reviewing an unrelated change; not customer-reported. The WAF is unreleased — `main` and `v5.3.0-alpha.1` only.
Contributor guide
Research direction
Start in waf/matcher.ts by comparing canonicalizePath with the query-string parser and tracing their current decode behavior. Decide the shared bounded decode depth and document that depth for rule authors; done means identical normalization for equivalent path and query payloads, with coverage added for the differing-depth case.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100