Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
66.67% |
42 / 63 |
|
50.00% |
1 / 2 |
CRAP | |
0.00% |
0 / 1 |
CheckMatch | |
66.67% |
42 / 63 |
|
50.00% |
1 / 2 |
30.00 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
execute | |
63.79% |
37 / 58 |
|
0.00% |
0 / 1 |
25.68 | |||
getAllowedParams | n/a |
0 / 0 |
n/a |
0 / 0 |
1 | |||||
getExamplesMessages | n/a |
0 / 0 |
n/a |
0 / 0 |
1 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\AbuseFilter\Api; |
4 | |
5 | use LogEventsList; |
6 | use LogicException; |
7 | use LogPage; |
8 | use MediaWiki\Api\ApiBase; |
9 | use MediaWiki\Api\ApiMain; |
10 | use MediaWiki\Api\ApiResult; |
11 | use MediaWiki\Extension\AbuseFilter\AbuseFilterPermissionManager; |
12 | use MediaWiki\Extension\AbuseFilter\AbuseFilterServices; |
13 | use MediaWiki\Extension\AbuseFilter\Parser\RuleCheckerFactory; |
14 | use MediaWiki\Extension\AbuseFilter\Special\SpecialAbuseLog; |
15 | use MediaWiki\Extension\AbuseFilter\VariableGenerator\VariableGeneratorFactory; |
16 | use MediaWiki\Extension\AbuseFilter\Variables\VariableHolder; |
17 | use MediaWiki\Extension\AbuseFilter\Variables\VariablesBlobStore; |
18 | use MediaWiki\Json\FormatJson; |
19 | use MediaWiki\Revision\RevisionRecord; |
20 | use RecentChange; |
21 | use Wikimedia\ParamValidator\ParamValidator; |
22 | |
23 | class CheckMatch extends ApiBase { |
24 | |
25 | private RuleCheckerFactory $ruleCheckerFactory; |
26 | private AbuseFilterPermissionManager $afPermManager; |
27 | private VariablesBlobStore $afVariablesBlobStore; |
28 | private VariableGeneratorFactory $afVariableGeneratorFactory; |
29 | |
30 | public function __construct( |
31 | ApiMain $main, |
32 | string $action, |
33 | RuleCheckerFactory $ruleCheckerFactory, |
34 | AbuseFilterPermissionManager $afPermManager, |
35 | VariablesBlobStore $afVariablesBlobStore, |
36 | VariableGeneratorFactory $afVariableGeneratorFactory |
37 | ) { |
38 | parent::__construct( $main, $action ); |
39 | $this->ruleCheckerFactory = $ruleCheckerFactory; |
40 | $this->afPermManager = $afPermManager; |
41 | $this->afVariablesBlobStore = $afVariablesBlobStore; |
42 | $this->afVariableGeneratorFactory = $afVariableGeneratorFactory; |
43 | } |
44 | |
45 | /** |
46 | * @inheritDoc |
47 | */ |
48 | public function execute() { |
49 | $performer = $this->getAuthority(); |
50 | $params = $this->extractRequestParams(); |
51 | $this->requireOnlyOneParameter( $params, 'vars', 'rcid', 'logid' ); |
52 | |
53 | // "Anti-DoS" |
54 | if ( !$this->afPermManager->canUseTestTools( $performer ) ) { |
55 | $this->dieWithError( 'apierror-abusefilter-canttest', 'permissiondenied' ); |
56 | } |
57 | |
58 | $vars = null; |
59 | if ( $params['vars'] ) { |
60 | $pairs = FormatJson::decode( $params['vars'], true ); |
61 | $vars = VariableHolder::newFromArray( $pairs ); |
62 | } elseif ( $params['rcid'] ) { |
63 | $rc = RecentChange::newFromId( $params['rcid'] ); |
64 | |
65 | if ( !$rc ) { |
66 | $this->dieWithError( [ 'apierror-nosuchrcid', $params['rcid'] ] ); |
67 | } |
68 | |
69 | $type = (int)$rc->getAttribute( 'rc_type' ); |
70 | $deletedValue = $rc->getAttribute( 'rc_deleted' ); |
71 | if ( |
72 | ( |
73 | $type === RC_LOG && |
74 | !LogEventsList::userCanBitfield( |
75 | $deletedValue, |
76 | LogPage::SUPPRESSED_ACTION | LogPage::SUPPRESSED_USER, |
77 | $performer |
78 | ) |
79 | ) || ( |
80 | $type !== RC_LOG && |
81 | !RevisionRecord::userCanBitfield( $deletedValue, RevisionRecord::SUPPRESSED_ALL, $performer ) |
82 | ) |
83 | ) { |
84 | // T223654 - Same check as in AbuseFilterChangesList |
85 | $this->dieWithError( 'apierror-permissiondenied-generic', 'deletedrc' ); |
86 | } |
87 | |
88 | $varGenerator = $this->afVariableGeneratorFactory->newRCGenerator( $rc, $this->getUser() ); |
89 | $vars = $varGenerator->getVars(); |
90 | } elseif ( $params['logid'] ) { |
91 | $row = $this->getDB()->newSelectQueryBuilder() |
92 | ->select( '*' ) |
93 | ->from( 'abuse_filter_log' ) |
94 | ->where( [ 'afl_id' => $params['logid'] ] ) |
95 | ->caller( __METHOD__ ) |
96 | ->fetchRow(); |
97 | |
98 | if ( !$row ) { |
99 | $this->dieWithError( [ 'apierror-abusefilter-nosuchlogid', $params['logid'] ], 'nosuchlogid' ); |
100 | } |
101 | |
102 | // TODO: Replace with dependency injection once security patch is uploaded publicly. |
103 | $afFilterLookup = AbuseFilterServices::getFilterLookup(); |
104 | $privacyLevel = $afFilterLookup->getFilter( $row->afl_filter_id, $row->afl_global ) |
105 | ->getPrivacyLevel(); |
106 | $canSeeDetails = $this->afPermManager->canSeeLogDetailsForFilter( $performer, $privacyLevel ); |
107 | if ( !$canSeeDetails ) { |
108 | $this->dieWithError( 'apierror-permissiondenied-generic', 'cannotseedetails' ); |
109 | } |
110 | |
111 | $visibility = SpecialAbuseLog::getEntryVisibilityForUser( $row, $performer, $this->afPermManager ); |
112 | if ( $visibility !== SpecialAbuseLog::VISIBILITY_VISIBLE ) { |
113 | // T223654 - Same check as in SpecialAbuseLog. Both the visibility of the AbuseLog entry |
114 | // and the corresponding revision are checked. |
115 | $this->dieWithError( 'apierror-permissiondenied-generic', 'deletedabuselog' ); |
116 | } |
117 | |
118 | $vars = $this->afVariablesBlobStore->loadVarDump( $row ); |
119 | } |
120 | if ( $vars === null ) { |
121 | // @codeCoverageIgnoreStart |
122 | throw new LogicException( 'Impossible.' ); |
123 | // @codeCoverageIgnoreEnd |
124 | } |
125 | |
126 | $ruleChecker = $this->ruleCheckerFactory->newRuleChecker( $vars ); |
127 | if ( !$ruleChecker->checkSyntax( $params['filter'] )->isValid() ) { |
128 | $this->dieWithError( 'apierror-abusefilter-badsyntax', 'badsyntax' ); |
129 | } |
130 | |
131 | $result = [ |
132 | ApiResult::META_BC_BOOLS => [ 'result' ], |
133 | 'result' => $ruleChecker->checkConditions( $params['filter'] )->getResult(), |
134 | ]; |
135 | |
136 | $this->getResult()->addValue( |
137 | null, |
138 | $this->getModuleName(), |
139 | $result |
140 | ); |
141 | } |
142 | |
143 | /** |
144 | * @codeCoverageIgnore Merely declarative |
145 | * @inheritDoc |
146 | */ |
147 | public function getAllowedParams() { |
148 | return [ |
149 | 'filter' => [ |
150 | ParamValidator::PARAM_REQUIRED => true, |
151 | ], |
152 | 'vars' => null, |
153 | 'rcid' => [ |
154 | ParamValidator::PARAM_TYPE => 'integer' |
155 | ], |
156 | 'logid' => [ |
157 | ParamValidator::PARAM_TYPE => 'integer' |
158 | ], |
159 | ]; |
160 | } |
161 | |
162 | /** |
163 | * @codeCoverageIgnore Merely declarative |
164 | * @inheritDoc |
165 | */ |
166 | protected function getExamplesMessages() { |
167 | return [ |
168 | 'action=abusefiltercheckmatch&filter=!("autoconfirmed"%20in%20user_groups)&rcid=15' |
169 | => 'apihelp-abusefiltercheckmatch-example-1', |
170 | ]; |
171 | } |
172 | } |