Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
95.83% |
46 / 48 |
|
50.00% |
2 / 4 |
CRAP | |
0.00% |
0 / 1 |
AbuseFilterChecker | |
95.83% |
46 / 48 |
|
50.00% |
2 / 4 |
11 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 | |||
checkTitleForUser | |
90.00% |
9 / 10 |
|
0.00% |
0 / 1 |
2.00 | |||
checkSectionForTitleAndUser | |
93.75% |
15 / 16 |
|
0.00% |
0 / 1 |
4.00 | |||
getResults | |
100.00% |
16 / 16 |
|
100.00% |
1 / 1 |
4 |
1 | <?php |
2 | declare( strict_types = 1 ); |
3 | |
4 | namespace ContentTranslation; |
5 | |
6 | use MediaWiki\Extension\AbuseFilter\Consequences\ConsequencesLookup; |
7 | use MediaWiki\Extension\AbuseFilter\FilterLookup; |
8 | use MediaWiki\Extension\AbuseFilter\FilterRunnerFactory; |
9 | use MediaWiki\Extension\AbuseFilter\GlobalNameUtils; |
10 | use MediaWiki\Extension\AbuseFilter\VariableGenerator\VariableGeneratorFactory; |
11 | use MediaWiki\Extension\AbuseFilter\Variables\VariableHolder; |
12 | use MediaWiki\Page\WikiPageFactory; |
13 | use MediaWiki\Title\Title; |
14 | use MediaWiki\User\User; |
15 | |
16 | /** |
17 | * Utility class for checking AbuseFilter rules before publishing. |
18 | * |
19 | * The results from these methods return an array, where the keys are |
20 | * the public names of the rules, and values are arrays consisting of |
21 | * different actions the rules would cause. Those can be tag, warn, |
22 | * disallow and others. Example: |
23 | * @code |
24 | * array = [ |
25 | * 'rule1' => [ |
26 | * 'warn' => [ |
27 | * 'action' => 'warn', |
28 | * 'parameters' => [ 'abusefilter-warning' ] |
29 | * ] |
30 | * ] |
31 | * ]; |
32 | * @endcode |
33 | * |
34 | * With type 'warn' there is also warning_html for html warning message. |
35 | * |
36 | * @copyright See AUTHORS.txt |
37 | * @license GPL-2.0-or-later |
38 | */ |
39 | class AbuseFilterChecker { |
40 | private ?VariableGeneratorFactory $variableGeneratorFactory; |
41 | |
42 | private bool $isAbuseFilterExtensionLoaded; |
43 | |
44 | private WikiPageFactory $wikiPageFactory; |
45 | |
46 | private ?ConsequencesLookup $consequencesLookup; |
47 | |
48 | private ?FilterLookup $filterLookup; |
49 | |
50 | private ?FilterRunnerFactory $filterRunnerFactory; |
51 | |
52 | public function __construct( |
53 | $isAbuseFilterExtensionLoaded, |
54 | WikiPageFactory $wikiPageFactory, |
55 | ?VariableGeneratorFactory $variableGeneratorFactory, |
56 | ?ConsequencesLookup $consequencesLookup, |
57 | ?FilterLookup $filterLookup, |
58 | ?FilterRunnerFactory $filterRunnerFactory |
59 | ) { |
60 | $this->isAbuseFilterExtensionLoaded = $isAbuseFilterExtensionLoaded; |
61 | $this->wikiPageFactory = $wikiPageFactory; |
62 | $this->variableGeneratorFactory = $variableGeneratorFactory; |
63 | $this->consequencesLookup = $consequencesLookup; |
64 | $this->filterLookup = $filterLookup; |
65 | $this->filterRunnerFactory = $filterRunnerFactory; |
66 | } |
67 | |
68 | /** |
69 | * Check a title for any rule violations. |
70 | * |
71 | * @param Title $title Title to check |
72 | * @param User $user User performing the action |
73 | * |
74 | * @return array List of any rule violations |
75 | */ |
76 | public function checkTitleForUser( Title $title, User $user ): array { |
77 | if ( !$this->isAbuseFilterExtensionLoaded ) { |
78 | return []; |
79 | } |
80 | |
81 | $gen = $this->variableGeneratorFactory->newGenerator(); |
82 | $vars = $gen |
83 | ->addUserVars( $user ) |
84 | ->addTitleVars( $title, 'page' ) |
85 | ->addGenericVars() |
86 | ->getVariableHolder(); |
87 | $vars->setVar( 'action', 'edit' ); |
88 | |
89 | return $this->getResults( $user, $title, $vars ); |
90 | } |
91 | |
92 | /** |
93 | * Check some text for rule violations. |
94 | * |
95 | * @param User $user |
96 | * @param Title $title |
97 | * @param string|null $text Text to check |
98 | * @return array List of any rule violations |
99 | */ |
100 | public function checkSectionForTitleAndUser( User $user, Title $title, ?string $text ): array { |
101 | if ( !$this->isAbuseFilterExtensionLoaded ) { |
102 | return []; |
103 | } |
104 | |
105 | if ( $text === null || mb_strlen( $text ) < 150 ) { |
106 | // Don't validate sections that are too short. The validations |
107 | // happen while editing is going on. |
108 | return []; |
109 | } |
110 | |
111 | // Add AbuseFilter variables. Note that we are adding the title |
112 | // here. That will cause filters about titles executed for every section. |
113 | // But not passing title will cause content filters with namespace rules |
114 | // not to produce results. We will attempt to filter out title errors |
115 | // away with array_diff_key. |
116 | $gen = $this->variableGeneratorFactory->newGenerator(); |
117 | $vars = $gen |
118 | ->addUserVars( $user ) |
119 | ->addTitleVars( $title, 'page' ) |
120 | ->addEditVars( $this->wikiPageFactory->newFromTitle( $title ), $user ) |
121 | ->addGenericVars() |
122 | ->getVariableHolder(); |
123 | $vars->setVar( 'action', 'edit' ); |
124 | $vars->setVar( 'old_wikitext', '' ); |
125 | $vars->setVar( 'new_wikitext', $text ); |
126 | |
127 | $results = $this->getResults( $user, $title, $vars ); |
128 | return array_diff_key( $results, $this->checkTitleForUser( $title, $user ) ); |
129 | } |
130 | |
131 | private function getResults( User $user, Title $title, VariableHolder $vars ): array { |
132 | static $seriousActions = [ 'warn', 'block', 'disallow', 'degroup' ]; |
133 | |
134 | $runner = $this->filterRunnerFactory->newRunner( $user, $title, $vars, 'default' ); |
135 | $filters = $runner->checkAllFilters(); |
136 | |
137 | $filters = array_keys( array_filter( $filters ) ); |
138 | $actions = $this->consequencesLookup->getConsequencesForFilters( $filters ); |
139 | |
140 | $results = []; |
141 | foreach ( $actions as $key => $val ) { |
142 | [ $filterID, $isGlobal ] = GlobalNameUtils::splitGlobalName( $key ); |
143 | $rulename = $this->filterLookup->getFilter( $filterID, $isGlobal )->getName(); |
144 | |
145 | // No point alerting the user about non-serious actions. T136596 |
146 | $actionsForRule = array_keys( $val ); |
147 | if ( array_intersect( $seriousActions, $actionsForRule ) === [] ) { |
148 | continue; |
149 | } |
150 | |
151 | if ( isset( $val['warn'][0] ) ) { |
152 | $val['warn']['messageHtml'] = wfMessage( $val['warn'][0] )->params( $rulename )->parse(); |
153 | } |
154 | |
155 | $results[$key] = $val; |
156 | } |
157 | |
158 | return $results; |
159 | } |
160 | } |