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
249 lines
16 KiB
Plaintext
249 lines
16 KiB
Plaintext
# Drupal's fixes: on Drupal's paths, some fields skip the rules that refused
|
|
# ordinary Drupal requests there. They are the fields where Drupal sends a
|
|
# search, a name, a file name or an address. 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. The
|
|
# exclusions above the comment "The fields below are in bodies" also cover a
|
|
# body field of the same name whatever the query holds, and only those below it
|
|
# need a query without that name.
|
|
#
|
|
# Each path may start with a language prefix, such as /fr in
|
|
# /fr/admin/content. The prefix is two or three letters, then optionally a
|
|
# hyphen and more letters, as in /pt-br.
|
|
#
|
|
# Four rules read a search or a name as a command or a system file:
|
|
# 930120: .htaccess
|
|
# 932120: powershell
|
|
# 932250: ls -la
|
|
# 932260: python3
|
|
# Each query exclusion below removes these four from its fields, with two
|
|
# exceptions. Rule 900806 removes only 930120 and 932120 from url. Two more
|
|
# rules misfire on destination, the address of a page of results, so
|
|
# destination alone skips them too:
|
|
# 932230: ls -la
|
|
# 932235: python3
|
|
|
|
# Unless SWW_APPS lists drupal for the request's hostname, every rule below is
|
|
# skipped. Inspect sets tx.app_drupal when it does.
|
|
# The skip reaches only phase 1, so a rule of a later phase must check
|
|
# tx.app_drupal itself.
|
|
SecRule &TX:app_drupal "@eq 0" \
|
|
"id:900790,phase:1,pass,nolog,skipAfter:END_DRUPAL"
|
|
|
|
# Every admin page's filters and autocompletes, and the page to return to:
|
|
# title: /admin/content?title=python3
|
|
# name: /admin/content/media?name=python3
|
|
# user: /admin/people?user=python3
|
|
# filename, filemime: /admin/content/files?filename=python3
|
|
# info: /admin/content/block?info=python3
|
|
# subject, author_name: /admin/content/comment?subject=python3
|
|
# search: /admin/config/search/path?search=python3
|
|
# q: /admin/views/ajax/autocomplete/tag?q=python3
|
|
# destination: /admin/content?destination=/admin/content%3Ftitle%3Dpython3
|
|
SecRule REQUEST_FILENAME "@rx ^(?:/[a-z]{2,3}(?:-[a-z]+)?)?/admin(?:/|$)" \
|
|
"id:900800,phase:1,pass,nolog,\
|
|
ctl:ruleRemoveTargetById=930120;ARGS:/^(?:title|name|user|filename|filemime|info|subject|author_name|search|q|destination)$/,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:/^(?:title|name|user|filename|filemime|info|subject|author_name|search|q|destination)$/,\
|
|
ctl:ruleRemoveTargetById=932230;ARGS:destination,\
|
|
ctl:ruleRemoveTargetById=932235;ARGS:destination,\
|
|
ctl:ruleRemoveTargetById=932250;ARGS:/^(?:title|name|user|filename|filemime|info|subject|author_name|search|q|destination)$/,\
|
|
ctl:ruleRemoveTargetById=932260;ARGS:/^(?:title|name|user|filename|filemime|info|subject|author_name|search|q|destination)$/"
|
|
|
|
# The page to return to, on the other pages Drupal links to with one, such as
|
|
# /node/1/edit?destination=/search/node%3Fkeys%3Dpython3,
|
|
# /node/2/revisions/3/revert?destination=/admin/content%3Ftitle%3Dpython3 and
|
|
# /big_pipe/no-js?destination=/admin/content%3Ftitle%3Dpython3.
|
|
SecRule REQUEST_FILENAME \
|
|
"@rx ^(?:/[a-z]{2,3}(?:-[a-z]+)?)?/(?:node/(?:add/[^/]+|[0-9]+/[a-z]+|[0-9]+/revisions/[0-9]+/(?:revert|delete))|comment/[0-9]+/[a-z]+|media/(?:add/[^/]+|[0-9]+/[a-z]+)|taxonomy/term/[0-9]+/[a-z]+|user/[^/]+(?:/[a-z]+)?|block/add/[^/]+|big_pipe/no-js)$" \
|
|
"id:900801,phase:1,pass,nolog,\
|
|
ctl:ruleRemoveTargetById=930120;ARGS:destination,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:destination,\
|
|
ctl:ruleRemoveTargetById=932230;ARGS:destination,\
|
|
ctl:ruleRemoveTargetById=932235;ARGS:destination,\
|
|
ctl:ruleRemoveTargetById=932250;ARGS:destination,\
|
|
ctl:ruleRemoveTargetById=932260;ARGS:destination"
|
|
|
|
# The text searched for on the search pages: /search/node?keys=python3.
|
|
SecRule REQUEST_FILENAME "@rx ^(?:/[a-z]{2,3}(?:-[a-z]+)?)?/search(?:/|$)" \
|
|
"id:900802,phase:1,pass,nolog,\
|
|
ctl:ruleRemoveTargetById=930120;ARGS:keys,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:keys,\
|
|
ctl:ruleRemoveTargetById=932250;ARGS:keys,\
|
|
ctl:ruleRemoveTargetById=932260;ARGS:keys"
|
|
|
|
# What is typed into a field that autocompletes a tag, a user, a link in the
|
|
# rich text editor or a block's category:
|
|
# /entity_reference_autocomplete/taxonomy_term/default/KEY?q=python3
|
|
# /ckeditor5/basic_html/entity-link-suggestions?q=python3
|
|
# /block-category/autocomplete?q=python3
|
|
SecRule REQUEST_FILENAME \
|
|
"@rx ^(?:/[a-z]{2,3}(?:-[a-z]+)?)?/(?:entity_reference_autocomplete/|ckeditor5/[^/]+/entity-link-suggestions$|block-category/autocomplete$)" \
|
|
"id:900803,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 values a JSON:API client filters by, in fields named after what they
|
|
# filter: /jsonapi/node/article?filter[title]=python3 and
|
|
# filter[f][condition][value]=python3.
|
|
SecRule REQUEST_FILENAME "@rx ^(?:/[a-z]{2,3}(?:-[a-z]+)?)?/jsonapi/" \
|
|
"id:900804,phase:1,pass,nolog,\
|
|
ctl:ruleRemoveTargetById=930120;ARGS:/^filter\[/,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:/^filter\[/,\
|
|
ctl:ruleRemoveTargetById=932250;ARGS:/^filter\[/,\
|
|
ctl:ruleRemoveTargetById=932260;ARGS:/^filter\[/"
|
|
|
|
# A name, in the query or a body: the account whose password is reset, and
|
|
# the media library's filter by name:
|
|
# /user/password?name=python3
|
|
# /media-library?media_library_opener_id=...&name=python3
|
|
# /views/ajax?view_name=media_library&name=python3
|
|
SecRule REQUEST_FILENAME \
|
|
"@rx ^(?:/[a-z]{2,3}(?:-[a-z]+)?)?/(?:user/password|media-library|views/ajax)$" \
|
|
"id:900805,phase:1,pass,nolog,\
|
|
ctl:ruleRemoveTargetById=930120;ARGS:name,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:name,\
|
|
ctl:ruleRemoveTargetById=932250;ARGS:name,\
|
|
ctl:ruleRemoveTargetById=932260;ARGS:name"
|
|
|
|
# The address of a remote video shown in a page:
|
|
# 930120: /media/oembed?url=https%3A%2F%2Fvimeo.com%2Fchannels%2F.htaccess
|
|
# 932120: /media/oembed?url=https%3A%2F%2Fvimeo.com%2Fchannels%2Fpowershell
|
|
SecRule REQUEST_FILENAME "@rx ^(?:/[a-z]{2,3}(?:-[a-z]+)?)?/media/oembed$" \
|
|
"id:900806,phase:1,pass,nolog,\
|
|
ctl:ruleRemoveTargetById=930120;ARGS:url,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:url"
|
|
|
|
# The fields below are in bodies. Saving content sends its title, summary, tags,
|
|
# address, revision note, comment subject, media name and image text, which
|
|
# skip the same four rules there. A title such as "python3, ls -la and .htaccess"
|
|
# refused saving a node.
|
|
SecRule REQUEST_FILENAME \
|
|
"@rx ^(?:/[a-z]{2,3}(?:-[a-z]+)?)?/(?:node/(?:add/[^/]+|[0-9]+/edit)|comment/(?:reply/[^/]+/[0-9]+/[^/]+(?:/[0-9]+)?|[0-9]+/edit)|media/(?:add/[^/]+|[0-9]+/edit))$" \
|
|
"id:900810,phase:1,pass,nolog,chain"
|
|
SecRule &ARGS_GET:/^(?:title|subject|name|body|revision_log|path|field_tags|field_)/ "@eq 0" \
|
|
"ctl:ruleRemoveTargetById=930120;ARGS:/^(?:(?:title|subject|name|revision_log|revision_log_message)\[0\]\[value\]|body\[0\]\[summary\]|path\[0\]\[alias\]|field_tags\[target_id\]|field_[a-z0-9_]+\[0\]\[(?:alt|title)\])$/,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:/^(?:(?:title|subject|name|revision_log|revision_log_message)\[0\]\[value\]|body\[0\]\[summary\]|path\[0\]\[alias\]|field_tags\[target_id\]|field_[a-z0-9_]+\[0\]\[(?:alt|title)\])$/,\
|
|
ctl:ruleRemoveTargetById=932250;ARGS:/^(?:(?:title|subject|name|revision_log|revision_log_message)\[0\]\[value\]|body\[0\]\[summary\]|path\[0\]\[alias\]|field_tags\[target_id\]|field_[a-z0-9_]+\[0\]\[(?:alt|title)\])$/,\
|
|
ctl:ruleRemoveTargetById=932260;ARGS:/^(?:(?:title|subject|name|revision_log|revision_log_message)\[0\]\[value\]|body\[0\]\[summary\]|path\[0\]\[alias\]|field_tags\[target_id\]|field_[a-z0-9_]+\[0\]\[(?:alt|title)\])$/"
|
|
|
|
# The rich text editor sends HTML in body and comment_body. Each of these rules
|
|
# refused saving a node or a comment whose HTML held an image and a code block
|
|
# with a shell script, an SQL query and JavaScript, so they skip those fields:
|
|
# 930120, 932120, 932125, 932140, 932160, 932230, 932235, 932250, 932260,
|
|
# 934100, 941160, 941390, 942170 and 942230.
|
|
SecRule REQUEST_FILENAME \
|
|
"@rx ^(?:/[a-z]{2,3}(?:-[a-z]+)?)?/(?:node/(?:add/[^/]+|[0-9]+/edit)|comment/(?:reply/[^/]+/[0-9]+/[^/]+(?:/[0-9]+)?|[0-9]+/edit))$" \
|
|
"id:900811,phase:1,pass,nolog,chain"
|
|
SecRule &ARGS_GET:/^(?:comment_)?body\[/ "@eq 0" \
|
|
"ctl:ruleRemoveTargetById=930120;ARGS:/^(?:comment_)?body\[0\]\[value\]$/,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:/^(?:comment_)?body\[0\]\[value\]$/,\
|
|
ctl:ruleRemoveTargetById=932125;ARGS:/^(?:comment_)?body\[0\]\[value\]$/,\
|
|
ctl:ruleRemoveTargetById=932140;ARGS:/^(?:comment_)?body\[0\]\[value\]$/,\
|
|
ctl:ruleRemoveTargetById=932160;ARGS:/^(?:comment_)?body\[0\]\[value\]$/,\
|
|
ctl:ruleRemoveTargetById=932230;ARGS:/^(?:comment_)?body\[0\]\[value\]$/,\
|
|
ctl:ruleRemoveTargetById=932235;ARGS:/^(?:comment_)?body\[0\]\[value\]$/,\
|
|
ctl:ruleRemoveTargetById=932250;ARGS:/^(?:comment_)?body\[0\]\[value\]$/,\
|
|
ctl:ruleRemoveTargetById=932260;ARGS:/^(?:comment_)?body\[0\]\[value\]$/,\
|
|
ctl:ruleRemoveTargetById=934100;ARGS:/^(?:comment_)?body\[0\]\[value\]$/,\
|
|
ctl:ruleRemoveTargetById=941160;ARGS:/^(?:comment_)?body\[0\]\[value\]$/,\
|
|
ctl:ruleRemoveTargetById=941390;ARGS:/^(?:comment_)?body\[0\]\[value\]$/,\
|
|
ctl:ruleRemoveTargetById=942170;ARGS:/^(?:comment_)?body\[0\]\[value\]$/,\
|
|
ctl:ruleRemoveTargetById=942230;ARGS:/^(?:comment_)?body\[0\]\[value\]$/"
|
|
|
|
# A JSON:API client saving a node or a comment sends the same HTML in the
|
|
# JSON field body.value or comment_body.value, which skips the rules above. A
|
|
# title, summary, subject, name, revision note, block description or tag
|
|
# description beside it skips 930120, 932120, 932250 and 932260. A tag named
|
|
# python3 refused saving it.
|
|
SecRule REQUEST_FILENAME "@rx ^(?:/[a-z]{2,3}(?:-[a-z]+)?)?/jsonapi/" \
|
|
"id:900812,phase:1,pass,nolog,chain"
|
|
SecRule &ARGS_GET:/^json\.data\.attributes\./ "@eq 0" \
|
|
"ctl:ruleRemoveTargetById=930120;ARGS:/^json\.data\.attributes\.(?:title|subject|name|revision_log|info|description\.value|body\.summary|(?:comment_)?body\.value)$/,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:/^json\.data\.attributes\.(?:title|subject|name|revision_log|info|description\.value|body\.summary|(?:comment_)?body\.value)$/,\
|
|
ctl:ruleRemoveTargetById=932250;ARGS:/^json\.data\.attributes\.(?:title|subject|name|revision_log|info|description\.value|body\.summary|(?:comment_)?body\.value)$/,\
|
|
ctl:ruleRemoveTargetById=932260;ARGS:/^json\.data\.attributes\.(?:title|subject|name|revision_log|info|description\.value|body\.summary|(?:comment_)?body\.value)$/,\
|
|
ctl:ruleRemoveTargetById=932230;ARGS:/^json\.data\.attributes\.(?:comment_)?body\.value$/,\
|
|
ctl:ruleRemoveTargetById=932235;ARGS:/^json\.data\.attributes\.(?:comment_)?body\.value$/,\
|
|
ctl:ruleRemoveTargetById=932125;ARGS:/^json\.data\.attributes\.(?:comment_)?body\.value$/,\
|
|
ctl:ruleRemoveTargetById=932140;ARGS:/^json\.data\.attributes\.(?:comment_)?body\.value$/,\
|
|
ctl:ruleRemoveTargetById=932160;ARGS:/^json\.data\.attributes\.(?:comment_)?body\.value$/,\
|
|
ctl:ruleRemoveTargetById=934100;ARGS:/^json\.data\.attributes\.(?:comment_)?body\.value$/,\
|
|
ctl:ruleRemoveTargetById=941160;ARGS:/^json\.data\.attributes\.(?:comment_)?body\.value$/,\
|
|
ctl:ruleRemoveTargetById=941390;ARGS:/^json\.data\.attributes\.(?:comment_)?body\.value$/,\
|
|
ctl:ruleRemoveTargetById=942170;ARGS:/^json\.data\.attributes\.(?:comment_)?body\.value$/,\
|
|
ctl:ruleRemoveTargetById=942230;ARGS:/^json\.data\.attributes\.(?:comment_)?body\.value$/"
|
|
|
|
# Signing in, in name and pass or the JSON fields of the same names. A
|
|
# password holds any characters:
|
|
# 930120: a password ending in /etc/hosts, or a name of .htaccess
|
|
# 932120: a password or a name of powershell
|
|
# 932250: a password or a name of ls -la
|
|
# 932260: a password or a name starting with python3
|
|
SecRule REQUEST_FILENAME "@rx ^(?:/[a-z]{2,3}(?:-[a-z]+)?)?/user/login$" \
|
|
"id:900813,phase:1,pass,nolog,chain"
|
|
SecRule &ARGS_GET:/^(?:json\.)?(?:name|pass)$/ "@eq 0" \
|
|
"ctl:ruleRemoveTargetById=930120;ARGS:/^(?:json\.)?(?:name|pass)$/,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:/^(?:json\.)?(?:name|pass)$/,\
|
|
ctl:ruleRemoveTargetById=932250;ARGS:/^(?:json\.)?(?:name|pass)$/,\
|
|
ctl:ruleRemoveTargetById=932260;ARGS:/^(?:json\.)?(?:name|pass)$/"
|
|
|
|
# A person's name and email address, when signing up, when editing an
|
|
# account and when commenting without an account. A name of python3 refused
|
|
# signing up, and python3@example.com refused saving an account. An address
|
|
# keeps 932250, which no recorded address set off.
|
|
SecRule REQUEST_FILENAME \
|
|
"@rx ^(?:/[a-z]{2,3}(?:-[a-z]+)?)?/(?:user/register|user/[0-9]+/edit|comment/reply/[^/]+/[0-9]+/[^/]+(?:/[0-9]+)?)$" \
|
|
"id:900814,phase:1,pass,nolog,chain"
|
|
SecRule &ARGS_GET:/^(?:name|mail)$/ "@eq 0" \
|
|
"ctl:ruleRemoveTargetById=930120;ARGS:/^(?:name|mail)$/,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:/^(?:name|mail)$/,\
|
|
ctl:ruleRemoveTargetById=932250;ARGS:name,\
|
|
ctl:ruleRemoveTargetById=932260;ARGS:/^(?:name|mail)$/"
|
|
|
|
# Saving a tag, a custom block or a contact message sends its name,
|
|
# description, block description, body, subject or message, which skip the
|
|
# same four rules. A tag named python3 refused saving it.
|
|
SecRule REQUEST_FILENAME \
|
|
"@rx ^(?:/[a-z]{2,3}(?:-[a-z]+)?)?/(?:admin/structure/taxonomy/manage/[^/]+/add|taxonomy/term/[0-9]+/edit|block/add/[^/]+|admin/content/block/[0-9]+|contact(?:/[^/]+)?|user/[0-9]+/contact)$" \
|
|
"id:900815,phase:1,pass,nolog,chain"
|
|
SecRule &ARGS_GET:/^(?:name|description|info|body|subject|message)\[/ "@eq 0" \
|
|
"ctl:ruleRemoveTargetById=930120;ARGS:/^(?:name|description|info|body|subject|message)\[0\]\[value\]$/,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:/^(?:name|description|info|body|subject|message)\[0\]\[value\]$/,\
|
|
ctl:ruleRemoveTargetById=932250;ARGS:/^(?:name|description|info|body|subject|message)\[0\]\[value\]$/,\
|
|
ctl:ruleRemoveTargetById=932260;ARGS:/^(?:name|description|info|body|subject|message)\[0\]\[value\]$/"
|
|
|
|
# A client saving a node in Drupal's JSON format sends its title in the JSON
|
|
# field title.0.value: PATCH /node/2?_format=json with a title of python3.
|
|
SecRule REQUEST_FILENAME "@rx ^(?:/[a-z]{2,3}(?:-[a-z]+)?)?/node/[0-9]+$" \
|
|
"id:900816,phase:1,pass,nolog,chain"
|
|
SecRule &ARGS_GET:/^json\.title\./ "@eq 0" \
|
|
"ctl:ruleRemoveTargetById=930120;ARGS:json.title.0.value,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:json.title.0.value,\
|
|
ctl:ruleRemoveTargetById=932250;ARGS:json.title.0.value,\
|
|
ctl:ruleRemoveTargetById=932260;ARGS:json.title.0.value"
|
|
|
|
# A client resetting a password in Drupal's JSON format sends the account in
|
|
# the JSON field name: POST /user/password?_format=json with a name of python3.
|
|
SecRule REQUEST_FILENAME "@rx ^(?:/[a-z]{2,3}(?:-[a-z]+)?)?/user/password$" \
|
|
"id:900817,phase:1,pass,nolog,chain"
|
|
SecRule &ARGS_GET:json.name "@eq 0" \
|
|
"ctl:ruleRemoveTargetById=930120;ARGS:json.name,\
|
|
ctl:ruleRemoveTargetById=932120;ARGS:json.name,\
|
|
ctl:ruleRemoveTargetById=932250;ARGS:json.name,\
|
|
ctl:ruleRemoveTargetById=932260;ARGS:json.name"
|
|
|
|
# The same reset may name the account by its address in the JSON field mail:
|
|
# POST /user/password?_format=json with a mail of python3@example.com. An
|
|
# address skips only the two rules the recorded addresses set off.
|
|
SecRule REQUEST_FILENAME "@rx ^(?:/[a-z]{2,3}(?:-[a-z]+)?)?/user/password$" \
|
|
"id:900818,phase:1,pass,nolog,chain"
|
|
SecRule &ARGS_GET:json.mail "@eq 0" \
|
|
"ctl:ruleRemoveTargetById=932120;ARGS:json.mail,\
|
|
ctl:ruleRemoveTargetById=932260;ARGS:json.mail"
|
|
|
|
SecMarker END_DRUPAL
|