waf: decide every visitor request within rproxy's timeout (closes #394)
check / check (push) Failing after 2m17s
check / check (push) Failing after 2m17s
The Core Rule Set is the one check whose time a visitor can stretch. At paranoia level 4 it took over 2 s on a request with an 8 KiB request line, three 8 KiB Cookie lines and a 64 KiB form body. SWW_WAF_CORE_RULE_SET_BODY_INSPECTION_SIZE now takes at most 8K. The Core Rule Set also reads no JSON body over 1 KiB, since Coraza turns JSON into up to 8 times its size in names and values. That brings such a request under 1 s. A test per kind of input times the Core Rule Set on the worst request within the limits, at the slowest settings. SPEC.md lists the limits that bound it. Model: opus-5-5
This commit was merged in pull request #395.
This commit is contained in:
1 parent
23788920f7
commit
0a2b2d809f
10 files changed
+488
-85
No files matched your search
@@ -481,12 +481,21 @@ func (c *CoreRuleSet) Inspect(
|
||||
return Result{RuleIDs: ids, Score: score(tx)}, read, err
|
||||
}
|
||||
|
||||
// mostJSONRead is the largest JSON body the Core Rule Set reads, even while
|
||||
// SWW_WAF_CORE_RULE_SET_BODY_INSPECTION_SIZE is larger. Coraza names each
|
||||
// value in a JSON body by the whole path to it, so what it gives the Core
|
||||
// Rule Set to read can outgrow the body: up to 8 times its size, and up to
|
||||
// 4 KiB for a small body. 1 KiB of JSON thus gives at most 8 KiB, no more
|
||||
// than the largest body of another kind.
|
||||
const mostJSONRead = 1 << 10
|
||||
|
||||
// readBody reads body, the body of the request in tx, which has run on
|
||||
// the request's headers, while SWW_WAF_CORE_RULE_SET_BODY_INSPECTION_SIZE is
|
||||
// set and the body is of a kind the Core Rule Set reads: form data and
|
||||
// multipart, of which it reads the first c.bodyInspectionSize bytes, and
|
||||
// JSON and XML, which it reads only when they are no longer than that,
|
||||
// since they cannot be read in part.
|
||||
// since they cannot be read in part. The Core Rule Set reads no JSON body
|
||||
// longer than mostJSONRead either.
|
||||
// readBody reads one byte past c.bodyInspectionSize, to tell which they
|
||||
// are, gives the Core Rule Set what it reads, and returns what it read and
|
||||
// the error that ended the reading early, if one did.
|
||||
@@ -499,6 +508,8 @@ func (c *CoreRuleSet) readBody(
|
||||
}
|
||||
|
||||
inPart := false
|
||||
// largest is the largest JSON or XML body the Core Rule Set reads.
|
||||
largest := c.bodyInspectionSize
|
||||
// How the body is read is a variable of the transaction, which only
|
||||
// Coraza's interface for plugins reads.
|
||||
state := tx.(plugintypes.TransactionState) //nolint:forcetypeassert // every one is
|
||||
@@ -506,14 +517,16 @@ func (c *CoreRuleSet) readBody(
|
||||
switch state.Variables().RequestBodyProcessor().Get() {
|
||||
case "URLENCODED", "MULTIPART":
|
||||
inPart = true
|
||||
case "JSON", "XML":
|
||||
case "JSON":
|
||||
largest = min(largest, mostJSONRead)
|
||||
case "XML":
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
read, err := io.ReadAll(io.LimitReader(body, c.bodyInspectionSize+1))
|
||||
|
||||
if inPart || (err == nil && int64(len(read)) <= c.bodyInspectionSize) {
|
||||
if inPart || (err == nil && int64(len(read)) <= largest) {
|
||||
// Coraza holds what it reads of the body in memory, up to the
|
||||
// limit, so this cannot fail.
|
||||
_, _, _ = tx.WriteRequestBody(read)
|
||||
|
||||
Reference in new issue
Block a user