mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/AbuseFilter.git
synced 2024-11-27 15:30:42 +00:00
68ff668543
Bug: T269769 Change-Id: Ia41ecc2f8e6921ef3d5a16fec58202d584ad0727
450 lines
18 KiB
PHP
450 lines
18 KiB
PHP
<?php
|
|
|
|
namespace MediaWiki\Extension\AbuseFilter;
|
|
|
|
use MediaWiki\Extension\AbuseFilter\Hooks\AbuseFilterHookRunner;
|
|
|
|
/**
|
|
* This service can be used to manage the list of keywords recognized by the Parser
|
|
*/
|
|
class KeywordsManager {
|
|
public const SERVICE_NAME = 'AbuseFilterKeywordsManager';
|
|
|
|
/**
|
|
* Operators and functions that can be used in AbuseFilter code.
|
|
* They are shown in the dropdown in the filter editor.
|
|
* Keys of translatable messages with their descriptions are
|
|
* based on keys of this array.
|
|
* When editing this list or the messages, keep the order
|
|
* consistent in both lists.
|
|
*
|
|
* @var array
|
|
*/
|
|
private const BUILDER_VALUES = [
|
|
'op-arithmetic' => [
|
|
// Generates abusefilter-edit-builder-op-arithmetic-addition
|
|
'+' => 'addition',
|
|
// Generates abusefilter-edit-builder-op-arithmetic-subtraction
|
|
'-' => 'subtraction',
|
|
// Generates abusefilter-edit-builder-op-arithmetic-multiplication
|
|
'*' => 'multiplication',
|
|
// Generates abusefilter-edit-builder-op-arithmetic-divide
|
|
'/' => 'divide',
|
|
// Generates abusefilter-edit-builder-op-arithmetic-modulo
|
|
'%' => 'modulo',
|
|
// Generates abusefilter-edit-builder-op-arithmetic-pow
|
|
'**' => 'pow'
|
|
],
|
|
'op-comparison' => [
|
|
// Generates abusefilter-edit-builder-op-comparison-equal
|
|
'==' => 'equal',
|
|
// Generates abusefilter-edit-builder-op-comparison-equal-strict
|
|
'===' => 'equal-strict',
|
|
// Generates abusefilter-edit-builder-op-comparison-notequal
|
|
'!=' => 'notequal',
|
|
// Generates abusefilter-edit-builder-op-comparison-notequal-strict
|
|
'!==' => 'notequal-strict',
|
|
// Generates abusefilter-edit-builder-op-comparison-lt
|
|
'<' => 'lt',
|
|
// Generates abusefilter-edit-builder-op-comparison-gt
|
|
'>' => 'gt',
|
|
// Generates abusefilter-edit-builder-op-comparison-lte
|
|
'<=' => 'lte',
|
|
// Generates abusefilter-edit-builder-op-comparison-gte
|
|
'>=' => 'gte'
|
|
],
|
|
'op-bool' => [
|
|
// Generates abusefilter-edit-builder-op-bool-not
|
|
'!' => 'not',
|
|
// Generates abusefilter-edit-builder-op-bool-and
|
|
'&' => 'and',
|
|
// Generates abusefilter-edit-builder-op-bool-or
|
|
'|' => 'or',
|
|
// Generates abusefilter-edit-builder-op-bool-xor
|
|
'^' => 'xor'
|
|
],
|
|
'misc' => [
|
|
// Generates abusefilter-edit-builder-misc-in
|
|
'in' => 'in',
|
|
// Generates abusefilter-edit-builder-misc-contains
|
|
'contains' => 'contains',
|
|
// Generates abusefilter-edit-builder-misc-like
|
|
'like' => 'like',
|
|
// Generates abusefilter-edit-builder-misc-stringlit
|
|
'""' => 'stringlit',
|
|
// Generates abusefilter-edit-builder-misc-rlike
|
|
'rlike' => 'rlike',
|
|
// Generates abusefilter-edit-builder-misc-irlike
|
|
'irlike' => 'irlike',
|
|
// Generates abusefilter-edit-builder-misc-tern
|
|
'cond ? iftrue : iffalse' => 'tern',
|
|
// Generates abusefilter-edit-builder-misc-cond
|
|
'if cond then iftrue else iffalse end' => 'cond',
|
|
// Generates abusefilter-edit-builder-misc-cond-short
|
|
'if cond then iftrue end' => 'cond-short',
|
|
],
|
|
'funcs' => [
|
|
// Generates abusefilter-edit-builder-funcs-length
|
|
'length(string)' => 'length',
|
|
// Generates abusefilter-edit-builder-funcs-lcase
|
|
'lcase(string)' => 'lcase',
|
|
// Generates abusefilter-edit-builder-funcs-ucase
|
|
'ucase(string)' => 'ucase',
|
|
// Generates abusefilter-edit-builder-funcs-ccnorm
|
|
'ccnorm(string)' => 'ccnorm',
|
|
// Generates abusefilter-edit-builder-funcs-ccnorm-contains-any
|
|
'ccnorm_contains_any(haystack,needle1,needle2,..)' => 'ccnorm-contains-any',
|
|
// Generates abusefilter-edit-builder-funcs-ccnorm-contains-all
|
|
'ccnorm_contains_all(haystack,needle1,needle2,..)' => 'ccnorm-contains-all',
|
|
// Generates abusefilter-edit-builder-funcs-rmdoubles
|
|
'rmdoubles(string)' => 'rmdoubles',
|
|
// Generates abusefilter-edit-builder-funcs-specialratio
|
|
'specialratio(string)' => 'specialratio',
|
|
// Generates abusefilter-edit-builder-funcs-norm
|
|
'norm(string)' => 'norm',
|
|
// Generates abusefilter-edit-builder-funcs-count
|
|
'count(needle,haystack)' => 'count',
|
|
// Generates abusefilter-edit-builder-funcs-rcount
|
|
'rcount(needle,haystack)' => 'rcount',
|
|
// Generates abusefilter-edit-builder-funcs-get_matches
|
|
'get_matches(needle,haystack)' => 'get_matches',
|
|
// Generates abusefilter-edit-builder-funcs-rmwhitespace
|
|
'rmwhitespace(text)' => 'rmwhitespace',
|
|
// Generates abusefilter-edit-builder-funcs-rmspecials
|
|
'rmspecials(text)' => 'rmspecials',
|
|
// Generates abusefilter-edit-builder-funcs-ip_in_range
|
|
'ip_in_range(ip, range)' => 'ip_in_range',
|
|
// Generates abusefilter-edit-builder-funcs-ip_in_ranges
|
|
'ip_in_ranges(ip, range1, range2, ...)' => 'ip_in_ranges',
|
|
// Generates abusefilter-edit-builder-funcs-contains-any
|
|
'contains_any(haystack,needle1,needle2,...)' => 'contains-any',
|
|
// Generates abusefilter-edit-builder-funcs-contains-all
|
|
'contains_all(haystack,needle1,needle2,...)' => 'contains-all',
|
|
// Generates abusefilter-edit-builder-funcs-equals-to-any
|
|
'equals_to_any(haystack,needle1,needle2,...)' => 'equals-to-any',
|
|
// Generates abusefilter-edit-builder-funcs-substr
|
|
'substr(subject, offset, length)' => 'substr',
|
|
// Generates abusefilter-edit-builder-funcs-strpos
|
|
'strpos(haystack, needle)' => 'strpos',
|
|
// Generates abusefilter-edit-builder-funcs-str_replace
|
|
'str_replace(subject, search, replace)' => 'str_replace',
|
|
// Generates abusefilter-edit-builder-funcs-str_replace_regexp
|
|
'str_replace_regexp(subject, search, replace)' => 'str_replace_regexp',
|
|
// Generates abusefilter-edit-builder-funcs-rescape
|
|
'rescape(string)' => 'rescape',
|
|
// Generates abusefilter-edit-builder-funcs-set_var
|
|
'set_var(var,value)' => 'set_var',
|
|
// Generates abusefilter-edit-builder-funcs-sanitize
|
|
'sanitize(string)' => 'sanitize',
|
|
],
|
|
'vars' => [
|
|
// Generates abusefilter-edit-builder-vars-timestamp
|
|
'timestamp' => 'timestamp',
|
|
// Generates abusefilter-edit-builder-vars-accountname
|
|
'accountname' => 'accountname',
|
|
// Generates abusefilter-edit-builder-vars-action
|
|
'action' => 'action',
|
|
// Generates abusefilter-edit-builder-vars-addedlines
|
|
'added_lines' => 'addedlines',
|
|
// Generates abusefilter-edit-builder-vars-delta
|
|
'edit_delta' => 'delta',
|
|
// Generates abusefilter-edit-builder-vars-diff
|
|
'edit_diff' => 'diff',
|
|
// Generates abusefilter-edit-builder-vars-newsize
|
|
'new_size' => 'newsize',
|
|
// Generates abusefilter-edit-builder-vars-oldsize
|
|
'old_size' => 'oldsize',
|
|
// Generates abusefilter-edit-builder-vars-new-content-model
|
|
'new_content_model' => 'new-content-model',
|
|
// Generates abusefilter-edit-builder-vars-old-content-model
|
|
'old_content_model' => 'old-content-model',
|
|
// Generates abusefilter-edit-builder-vars-removedlines
|
|
'removed_lines' => 'removedlines',
|
|
// Generates abusefilter-edit-builder-vars-summary
|
|
'summary' => 'summary',
|
|
// Generates abusefilter-edit-builder-vars-page-id
|
|
'page_id' => 'page-id',
|
|
// Generates abusefilter-edit-builder-vars-page-ns
|
|
'page_namespace' => 'page-ns',
|
|
// Generates abusefilter-edit-builder-vars-page-title
|
|
'page_title' => 'page-title',
|
|
// Generates abusefilter-edit-builder-vars-page-prefixedtitle
|
|
'page_prefixedtitle' => 'page-prefixedtitle',
|
|
// Generates abusefilter-edit-builder-vars-page-age
|
|
'page_age' => 'page-age',
|
|
// Generates abusefilter-edit-builder-vars-page-last-edit-age
|
|
'page_last_edit_age' => 'page-last-edit-age',
|
|
// Generates abusefilter-edit-builder-vars-movedfrom-id
|
|
'moved_from_id' => 'movedfrom-id',
|
|
// Generates abusefilter-edit-builder-vars-movedfrom-ns
|
|
'moved_from_namespace' => 'movedfrom-ns',
|
|
// Generates abusefilter-edit-builder-vars-movedfrom-title
|
|
'moved_from_title' => 'movedfrom-title',
|
|
// Generates abusefilter-edit-builder-vars-movedfrom-prefixedtitle
|
|
'moved_from_prefixedtitle' => 'movedfrom-prefixedtitle',
|
|
// Generates abusefilter-edit-builder-vars-movedfrom-age
|
|
'moved_from_age' => 'movedfrom-age',
|
|
// Generates abusefilter-edit-builder-vars-movedfrom-last-edit-age
|
|
'moved_from_last_edit_age' => 'movedfrom-last-edit-age',
|
|
// Generates abusefilter-edit-builder-vars-movedto-id
|
|
'moved_to_id' => 'movedto-id',
|
|
// Generates abusefilter-edit-builder-vars-movedto-ns
|
|
'moved_to_namespace' => 'movedto-ns',
|
|
// Generates abusefilter-edit-builder-vars-movedto-title
|
|
'moved_to_title' => 'movedto-title',
|
|
// Generates abusefilter-edit-builder-vars-movedto-prefixedtitle
|
|
'moved_to_prefixedtitle' => 'movedto-prefixedtitle',
|
|
// Generates abusefilter-edit-builder-vars-movedto-age
|
|
'moved_to_age' => 'movedto-age',
|
|
// Generates abusefilter-edit-builder-vars-movedto-last-edit-age
|
|
'moved_to_last_edit_age' => 'movedto-last-edit-age',
|
|
// Generates abusefilter-edit-builder-vars-user-editcount
|
|
'user_editcount' => 'user-editcount',
|
|
// Generates abusefilter-edit-builder-vars-user-age
|
|
'user_age' => 'user-age',
|
|
// Generates abusefilter-edit-builder-vars-user-name
|
|
'user_name' => 'user-name',
|
|
// Generates abusefilter-edit-builder-vars-user-type
|
|
'user_type' => 'user-type',
|
|
// Generates abusefilter-edit-builder-vars-user-groups
|
|
'user_groups' => 'user-groups',
|
|
// Generates abusefilter-edit-builder-vars-user-rights
|
|
'user_rights' => 'user-rights',
|
|
// Generates abusefilter-edit-builder-vars-user-blocked
|
|
'user_blocked' => 'user-blocked',
|
|
// Generates abusefilter-edit-builder-vars-user-emailconfirm
|
|
'user_emailconfirm' => 'user-emailconfirm',
|
|
// Generates abusefilter-edit-builder-vars-old-wikitext
|
|
'old_wikitext' => 'old-wikitext',
|
|
// Generates abusefilter-edit-builder-vars-new-wikitext
|
|
'new_wikitext' => 'new-wikitext',
|
|
// Generates abusefilter-edit-builder-vars-added-links
|
|
'added_links' => 'added-links',
|
|
// Generates abusefilter-edit-builder-vars-removed-links
|
|
'removed_links' => 'removed-links',
|
|
// Generates abusefilter-edit-builder-vars-all-links
|
|
'all_links' => 'all-links',
|
|
// Generates abusefilter-edit-builder-vars-new-pst
|
|
'new_pst' => 'new-pst',
|
|
// Generates abusefilter-edit-builder-vars-diff-pst
|
|
'edit_diff_pst' => 'diff-pst',
|
|
// Generates abusefilter-edit-builder-vars-addedlines-pst
|
|
'added_lines_pst' => 'addedlines-pst',
|
|
// Generates abusefilter-edit-builder-vars-new-text
|
|
'new_text' => 'new-text',
|
|
// Generates abusefilter-edit-builder-vars-new-html
|
|
'new_html' => 'new-html',
|
|
// Generates abusefilter-edit-builder-vars-restrictions-edit
|
|
'page_restrictions_edit' => 'restrictions-edit',
|
|
// Generates abusefilter-edit-builder-vars-restrictions-move
|
|
'page_restrictions_move' => 'restrictions-move',
|
|
// Generates abusefilter-edit-builder-vars-restrictions-create
|
|
'page_restrictions_create' => 'restrictions-create',
|
|
// Generates abusefilter-edit-builder-vars-restrictions-upload
|
|
'page_restrictions_upload' => 'restrictions-upload',
|
|
// Generates abusefilter-edit-builder-vars-recent-contributors
|
|
'page_recent_contributors' => 'recent-contributors',
|
|
// Generates abusefilter-edit-builder-vars-first-contributor
|
|
'page_first_contributor' => 'first-contributor',
|
|
// Generates abusefilter-edit-builder-vars-movedfrom-restrictions-edit
|
|
'moved_from_restrictions_edit' => 'movedfrom-restrictions-edit',
|
|
// Generates abusefilter-edit-builder-vars-movedfrom-restrictions-move
|
|
'moved_from_restrictions_move' => 'movedfrom-restrictions-move',
|
|
// Generates abusefilter-edit-builder-vars-movedfrom-restrictions-create
|
|
'moved_from_restrictions_create' => 'movedfrom-restrictions-create',
|
|
// Generates abusefilter-edit-builder-vars-movedfrom-restrictions-upload
|
|
'moved_from_restrictions_upload' => 'movedfrom-restrictions-upload',
|
|
// Generates abusefilter-edit-builder-vars-movedfrom-recent-contributors
|
|
'moved_from_recent_contributors' => 'movedfrom-recent-contributors',
|
|
// Generates abusefilter-edit-builder-vars-movedfrom-first-contributor
|
|
'moved_from_first_contributor' => 'movedfrom-first-contributor',
|
|
// Generates abusefilter-edit-builder-vars-movedto-restrictions-edit
|
|
'moved_to_restrictions_edit' => 'movedto-restrictions-edit',
|
|
// Generates abusefilter-edit-builder-vars-movedto-restrictions-move
|
|
'moved_to_restrictions_move' => 'movedto-restrictions-move',
|
|
// Generates abusefilter-edit-builder-vars-movedto-restrictions-create
|
|
'moved_to_restrictions_create' => 'movedto-restrictions-create',
|
|
// Generates abusefilter-edit-builder-vars-movedto-restrictions-upload
|
|
'moved_to_restrictions_upload' => 'movedto-restrictions-upload',
|
|
// Generates abusefilter-edit-builder-vars-movedto-recent-contributors
|
|
'moved_to_recent_contributors' => 'movedto-recent-contributors',
|
|
// Generates abusefilter-edit-builder-vars-movedto-first-contributor
|
|
'moved_to_first_contributor' => 'movedto-first-contributor',
|
|
// Generates abusefilter-edit-builder-vars-old-links
|
|
'old_links' => 'old-links',
|
|
// Generates abusefilter-edit-builder-vars-file-sha1
|
|
'file_sha1' => 'file-sha1',
|
|
// Generates abusefilter-edit-builder-vars-file-size
|
|
'file_size' => 'file-size',
|
|
// Generates abusefilter-edit-builder-vars-file-mime
|
|
'file_mime' => 'file-mime',
|
|
// Generates abusefilter-edit-builder-vars-file-mediatype
|
|
'file_mediatype' => 'file-mediatype',
|
|
// Generates abusefilter-edit-builder-vars-file-width
|
|
'file_width' => 'file-width',
|
|
// Generates abusefilter-edit-builder-vars-file-height
|
|
'file_height' => 'file-height',
|
|
// Generates abusefilter-edit-builder-vars-file-bits-per-channel
|
|
'file_bits_per_channel' => 'file-bits-per-channel',
|
|
// Generates abusefilter-edit-builder-vars-wiki-name
|
|
'wiki_name' => 'wiki-name',
|
|
// Generates abusefilter-edit-builder-vars-wiki-language
|
|
'wiki_language' => 'wiki-language',
|
|
],
|
|
];
|
|
|
|
/**
|
|
* Old vars which aren't in use anymore.
|
|
* The translatable messages that are based
|
|
* on them are not shown in the filter editor,
|
|
* but may still be shown in the log descriptions of
|
|
* filter actions that were taken by filters
|
|
* that used them.
|
|
*
|
|
* @var array
|
|
*/
|
|
private const DISABLED_VARS = [
|
|
// Generates abusefilter-edit-builder-vars-old-text
|
|
'old_text' => 'old-text',
|
|
// Generates abusefilter-edit-builder-vars-old-html
|
|
'old_html' => 'old-html',
|
|
// Generates abusefilter-edit-builder-vars-minor-edit
|
|
'minor_edit' => 'minor-edit'
|
|
];
|
|
|
|
private const DEPRECATED_VARS = [
|
|
'article_text' => 'page_title',
|
|
'article_prefixedtext' => 'page_prefixedtitle',
|
|
'article_namespace' => 'page_namespace',
|
|
'article_articleid' => 'page_id',
|
|
'article_restrictions_edit' => 'page_restrictions_edit',
|
|
'article_restrictions_move' => 'page_restrictions_move',
|
|
'article_restrictions_create' => 'page_restrictions_create',
|
|
'article_restrictions_upload' => 'page_restrictions_upload',
|
|
'article_recent_contributors' => 'page_recent_contributors',
|
|
'article_first_contributor' => 'page_first_contributor',
|
|
'moved_from_text' => 'moved_from_title',
|
|
'moved_from_prefixedtext' => 'moved_from_prefixedtitle',
|
|
'moved_from_articleid' => 'moved_from_id',
|
|
'moved_to_text' => 'moved_to_title',
|
|
'moved_to_prefixedtext' => 'moved_to_prefixedtitle',
|
|
'moved_to_articleid' => 'moved_to_id',
|
|
];
|
|
|
|
/** @var string[][] Final list of builder values */
|
|
private $builderValues;
|
|
|
|
/** @var string[] Final list of deprecated vars */
|
|
private $deprecatedVars;
|
|
|
|
/** @var AbuseFilterHookRunner */
|
|
private $hookRunner;
|
|
|
|
/**
|
|
* @param AbuseFilterHookRunner $hookRunner
|
|
*/
|
|
public function __construct( AbuseFilterHookRunner $hookRunner ) {
|
|
$this->hookRunner = $hookRunner;
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
*/
|
|
public function getDisabledVariables(): array {
|
|
return self::DISABLED_VARS;
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
*/
|
|
public function getDeprecatedVariables(): array {
|
|
if ( $this->deprecatedVars === null ) {
|
|
$this->deprecatedVars = self::DEPRECATED_VARS;
|
|
$this->hookRunner->onAbuseFilter_deprecatedVariables( $this->deprecatedVars );
|
|
}
|
|
return $this->deprecatedVars;
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
*/
|
|
public function getBuilderValues(): array {
|
|
if ( $this->builderValues === null ) {
|
|
$this->builderValues = self::BUILDER_VALUES;
|
|
$this->hookRunner->onAbuseFilter_builder( $this->builderValues );
|
|
}
|
|
return $this->builderValues;
|
|
}
|
|
|
|
/**
|
|
* @param string $name
|
|
* @return bool
|
|
*/
|
|
public function isVarDisabled( string $name ): bool {
|
|
return array_key_exists( $name, self::DISABLED_VARS );
|
|
}
|
|
|
|
/**
|
|
* @param string $name
|
|
* @return bool
|
|
*/
|
|
public function isVarDeprecated( string $name ): bool {
|
|
return array_key_exists( $name, $this->getDeprecatedVariables() );
|
|
}
|
|
|
|
/**
|
|
* @param string $name
|
|
* @return bool
|
|
*/
|
|
public function isVarInUse( string $name ): bool {
|
|
return array_key_exists( $name, $this->getVarsMappings() );
|
|
}
|
|
|
|
/**
|
|
* Check whether the given name corresponds to a known variable.
|
|
* @param string $name
|
|
* @return bool
|
|
*/
|
|
public function varExists( string $name ): bool {
|
|
return $this->isVarInUse( $name ) ||
|
|
$this->isVarDisabled( $name ) ||
|
|
$this->isVarDeprecated( $name );
|
|
}
|
|
|
|
/**
|
|
* Get the message for a builtin variable; takes deprecated variables into account.
|
|
* Returns null for non-builtin variables.
|
|
*
|
|
* @param string $var
|
|
* @return string|null
|
|
*/
|
|
public function getMessageKeyForVar( string $var ): ?string {
|
|
if ( !$this->varExists( $var ) ) {
|
|
return null;
|
|
}
|
|
if ( $this->isVarDeprecated( $var ) ) {
|
|
$var = $this->getDeprecatedVariables()[$var];
|
|
}
|
|
|
|
$key = self::DISABLED_VARS[$var] ??
|
|
$this->getVarsMappings()[$var];
|
|
return "abusefilter-edit-builder-vars-$key";
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
*/
|
|
public function getVarsMappings(): array {
|
|
return $this->getBuilderValues()['vars'];
|
|
}
|
|
|
|
/**
|
|
* Get a list of core variables, i.e. variables defined in AbuseFilter (ignores hooks).
|
|
* You usually want to use getVarsMappings(), not this one.
|
|
* @return string[]
|
|
*/
|
|
public function getCoreVariables(): array {
|
|
return array_keys( self::BUILDER_VALUES['vars'] );
|
|
}
|
|
}
|