check / check (push) Successful in 10m27s
`smallwebwaf servewaf` runs today's checks on the visitor's request each question carries, answers rproxy, and passes nothing to an app. rproxy reports every request servewaf answered. A report counts an allowed request's bytes and writes servewaf's second log line. The table holds a copy of what a report needs, never the request, so a report cannot reach a request being handled. Judgement call: a question's body is read before any check; over 64 KiB gets 400. Judgement call: a report's bytes and durations have ceilings, so they cannot overflow. Judgement call: a `close` disposition for SWWAF_BAN_RESPONSE=close. Model: opus-5-5
5.1 KiB
5.1 KiB
Alerts
The alert events, the JSON object the webhook gets, and the messages Slack and
ntfy get. "Alerting" in SPEC.md says when an alert is sent, held
back or dropped.
Events
ban: a bansmallwebwafmade.permanent_ban: a permanent ban it made, or an existing ban made permanent by a request.waf_block: the Core Rule Set scored a request at or over the threshold. The request was refused unless the alert'sdetailgivesmode:detectorobserve.anomaly: a count over an anomaly threshold.reputation_hit: a request from a client on a blocklist, the CrowdSec decision list or a DNS blocklist, or with an AbuseIPDB score that is a hit. One alert per source.source_failure: one of:- GeoJS failing;
- a list that cannot be fetched;
- a DNS blocklist or AbuseIPDB failing or refusing a query;
- the day's AbuseIPDB checks used up.
file_error: one of:- a rule file or state file edit with an error;
- a state file that cannot be written;
- a replacement lookup database that cannot be read.
summary: the hourly summary of alerts held back.
The JSON object
This is the alert for a ban for a broken rate limit, shown indented; it is sent on one line:
{
"instance": "fsn1app1/gitea",
"time": "2026-10-06T12:00:50.123461Z",
"event": "ban",
"client": "203.0.113.9",
"netblock": "203.0.113.9/32",
"asn": "",
"as_name": "",
"country": "",
"reason": "requests per minute over the limit of 500",
"detail": {
"ban_expires": "2026-10-06T13:00:50.123Z",
"cause": "limit",
"notes": {
"asn": "",
"as_name": "",
"country": "",
"kind": "requests",
"limit": 500,
"window": "minute",
"count": 501,
"request": {
"time": "2026-10-06T12:00:50.123456789Z",
"method": "GET",
"host": "app.example",
"path": "/owner/repo/commits/branch/main?page=501",
"status": 403,
"user_agent": "scraper/1.0"
},
"requests": 501,
"refused": 0,
"earlier_bans": {
"limit": 0,
"attack": 0,
"admin": 0,
"crowdsec": 0
}
}
},
"suppressed_repeats": 0
}
instance:SWWAF_INSTANCE_NAME.time: when the alert was raised.event: one of the events above.host: the site of the request that raised the alert. Onlyservewafsends this field, since it serves many sites, and not forsource_failure,file_errorandsummary.client: the address whose request raised the alert. Empty forsource_failure,file_errorandsummary.netblock: empty forsource_failure,file_errorandsummary; otherwise one of:- the ban's netblock;
- the netblock an anomaly counted, empty in the
asnandtotalscopes; - for the other events, the client as rate limiting counts it: an IPv4 address as a /32, or an IPv6 group.
asn,as_nameandcountry: the client's, as known when the alert was raised. They are empty when GeoJS had not answered yet.reason: a short sentence, such asrequests per minute over the limit of 500orlisted by a blocklist.detail: what is particular to the event:- for a ban, its
cause,ban_expiresandnotes(the notes are listed underbans.jsoninstate-files.md); - for an anomaly, the
scope,window,kind,countandthreshold; - for a reputation hit, the
sourceand, for AbuseIPDB, thescore; - for a Core Rule Set match, the
rule_ids,score,methodandpath; - for a source failure, the
sourceanderror; - for a file error, the
fileanderror; - for a summary, the
hourit covers, thecountof alerts held back, and that count by event inevents; mode, when the request was not refused:detectorobserve.
- for a ban, its
suppressed_repeats: how many repeats the cooldown held back before this alert.
Slack and ntfy
Slack and ntfy get a message with a title and a text. The title is the instance
and the event, such as fsn1app1/gitea: ban. The text is the reason, then one
line for each of these that the alert has:
- the host, the client, the netblock and the country;
- the file, the source, the error and the mode from
detail; - the suppressed repeats, if any.
Slack gets the title in bold and the text below it, with &, < and >
escaped. ntfy gets the text as the message, with these headers:
Title: the title;Authorization: the token, asBearer <token>, whileSWWAF_ALERT_NTFY_TOKENis set;PriorityandTags: by event, as listed below.
The priority and tag by event:
ban: prioritydefault, tagno_entry.permanent_ban: priorityhigh, tagno_entry.waf_block: prioritydefault, tagshield.anomaly: priorityhigh, tagchart_with_upwards_trend.reputation_hit: prioritylow, taglabel.source_failureandfile_error: priorityhigh, tagwarning.summary: prioritydefault, tagbar_chart.