# 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_CORE_RULE_SET_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//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, "

notes on python3, powershell, ...

" # 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. A # report forwarded to another server lists that server in forward_to_domains. # Their names contain .forward, a file in a home folder, so 930120 refused every # report the web app sent. The names are: # json.forward # json.forward_to_domains # json.forward_to_domains.0, json.forward_to_domains.1 and so on, one per # server, such as remote.example 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(?:\.\d+)?)?$/" # 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\]$/"