Core Rule Set: Mastodon's ordinary requests pass by default (closes #238)
check / check (push) Successful in 4m23s
check / check (push) Successful in 4m23s
Adds mastodon.conf, the twelfth change. On Mastodon's paths, the fields that carry a search, a name, a title, an address, a password or free text skip the four rules that read them as commands or system files. They cover the API and the web app's JSON, the admin and settings pages, sign-up and sign-in, and the objects other servers deliver to an inbox. A post's text skips 932125, 932230 and 932235 too. Hashtags and sign-up usernames skip only 932120 and 932260. Uploaded file names skip 932180, a report's forward keys skip 930120 by name, and the Accept header of a link lookup skips 920600 on profile and post links. The requests recorded from Mastodon 4.7.5 pass at the defaults and while bodies are read. Model: opus-5-5
This commit is contained in:
1 parent
3d9e3976b0
commit
ed5d9a743c
7 files changed
+1478
-27
No files matched your search
@@ -0,0 +1,199 @@
|
||||
# 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
|
||||
# The web app sends these in JSON:
|
||||
# spoiler_text, title and the poll's options
|
||||
# description, and media_attributes[].description when it edits a post
|
||||
# display_name, note and fields_attributes[].name and .value, on
|
||||
# /api/v1/profile, {"note":"ls -la shows the .htaccess file"}
|
||||
# comment, on a report or a note on an account
|
||||
SecRule REQUEST_FILENAME "@rx ^/api/v[12]/(?:statuses|media|accounts|profile|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+|description|media_attributes\.\d+\.description|display_name|note|fields_attributes\.\d+\.(?:name|value)|comment))$/ "@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+|description|media_attributes\.\d+\.description|display_name|note|fields_attributes\.\d+\.(?:name|value)|comment))$/,\
|
||||
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+|description|media_attributes\.\d+\.description|display_name|note|fields_attributes\.\d+\.(?:name|value)|comment))$/,\
|
||||
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+|description|media_attributes\.\d+\.description|display_name|note|fields_attributes\.\d+\.(?:name|value)|comment))$/,\
|
||||
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+|description|media_attributes\.\d+\.description|display_name|note|fields_attributes\.\d+\.(?:name|value)|comment))$/"
|
||||
|
||||
# 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$|^/api/v1/profile$" \
|
||||
"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"
|
||||
# admin_account_action[text]: warning an account, on
|
||||
# /admin/accounts/<id>/action
|
||||
# report_note[content], account_moderation_note[content]: a moderator's
|
||||
# note on a report or an account, "ls -la shows the .htaccess file"
|
||||
# account_warning_preset[title], [text]: a preset warning
|
||||
# user_role[name]: a role
|
||||
# terms_of_service[text], [changelog]: the terms of service
|
||||
SecRule REQUEST_FILENAME "@rx ^/admin/(?:domain_blocks|ip_blocks|rules|announcements|settings|accounts/[^/]+/action|report_notes|account_moderation_notes|warning_presets|roles|terms_of_service)(?:/|$)" \
|
||||
"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)\]|admin_account_action\[text\]|(?:report_note|account_moderation_note)\[content\]|account_warning_preset\[(?:title|text)\]|user_role\[name\]|terms_of_service\[(?:text|changelog)\])$/ "@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)\]|admin_account_action\[text\]|(?:report_note|account_moderation_note)\[content\]|account_warning_preset\[(?:title|text)\]|user_role\[name\]|terms_of_service\[(?:text|changelog)\])$/,\
|
||||
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)\]|admin_account_action\[text\]|(?:report_note|account_moderation_note)\[content\]|account_warning_preset\[(?:title|text)\]|user_role\[name\]|terms_of_service\[(?:text|changelog)\])$/,\
|
||||
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)\]|admin_account_action\[text\]|(?:report_note|account_moderation_note)\[content\]|account_warning_preset\[(?:title|text)\]|user_role\[name\]|terms_of_service\[(?:text|changelog)\])$/,\
|
||||
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)\]|admin_account_action\[text\]|(?:report_note|account_moderation_note)\[content\]|account_warning_preset\[(?:title|text)\]|user_role\[name\]|terms_of_service\[(?:text|changelog)\])$/"
|
||||
|
||||
# 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
|
||||
# user[invite_request_attributes][text]: why someone signs up, while sign-ups
|
||||
# need approval
|
||||
# account_migration[current_password], form_redirect[current_password],
|
||||
# form_delete_confirmation[password]: moving or deleting an account
|
||||
SecRule REQUEST_FILENAME "@rx ^/(?:filters|settings/(?:applications|aliases|migration|delete)|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)\]|user\[invite_request_attributes\]\[text\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[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)\]|user\[invite_request_attributes\]\[text\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[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)\]|user\[invite_request_attributes\]\[text\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[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)\]|user\[invite_request_attributes\]\[text\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[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)\]|user\[invite_request_attributes\]\[text\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[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)$/"
|
||||
|
||||
# When someone on another server looks up a link to a profile or a post here,
|
||||
# such as /@admin or /@admin/117589811962372311, that server fetches the link
|
||||
# with this header. It sends it again to the account or post the link leads to.
|
||||
# 920600 refused it as a malformed Accept header:
|
||||
# Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams",
|
||||
# application/activity+json, text/html;q=0.1
|
||||
SecRule REQUEST_FILENAME "@rx ^/@[^/]+(?:/[0-9]+)?$|^/(?:ap/)?users/|^/actor$" \
|
||||
"id:900959,phase:1,pass,nolog,\
|
||||
ctl:ruleRemoveTargetById=920600;REQUEST_HEADERS:Accept"
|
||||
|
||||
# Signing up through the API, in form data or JSON:
|
||||
# password, reason: password=ls+-la+shows+the+.htaccess+file
|
||||
SecRule REQUEST_FILENAME "@streq /api/v1/accounts" \
|
||||
"id:900960,phase:1,pass,nolog,chain"
|
||||
SecRule &ARGS_GET:/^(?:json\.)?(?:password|reason)$/ "@eq 0" \
|
||||
"ctl:ruleRemoveTargetById=930120;ARGS:/^(?:json\.)?(?:password|reason)$/,\
|
||||
ctl:ruleRemoveTargetById=932120;ARGS:/^(?:json\.)?(?:password|reason)$/,\
|
||||
ctl:ruleRemoveTargetById=932250;ARGS:/^(?:json\.)?(?:password|reason)$/,\
|
||||
ctl:ruleRemoveTargetById=932260;ARGS:/^(?:json\.)?(?:password|reason)$/"
|
||||
|
||||
# The web app's reports have the JSON keys forward and forward_to_domains. Their
|
||||
# names, json.forward and json.forward_to_domains, contain .forward, a file in a
|
||||
# home folder, so 930120 refused every report the web app sent.
|
||||
SecRule REQUEST_FILENAME "@streq /api/v1/reports" \
|
||||
"id:900961,phase:1,pass,nolog,chain"
|
||||
SecRule &ARGS_GET:/^json\.forward/ "@eq 0" \
|
||||
"ctl:ruleRemoveTargetById=930120;ARGS_NAMES:/^json\.forward(?:_to_domains)?$/"
|
||||
|
||||
# A hashtag and a username hold only letters, digits and underscores. So the
|
||||
# web app's featured hashtags and the sign-up form's username skip only 932120
|
||||
# and 932260, which refused the hashtag or username python3 or powershell.
|
||||
SecRule REQUEST_FILENAME "@streq /api/v1/featured_tags" \
|
||||
"id:900962,phase:1,pass,nolog,chain"
|
||||
SecRule &ARGS_GET:/^json\.name$/ "@eq 0" \
|
||||
"ctl:ruleRemoveTargetById=932120;ARGS:/^json\.name$/,\
|
||||
ctl:ruleRemoveTargetById=932260;ARGS:/^json\.name$/"
|
||||
SecRule REQUEST_FILENAME "@streq /auth" \
|
||||
"id:900963,phase:1,pass,nolog,chain"
|
||||
SecRule &ARGS_GET:/^user\[account_attributes\]\[username\]$/ "@eq 0" \
|
||||
"ctl:ruleRemoveTargetById=932120;ARGS:/^user\[account_attributes\]\[username\]$/,\
|
||||
ctl:ruleRemoveTargetById=932260;ARGS:/^user\[account_attributes\]\[username\]$/"
|
||||
Reference in new issue
Block a user