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