mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/AbuseFilter.git
synced 2024-11-24 06:03:49 +00:00
85022190e5
Why: * An AbuseFilter variable is needed that allows filters to determine what type the current user is. That is, whether the user is an IP address, temporary account, named user or external user. * Currently filters implement this by inspecting the value in the 'user_name' variable, but this is likely to break when temporary accounts are enabled as IPs would be hidden. * Giving a dedicated variable that indicates the type of the user allows filters to work out this information without having to know the specific username of the user before performing the check. What: * Add the 'user_type' variable which is lazily computed. It can have the value 'named', 'temp', 'ip' or 'external' depending on the type of the user. If the user does not match any of these, then the value is 'unknown'. * Replace call to deprecated User::newFromIdentity with a use of the UserFactory service that is dependency injected. * Add and update tests to ensure consistent test coverage. Bug: T357615 Change-Id: Ifffa891879e7e49d2430a0330116b34c5a03049d
422 lines
16 KiB
PHP
422 lines
16 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';
|
|
|
|
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-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-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-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',
|
|
],
|
|
];
|
|
|
|
/** @var array Old vars which aren't in use anymore */
|
|
private const DISABLED_VARS = [
|
|
'old_text' => 'old-text',
|
|
'old_html' => 'old-html',
|
|
'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'] );
|
|
}
|
|
}
|