SPEC: parameter change for every app, gitea name refusals, v3 uploads (closes #6)

Fifth review of the spec update:

- The change that leaves gitea's path and branch parameters out of
  930120, 932160 and 932260 says it holds in front of every app, that
  commands and file URLs pass there too, and that no setting restores
  the rules; Risks names an app that uses one of them as a server file
  or in a shell.
- The gitea notes no longer claim that any branch or file name passes:
  they name `refSubUrl`, `name`, `tag`, `template` and `rule_name`,
  which keep the rules, and what each refusal looks like.
- `actions/upload-artifact@v3` is refused once or twice per upload and
  the step fails; only more than 30 refusals a minute ban the runner.

Model: opus-5-5
This commit is contained in:
2026-09-28 21:04:34 +00:00
parent 44a95a6396
commit 8b3aba84b2
+74 -36
View File
@@ -538,9 +538,9 @@ The settings, by group:
- `WAF_PARANOIA_LEVEL` (default `1`), `WAF_ANOMALY_THRESHOLD` (default `5`):
the Core Rule Set's own two tuning values, at the Core Rule Set's own
defaults.
- The sidecar also changes the Core Rule Set 4.25.0 in five ways that no
setting undoes, since in front of gitea each would otherwise refuse
ordinary requests:
- The sidecar also changes the Core Rule Set 4.25.0 in five ways, since in
front of gitea it would otherwise refuse ordinary requests. The changes
hold in front of every app, and no setting undoes them:
- PUT, PATCH and DELETE are allowed methods besides GET, HEAD, POST and
OPTIONS; APIs, container image pushes and package uploads use them.
Other methods stay refused.
@@ -566,11 +566,18 @@ The settings, by group:
such as `.gitignore`, `package.json`, `docker-compose.yml`,
`bin/docker-entrypoint` or a branch named `docker-build`, and gitea
reads these values as names within a repository or its own records, or
as a page of its own site. What only these three rules refuse, such as
`/etc/passwd` or `whoami` on its own, is therefore let through in
those parameters; path traversal (`../`), SQL and script injection and
PHP, Java and Node.js code are still refused there, and every other
parameter and cookie keeps all three rules.
as a page of its own site. Like the other changes, this one holds in
front of every app, not only gitea, and no setting restores the three
rules in those parameters. What only these three rules refuse is let
through there, and that is more than a name such as `/etc/passwd` or
`whoami` on its own: commands such as `|cat /etc/passwd`,
`wget http://…` and `nc -e /bin/sh …`, and `file:///etc/passwd`, pass
as well. Path traversal (`../`), SQL and script injection and PHP,
Java and Node.js code are still refused there, and every other
parameter and cookie keeps all three rules. An app that uses one of
these parameters as a file on the server, or passes it to a shell,
gets no help from the three rules there (see "Risks the design has to
handle").
- Responses are not inspected. A raw file from a repository, such as a
shell script, looks to the response rules like source code leaking
from the server.
@@ -1136,36 +1143,59 @@ networks:
`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.
- At the defaults (see "Configuration surface", attack detection), the Core
Rule Set lets gitea's ordinary use through, whatever its files and
branches are called: browsing and views of files in a repository, with
their history, blame and the file tree; diffs, including their hidden
lines and large files, and pull request review; git's clone, fetch and
push over HTTP; signing in, including the return to the page a visitor
came from and sign-in with Git Credential Manager, git-credential-oauth or
tea; the API's calls for a file and its commits; pushing and pulling
container images and packages; Actions runners, and artifacts uploaded
with `actions/upload-artifact@v4`; and posting issues, pull requests,
comments, wiki pages and files saved in the web editor, code included,
since no body is read. It can still refuse a query string that reads to it
as an attack, most often a search: one for a name on its lists of system
files and commands, such as `package.json`, `.gitignore` or
`docker-compose.yml`, or for text that starts with a command name, such as
`python3` or `ssh key`; or one holding a shell command with its options or
a system path (`ls -la`, `sed -i`, `/bin/sh`), a command in backticks,
script code (`fetch(`, `${VAR}`, `process.env`), HTML (`<img src=`), an
SQL statement (`SELECT * FROM users WHERE`), or a URL naming an IP address
or `localhost`. The lists refuse such a name in any other query parameter
Rule Set lets gitea's ordinary use through, apart from the refusals in the
next note: browsing and views of files in a repository, with their
history, blame and the file tree; diffs, including their hidden lines and
large files, and pull request review; git's clone, fetch and push over
HTTP; signing in, including the return to the page a visitor came from and
sign-in with Git Credential Manager, git-credential-oauth or tea; the
API's calls for a file and its commits; pushing and pulling container
images and packages; Actions runners, and artifacts uploaded with
`actions/upload-artifact@v4`; and posting issues, pull requests, comments,
wiki pages and files saved in the web editor, code included, since no body
is read.
- The Core Rule Set can still refuse the requests below. 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 `WAF_DISABLED_RULES`
can switch off.
- A query string that reads to it as an attack, most often a search: one
for a name on its lists of system files and commands, such as
`package.json`, `.gitignore` or `docker-compose.yml`, or for text that
starts with a command name, such as `python3` or `ssh key`; or one
holding a shell command with its options or a system path (`ls -la`,
`sed -i`, `/bin/sh`), a command in backticks, script code (`fetch(`,
`${VAR}`, `process.env`), HTML (`<img src=`), an SQL statement
(`SELECT * FROM users WHERE`), or a URL naming an IP address or
`localhost`. The lists refuse such a name in any other query parameter
too, such as a release attachment uploaded through the API as
`docker-compose.yml`. A path can be refused as well: a file name ending in
`~`, or an `.xhtml` file whose path holds a space. Such a refusal answers
only that request, with 403, and bans no one by itself: it counts toward
the error burst, which a person searching does not reach. The request log
names the rule in `waf_rule_ids`, which `WAF_DISABLED_RULES` can switch
off.
`docker-compose.yml`.
- A branch, tag or file name that starts with `docker-`, `python3`,
`ansible`, `base64`, `whoami` or another entry on the Core Rule Set's
list of commands (932260), where gitea's own pages send it in a query
parameter that 930120, 932160 and 932260 still check: `refSubUrl`, the
branch or tag of a directory listing, sent when the listing asks for
its entries' last commits in a second request, as it does whenever
gitea takes more than a second to work them out; `name`, when a branch
is deleted or restored on the branches page; `tag`, when a release is
started from a tag; `template`, the file of an issue template, when an
issue is opened from it; and `rule_name`, when a branch protection
rule is opened for editing in the repository's settings. For a branch
named `docker-build`, a directory listing that makes that second
request leaves those last commits out and shows an error, and the
branches page shows an error instead of deleting or restoring the
branch. A release started from such a tag, an issue from such a
template or such a rule opened for editing gets the sidecar's 403
answer in place of its form.
- A path: a file name ending in `~`, or an `.xhtml` file whose path
holds a space.
- Artifact uploads from `actions/upload-artifact@v3` send the header
`Content-Range`, which the Core Rule Set refuses (920450). Each file of
the artifact is refused with 403 and the step fails; an artifact of more
than 30 files also bans the runner through the error burst.
`Content-Range`, which the Core Rule Set refuses (920450). The action
sends two files at a time, does not retry a 403 and sends no further file
once one is refused, so an upload is refused once or twice, however many
files it holds, and the step fails. One upload does not ban the runner;
more than 30 such refusals from its address within a minute, as when many
jobs upload at once, ban it through the error burst.
`actions/upload-artifact@v4` does not send the header.
- An Actions runner that reaches gitea through the sidecar sends requests
all day. One older than version 0.4 (April 2026) asks for work every 2
@@ -1248,6 +1278,14 @@ networks:
attack is still subject to the rule files, the limits and the bans, and
`WAF_BODY_LIMIT` switches body inspection on for apps whose forms carry no
code.
- An app that uses `path`, `ref` or another of the query parameters left out of
930120, 932160 and 932260 (see "Configuration surface", attack detection) as a
file on the server, or passes it to a shell: that change holds in front of
every app and no setting restores the three rules, so what only they refuse
reaches the app in those parameters, such as `/etc/passwd`, `|cat /etc/passwd`
or `nc -e /bin/sh …`. Path traversal (`../`) is still refused there. Such an
app has to check those values itself, or refuse what it must never receive in
them with a rule file.
- The admin endpoints can be reached from the internet: all but the health check
need a token and are off while it is unset, and a missing or wrong token
counts toward the error burst, so a client guessing tokens is soon banned.