Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
93.55% covered (success)
93.55%
29 / 31
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
RemoveProtectedFlagFromFilter
100.00% covered (success)
100.00%
29 / 29
100.00% covered (success)
100.00%
2 / 2
4
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 execute
100.00% covered (success)
100.00%
23 / 23
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3namespace MediaWiki\Extension\AbuseFilter\Maintenance;
4
5use MediaWiki\Extension\AbuseFilter\Filter\Flags;
6use MediaWiki\Maintenance\Maintenance;
7
8// @codeCoverageIgnoreStart
9$IP = getenv( 'MW_INSTALL_PATH' );
10if ( $IP === false ) {
11    $IP = __DIR__ . '/../../..';
12}
13require_once "$IP/maintenance/Maintenance.php";
14// @codeCoverageIgnoreEnd
15
16/**
17 * Maintenance script that allows an individual filter's privacy level to remove the
18 * "protected" flag from a filter, while keeping other privacy flags. This is for
19 * correcting filters that were mistakenly allowed to be protected (T378551).
20 *
21 * Before running this script, ensure that this filter does not use protected
22 * variables. Also ensure that removing the protected flag will not leak private
23 * data. (For example if the filter used protected variables in the past and was
24 * triggered, this could leak the data of the users who triggered it.)
25 *
26 * After running this script, make an edit in the "Notes" section of the affected
27 * filters, to explain that the script was run, and why.
28 *
29 * @ingroup Maintenance
30 * @since 1.44
31 */
32class RemoveProtectedFlagFromFilter extends Maintenance {
33    public function __construct() {
34        parent::__construct();
35
36        $this->addDescription(
37            'Remove the "protected" flag from a filter, while keeping other privacy flags'
38        );
39        $this->addArg( 'filter', 'ID of the protected filter to update' );
40        $this->requireExtension( 'Abuse Filter' );
41    }
42
43    /**
44     * @inheritDoc
45     */
46    public function execute() {
47        $filter = $this->getArg( 0 );
48
49        $privacyLevel = $this->getReplicaDB()->newSelectQueryBuilder()
50            ->select( 'af_hidden' )
51            ->from( 'abuse_filter' )
52            ->where( [
53                'af_id' => $filter
54            ] )
55            ->caller( __METHOD__ )
56            ->fetchField();
57
58        if ( $privacyLevel === false ) {
59            $this->fatalError( "Filter $filter not found.\n" );
60        }
61
62        if ( ( $privacyLevel & Flags::FILTER_USES_PROTECTED_VARS ) === 0 ) {
63            $this->output( "Filter $filter is not protected. Nothing to update.\n" );
64            return false;
65        }
66
67        // The new privacy level is the old level with the bit representing "protected" unset.
68        $newPrivacyLevel = (string)( $privacyLevel & ( ~Flags::FILTER_USES_PROTECTED_VARS ) );
69
70        $this->getPrimaryDB()->newUpdateQueryBuilder()
71            ->update( 'abuse_filter' )
72            ->set( [ 'af_hidden' => $newPrivacyLevel ] )
73            ->where( [ 'af_id' => $filter ] )
74            ->caller( __METHOD__ )
75            ->execute();
76
77        $this->output( "Successfully removed \"protected\" flag from filter $filter.\n" );
78        return true;
79    }
80}
81
82$maintClass = RemoveProtectedFlagFromFilter::class;
83require_once RUN_MAINTENANCE_IF_MAIN;