SPEC: the one place for behaviour and reasons; docs/ as lookup lists only
check / check (push) Waiting to run

docs/core-rule-set.md, docs/lookup.md, docs/reputation.md and docs/bans.md
restated the spec and are folded back into it and removed. The remaining
docs files hold only lists: settings with their defaults and value forms,
the request log's fields, the metrics, the endpoints, the state files and
their contents, the rule file format and default file, the alert events
and messages, the image's contents and pins, and the gitea notes. Every
behaviour each of them held moves to the spec, said once. The settings
groups have the same names in both files. Stale comments in
internal/state and script/example-app point at the right places.

Model: fable-5-1
This commit is contained in:
2026-10-22 21:51:39 +00:00
parent d712bb1496
commit 4ea0a975c8
17 changed files with 918 additions and 1273 deletions
+20 -19
View File
@@ -13,16 +13,19 @@ checked again when gitea or the Core Rule Set changes version.
by it.
- A clone is a response. It fits the defaults of 30 minutes and 5 GiB for all
but the largest repositories and slowest links.
- A push is a request, and so is every other upload: a git push, an LFS object,
a container image layer, a package file or a release attachment. At the
defaults, an upload larger than 100 MiB or taking more than 60 seconds is cut
off. The client is answered `413` for a body that is too large, before
anything reaches gitea when the request announces its size. It is answered
`408` for a body it sends too slowly, or `504` if gitea is too slow to take
it. The upload fails, and no one is banned for it. A gitea that takes large
uploads needs `SWWAF_REQUEST_MAX_BYTES`, `SWWAF_CLIENT_REQUEST_TIMEOUT` and
`SWWAF_UPSTREAM_REQUEST_TIMEOUT` raised to fit. The Core Rule Set does not
read an upload's body, which streams through without being held in memory.
- A push is a request, and so is every other upload:
- a git push;
- an LFS object;
- a container image layer;
- a package file;
- a release attachment.
At the defaults, an upload larger than 100 MiB or taking more than 60
seconds is cut off, and no one is banned for it. "Size and time limits" in
`SPEC.md` gives the answers the client gets. A gitea that takes large
uploads needs `SWWAF_REQUEST_MAX_BYTES`, `SWWAF_CLIENT_REQUEST_TIMEOUT` and
`SWWAF_UPSTREAM_REQUEST_TIMEOUT` raised to fit.
- Archive download and blame or history pages are what scrapers hammer. The
request limits do most of the work there.
- An Actions runner that reaches gitea through `smallwebwaf` sends requests all
@@ -32,8 +35,7 @@ checked again when gitea or the Core Rule Set changes version.
jobs. It is then banned, the job it is running fails, and each repeat within a
day makes the ban longer. Newer runners ask less often, but a busy one, or
several on one address, can still pass it. Put the runners' addresses in
`SWWAF_RATE_LIMIT_EXEMPT_NETS`. That takes them out of the request and byte
limits, while the error burst, attack detection and bans still apply.
`SWWAF_RATE_LIMIT_EXEMPT_NETS`.
## What the Core Rule Set lets through
@@ -55,10 +57,9 @@ the refusals in the next section:
## What the Core Rule Set still refuses
Each refusal answers only that request, with `403`, and bans no one by itself.
It counts toward the error burst, which a person does not reach this way. The
request log names the rule in `waf_rule_ids`, which `SWWAF_WAF_DISABLED_RULES`
can switch off.
Each refusal answers only that request ("Bans" in `SPEC.md` says what it counts
toward). The request log names the rule in `waf_rule_ids`, which
`SWWAF_WAF_DISABLED_RULES` can switch off.
### A query string that reads as an attack
@@ -151,9 +152,9 @@ carries it.
A push can reuse a layer of an image docker has pushed to that gitea before.
docker then asks gitea to take the layer from that image, naming it in the query
parameter `from`, which 932260 checks. The request is refused when the image's
owner, or its name after the owner, starts with an entry on the list of
commands. Examples are `owner/python3-app`, `owner/docker-base`, and any image
of an owner named `ansible` or `docker-team`.
owner starts with an entry on the list of commands. So is one whose name after
the owner does. Examples are `owner/python3-app`, `owner/docker-base`, and any
image of an owner named `ansible` or `docker-team`.
What happens next depends on docker's image store. A docker that keeps its
images in containerd's image store fails the push, and fails each new try the