Core Rule Set: Ghost's ordinary requests pass by default (closes #357)
check / check (push) Waiting to run

Ghost 6.76.0's ordinary traffic, recorded in `testdata/ghost-requests.txt`,
passes at the default settings and while bodies are read. It covers public
pages, members' sign-up and sign-in, the admin, the editor saving and
publishing posts and pages with code blocks and HTML cards, tags, settings,
staff invitations, uploads, newsletter settings, and both APIs. The new
`ghost.conf`, the sixteenth change, lets through `filter`, the member
`search` and the embed `url` in the query, and plain text, addresses,
articles, code injection and passwords in JSON bodies. A `<script>` element
and a `javascript:` link stay refused in articles and code injection.
`README.md` lists Ghost; `SPEC.md` lists the change.

Model: opus-5-5
This commit is contained in:
sneak committed 2026-11-11 14:18:36 +00:00
1 parent 883969a9cd
commit 6fbf695ad1
8 files changed
+1470 -14

No files matched your search

+80 -5
View File
@@ -84,7 +84,7 @@ never as a time.
of bans is full.
- `internal/rules`: reads the rule files at start and again when they
change, and tells which rules a request matches.
- `internal/coreruleset`: the Core Rule Set, with the fifteen changes listed
- `internal/coreruleset`: the Core Rule Set, with the sixteen changes listed
under "Attack detection", as Coraza directives.
- `internal/lookup`: looks clients up through GeoJS or in the lookup
database.
@@ -681,7 +681,7 @@ files, the limits and the bans still apply to the client that sends it.
- `SWW_CORE_RULE_SET_EXCLUSIONS_FILE` names a file of exclusions, written as
Coraza directives. An exclusion switches one rule off for some fields or paths
only, where `SWW_WAF_DISABLED_RULES` switches it off everywhere. The file
holds an app's own exclusions. The eighth to fifteenth changes below hold
holds an app's own exclusions. The eighth to sixteenth changes below hold
those of the apps they name.
- `waf` loads the file after `smallwebwaf`'s own setup rules and before the
Core Rule Set's rules. The Core Rule Set loads its own
@@ -728,7 +728,7 @@ files, the limits and the bans still apply to the client that sends it.
scanner from an ordinary client. In front of a code forge, registry and git
clients get `404` and `401` by design, hundreds of times a minute.
`smallwebwaf` changes the Core Rule Set 4.25.0 in fifteen ways. Without them, it
`smallwebwaf` changes the Core Rule Set 4.25.0 in sixteen ways. Without them, it
would refuse ordinary requests to these apps:
- gitea;
@@ -738,7 +738,8 @@ would refuse ordinary requests to these apps:
- MediaWiki;
- Mastodon;
- Vaultwarden;
- Nextcloud.
- Nextcloud;
- Ghost.
The changes hold in front of every app, and no setting undoes them:
@@ -774,7 +775,7 @@ The changes hold in front of every app, and no setting undoes them:
catch reaches the app, such as the command `|cat /etc/passwd`. Path
traversal, SQL and script injection, and PHP, Java and Node.js code are
still refused there. Other parameters keep all three rules. The eighth to
fifteenth changes switch some of them off for some fields on the paths of
sixteenth changes switch some of them off for some fields on the paths of
the apps they name. An app that uses one of these parameters as a file on
the server, or passes it to a shell, must check the value itself. A rule
file can refuse it instead.
@@ -1599,6 +1600,80 @@ The changes hold in front of every app, and no setting undoes them:
at the site root. An app other than Forgejo that serves such a path must
check `q` itself.
16. On Ghost's paths, some fields skip the rules that refused ordinary Ghost
requests there. `internal/coreruleset/ghost.conf` lists each rule and the
request it refused. Ghost's admin and integrations call the Admin API under
`/ghost/api/admin/`. Themes and Ghost's scripts call the Content API under
`/ghost/api/content/`, and members call `/members/api/`.
Three query fields skip some rules:
- `filter`, a query in Ghost's own filter language, skips 932120 on the
Admin API and the Content API, so `tag:powershell` passes;
- `search`, a search for a member, skips 930120, 932120, 932250 and 932260
on the Admin API;
- `url`, the address the editor looks up for an embed or a bookmark, skips
930120 and 932120.
The fields of JSON bodies where a person types plain text skip 930120,
932120, 932125, 932230, 932250, 932260 and 932380. They are these fields of
posts, pages, tags, labels, members, newsletters and snippets, on the Admin
API:
- `title`, `name` and `slug`;
- a post's `tags` and a member's `labels`, by name or slug;
- `custom_excerpt`, `description`, `note` and `sender_name`;
- the `meta_`, `og_` and `twitter_` titles and descriptions.
A member's `name` at `/members/api/` skips them too. A member's `email`
skips 930120, 932120 and 932260, at `/members/api/` and when the admin adds
or edits a member.
The articles carry HTML on purpose. These fields on the Admin API hold them:
- `lexical`, which the editor saves, and `mobiledoc` and `html`, which
integrations send, for posts and pages;
- `lexical` and `mobiledoc` of snippets;
- `footer_content` of newsletters.
The articles skip these rules:
- 930120, 932120, 932125, 932160, 932230, 932235, 932250, 932260, 932370 and
932380, which refused commands in a code block;
- 934100, 941180 and 941390, which refused JavaScript in a code block;
- 942190, which refused an SQL query in a code block;
- 941100, 941120 and 941160, which refused an HTML card.
Code injection is the HTML that Ghost adds to the head and the foot of a
page. These fields on the Admin API hold it:
- `codeinjection_head` and `codeinjection_foot` of posts and pages;
- `value` of settings, since the site's settings hold its code injection.
Code injection skips these rules:
- 930120, 932120, 932125, 932230, 932250, 932260 and 932380, as plain text
does;
- 921130, 941100, 941160 and 941270, which refused `<meta>` and `<link>`
elements.
`<script>alert(1)</script>` and `javascript:alert(1)` are still refused in
the articles and code injection (941110, 941170 and 941210). Path traversal
and SQL injection are still refused in every field above. Script injection
is too, outside the articles. `; wget http://example.com/x.sh` is still
refused in the query fields, the plain text and the addresses.
Coraza names a staff user's `profile_image` field
`json.users.0.profile_image`. That name holds `.profile`, a file on the list
of system files, so the name skips 930120 when a profile is saved.
The passwords skip each rule that random passwords set off. The numbers are
in `internal/coreruleset/ghost.conf`. They are `password` when signing in,
and the passwords sent when creating the first staff user, accepting an
invitation, and resetting or changing a password. `' OR '1'='1`,
`../../etc/passwd`, `; cat /etc/passwd` and `<img src=x onerror=x()>` sent
as a password reach Ghost; `<script>alert(1)</script>` and
`javascript:alert(1)` are still refused.
These body exclusions matter only while `SWW_WAF_BODY_LIMIT` is set. None
applies when the query has a parameter of the same name. Elsewhere these
fields keep every rule. The paths are those of Ghost at the site root. An
app other than Ghost that serves one of them must check these fields itself.
The parameter and cookie names in these changes are matched without regard to
case, as Coraza matches them.