# The eleventh change: on MediaWiki's paths, some fields skip the rules that # refused ordinary MediaWiki requests there. They are the fields where MediaWiki # sends a search, a title, a name, a file name, an address, a summary or a # reason, and the fields that hold wikitext. 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. MediaWiki's # paths are matched at the site root and under /w/. # # WordPress serves /index.php too. So /index.php counts as MediaWiki's only # with a page title, in the query or after the path: # /index.php?title=Main_Page # /index.php/Main_Page, where a wiki without short URLs serves its pages # Pages under /wiki/ need neither. # # Four rules read a search, a title or a name as a command or a system file. # Each exclusion below for such a field removes these four: # 930120: .htaccess # 932120: powershell # 932250: ls -la # 932260: python3 # The pages and their titles, the searches, and the special pages' fields # for a page, a user, a file or an address: # title: /index.php?title=Python3_notes&action=history # search: /index.php?search=python3&title=Special:Search # target: /wiki/Special:WhatLinksHere?target=Python3+notes # page, user: /wiki/Special:Log?user=Admin&page=Python3+notes # username: /wiki/Special:ListUsers?username=python3 # from, to: /wiki/Special:AllPages?from=python3 # prefix: /wiki/Special:PrefixIndex?prefix=python3 # returnto, returntoquery: /wiki/Special:UserLogin?returnto=Python3_notes # ilsearch: /wiki/Special:ListFiles?ilsearch=python3 # filename: /wiki/Special:FileDuplicateSearch?filename=Pic+python3.png # wpDestFile: /wiki/Special:Upload?wpDestFile=Pic+python3.png # wpTarget: /wiki/Special:BlockList?wpTarget=Python3, and blocking or # unblocking the user Python3 # Uploaded files' names skip 932180, as on the API below. SecRule REQUEST_FILENAME "@rx ^/(?:w/)?index\.php(?:$|/.)|^/wiki/" \ "id:900900,phase:1,pass,nolog,chain" SecRule REQUEST_FILENAME|ARGS_GET_NAMES "@rx ^/(?:w/)?index\.php/.|^/wiki/|^title$" \ "ctl:ruleRemoveTargetById=930120;ARGS:/^(?:title|search|target|page|user|username|from|to|prefix|returnto|returntoquery|ilsearch|filename|wpdestfile|wptarget)$/,\ ctl:ruleRemoveTargetById=932120;ARGS:/^(?:title|search|target|page|user|username|from|to|prefix|returnto|returntoquery|ilsearch|filename|wpdestfile|wptarget)$/,\ ctl:ruleRemoveTargetById=932250;ARGS:/^(?:title|search|target|page|user|username|from|to|prefix|returnto|returntoquery|ilsearch|filename|wpdestfile|wptarget)$/,\ ctl:ruleRemoveTargetById=932260;ARGS:/^(?:title|search|target|page|user|username|from|to|prefix|returnto|returntoquery|ilsearch|filename|wpdestfile|wptarget)$/,\ ctl:ruleRemoveTargetById=932180;FILES" # The API's searches, titles, names and file names, in the query or the body: # search: /api.php?action=opensearch&search=python3 # srsearch, pssearch: /api.php?action=query&list=search&srsearch=python3 # titles, title, page: /api.php?action=parse&page=Python3+notes # from, to: moving a page, from=Python3+notes&to=Ls+-la+notes # apfrom, apto, apprefix: /api.php?action=query&list=allpages&apfrom=python3 # aifrom, aiprefix: /api.php?action=query&list=allimages&aiprefix=python3 # aufrom, auprefix: /api.php?action=query&list=allusers&auprefix=python3 # bltitle: /api.php?action=query&list=backlinks&bltitle=Python3+notes # gsrsearch, gpssearch, gapfrom, gapto, gapprefix, gaifrom, gaiprefix, # gbltitle, geuquery: the same fields from a generator, # /api.php?action=query&generator=allpages&gapprefix=python3 # euquery: /api.php?action=query&list=exturlusage&euquery=example.com/python3 # letitle: /api.php?action=query&list=logevents&letitle=Python3+notes # ucuser: /api.php?action=query&list=usercontribs&ucuser=python3 # bkusers: /api.php?action=query&list=blocks&bkusers=Python3 # user: blocking, unblocking or changing the groups of a user, user=Python3 # summary, sectiontitle: saving a page, summary=Notes+on+python3 # filename, comment: uploading a file, filename=Api+python3.png # reason: moving or deleting a page, reason=python3 # lgname, lgpassword, username, password: signing in, password=python3 # Uploaded files' names skip 932180, which refused uploading # "Api .htaccess.png" as a server's own file. Coraza keeps these names without # the field each came in. MediaWiki refuses a file whose extension it does not # allow. SecRule REQUEST_FILENAME "@rx ^/(?:w/)?api\.php$" \ "id:900901,phase:1,pass,nolog,\ ctl:ruleRemoveTargetById=930120;ARGS:/^(?:search|srsearch|pssearch|titles?|page|from|to|apfrom|apto|apprefix|aifrom|aiprefix|aufrom|auprefix|bltitle|gsrsearch|gpssearch|gapfrom|gapto|gapprefix|gaifrom|gaiprefix|gbltitle|geuquery|euquery|letitle|ucuser|bkusers|user|summary|sectiontitle|filename|comment|reason|lgname|lgpassword|username|password)$/,\ ctl:ruleRemoveTargetById=932120;ARGS:/^(?:search|srsearch|pssearch|titles?|page|from|to|apfrom|apto|apprefix|aifrom|aiprefix|aufrom|auprefix|bltitle|gsrsearch|gpssearch|gapfrom|gapto|gapprefix|gaifrom|gaiprefix|gbltitle|geuquery|euquery|letitle|ucuser|bkusers|user|summary|sectiontitle|filename|comment|reason|lgname|lgpassword|username|password)$/,\ ctl:ruleRemoveTargetById=932250;ARGS:/^(?:search|srsearch|pssearch|titles?|page|from|to|apfrom|apto|apprefix|aifrom|aiprefix|aufrom|auprefix|bltitle|gsrsearch|gpssearch|gapfrom|gapto|gapprefix|gaifrom|gaiprefix|gbltitle|geuquery|euquery|letitle|ucuser|bkusers|user|summary|sectiontitle|filename|comment|reason|lgname|lgpassword|username|password)$/,\ ctl:ruleRemoveTargetById=932260;ARGS:/^(?:search|srsearch|pssearch|titles?|page|from|to|apfrom|apto|apprefix|aifrom|aiprefix|aufrom|auprefix|bltitle|gsrsearch|gpssearch|gapfrom|gapto|gapprefix|gaifrom|gaiprefix|gbltitle|geuquery|euquery|letitle|ucuser|bkusers|user|summary|sectiontitle|filename|comment|reason|lgname|lgpassword|username|password)$/,\ ctl:ruleRemoveTargetById=932180;FILES" # The search box's suggestions and the search through the REST API: # /rest.php/v1/search/title?q=python3. SecRule REQUEST_FILENAME "@rx ^/(?:w/)?rest\.php/" \ "id:900902,phase:1,pass,nolog,\ ctl:ruleRemoveTargetById=930120;ARGS:q,\ ctl:ruleRemoveTargetById=932120;ARGS:q,\ ctl:ruleRemoveTargetById=932250;ARGS:q,\ ctl:ruleRemoveTargetById=932260;ARGS:q" # The names of the scripts and styles a page loads, which 930120 read as a # system file: /load.php?modules=jquery,moment,oojs,...|mediawiki.storage. SecRule REQUEST_FILENAME "@rx ^/(?:w/)?load\.php$" \ "id:900903,phase:1,pass,nolog,\ ctl:ruleRemoveTargetById=930120;ARGS:modules" # The fields below are in bodies, and skip the four rules there: # wpSummary: saving a page, "Notes on python3, ls -la and .htaccess" # wpUploadDescription: uploading a file # wpName, wpPassword: signing in # wpOldTitle, wpNewTitleMain, wpReason: moving a page to "Python3 notes" SecRule REQUEST_FILENAME "@rx ^/(?:w/)?index\.php(?:$|/.)|^/wiki/" \ "id:900910,phase:1,pass,nolog,chain" SecRule REQUEST_FILENAME|ARGS_GET_NAMES "@rx ^/(?:w/)?index\.php/.|^/wiki/|^title$" "chain" SecRule &ARGS_GET:/^wp(?:summary|uploaddescription|name|password|oldtitle|newtitlemain|reason)$/ "@eq 0" \ "ctl:ruleRemoveTargetById=930120;ARGS:/^wp(?:summary|uploaddescription|name|password|oldtitle|newtitlemain|reason)$/,\ ctl:ruleRemoveTargetById=932120;ARGS:/^wp(?:summary|uploaddescription|name|password|oldtitle|newtitlemain|reason)$/,\ ctl:ruleRemoveTargetById=932250;ARGS:/^wp(?:summary|uploaddescription|name|password|oldtitle|newtitlemain|reason)$/,\ ctl:ruleRemoveTargetById=932260;ARGS:/^wp(?:summary|uploaddescription|name|password|oldtitle|newtitlemain|reason)$/" # Wikitext: the edit form sends it in wpTextbox1, the API in text, appendtext # or prependtext, and the REST API in the JSON field source. Each of these # rules refused saving or previewing a page whose wikitext held a template, a # table and code blocks with a shell script, an SQL query and JavaScript, so # they skip those fields: 930120, 932120, 932125, 932160, 932230, 932235, # 932250, 932260, 941180, 941390, 942190 and 942270. SecRule REQUEST_FILENAME "@rx ^/(?:w/)?index\.php(?:$|/.)|^/wiki/" \ "id:900911,phase:1,pass,nolog,chain" SecRule REQUEST_FILENAME|ARGS_GET_NAMES "@rx ^/(?:w/)?index\.php/.|^/wiki/|^title$" "chain" SecRule &ARGS_GET:wpTextbox1 "@eq 0" \ "ctl:ruleRemoveTargetById=930120;ARGS:wpTextbox1,\ ctl:ruleRemoveTargetById=932120;ARGS:wpTextbox1,\ ctl:ruleRemoveTargetById=932125;ARGS:wpTextbox1,\ ctl:ruleRemoveTargetById=932160;ARGS:wpTextbox1,\ ctl:ruleRemoveTargetById=932230;ARGS:wpTextbox1,\ ctl:ruleRemoveTargetById=932235;ARGS:wpTextbox1,\ ctl:ruleRemoveTargetById=932250;ARGS:wpTextbox1,\ ctl:ruleRemoveTargetById=932260;ARGS:wpTextbox1,\ ctl:ruleRemoveTargetById=941180;ARGS:wpTextbox1,\ ctl:ruleRemoveTargetById=941390;ARGS:wpTextbox1,\ ctl:ruleRemoveTargetById=942190;ARGS:wpTextbox1,\ ctl:ruleRemoveTargetById=942270;ARGS:wpTextbox1" SecRule REQUEST_FILENAME "@rx ^/(?:w/)?api\.php$" \ "id:900912,phase:1,pass,nolog,chain" SecRule &ARGS_GET:/^(?:text|appendtext|prependtext)$/ "@eq 0" \ "ctl:ruleRemoveTargetById=930120;ARGS:/^(?:text|appendtext|prependtext)$/,\ ctl:ruleRemoveTargetById=932120;ARGS:/^(?:text|appendtext|prependtext)$/,\ ctl:ruleRemoveTargetById=932125;ARGS:/^(?:text|appendtext|prependtext)$/,\ ctl:ruleRemoveTargetById=932160;ARGS:/^(?:text|appendtext|prependtext)$/,\ ctl:ruleRemoveTargetById=932230;ARGS:/^(?:text|appendtext|prependtext)$/,\ ctl:ruleRemoveTargetById=932235;ARGS:/^(?:text|appendtext|prependtext)$/,\ ctl:ruleRemoveTargetById=932250;ARGS:/^(?:text|appendtext|prependtext)$/,\ ctl:ruleRemoveTargetById=932260;ARGS:/^(?:text|appendtext|prependtext)$/,\ ctl:ruleRemoveTargetById=941180;ARGS:/^(?:text|appendtext|prependtext)$/,\ ctl:ruleRemoveTargetById=941390;ARGS:/^(?:text|appendtext|prependtext)$/,\ ctl:ruleRemoveTargetById=942190;ARGS:/^(?:text|appendtext|prependtext)$/,\ ctl:ruleRemoveTargetById=942270;ARGS:/^(?:text|appendtext|prependtext)$/" # A REST API client saving a page sends its wikitext in source, which skips # the rules above, and its summary in comment, which skips the four rules. SecRule REQUEST_FILENAME "@rx ^/(?:w/)?rest\.php/" \ "id:900913,phase:1,pass,nolog,chain" SecRule &ARGS_GET:/^json\.(?:source|comment)$/ "@eq 0" \ "ctl:ruleRemoveTargetById=930120;ARGS:/^json\.(?:source|comment)$/,\ ctl:ruleRemoveTargetById=932120;ARGS:/^json\.(?:source|comment)$/,\ ctl:ruleRemoveTargetById=932250;ARGS:/^json\.(?:source|comment)$/,\ ctl:ruleRemoveTargetById=932260;ARGS:/^json\.(?:source|comment)$/,\ ctl:ruleRemoveTargetById=932125;ARGS:json.source,\ ctl:ruleRemoveTargetById=932160;ARGS:json.source,\ ctl:ruleRemoveTargetById=932230;ARGS:json.source,\ ctl:ruleRemoveTargetById=932235;ARGS:json.source,\ ctl:ruleRemoveTargetById=941180;ARGS:json.source,\ ctl:ruleRemoveTargetById=941390;ARGS:json.source,\ ctl:ruleRemoveTargetById=942190;ARGS:json.source,\ ctl:ruleRemoveTargetById=942270;ARGS:json.source" # The reasons on the admin pages are in bodies. They skip only 930120 and # 932120, which refused "Spamming links to .htaccess guides" and the same with # powershell: # wpReason-other: blocking a user # user-reason: changing a user's groups # mwProtect-reason: protecting a page SecRule REQUEST_FILENAME "@rx ^/(?:w/)?index\.php(?:$|/.)|^/wiki/" \ "id:900914,phase:1,pass,nolog,chain" SecRule REQUEST_FILENAME|ARGS_GET_NAMES "@rx ^/(?:w/)?index\.php/.|^/wiki/|^title$" "chain" SecRule &ARGS_GET:/^(?:wpreason-other|user-reason|mwprotect-reason)$/ "@eq 0" \ "ctl:ruleRemoveTargetById=930120;ARGS:/^(?:wpreason-other|user-reason|mwprotect-reason)$/,\ ctl:ruleRemoveTargetById=932120;ARGS:/^(?:wpreason-other|user-reason|mwprotect-reason)$/" # Special:EditWatchlist/raw sends the watched titles in wpTitles, one per # line. Watchlists holding "Lighthouse notes" and then one of these titles # were refused: # "Python3 notes", "Powershell notes", "Ls -la notes" and ".htaccess notes" # wpTitles always skips 930120, 932120 and 932250. Rule 900916 below skips # 932125, 932230, 932235, 932260 and 932380 only when wpTitles holds no ";", # "|", "&", backtick or "{", so "; wget http://example.com/x.sh" is still # refused. A watchlist that mixes a title holding one of these with a title # starting with a command name is refused too. SecRule REQUEST_FILENAME "@rx ^/(?:w/)?index\.php(?:$|/.)|^/wiki/" \ "id:900915,phase:1,pass,nolog,chain" SecRule REQUEST_FILENAME|ARGS_GET_NAMES "@rx ^/(?:w/)?index\.php/.|^/wiki/|^title$" "chain" SecRule &ARGS_GET:wpTitles "@eq 0" \ "ctl:ruleRemoveTargetById=930120;ARGS:wpTitles,\ ctl:ruleRemoveTargetById=932120;ARGS:wpTitles,\ ctl:ruleRemoveTargetById=932250;ARGS:wpTitles" # This rule is in phase 2, because a phase 1 rule runs before the body is read # and cannot see what wpTitles holds. It checks that the body sends wpTitles # once, since it would otherwise skip these rules for a second wpTitles # holding a command. SecRule REQUEST_FILENAME "@rx ^/(?:w/)?index\.php(?:$|/.)|^/wiki/" \ "id:900916,phase:2,pass,nolog,chain" SecRule REQUEST_FILENAME|ARGS_GET_NAMES "@rx ^/(?:w/)?index\.php/.|^/wiki/|^title$" "chain" SecRule &ARGS_GET:wpTitles "@eq 0" "chain" SecRule &ARGS_POST:wpTitles "@eq 1" "chain" SecRule ARGS_POST:wpTitles "!@rx [;|&`{]" \ "ctl:ruleRemoveTargetById=932125;ARGS:wpTitles,\ ctl:ruleRemoveTargetById=932230;ARGS:wpTitles,\ ctl:ruleRemoveTargetById=932235;ARGS:wpTitles,\ ctl:ruleRemoveTargetById=932260;ARGS:wpTitles,\ ctl:ruleRemoveTargetById=932380;ARGS:wpTitles"