mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/AbuseFilter.git
synced 2024-11-28 07:50:24 +00:00
6cdebb7ac0
This bug is triggering when a user submit an old revision again. AbuseFilter considered it to be a null edit although the old revision is certainly a different text than the current one in the database. This patch make sure we skip filtering only if we will be comparing with the lastest content. Follow up r52740
304 lines
11 KiB
PHP
304 lines
11 KiB
PHP
<?php
|
|
if ( !defined( 'MEDIAWIKI' ) ) {
|
|
die();
|
|
}
|
|
|
|
class AbuseFilterHooks {
|
|
// So far, all of the error message out-params for these hooks accept HTML.
|
|
// Hooray!
|
|
|
|
/**
|
|
* Entry points for MediaWiki hook 'EditFilterMerged'
|
|
*
|
|
* @param $editor EditPage instance (object)
|
|
* @param $text Content of the edit box
|
|
* @param &$error Error message to return
|
|
* @param $summary Edit summary for page
|
|
* @return bool
|
|
*/
|
|
public static function onEditFilterMerged( $editor, $text, &$error, $summary ) {
|
|
// Load vars
|
|
$vars = new AbuseFilterVariableHolder;
|
|
|
|
// Cache article object so we can share a parse operation
|
|
$title = $editor->mTitle;
|
|
$articleCacheKey = $title->getNamespace() . ':' . $title->getText();
|
|
AFComputedVariable::$articleCache[$articleCacheKey] = $editor->mArticle;
|
|
|
|
// Check for null edits.
|
|
$oldtext = '';
|
|
|
|
if ( $editor->mArticle->exists() ) {
|
|
// Fetch text which might be an old revision
|
|
$oldtext = $editor->mArticle->getContent();
|
|
}
|
|
|
|
if ( $editor->mArticle->isCurrent() && strcmp( $oldtext, $text ) == 0) {
|
|
// Do not trigger if used text was the latest in the database and user
|
|
// submitted text does not introduce any difference.
|
|
// See bugs 19267 & 31656.
|
|
return true;
|
|
}
|
|
|
|
global $wgUser;
|
|
$vars->addHolder( AbuseFilter::generateUserVars( $wgUser ) );
|
|
$vars->addHolder( AbuseFilter::generateTitleVars( $editor->mTitle , 'ARTICLE' ) );
|
|
$vars->setVar( 'ACTION', 'edit' );
|
|
$vars->setVar( 'SUMMARY', $summary );
|
|
$vars->setVar( 'minor_edit', $editor->minoredit );
|
|
|
|
$vars->setVar( 'old_wikitext', $oldtext );
|
|
$vars->setVar( 'new_wikitext', $text );
|
|
|
|
$vars->addHolder( AbuseFilter::getEditVars( $editor->mTitle ) );
|
|
|
|
$filter_result = AbuseFilter::filterAction( $vars, $editor->mTitle );
|
|
|
|
if ( $filter_result !== true ) {
|
|
global $wgOut;
|
|
$wgOut->addHTML( $filter_result );
|
|
$editor->showEditForm();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public static function onGetAutoPromoteGroups( $user, &$promote ) {
|
|
global $wgMemc;
|
|
|
|
$key = AbuseFilter::autoPromoteBlockKey( $user );
|
|
|
|
if ( $wgMemc->get( $key ) ) {
|
|
$promote = array();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public static function onAbortMove( $oldTitle, $newTitle, $user, &$error, $reason ) {
|
|
$vars = new AbuseFilterVariableHolder;
|
|
|
|
global $wgUser;
|
|
$vars->addHolder(
|
|
AbuseFilterVariableHolder::merge(
|
|
AbuseFilter::generateUserVars( $wgUser ),
|
|
AbuseFilter::generateTitleVars( $oldTitle, 'MOVED_FROM' ),
|
|
AbuseFilter::generateTitleVars( $newTitle, 'MOVED_TO' )
|
|
)
|
|
);
|
|
$vars->setVar( 'SUMMARY', $reason );
|
|
$vars->setVar( 'ACTION', 'move' );
|
|
|
|
$filter_result = AbuseFilter::filterAction( $vars, $oldTitle );
|
|
|
|
$error = $filter_result;
|
|
|
|
return $filter_result == '' || $filter_result === true;
|
|
}
|
|
|
|
public static function onArticleDelete( &$article, &$user, &$reason, &$error ) {
|
|
$vars = new AbuseFilterVariableHolder;
|
|
|
|
global $wgUser;
|
|
$vars->addHolder( AbuseFilter::generateUserVars( $wgUser ) );
|
|
$vars->addHolder( AbuseFilter::generateTitleVars( $article->mTitle, 'ARTICLE' ) );
|
|
$vars->setVar( 'SUMMARY', $reason );
|
|
$vars->setVar( 'ACTION', 'delete' );
|
|
|
|
$filter_result = AbuseFilter::filterAction( $vars, $article->mTitle );
|
|
|
|
$error = $filter_result;
|
|
|
|
return $filter_result == '' || $filter_result === true;
|
|
}
|
|
|
|
public static function onAbortNewAccount( $user, &$message ) {
|
|
if ( $user->getName() == wfMsgForContent( 'abusefilter-blocker' ) ) {
|
|
$message = wfMsg( 'abusefilter-accountreserved' );
|
|
return false;
|
|
}
|
|
$vars = new AbuseFilterVariableHolder;
|
|
// Add variables only for a registered user, so IP addresses of
|
|
// new users won't be exposed
|
|
global $wgUser;
|
|
if ( $wgUser->getId() ) {
|
|
$vars->addHolder( AbuseFilter::generateUserVars( $wgUser ) );
|
|
}
|
|
|
|
$vars->setVar( 'ACTION', 'createaccount' );
|
|
$vars->setVar( 'ACCOUNTNAME', $user->getName() );
|
|
|
|
$filter_result = AbuseFilter::filterAction(
|
|
$vars, SpecialPage::getTitleFor( 'Userlogin' ) );
|
|
|
|
$message = $filter_result;
|
|
|
|
return $filter_result == '' || $filter_result === true;
|
|
}
|
|
|
|
public static function onRecentChangeSave( $recentChange ) {
|
|
$title = Title::makeTitle(
|
|
$recentChange->mAttribs['rc_namespace'],
|
|
$recentChange->mAttribs['rc_title']
|
|
);
|
|
$action = $recentChange->mAttribs['rc_log_type'] ?
|
|
$recentChange->mAttribs['rc_log_type'] : 'edit';
|
|
$actionID = implode( '-', array(
|
|
$title->getPrefixedText(), $recentChange->mAttribs['rc_user_text'], $action
|
|
) );
|
|
|
|
if ( !empty( AbuseFilter::$tagsToSet[$actionID] )
|
|
&& count( $tags = AbuseFilter::$tagsToSet[$actionID] ) )
|
|
{
|
|
ChangeTags::addTags(
|
|
$tags,
|
|
$recentChange->mAttribs['rc_id'],
|
|
$recentChange->mAttribs['rc_this_oldid'],
|
|
$recentChange->mAttribs['rc_logid']
|
|
);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public static function onListDefinedTags( &$emptyTags ) {
|
|
# This is a pretty awful hack.
|
|
$dbr = wfGetDB( DB_SLAVE );
|
|
|
|
$res = $dbr->select(
|
|
array( 'abuse_filter_action', 'abuse_filter' ),
|
|
'afa_parameters',
|
|
array( 'afa_consequence' => 'tag', 'af_enabled' => true ),
|
|
__METHOD__,
|
|
array(),
|
|
array( 'abuse_filter' => array( 'INNER JOIN', 'afa_filter=af_id' ) )
|
|
);
|
|
|
|
foreach ( $res as $row ) {
|
|
$emptyTags = array_filter(
|
|
array_merge( explode( "\n", $row->afa_parameters ), $emptyTags )
|
|
);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @static
|
|
* @param $updater DatabaseUpdater
|
|
* @return bool
|
|
*/
|
|
public static function onLoadExtensionSchemaUpdates( $updater = null ) {
|
|
$dir = dirname( __FILE__ );
|
|
|
|
if ( $updater === null ) {
|
|
global $wgExtNewTables, $wgExtNewFields, $wgExtPGNewFields, $wgExtPGAlteredFields, $wgExtNewIndexes, $wgDBtype;
|
|
// DB updates
|
|
if ( $wgDBtype == 'mysql' ) {
|
|
$wgExtNewTables[] = array( 'abuse_filter', "$dir/abusefilter.tables.sql" );
|
|
$wgExtNewTables[] = array( 'abuse_filter_history', "$dir/db_patches/patch-abuse_filter_history.sql" );
|
|
$wgExtNewFields[] = array( 'abuse_filter_history', 'afh_changed_fields', "$dir/db_patches/patch-afh_changed_fields.sql" );
|
|
$wgExtNewFields[] = array( 'abuse_filter', 'af_deleted', "$dir/db_patches/patch-af_deleted.sql" );
|
|
$wgExtNewFields[] = array( 'abuse_filter', 'af_actions', "$dir/db_patches/patch-af_actions.sql" );
|
|
$wgExtNewFields[] = array( 'abuse_filter', 'af_global', "$dir/db_patches/patch-global_filters.sql" );
|
|
$wgExtNewIndexes[] = array( 'abuse_filter_log', 'filter_timestamp', "$dir/db_patches/patch-fix-indexes.sql" );
|
|
} elseif ( $wgDBtype == 'postgres' ) {
|
|
$wgExtNewTables = array_merge( $wgExtNewTables,
|
|
array(
|
|
array( 'abuse_filter', "$dir/abusefilter.tables.pg.sql" ),
|
|
array( 'abuse_filter_history', "$dir/db_patches/patch-abuse_filter_history.pg.sql" ),
|
|
) );
|
|
$wgExtPGNewFields[] = array( 'abuse_filter', 'af_actions', "TEXT NOT NULL DEFAULT ''" );
|
|
$wgExtPGNewFields[] = array( 'abuse_filter', 'af_deleted', 'SMALLINT NOT NULL DEFAULT 0' );
|
|
$wgExtPGNewFields[] = array( 'abuse_filter', 'af_global', 'SMALLINT NOT NULL DEFAULT 0' );
|
|
|
|
$wgExtPGNewFields[] = array( 'abuse_filter_log', 'afl_wiki', 'TEXT' );
|
|
$wgExtPGNewFields[] = array( 'abuse_filter_log', 'afl_deleted', 'SMALLINT' );
|
|
$wgExtPGAlteredFields[] = array( 'abuse_filter_log', 'afl_filter', 'TEXT' );
|
|
|
|
$wgExtNewIndexes[] = array( 'abuse_filter_log', 'abuse_filter_log_ip', "(afl_ip)" );
|
|
}
|
|
} else {
|
|
if ( $updater->getDB()->getType() == 'mysql' ) {
|
|
$updater->addExtensionUpdate( array( 'addTable', 'abuse_filter', "$dir/abusefilter.tables.sql", true ) );
|
|
$updater->addExtensionUpdate( array( 'addTable', 'abuse_filter_history', "$dir/db_patches/patch-abuse_filter_history.sql", true ) );
|
|
$updater->addExtensionUpdate( array( 'addField', 'abuse_filter_history', 'afh_changed_fields', "$dir/db_patches/patch-afh_changed_fields.sql", true ) );
|
|
$updater->addExtensionUpdate( array( 'addField', 'abuse_filter', 'af_deleted', "$dir/db_patches/patch-af_deleted.sql", true ) );
|
|
$updater->addExtensionUpdate( array( 'addField', 'abuse_filter', 'af_actions', "$dir/db_patches/patch-af_actions.sql", true ) );
|
|
$updater->addExtensionUpdate( array( 'addField', 'abuse_filter', 'af_global', "$dir/db_patches/patch-global_filters.sql", true ) );
|
|
$updater->addExtensionUpdate( array( 'addIndex', 'abuse_filter_log', 'filter_timestamp', "$dir/db_patches/patch-fix-indexes.sql", true ) );
|
|
} elseif ( $updater->getDB()->getType() == 'postgres' ) {
|
|
$updater->addExtensionUpdate( array( 'addTable', 'abuse_filter', "$dir/abusefilter.tables.pg.sql", true ) );
|
|
$updater->addExtensionUpdate( array( 'addTable', 'abuse_filter_history', "$dir/db_patches/patch-abuse_filter_history.pg.sql", true ) );
|
|
$updater->addExtensionUpdate( array( 'addPgField', 'abuse_filter', 'af_actions', "TEXT NOT NULL DEFAULT ''" ) );
|
|
$updater->addExtensionUpdate( array( 'addPgField', 'abuse_filter', 'af_deleted', 'SMALLINT NOT NULL DEFAULT 0' ) );
|
|
$updater->addExtensionUpdate( array( 'addPgField', 'abuse_filter', 'af_global', 'SMALLINT NOT NULL DEFAULT 0' ) );
|
|
$updater->addExtensionUpdate( array( 'addPgField', 'abuse_filter_log', 'afl_wiki', 'TEXT' ) );
|
|
$updater->addExtensionUpdate( array( 'addPgField', 'abuse_filter_log', 'afl_deleted', 'SMALLINT' ) );
|
|
$updater->addExtensionUpdate( array( 'changeField', 'abuse_filter_log', 'afl_filter', 'TEXT' ) );
|
|
$updater->addExtensionUpdate( array( 'addPgExtIndex', 'abuse_filter_log', 'abuse_filter_log_ip', "(afl_ip)" ) );
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public static function onContributionsToolLinks( $id, $nt, &$tools ) {
|
|
global $wgUser;
|
|
if ( $wgUser->isAllowed( 'abusefilter-log' ) ) {
|
|
$sk = $wgUser->getSkin();
|
|
$tools[] = $sk->link(
|
|
SpecialPage::getTitleFor( 'AbuseLog' ),
|
|
wfMsg( 'abusefilter-log-linkoncontribs' ),
|
|
array( 'title' =>
|
|
wfMsgExt( 'abusefilter-log-linkoncontribs-text', 'parseinline' ) ),
|
|
array( 'wpSearchUser' => $nt->getText() )
|
|
);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public static function onUploadVerification( $saveName, $tempName, &$error ) {
|
|
$vars = new AbuseFilterVariableHolder;
|
|
|
|
global $wgUser;
|
|
$title = Title::makeTitle( NS_FILE, $saveName );
|
|
$vars->addHolder(
|
|
AbuseFilterVariableHolder::merge(
|
|
AbuseFilter::generateUserVars( $wgUser ),
|
|
AbuseFilter::generateTitleVars( $title, 'FILE' )
|
|
)
|
|
);
|
|
|
|
$vars->setVar( 'ACTION', 'upload' );
|
|
$vars->setVar( 'file_sha1', sha1_file( $tempName ) ); // TODO share with save
|
|
|
|
$filter_result = AbuseFilter::filterAction( $vars, $title );
|
|
|
|
if ( is_string( $filter_result ) ) {
|
|
$error = $filter_result;
|
|
}
|
|
|
|
return $filter_result == '' || $filter_result === true;
|
|
}
|
|
|
|
/**
|
|
* Adds global variables to the Javascript as needed
|
|
*
|
|
* @param array $vars
|
|
* @return bool
|
|
*/
|
|
public static function onMakeGlobalVariablesScript( array &$vars ) {
|
|
if ( AbuseFilter::$editboxName !== null ) {
|
|
$vars['abuseFilterBoxName'] = AbuseFilter::$editboxName;
|
|
}
|
|
|
|
if ( AbuseFilterViewExamine::$examineType !== null ) {
|
|
$vars['abuseFilterExamine'] = array(
|
|
'type' => AbuseFilterViewExamine::$examineType,
|
|
'id' => AbuseFilterViewExamine::$examineId,
|
|
);
|
|
}
|
|
return true;
|
|
}
|
|
}
|