variablesManager = $variablesManager; $this->blockedDomainStorage = $blockedDomainStorage; } /** * @param VariableHolder $vars variables by the action * @param User $user User that tried to add the domain, used for logging * @param Title $title Title of the page that was attempted on, used for logging * @return Status Error status if it's a match, good status if not */ public function filter( VariableHolder $vars, $user, $title ) { global $wgAbuseFilterEnableBlockedExternalDomain; $status = Status::newGood(); if ( !$wgAbuseFilterEnableBlockedExternalDomain ) { return $status; } try { $urls = $this->variablesManager->getVar( $vars, 'added_links', VariablesManager::GET_STRICT ); } catch ( UnsetVariableException $_ ) { return $status; } $addedDomains = []; foreach ( $urls->toArray() as $addedUrl ) { $parsedHost = parse_url( (string)$addedUrl->getData(), PHP_URL_HOST ); if ( !is_string( $parsedHost ) ) { continue; } // Given that we block subdomains of blocked domains too // pretend that all of higher-level domains are added as well // so for foo.bar.com, you will have three domains to check: // foo.bar.com, bar.com, and com // This saves string search in the large list of blocked domains // making it much faster. $domainString = ''; $domainPieces = array_reverse( explode( '.', strtolower( $parsedHost ) ) ); foreach ( $domainPieces as $domainPiece ) { if ( !$domainString ) { $domainString = $domainPiece; } else { $domainString = $domainPiece . '.' . $domainString; } // It should be a map, benchmark at https://phabricator.wikimedia.org/P48956 $addedDomains[$domainString] = true; } } if ( !$addedDomains ) { return $status; } $blockedDomains = $this->blockedDomainStorage->loadComputed(); $blockedDomainsAdded = array_intersect_key( $addedDomains, $blockedDomains ); if ( !$blockedDomainsAdded ) { return $status; } $blockedDomainsAdded = array_keys( $blockedDomainsAdded ); $error = Message::newFromSpecifier( 'abusefilter-blocked-domains-attempted' ); $error->params( Message::listParam( $blockedDomainsAdded ) ); $status->fatal( $error ); $this->logFilterHit( $user, $title, implode( ' ', $blockedDomainsAdded ) ); return $status; } /** * Logs the filter hit to Special:Log * * @param User $user * @param Title $title * @param string $blockedDomain The blocked domain the user attempted to add */ private function logFilterHit( User $user, $title, $blockedDomain ) { $logEntry = new ManualLogEntry( 'abusefilterblockeddomainhit', 'hit' ); $logEntry->setPerformer( $user ); $logEntry->setTarget( $title ); $logEntry->setParameters( [ '4::blocked' => $blockedDomain ] ); $logid = $logEntry->insert(); $log = new LogPage( 'abusefilterblockeddomainhit' ); if ( $log->isRestricted() ) { // Make sure checkusers can see this action if the log is restricted // (which is the default) if ( ExtensionRegistry::getInstance()->isLoaded( 'CheckUser' ) ) { $rc = $logEntry->getRecentChange( $logid ); CUHooks::updateCheckUserData( $rc ); } } else { // If the log is unrestricted, publish normally to RC, // which will also update checkuser $logEntry->publish( $logid, "rc" ); } } }