check / check (push) Failing after 16m23s
Adds mastodon.conf, the twelfth change. On Mastodon's paths, the fields that carry a search, a name, a title, an address or free text skip the four rules that read them as commands or system files. These cover the API, the admin and settings pages, sign-in, and the objects other servers deliver to an inbox. A post's text skips 932125, 932230 and 932235 too. Uploaded file names skip 932180, and an ActivityPub Accept header skips 920600 on account paths. Only Mastodon's own resources under /api/v1/ and /admin/ count, since gitea and Drupal serve those prefixes. The requests recorded from Mastodon 4.7.5 pass at the defaults and while bodies are read. Model: opus-5-5
148 lines
12 KiB
Plaintext
148 lines
12 KiB
Plaintext
# The twelfth change: on Mastodon's paths, some fields skip the rules that
|
|
# refused ordinary Mastodon requests there. They are the fields where Mastodon
|
|
# sends a search, a name, a title, a file name, an address or free text. Each
|
|
# exclusion runs with the request, before the rules it switches off, so this
|
|
# file is loaded before the Core Rule Set's rules. Each comment names a request
|
|
# a rule refused. An exclusion on a field of a body applies only when the query
|
|
# has no parameter of that name, so it matters only while SWW_WAF_BODY_LIMIT is
|
|
# set.
|
|
#
|
|
# Gitea serves /api/v1/ too, and Drupal serves /admin/. So only Mastodon's own
|
|
# resources under them count as Mastodon's paths.
|
|
#
|
|
# Four rules read a search, a name or free text as a command or a system file.
|
|
# Each exclusion below for such a field removes these four, and only these:
|
|
# 930120: .htaccess
|
|
# 932120: powershell
|
|
# 932250: ls -la
|
|
# 932260: python3
|
|
|
|
# The API's searches and names, in the query:
|
|
# q: /api/v2/search?q=python3, /api/v1/accounts/search?q=ls+-la
|
|
# acct: /api/v1/accounts/lookup?acct=python3
|
|
# username, display_name, email, by_domain: the admin API's search of
|
|
# accounts, /api/v1/admin/accounts?username=python3&display_name=powershell
|
|
SecRule REQUEST_FILENAME "@rx ^/api/v[12]/(?:search|accounts|admin/accounts)(?:/|$)" \
|
|
"id:900950,phase:1,pass,nolog,\
|
|
ctl:ruleRemoveTargetById=930120;ARGS:/^(?:q|acct|username|display_name|email|by_domain)$/,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:/^(?:q|acct|username|display_name|email|by_domain)$/,\
|
|
ctl:ruleRemoveTargetById=932250;ARGS:/^(?:q|acct|username|display_name|email|by_domain)$/,\
|
|
ctl:ruleRemoveTargetById=932260;ARGS:/^(?:q|acct|username|display_name|email|by_domain)$/"
|
|
|
|
# The API's titles, names and free text, in form data or JSON bodies:
|
|
# spoiler_text, poll[options][]: posting, spoiler_text=python3+details
|
|
# description: describing an uploaded file, description=Screenshot+of+ls+-la
|
|
# display_name, note, fields_attributes[0][name], fields_attributes[0][value]:
|
|
# editing a profile, note=python3+and+powershell
|
|
# title: naming a list or a filter, title=python3+friends
|
|
# keyword, keywords_attributes[][keyword]: a filter's words, keyword=powershell
|
|
# name: featuring a hashtag, name=python3
|
|
# comment: a report, a note on an account or an IP block, comment=Knows+python3
|
|
# text: warning an account, text=Please+stop+posting+powershell+malware
|
|
# private_comment, public_comment: blocking a domain
|
|
SecRule REQUEST_FILENAME "@rx ^/api/v[12]/(?:statuses|media|accounts|lists|filters|featured_tags|reports|admin/(?:accounts|domain_blocks|ip_blocks))(?:/|$)" \
|
|
"id:900951,phase:1,pass,nolog,chain"
|
|
SecRule &ARGS_GET:/^(?:spoiler_text|poll\[options\]\[\]|description|display_name|note|fields_attributes\[\d+\]\[(?:name|value)\]|title|keyword|keywords_attributes\[\d*\]\[keyword\]|name|comment|text|private_comment|public_comment|json\.(?:spoiler_text|title|poll\.options\.\d+))$/ "@eq 0" \
|
|
"ctl:ruleRemoveTargetById=930120;ARGS:/^(?:spoiler_text|poll\[options\]\[\]|description|display_name|note|fields_attributes\[\d+\]\[(?:name|value)\]|title|keyword|keywords_attributes\[\d*\]\[keyword\]|name|comment|text|private_comment|public_comment|json\.(?:spoiler_text|title|poll\.options\.\d+))$/,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:/^(?:spoiler_text|poll\[options\]\[\]|description|display_name|note|fields_attributes\[\d+\]\[(?:name|value)\]|title|keyword|keywords_attributes\[\d*\]\[keyword\]|name|comment|text|private_comment|public_comment|json\.(?:spoiler_text|title|poll\.options\.\d+))$/,\
|
|
ctl:ruleRemoveTargetById=932250;ARGS:/^(?:spoiler_text|poll\[options\]\[\]|description|display_name|note|fields_attributes\[\d+\]\[(?:name|value)\]|title|keyword|keywords_attributes\[\d*\]\[keyword\]|name|comment|text|private_comment|public_comment|json\.(?:spoiler_text|title|poll\.options\.\d+))$/,\
|
|
ctl:ruleRemoveTargetById=932260;ARGS:/^(?:spoiler_text|poll\[options\]\[\]|description|display_name|note|fields_attributes\[\d+\]\[(?:name|value)\]|title|keyword|keywords_attributes\[\d*\]\[keyword\]|name|comment|text|private_comment|public_comment|json\.(?:spoiler_text|title|poll\.options\.\d+))$/"
|
|
|
|
# A post's text, in status, form data from an app or JSON from the web app.
|
|
# Each of these rules refused posting "Today I learned: `ls -la ~/.config`
|
|
# lists hidden files, and `python3 -m http.server` serves a folder", so they
|
|
# skip status: the four rules, 932125, 932230 and 932235.
|
|
SecRule REQUEST_FILENAME "@rx ^/api/v1/statuses(?:/|$)" \
|
|
"id:900952,phase:1,pass,nolog,chain"
|
|
SecRule &ARGS_GET:/^(?:json\.)?status$/ "@eq 0" \
|
|
"ctl:ruleRemoveTargetById=930120;ARGS:/^(?:json\.)?status$/,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:/^(?:json\.)?status$/,\
|
|
ctl:ruleRemoveTargetById=932125;ARGS:/^(?:json\.)?status$/,\
|
|
ctl:ruleRemoveTargetById=932230;ARGS:/^(?:json\.)?status$/,\
|
|
ctl:ruleRemoveTargetById=932235;ARGS:/^(?:json\.)?status$/,\
|
|
ctl:ruleRemoveTargetById=932250;ARGS:/^(?:json\.)?status$/,\
|
|
ctl:ruleRemoveTargetById=932260;ARGS:/^(?:json\.)?status$/"
|
|
|
|
# Uploaded files' names skip 932180, which refused uploading
|
|
# "Example .htaccess.png" as a server's own file, for a post or as a profile's
|
|
# picture. Coraza keeps these names without the field each came in. Mastodon
|
|
# refuses a file of a type it does not allow.
|
|
SecRule REQUEST_FILENAME "@rx ^/api/v[12]/media$|^/api/v1/accounts/update_credentials$" \
|
|
"id:900953,phase:1,pass,nolog,\
|
|
ctl:ruleRemoveTargetById=932180;FILES"
|
|
|
|
# The admin pages' searches, in the query:
|
|
# username, display_name, email: /admin/accounts?username=python3
|
|
# by_domain: /admin/instances?by_domain=python3
|
|
# shortcode: /admin/custom_emojis?shortcode=python3
|
|
# name: /admin/tags?name=python3
|
|
SecRule REQUEST_FILENAME "@rx ^/admin/(?:accounts|instances|custom_emojis|tags)$" \
|
|
"id:900954,phase:1,pass,nolog,\
|
|
ctl:ruleRemoveTargetById=930120;ARGS:/^(?:username|display_name|email|by_domain|shortcode|name)$/,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:/^(?:username|display_name|email|by_domain|shortcode|name)$/,\
|
|
ctl:ruleRemoveTargetById=932250;ARGS:/^(?:username|display_name|email|by_domain|shortcode|name)$/,\
|
|
ctl:ruleRemoveTargetById=932260;ARGS:/^(?:username|display_name|email|by_domain|shortcode|name)$/"
|
|
|
|
# The admin pages' free text, in bodies:
|
|
# domain_block[private_comment], domain_block[public_comment]: blocking a
|
|
# domain, "Spam about powershell and .htaccess"
|
|
# ip_block[comment]: blocking an address
|
|
# rule[text], rule[hint]: a server rule, "No posting of powershell malware"
|
|
# announcement[text]: an announcement
|
|
# form_admin_settings[site_title], [site_short_description],
|
|
# [site_extended_description]: the server's name and descriptions,
|
|
# "Python3 social"
|
|
SecRule REQUEST_FILENAME "@rx ^/admin/(?:domain_blocks|ip_blocks|rules|announcements|settings)(?:/|$)" \
|
|
"id:900955,phase:1,pass,nolog,chain"
|
|
SecRule &ARGS_GET:/^(?:domain_block\[(?:private|public)_comment\]|ip_block\[comment\]|rule\[(?:text|hint)\]|announcement\[text\]|form_admin_settings\[site_(?:title|short_description|extended_description)\])$/ "@eq 0" \
|
|
"ctl:ruleRemoveTargetById=930120;ARGS:/^(?:domain_block\[(?:private|public)_comment\]|ip_block\[comment\]|rule\[(?:text|hint)\]|announcement\[text\]|form_admin_settings\[site_(?:title|short_description|extended_description)\])$/,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:/^(?:domain_block\[(?:private|public)_comment\]|ip_block\[comment\]|rule\[(?:text|hint)\]|announcement\[text\]|form_admin_settings\[site_(?:title|short_description|extended_description)\])$/,\
|
|
ctl:ruleRemoveTargetById=932250;ARGS:/^(?:domain_block\[(?:private|public)_comment\]|ip_block\[comment\]|rule\[(?:text|hint)\]|announcement\[text\]|form_admin_settings\[site_(?:title|short_description|extended_description)\])$/,\
|
|
ctl:ruleRemoveTargetById=932260;ARGS:/^(?:domain_block\[(?:private|public)_comment\]|ip_block\[comment\]|rule\[(?:text|hint)\]|announcement\[text\]|form_admin_settings\[site_(?:title|short_description|extended_description)\])$/"
|
|
|
|
# The settings pages' names, words and addresses, and passwords, in bodies:
|
|
# custom_filter[title], custom_filter[keywords_attributes][0][keyword]: a
|
|
# filter, "python3 spam" and "ls -la"
|
|
# doorkeeper_application[name], doorkeeper_application[website]: an
|
|
# application, "https://example.org/powershell"
|
|
# account_alias[acct], account_migration[acct]: moving an account
|
|
# user[password], user[password_confirmation], user[current_password]:
|
|
# signing in, signing up and changing the password
|
|
SecRule REQUEST_FILENAME "@rx ^/(?:filters|settings/(?:applications|aliases|migration)|auth)(?:/|$)" \
|
|
"id:900956,phase:1,pass,nolog,chain"
|
|
SecRule &ARGS_GET:/^(?:custom_filter\[(?:title|keywords_attributes\]\[\d+\]\[keyword)\]|doorkeeper_application\[(?:name|website)\]|account_(?:alias|migration)\[acct\]|user\[(?:password|password_confirmation|current_password)\])$/ "@eq 0" \
|
|
"ctl:ruleRemoveTargetById=930120;ARGS:/^(?:custom_filter\[(?:title|keywords_attributes\]\[\d+\]\[keyword)\]|doorkeeper_application\[(?:name|website)\]|account_(?:alias|migration)\[acct\]|user\[(?:password|password_confirmation|current_password)\])$/,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:/^(?:custom_filter\[(?:title|keywords_attributes\]\[\d+\]\[keyword)\]|doorkeeper_application\[(?:name|website)\]|account_(?:alias|migration)\[acct\]|user\[(?:password|password_confirmation|current_password)\])$/,\
|
|
ctl:ruleRemoveTargetById=932250;ARGS:/^(?:custom_filter\[(?:title|keywords_attributes\]\[\d+\]\[keyword)\]|doorkeeper_application\[(?:name|website)\]|account_(?:alias|migration)\[acct\]|user\[(?:password|password_confirmation|current_password)\])$/,\
|
|
ctl:ruleRemoveTargetById=932260;ARGS:/^(?:custom_filter\[(?:title|keywords_attributes\]\[\d+\]\[keyword)\]|doorkeeper_application\[(?:name|website)\]|account_(?:alias|migration)\[acct\]|user\[(?:password|password_confirmation|current_password)\])$/"
|
|
|
|
# The web app's search page: /search?q=python3.
|
|
SecRule REQUEST_FILENAME "@streq /search" \
|
|
"id:900957,phase:1,pass,nolog,\
|
|
ctl:ruleRemoveTargetById=930120;ARGS:q,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:q,\
|
|
ctl:ruleRemoveTargetById=932250;ARGS:q,\
|
|
ctl:ruleRemoveTargetById=932260;ARGS:q"
|
|
|
|
# Deliveries from other servers to an inbox, in JSON. The fields of the object
|
|
# delivered skip the four rules:
|
|
# content, contentMap: a post, "<p>notes on python3, powershell, ...</p>"
|
|
# summary, summaryMap: a post's content warning, or a profile's text
|
|
# name: a profile's name, "Alice powershell"
|
|
# attachment: a picture's description, or a profile's fields
|
|
# oneOf, anyOf: a poll's options, "python3"
|
|
SecRule REQUEST_FILENAME "@rx ^/(?:inbox|users/[^/]+/inbox|ap/users/[0-9]+/inbox)$" \
|
|
"id:900958,phase:1,pass,nolog,chain"
|
|
SecRule &ARGS_GET:/^json\.object\./ "@eq 0" \
|
|
"ctl:ruleRemoveTargetById=930120;ARGS:/^json\.object\.(?:content|contentmap\.[^.]+|summary|summarymap\.[^.]+|name|attachment\.\d+\.(?:name|value)|(?:oneof|anyof)\.\d+\.name)$/,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:/^json\.object\.(?:content|contentmap\.[^.]+|summary|summarymap\.[^.]+|name|attachment\.\d+\.(?:name|value)|(?:oneof|anyof)\.\d+\.name)$/,\
|
|
ctl:ruleRemoveTargetById=932250;ARGS:/^json\.object\.(?:content|contentmap\.[^.]+|summary|summarymap\.[^.]+|name|attachment\.\d+\.(?:name|value)|(?:oneof|anyof)\.\d+\.name)$/,\
|
|
ctl:ruleRemoveTargetById=932260;ARGS:/^json\.object\.(?:content|contentmap\.[^.]+|summary|summarymap\.[^.]+|name|attachment\.\d+\.(?:name|value)|(?:oneof|anyof)\.\d+\.name)$/"
|
|
|
|
# Another server fetching an account or a post asks for ActivityPub's type with
|
|
# its profile, which 920600 refused as a malformed Accept header:
|
|
# Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams"
|
|
SecRule REQUEST_FILENAME "@rx ^/(?:ap/)?users/|^/actor$" \
|
|
"id:900959,phase:1,pass,nolog,\
|
|
ctl:ruleRemoveTargetById=920600;REQUEST_HEADERS:Accept"
|