Add rangeblocks, a bit of a nuclear option for the abuse filter. Not intended for Wikimedia use

This commit is contained in:
Andrew Garrett 2008-09-18 13:01:50 +00:00
parent e2af4ae133
commit 74097d106e
4 changed files with 66 additions and 18 deletions

View file

@ -9,6 +9,7 @@ class AbuseFilter {
public static $modifyCache = array();
public static $condLimitEnabled = true;
public static $condCount = 0;
public static $filters = array();
public static function generateUserVars( $user ) {
$vars = array();
@ -130,6 +131,7 @@ class AbuseFilter {
$filter_matched = array();
while ( $row = $dbr->fetchObject( $res ) ) {
self::$filters[$row->af_id] = $row;
if ( self::checkConditions( $row->af_pattern, $vars ) ) {
$blocking_filters[$row->af_id] = $row;
@ -219,7 +221,7 @@ class AbuseFilter {
case 'warn':
wfLoadExtensionMessages( 'AbuseFilter' );
if (!$_SESSION['abusefilter-warned']) {
if (!isset($_SESSION['abusefilter-warned']) || !$_SESSION['abusefilter-warned']) {
$_SESSION['abusefilter-warned'] = true;
// Threaten them a little bit
@ -259,12 +261,11 @@ class AbuseFilter {
$block = new Block;
$block->mAddress = $wgUser->getName();
$block->mUser = $wgUser->getId();
$block->mBy = User::idFromName( wfMsgForContent( 'abusefilter-blocker' ) ); // Let's say the site owner blocked them
$block->mByName = wfMsgForContent( 'abusefilter-blocker' );
$block->mBy = $filterUser->getId();
$block->mByName = $filterUser->getName();
$block->mReason = wfMsgForContent( 'abusefilter-blockreason', $rule_desc );
$block->mTimestamp = wfTimestampNow();
$block->mEnableAutoblock = 1;
$block->mAngryAutoblock = 1; // Block lots of IPs
$block->mAnonOnly = 1;
$block->mCreateAccount = 1;
$block->mExpiry = 'infinity';
@ -282,6 +283,47 @@ class AbuseFilter {
$display .= wfMsgNoTrans( 'abusefilter-blocked-display', $rule_desc ) ."<br />\n";
break;
case 'rangeblock':
wfLoadExtensionMessages( 'AbuseFilter' );
global $wgUser;
$filterUser = AbuseFilter::getFilterUser();
$range = IP::toHex( wfGetIP() );
$range = substr( $range, 0, 4 ) . '0000';
$range = long2ip( hexdec( $range ) );
$range .= "/16";
$range = Block::normaliseRange( $range );
// Create a block.
$block = new Block;
$block->mAddress = $range;
$block->mUser = 0;
$block->mBy = $filterUser->getId();
$block->mByName = $filterUser->getName();
$block->mReason = wfMsgForContent( 'abusefilter-blockreason', $rule_desc );
$block->mTimestamp = wfTimestampNow();
$block->mAnonOnly = 0;
$block->mCreateAccount = 1;
$block->mExpiry = Block::parseExpiryInput( '1 week' );
$block->initialiseRange();
$block->insert();
// Log it
# Prepare log parameters
$logParams = array();
$logParams[] = 'indefinite';
$logParams[] = 'nocreate, angry-autoblock';
$log = new LogPage( 'block' );
$log->addEntry( 'block', SpecialPage::getTitleFor( 'Contributions', $range ),
wfMsgForContent( 'abusefilter-blockreason', $rule_desc ), $logParams, self::getFilterUser() );
$display .= wfMsgNoTrans( 'abusefilter-blocked-display', $rule_desc ) ."<br />\n";
break;
case 'throttle':
$throttleId = array_shift( $parameters );
list( $rateCount, $ratePeriod ) = explode( ',', array_shift( $parameters ) );
@ -466,7 +508,7 @@ class AbuseFilter {
$anyMatch = false;
global $wgAbuseFilterEmergencyDisableThreshold, $wgAbuseFilterEmergencyDisableCount;
global $wgAbuseFilterEmergencyDisableThreshold, $wgAbuseFilterEmergencyDisableCount, $wgAbuseFilterEmergencyDisableAge;
foreach( $filters as $filter => $matched ) {
if ($matched) {
@ -479,7 +521,10 @@ class AbuseFilter {
$wgMemc->set( self::filterMatchesKey( $filter ), 1, self::$statsStoragePeriod );
}
if ($match_count > $wgAbuseFilterEmergencyDisableCount && ($match_count / $total) > $wgAbuseFilterEmergencyDisableThreshold) {
$filter_age = wfTimestamp( TS_UNIX, self::$filters[$filter]['af_timestamp'] );
$throttle_exempt_time = $filter_age + $wgAbuseFilterEmergencyDisableAge;
if ($throttle_exempt_time > time() && $match_count > $wgAbuseFilterEmergencyDisableCount && ($match_count / $total) > $wgAbuseFilterEmergencyDisableThreshold) {
// More than X matches, constituting more than Y% of last Z edits. Disable it.
$dbw = wfGetDB( DB_MASTER );
$dbw->update( 'abuse_filter', array( 'af_enabled' => 0, 'af_throttled' => 1 ), array( 'af_id' => $filter ), __METHOD__ );

View file

@ -42,7 +42,7 @@ In addition, as a security measure, some privileges routinely granted to establi
A brief description of the abuse rule which your action matched is: $1",
'abusefilter-blocker' => 'Abuse filter',
'abusefilter-blockreason' => 'Automatically blocked by abuse filter. Rule description: $1',
'abusefilter-blockreason' => 'Automatically blocked by abuse filter. Description of matched rule: $1',
'abusefilter-degroupreason' => 'Rights automatically stripped by abuse filter. Rule description: $1',
'abusefilter-accountreserved' => 'This account name is reserved for use by the abuse filter.',
@ -133,6 +133,7 @@ A brief description of the abuse rule which your action matched is: $1",
'abusefilter-edit-action-degroup' => 'Remove the user from all privileged groups',
'abusefilter-edit-action-block' => 'Block the user and/or IP address from editing',
'abusefilter-edit-action-throttle' => 'Trigger actions only if the user trips a rate limit',
'abusefilter-edit-action-rangeblock' => 'Block the /16 range from which the user originates.',
'abusefilter-edit-throttle-count' => 'Number of actions to allow:',
'abusefilter-edit-throttle-period' => 'Period of time:',
'abusefilter-edit-throttle-seconds' => '$1 {{PLURAL:$1|second|seconds}}',
@ -171,6 +172,7 @@ A brief description of the abuse rule which your action matched is: $1",
'abusefilter-edit-builder-misc-ternary' => 'Ternary operator (1 ? 2 : 3)',
'abusefilter-edit-builder-misc-in' => 'contained in string (in)',
'abusefilter-edit-builder-misc-like' => 'Matches regex (like)',
'abusefilter-edit-builder-misc-stringlit' => 'String literal ("")',
'abusefilter-edit-builder-group-funcs' => 'Functions',
'abusefilter-edit-builder-funcs-length' => 'String length (length)',
'abusefilter-edit-builder-funcs-lcase' => 'To lower case (lcase)',

View file

@ -27,10 +27,10 @@ $wgExtensionCredits['other'][] = array(
$wgExtensionMessagesFiles['AbuseFilter'] = "$dir/AbuseFilter.i18n.php";
$wgExtensionAliasesFiles['AbuseFilter'] = "$dir/AbuseFilter.alias.php";
$wgAutoloadClasses[ 'AbuseFilter' ] = "$dir/AbuseFilter.class.php";
$wgAutoloadClasses[ 'AbuseFilterParser' ] = "$dir/AbuseFilter.parser.php";
$wgAutoloadClasses[ 'AbuseFilterParserNative' ] = "$dir/AbuseFilter.nativeparser.php";
$wgAutoloadClasses[ 'AbuseFilterHooks' ] = "$dir/AbuseFilter.hooks.php";
$wgAutoloadClasses['AbuseFilter'] = "$dir/AbuseFilter.class.php";
$wgAutoloadClasses['AbuseFilterParser'] = "$dir/AbuseFilter.parser.php";
$wgAutoloadClasses['AbuseFilterParserNative'] = "$dir/AbuseFilter.nativeparser.php";
$wgAutoloadClasses['AbuseFilterHooks'] = "$dir/AbuseFilter.hooks.php";
$wgAutoloadClasses['SpecialAbuseLog'] = "$dir/SpecialAbuseLog.php";
$wgAutoloadClasses['SpecialAbuseFilter'] = "$dir/SpecialAbuseFilter.php";
@ -50,17 +50,18 @@ $wgAvailableRights[] = 'abusefilter-view';
$wgAvailableRights[] = 'abusefilter-log';
$wgAvailableRights[] = 'abusefilter-private';
$wgAbuseFilterAvailableActions = array( 'flag', 'throttle', 'warn', 'disallow', 'blockautopromote', 'block', 'degroup' );
$wgAbuseFilterAvailableActions = array( 'flag', 'throttle', 'warn', 'disallow', 'blockautopromote', 'block', 'degroup', 'rangeblock' );
// Conditions take about 4ms to check, so 100 conditions would take 400ms
$wgAbuseFilterConditionLimit = 1000;
// Disable filters if they match more than X edits, constituting more than Y% of the last Z edits
$wgAbuseFilterEmergencyDisableThreshold = 0.05;
$wgAbuseFilterEmergencyDisableCount = 5;
// Disable filters if they match more than X edits, constituting more than Y% of the last Z edits, if they have been changed in the last S seconds
$wgAbuseFilterEmergencyDisableThreshold = 0.50;
$wgAbuseFilterEmergencyDisableCount = 2;
$wgAbuseFilterEmergencyDisableAge = 86400; // One day.
// Abuse filter parser class
$wgAbuseFilterParserClass = 'AbuseFilterParserNative';
$wgAbuseFilterParserClass = 'AbuseFilterParser';
$wgAbuseFilterNativeParser = "$dir/parser_native/af_parser";
$wgAbuseFilterNativeSyntaxCheck = "$dir/parser_native/syntax_check";
$wgAbuseFilterNativeExpressionEvaluator = "$dir/parser_native/af_expr";

View file

@ -397,7 +397,7 @@ class SpecialAbuseFilter extends SpecialPage {
'op-arithmetic' => array('+' => 'addition', '-' => 'subtraction', '*' => 'multiplication', '/' => 'divide', '%' => 'modulo', '**' => 'pow'),
'op-comparison' => array('==' => 'equal', '!=' => 'notequal', '<' => 'lt', '>' => 'gt', '<=' => 'lte', '>=' => 'gte'),
'op-bool' => array( '!' => 'not', '&' => 'and', '|' => 'or', '^' => 'xor' ),
'misc' => array( 'val1 ? iftrue : iffalse' => 'ternary', 'in' => 'in', 'like' => 'like' ),
'misc' => array( 'val1 ? iftrue : iffalse' => 'ternary', 'in' => 'in', 'like' => 'like', '""' => 'stringlit', ),
'funcs' => array( 'length(string)' => 'length', 'lcase(string)' => 'lcase', 'ccnorm(string)' => 'ccnorm', 'rmdoubles(string)' => 'rmdoubles', 'specialratio(string)' => 'specialratio', 'norm(string)' => 'norm', 'count(needle,haystack)' => 'count' ),
'vars' => array( 'ACCOUNTNAME' => 'accountname', 'ACTION' => 'action', 'ADDED_LINES' => 'addedlines', 'EDIT_DELTA' => 'delta', 'EDIT_DIFF' => 'diff', 'NEW_SIZE' => 'newsize', 'OLD_SIZE' => 'oldsize', 'REMOVED_LINES' => 'removedlines', 'SUMMARY' => 'summary', 'ARTICLE_ARTICLEID' => 'article-id', 'ARTICLE_NAMESPACE' => 'article-ns', 'ARTICLE_TEXT' => 'article-text', 'ARTICLE_PREFIXEDTEXT' => 'article-prefixedtext', 'MOVED_FROM_ARTICLEID' => 'movedfrom-id', 'MOVED_FROM_NAMESPACE' => 'movedfrom-ns', 'MOVED_FROM_TEXT' => 'movedfrom-text', 'MOVED_FROM_PREFIXEDTEXT' => 'movedfrom-prefixedtext', 'MOVED_TO_ARTICLEID' => 'movedto-id', 'MOVED_TO_NAMESPACE' => 'movedto-ns', 'MOVED_TO_TEXT' => 'movedto-text', 'MOVED_TO_PREFIXEDTEXT' => 'movedto-prefixedtext', 'USER_EDITCOUNT' => 'user-editcount', 'USER_AGE' => 'user-age', 'USER_NAME' => 'user-name', 'USER_GROUPS' => 'user-groups', 'USER_EMAILCONFIRM' => 'user-emailconfirm'),
);