check / check (push) Successful in 5m51s
Every setting of discriminator and rproxy is read through viper, in place of the os.LookupEnv function Main passed down. NewViper turns AutomaticEnv and AllowEmptyEnv on, so a setting that is set but empty reads as set. A setting may also be given as a file, the _FILE form, which rproxy's settings now have too; rproxy still writes nothing to disk. The settings are renamed. Discriminator's take the prefix SWW_WAF_, rproxy's take SWW_RPROXY_, and the shared SWW_COMMAND keeps SWW_. The Core Rule Set's settings take SWW_WAF_CORE_RULE_SET_ rather than SWW_WAF_WAF_. Model: deepseek-v4-pro (first version); opus-5-5 (rework)
198 lines
10 KiB
Plaintext
198 lines
10 KiB
Plaintext
# The ninth change: on WordPress's paths, some fields skip the rules that refused
|
|
# ordinary WordPress requests there. 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 the 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.
|
|
|
|
# WordPress takes a call to its REST API at any of four addresses:
|
|
# /wp-json/wp/v2/posts/1
|
|
# /index.php/wp-json/wp/v2/posts/1
|
|
# /index.php?rest_route=/wp/v2/posts/1
|
|
# /?rest_route=/wp/v2/posts/1
|
|
# These two rules put the route, here /wp/v2/posts/1, in tx.rest_route for the
|
|
# exclusions below. A call that names a route in both its path and its query
|
|
# gets none of them, since WordPress follows the query.
|
|
SecRule &ARGS_GET:rest_route "@eq 0" "id:900705,phase:1,pass,nolog,chain"
|
|
SecRule REQUEST_FILENAME "@rx ^/(?:index\.php/)?wp-json(/.*)$" \
|
|
"capture,setvar:tx.rest_route=%{tx.1}"
|
|
SecRule REQUEST_FILENAME "@rx ^/(?:index\.php)?$" "id:900706,phase:1,pass,nolog,chain"
|
|
SecRule ARGS_GET:rest_route "@rx ^(/.*)$" "capture,setvar:tx.rest_route=%{tx.1}"
|
|
|
|
# The block editor saves a post with POST and the header X-HTTP-Method-Override:
|
|
# PUT, which the Core Rule Set refused (920450). So does saving a user's editor
|
|
# preferences.
|
|
SecRule TX:rest_route "@beginsWith /" \
|
|
"id:900700,phase:1,pass,nolog,\
|
|
ctl:ruleRemoveTargetById=920450;REQUEST_HEADERS_NAMES:X-HTTP-Method-Override"
|
|
|
|
# The block editor and the site editor send HTML in the JSON field content, which
|
|
# skips these rules when saving or autosaving a post, a page, a template or a
|
|
# template part:
|
|
# 930120: cat /etc/hosts
|
|
# 932230: a code block with a shell pipeline and an SQL query
|
|
# 932250: the same code block
|
|
# 934100: console.log('ok')
|
|
# 941100: <iframe src="..." onload="...">
|
|
# 941110: <script>
|
|
# 941120: onload=
|
|
# 941160: <img src=
|
|
# 941180: <!-- wp:paragraph -->, the comment that starts every block
|
|
# 941390: console.log(
|
|
# 942190: how to select a table from the catalog
|
|
# 942270: how to select a table from the catalog
|
|
SecRule TX:rest_route \
|
|
"@rx ^/wp/v2/(?:(?:posts|pages)(?:/[0-9]+(?:/autosaves)?)?|template(?:s|-parts)(?:/[^/]+//[^/]+(?:/autosaves)?)?)$" \
|
|
"id:900701,phase:1,pass,nolog,chain"
|
|
SecRule &ARGS_GET:json.content "@eq 0" \
|
|
"ctl:ruleRemoveTargetById=930120;ARGS:json.content,\
|
|
ctl:ruleRemoveTargetById=932230;ARGS:json.content,\
|
|
ctl:ruleRemoveTargetById=932250;ARGS:json.content,\
|
|
ctl:ruleRemoveTargetById=934100;ARGS:json.content,\
|
|
ctl:ruleRemoveTargetById=941100;ARGS:json.content,\
|
|
ctl:ruleRemoveTargetById=941110;ARGS:json.content,\
|
|
ctl:ruleRemoveTargetById=941120;ARGS:json.content,\
|
|
ctl:ruleRemoveTargetById=941160;ARGS:json.content,\
|
|
ctl:ruleRemoveTargetById=941180;ARGS:json.content,\
|
|
ctl:ruleRemoveTargetById=941390;ARGS:json.content,\
|
|
ctl:ruleRemoveTargetById=942190;ARGS:json.content,\
|
|
ctl:ruleRemoveTargetById=942270;ARGS:json.content"
|
|
|
|
# A reply to a comment from the list of comments, in content:
|
|
# 932125: ls -la, in the reply's text
|
|
# 932230: ls -la, in the reply's text
|
|
# 932250: ls -la, in the reply's text
|
|
SecRule REQUEST_FILENAME "@streq /wp-admin/admin-ajax.php" \
|
|
"id:900702,phase:1,pass,nolog,chain"
|
|
SecRule &ARGS_GET:content "@eq 0" \
|
|
"ctl:ruleRemoveTargetById=932125;ARGS:content,\
|
|
ctl:ruleRemoveTargetById=932230;ARGS:content,\
|
|
ctl:ruleRemoveTargetById=932250;ARGS:content"
|
|
|
|
# Saving a plugin's or a theme's file from its file editor sends the file in
|
|
# newcontent, which skips these rules, each of which refused saving the plugin
|
|
# Hello Dolly's hello.php. Some of them refused only the whole file:
|
|
# 932140: if ( 'en_' !== substr(
|
|
# 932235: the whole file
|
|
# 933100: the whole file
|
|
# 933160: if ( 'en_' !== substr(
|
|
# 933210: $lyrics = explode(
|
|
# 941100: <style type='text/css'>
|
|
# 941160: <style type='text/css'>
|
|
# 941190: the whole file
|
|
# 942151: if ( 'en_' !== substr(
|
|
# 942540: Dolly'll never go away again";
|
|
SecRule REQUEST_FILENAME "@streq /wp-admin/admin-ajax.php" \
|
|
"id:900703,phase:1,pass,nolog,chain"
|
|
SecRule &ARGS_GET:newcontent "@eq 0" \
|
|
"ctl:ruleRemoveTargetById=932140;ARGS:newcontent,\
|
|
ctl:ruleRemoveTargetById=932235;ARGS:newcontent,\
|
|
ctl:ruleRemoveTargetById=933100;ARGS:newcontent,\
|
|
ctl:ruleRemoveTargetById=933160;ARGS:newcontent,\
|
|
ctl:ruleRemoveTargetById=933210;ARGS:newcontent,\
|
|
ctl:ruleRemoveTargetById=941100;ARGS:newcontent,\
|
|
ctl:ruleRemoveTargetById=941160;ARGS:newcontent,\
|
|
ctl:ruleRemoveTargetById=941190;ARGS:newcontent,\
|
|
ctl:ruleRemoveTargetById=942151;ARGS:newcontent,\
|
|
ctl:ruleRemoveTargetById=942540;ARGS:newcontent"
|
|
|
|
# Signing in, in pwd, where a password holds any characters:
|
|
# 930120: a password ending in /etc/hosts
|
|
SecRule REQUEST_FILENAME "@streq /wp-login.php" \
|
|
"id:900704,phase:1,pass,nolog,chain"
|
|
SecRule &ARGS_GET:pwd "@eq 0" "ctl:ruleRemoveTargetById=930120;ARGS:pwd"
|
|
|
|
# Saving through the REST API, the JSON fields title, excerpt and name skip these
|
|
# rules. WordPress writes a slug such as htaccess or ls-la, so slug skips only
|
|
# 932120 and 932260:
|
|
# 930120: .htaccess
|
|
# 932120: powershell
|
|
# 932250: ls -la
|
|
# 932260: python3
|
|
SecRule TX:rest_route "@beginsWith /wp/v2/" "id:900712,phase:1,pass,nolog,chain"
|
|
SecRule &ARGS_GET:/^json\.(?:title|excerpt|slug|name)$/ "@eq 0" \
|
|
"ctl:ruleRemoveTargetById=930120;ARGS:/^json\.(?:title|excerpt|name)$/,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:/^json\.(?:title|excerpt|slug|name)$/,\
|
|
ctl:ruleRemoveTargetById=932250;ARGS:/^json\.(?:title|excerpt|name)$/,\
|
|
ctl:ruleRemoveTargetById=932260;ARGS:/^json\.(?:title|excerpt|slug|name)$/"
|
|
|
|
# A comment's author gives the address of their site in url, which skips these
|
|
# rules there:
|
|
# 930120: http://blog.example/.htaccess
|
|
# 932120: http://blog.example/powershell-basics/
|
|
SecRule REQUEST_FILENAME "@streq /wp-comments-post.php" \
|
|
"id:900713,phase:1,pass,nolog,chain"
|
|
SecRule &ARGS_GET:url "@eq 0" \
|
|
"ctl:ruleRemoveTargetById=930120;ARGS:url,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:url"
|
|
|
|
# The block editor's link previews and embeds send an address in url, which
|
|
# skips these rules there:
|
|
# 930120: http://blog.example/.htaccess
|
|
# 932120: http://blog.example/powershell-basics/
|
|
SecRule TX:rest_route "@rx ^/(?:wp-block-editor/v1/url-details|oembed/1\.0/proxy)$" \
|
|
"id:900714,phase:1,pass,nolog,\
|
|
ctl:ruleRemoveTargetById=930120;ARGS:url,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:url"
|
|
|
|
# The plugin and theme file editors name the file open in file, which skips
|
|
# these rules there:
|
|
# 930120: akismet/views/config.php
|
|
# 932120: install-script.sh
|
|
# 932160: bin/install.sh
|
|
# 932260: docker-compose.yml
|
|
# 941180: document.write.js
|
|
SecRule REQUEST_FILENAME "@rx ^/wp-admin/(?:plugin|theme)-editor\.php$" \
|
|
"id:900709,phase:1,pass,nolog,\
|
|
ctl:ruleRemoveTargetById=930120;ARGS:file,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:file,\
|
|
ctl:ruleRemoveTargetById=932160;ARGS:file,\
|
|
ctl:ruleRemoveTargetById=932260;ARGS:file,\
|
|
ctl:ruleRemoveTargetById=941180;ARGS:file"
|
|
|
|
# Searches, the names of tags, categories, plugins, themes and plugins' settings
|
|
# pages, and addresses can hold any word. In the fields of the next three rules,
|
|
# each request skips the rules that refused such words:
|
|
# 930120: .htaccess
|
|
# 932120: powershell
|
|
# 932250: ls -la
|
|
# 932260: python3
|
|
# An address repeats the search it came from, so only the addresses of the first
|
|
# group also skip:
|
|
# 932235: python3, in the address of a page of results
|
|
|
|
# All of wp-admin, and signing in. A plugin's settings page is named in page.
|
|
# The site editor names a template in p and postId, and a plugin to install is
|
|
# named in slug. WordPress writes these three as slugs, such as htaccess or
|
|
# ls-la, so they skip only 932120 and 932260. Names are matched in lower case,
|
|
# so postId is written postid.
|
|
SecRule REQUEST_FILENAME "@rx ^/(?:wp-admin/|wp-login\.php$)" \
|
|
"id:900707,phase:1,pass,nolog,\
|
|
ctl:ruleRemoveTargetById=930120;ARGS:/^(?:s|search|q|term|tag|category_name|plugin|checked\[\]|theme|stylesheet|page|url|return|redirect_to|wp_http_referer|_wp_http_referer)$/,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:/^(?:s|search|q|term|tag|category_name|plugin|checked\[\]|theme|stylesheet|page|p|postid|slug|url|return|redirect_to|wp_http_referer|_wp_http_referer)$/,\
|
|
ctl:ruleRemoveTargetById=932235;ARGS:/^(?:url|return|redirect_to|wp_http_referer|_wp_http_referer)$/,\
|
|
ctl:ruleRemoveTargetById=932250;ARGS:/^(?:s|search|q|term|tag|category_name|plugin|checked\[\]|theme|stylesheet|page|url|return|redirect_to|wp_http_referer|_wp_http_referer)$/,\
|
|
ctl:ruleRemoveTargetById=932260;ARGS:/^(?:s|search|q|term|tag|category_name|plugin|checked\[\]|theme|stylesheet|page|p|postid|slug|url|return|redirect_to|wp_http_referer|_wp_http_referer)$/"
|
|
|
|
# Every route of WordPress's own REST API, and the embed address each post links
|
|
# to.
|
|
SecRule TX:rest_route "@rx ^/(?:wp/v2/|oembed/1\.0/embed$)" \
|
|
"id:900708,phase:1,pass,nolog,\
|
|
ctl:ruleRemoveTargetById=930120;ARGS:/^(?:search|term|slug|url)$/,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:/^(?:search|term|slug|url)$/,\
|
|
ctl:ruleRemoveTargetById=932250;ARGS:/^(?:search|term|slug|url)$/,\
|
|
ctl:ruleRemoveTargetById=932260;ARGS:/^(?:search|term|slug|url)$/"
|
|
|
|
# The site's own pages, and the later pages of a search.
|
|
SecRule REQUEST_FILENAME "@rx ^/(?:(?:index\.php/)?page/[0-9]+/?|index\.php)?$" \
|
|
"id:900710,phase:1,pass,nolog,\
|
|
ctl:ruleRemoveTargetById=930120;ARGS:/^(?:s|tag)$/,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:/^(?:s|tag)$/,\
|
|
ctl:ruleRemoveTargetById=932250;ARGS:/^(?:s|tag)$/,\
|
|
ctl:ruleRemoveTargetById=932260;ARGS:/^(?:s|tag)$/"
|
|
|
|
# The media list sends its own address in _wp_http_referer, which ends in &ps and
|
|
# so skips 932340 there.
|
|
SecRule REQUEST_FILENAME "@streq /wp-admin/upload.php" \
|
|
"id:900711,phase:1,pass,nolog,ctl:ruleRemoveTargetById=932340;ARGS:_wp_http_referer"
|