check / check (push) Successful in 6m21s
Each app's fixes are one .conf file and one entry in apps. They apply only while Inspect has set the app's transaction variable, such as tx.app_gitea. SWW_APPS names each hostname's apps. A hostname gets only its apps' fixes; while SWW_APPS is unset, every hostname gets every app's fixes. A hostname in SWW_APPS that is not a DNS name, such as *.example.com, stops the start. Unset, SWW_APPS is logged at start as null; set empty, as "". Model: opus-5-5
224 lines
14 KiB
Plaintext
224 lines
14 KiB
Plaintext
# MediaWiki's fixes: 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
|
|
|
|
# Unless SWW_APPS lists mediawiki for the request's hostname, every rule below
|
|
# is skipped. Inspect sets tx.app_mediawiki when it does.
|
|
# The skip reaches only phase 1, so a rule of a later phase must check
|
|
# tx.app_mediawiki itself.
|
|
SecRule &TX:app_mediawiki "@eq 0" \
|
|
"id:900890,phase:1,pass,nolog,skipAfter:END_MEDIAWIKI"
|
|
|
|
# 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. It checks tx.app_mediawiki, since the skip above does not
|
|
# reach phase 2.
|
|
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 &TX:app_mediawiki "@eq 1" "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"
|
|
|
|
SecMarker END_MEDIAWIKI
|