Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
230db01127 |
No files matched your search
@@ -125,7 +125,7 @@ produces a final answer ends the checks. Step 15 runs for each request
|
|||||||
limits apply to the whole exchange. Each step names the section that holds its
|
limits apply to the whole exchange. Each step names the section that holds its
|
||||||
rules. Each check is a heuristic. Its name, in parentheses below, is the value
|
rules. Each check is a heuristic. Its name, in parentheses below, is the value
|
||||||
of the request log's `heuristic` field. `refusal_limit` and `byte_limits` give
|
of the request log's `heuristic` field. `refusal_limit` and `byte_limits` give
|
||||||
theirs in `report_heuristic` instead.
|
theirs in `after_answer_heuristic` instead.
|
||||||
|
|
||||||
1. `GET /_smallwebwaf/healthz` is answered at once by `rproxy`, before any
|
1. `GET /_smallwebwaf/healthz` is answered at once by `rproxy`, before any
|
||||||
check, so a health checker never needs an exemption and is never refused.
|
check, so a health checker never needs an exemption and is never refused.
|
||||||
|
|||||||
+5
-4
@@ -127,10 +127,11 @@ Every one of these lines has:
|
|||||||
- `core_rule_set`: `score`, and `threshold`, `SWWAF_WAF_ANOMALY_THRESHOLD`;
|
- `core_rule_set`: `score`, and `threshold`, `SWWAF_WAF_ANOMALY_THRESHOLD`;
|
||||||
- `refusal_limit`: `window`, `count` and `limit`, of the error burst;
|
- `refusal_limit`: `window`, `count` and `limit`, of the error burst;
|
||||||
- `byte_limits`: `window`, `count` and `limit`, the count in bytes.
|
- `byte_limits`: `window`, `count` and `limit`, the count in bytes.
|
||||||
- `report_heuristic`: the heuristic that banned the client after `servewaf`
|
- `after_answer_heuristic`: the heuristic that banned the client after
|
||||||
answered the request, in `observe` mode too: `refusal_limit` or `byte_limits`.
|
`servewaf` answered the request, in `observe` mode too: `refusal_limit` or
|
||||||
- `report_heuristic_figures`: an object of the figures that heuristic acted on,
|
`byte_limits`.
|
||||||
as listed above.
|
- `after_answer_heuristic_figures`: an object of the figures that heuristic
|
||||||
|
acted on, as listed above.
|
||||||
|
|
||||||
`byte_limits` bans when the report comes, so only the `report` line names
|
`byte_limits` bans when the report comes, so only the `report` line names
|
||||||
it.
|
it.
|
||||||
|
|||||||
@@ -136,10 +136,11 @@ type Line struct {
|
|||||||
// name.
|
// name.
|
||||||
Heuristic string `json:"heuristic,omitempty"`
|
Heuristic string `json:"heuristic,omitempty"`
|
||||||
HeuristicFigures map[string]any `json:"heuristic_figures,omitempty"`
|
HeuristicFigures map[string]any `json:"heuristic_figures,omitempty"`
|
||||||
// ReportHeuristic and ReportHeuristicFigures are the same for the
|
// AfterAnswerHeuristic and AfterAnswerHeuristicFigures are the same for
|
||||||
// heuristic that banned the client after servewaf answered the request.
|
// the heuristic that banned the client after servewaf answered the
|
||||||
ReportHeuristic string `json:"report_heuristic,omitempty"`
|
// request.
|
||||||
ReportHeuristicFigures map[string]any `json:"report_heuristic_figures,omitempty"`
|
AfterAnswerHeuristic string `json:"after_answer_heuristic,omitempty"`
|
||||||
|
AfterAnswerHeuristicFigures map[string]any `json:"after_answer_heuristic_figures,omitempty"` //nolint:lll // a struct tag cannot be split
|
||||||
// LimitPercent and LimitPercentSetting are, for a request the rate
|
// LimitPercent and LimitPercentSetting are, for a request the rate
|
||||||
// limits counted whose client a biased threshold gives a percentage of
|
// limits counted whose client a biased threshold gives a percentage of
|
||||||
// the rate limits below 100, that percentage and the setting that gave
|
// the rate limits below 100, that percentage and the setting that gave
|
||||||
|
|||||||
@@ -104,12 +104,12 @@ func TestByteLimitBanNamesItsHeuristicWindowCountAndLimit(t *testing.T) {
|
|||||||
s, _ := startWithAnswers(t, map[string]string{bytesLimitPerMinute: byteLimit})
|
s, _ := startWithAnswers(t, map[string]string{bytesLimitPerMinute: byteLimit})
|
||||||
|
|
||||||
line, _ := s.download()
|
line, _ := s.download()
|
||||||
wantReportHeuristic(t, line, "", "null")
|
wantAfterAnswerHeuristic(t, line, "", "null")
|
||||||
|
|
||||||
// Two answers of 70 bytes are over the limit of 99.
|
// Two answers of 70 bytes are over the limit of 99.
|
||||||
line, _ = s.download()
|
line, _ = s.download()
|
||||||
wantHeuristic(t, line, "", "null")
|
wantHeuristic(t, line, "", "null")
|
||||||
wantReportHeuristic(t, line, "byte_limits",
|
wantAfterAnswerHeuristic(t, line, "byte_limits",
|
||||||
`{"window": "minute", "count": 140, "limit": 99}`)
|
`{"window": "minute", "count": 140, "limit": 99}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ func (headerLimits) name() string { return "header_limits" }
|
|||||||
|
|
||||||
func (headerLimits) check(_ context.Context, rq *request, _ time.Time) *verdict {
|
func (headerLimits) check(_ context.Context, rq *request, _ time.Time) *verdict {
|
||||||
fields, cookies := rq.headerCounts()
|
fields, cookies := rq.headerCounts()
|
||||||
if fields <= maxHeaderFields && cookies <= maxCookies {
|
if !overHeaderLimits(fields, cookies) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,12 +38,10 @@ func (headerLimits) check(_ context.Context, rq *request, _ time.Time) *verdict
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// overHeaderLimits reports whether the request is over the header limits: it
|
// overHeaderLimits reports whether a request with these counts of header fields
|
||||||
// has more than maxHeaderFields fields or more than maxCookies cookies, as
|
// and cookies, as headerCounts counts them, is over the header limits: it has
|
||||||
// headerCounts counts them.
|
// more than maxHeaderFields fields or more than maxCookies cookies.
|
||||||
func (rq *request) overHeaderLimits() bool {
|
func overHeaderLimits(fields, cookies int) bool {
|
||||||
fields, cookies := rq.headerCounts()
|
|
||||||
|
|
||||||
return fields > maxHeaderFields || cookies > maxCookies
|
return fields > maxHeaderFields || cookies > maxCookies
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -104,15 +104,15 @@ func (rq *request) runHeuristics(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// runBanningHeuristics runs list, afterAnswer or onReport, on the request at
|
// runBanningHeuristics runs list, afterAnswer or onReport, on the request at
|
||||||
// the time it is called. The log line's report_heuristic and
|
// the time it is called. The log line's after_answer_heuristic and
|
||||||
// report_heuristic_figures name the heuristic that gave a verdict, and give its
|
// after_answer_heuristic_figures name the heuristic that gave a verdict, and
|
||||||
// figures, so that heuristic and heuristic_figures still name the one that
|
// give its figures, so that heuristic and heuristic_figures still name the one
|
||||||
// refused the request. ctx is the request's own context.
|
// that refused the request. ctx is the request's own context.
|
||||||
func (rq *request) runBanningHeuristics(ctx context.Context, list []heuristic) {
|
func (rq *request) runBanningHeuristics(ctx context.Context, list []heuristic) {
|
||||||
name, v := rq.firstVerdict(ctx, list, rq.h.now())
|
name, v := rq.firstVerdict(ctx, list, rq.h.now())
|
||||||
if v != nil {
|
if v != nil {
|
||||||
rq.line.ReportHeuristic = name
|
rq.line.AfterAnswerHeuristic = name
|
||||||
rq.line.ReportHeuristicFigures = v.figures
|
rq.line.AfterAnswerHeuristicFigures = v.figures
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -115,13 +115,13 @@ func TestRefusalLimitBanNamesItsHeuristicWindowCountAndLimit(t *testing.T) {
|
|||||||
|
|
||||||
line := blockRule.send(s, client)
|
line := blockRule.send(s, client)
|
||||||
wantHeuristic(t, line, "rule_files", ruleFigures)
|
wantHeuristic(t, line, "rule_files", ruleFigures)
|
||||||
wantReportHeuristic(t, line, "", "null")
|
wantAfterAnswerHeuristic(t, line, "", "null")
|
||||||
|
|
||||||
// The second refusal is the request refusal_limit bans for, and the line
|
// The second refusal is the request refusal_limit bans for, and the line
|
||||||
// names both.
|
// names both.
|
||||||
line = blockRule.send(s, client)
|
line = blockRule.send(s, client)
|
||||||
wantHeuristic(t, line, "rule_files", ruleFigures)
|
wantHeuristic(t, line, "rule_files", ruleFigures)
|
||||||
wantReportHeuristic(t, line, "refusal_limit",
|
wantAfterAnswerHeuristic(t, line, "refusal_limit",
|
||||||
`{"window": "minute", "count": 2, "limit": 1}`)
|
`{"window": "minute", "count": 2, "limit": 1}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -286,16 +286,17 @@ func wantHeuristic(t *testing.T, line logLine, heuristic, figures string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// wantReportHeuristic checks report_heuristic and report_heuristic_figures as
|
// wantAfterAnswerHeuristic checks after_answer_heuristic and
|
||||||
// wantHeuristic checks heuristic and heuristic_figures.
|
// after_answer_heuristic_figures as wantHeuristic checks heuristic and
|
||||||
func wantReportHeuristic(t *testing.T, line logLine, heuristic, figures string) {
|
// heuristic_figures.
|
||||||
|
func wantAfterAnswerHeuristic(t *testing.T, line logLine, heuristic, figures string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
if line.ReportHeuristic != heuristic ||
|
if line.AfterAnswerHeuristic != heuristic ||
|
||||||
!reflect.DeepEqual(line.ReportHeuristicFigures, decodeFigures(t, figures)) {
|
!reflect.DeepEqual(line.AfterAnswerHeuristicFigures, decodeFigures(t, figures)) {
|
||||||
t.Errorf("log line has report_heuristic %q with report_heuristic_figures %v, "+
|
t.Errorf("log line has after_answer_heuristic %q with "+
|
||||||
"want %q with %s",
|
"after_answer_heuristic_figures %v, want %q with %s",
|
||||||
line.ReportHeuristic, line.ReportHeuristicFigures, heuristic, figures)
|
line.AfterAnswerHeuristic, line.AfterAnswerHeuristicFigures, heuristic, figures)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ func (rq *request) refusedWhateverTheAnswer(now time.Time) bool {
|
|||||||
|
|
||||||
_, decided := rq.crowdSecDecisionInForce(now)
|
_, decided := rq.crowdSecDecisionInForce(now)
|
||||||
|
|
||||||
return decided || rq.overHeaderLimits() ||
|
return decided || overHeaderLimits(rq.headerCounts()) ||
|
||||||
rq.refusedByBlocklists(rq.h.lists.ListedBy(rq.client)) ||
|
rq.refusedByBlocklists(rq.h.lists.ListedBy(rq.client)) ||
|
||||||
rq.overAWholeRateLimit(now)
|
rq.overAWholeRateLimit(now)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user