mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/AbuseFilter.git
synced 2024-11-24 06:03:49 +00:00
Move keywords handlers to the Parser
Just like we do for functions, it doesn't really make sense to have keywords separately, in AFPData. Change-Id: I208a9b1ce2bd12038e9fbcc515c48d604ec80eb8
This commit is contained in:
parent
2fdf091eb9
commit
3f171dc0a5
|
@ -13,9 +13,12 @@ class AFPData {
|
|||
// Special purpose for creating instances that will be populated later
|
||||
const DEMPTY = 'empty';
|
||||
|
||||
// Translation table mapping shell-style wildcards to PCRE equivalents.
|
||||
// Derived from <http://www.php.net/manual/en/function.fnmatch.php#100207>
|
||||
private static $wildcardMap = [
|
||||
/**
|
||||
* Translation table mapping shell-style wildcards to PCRE equivalents.
|
||||
* Derived from <http://www.php.net/manual/en/function.fnmatch.php#100207>
|
||||
* @internal
|
||||
*/
|
||||
public static $wildcardMap = [
|
||||
'\*' => '.*',
|
||||
'\+' => '\+',
|
||||
'\-' => '\-',
|
||||
|
@ -177,42 +180,6 @@ class AFPData {
|
|||
return new AFPData( $type, $res );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if $a contains $b
|
||||
*
|
||||
* @param AFPData $a
|
||||
* @param AFPData $b
|
||||
* @return AFPData
|
||||
*/
|
||||
private static function containmentKeyword( AFPData $a, AFPData $b ) {
|
||||
$a = $a->toString();
|
||||
$b = $b->toString();
|
||||
|
||||
if ( $a === '' || $b === '' ) {
|
||||
return new AFPData( self::DBOOL, false );
|
||||
}
|
||||
|
||||
return new AFPData( self::DBOOL, strpos( $a, $b ) !== false );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AFPData $a
|
||||
* @param AFPData $b
|
||||
* @return AFPData
|
||||
*/
|
||||
public static function keywordIn( AFPData $a, AFPData $b ) {
|
||||
return self::containmentKeyword( $b, $a );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AFPData $a
|
||||
* @param AFPData $b
|
||||
* @return AFPData
|
||||
*/
|
||||
public static function keywordContains( AFPData $a, AFPData $b ) {
|
||||
return self::containmentKeyword( $a, $b );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AFPData $d2
|
||||
* @param bool $strict whether to also check types
|
||||
|
@ -260,67 +227,6 @@ class AFPData {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AFPData $str
|
||||
* @param AFPData $pattern
|
||||
* @return AFPData
|
||||
*/
|
||||
public static function keywordLike( AFPData $str, AFPData $pattern ) {
|
||||
$str = $str->toString();
|
||||
$pattern = '#^' . strtr( preg_quote( $pattern->toString(), '#' ), self::$wildcardMap ) . '$#u';
|
||||
Wikimedia\suppressWarnings();
|
||||
$result = preg_match( $pattern, $str );
|
||||
Wikimedia\restoreWarnings();
|
||||
|
||||
return new AFPData( self::DBOOL, (bool)$result );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AFPData $str
|
||||
* @param AFPData $regex
|
||||
* @param int $pos
|
||||
* @param bool $insensitive
|
||||
* @return AFPData
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function keywordRegex( AFPData $str, AFPData $regex, $pos, $insensitive = false ) {
|
||||
$str = $str->toString();
|
||||
$pattern = $regex->toString();
|
||||
|
||||
$pattern = preg_replace( '!(\\\\\\\\)*(\\\\)?/!', '$1\/', $pattern );
|
||||
$pattern = "/$pattern/u";
|
||||
|
||||
if ( $insensitive ) {
|
||||
$pattern .= 'i';
|
||||
}
|
||||
|
||||
Wikimedia\suppressWarnings();
|
||||
$result = preg_match( $pattern, $str );
|
||||
Wikimedia\restoreWarnings();
|
||||
if ( $result === false ) {
|
||||
throw new AFPUserVisibleException(
|
||||
'regexfailure',
|
||||
// Coverage bug
|
||||
// @codeCoverageIgnoreStart
|
||||
$pos,
|
||||
// @codeCoverageIgnoreEnd
|
||||
[ $pattern ]
|
||||
);
|
||||
}
|
||||
|
||||
return new AFPData( self::DBOOL, (bool)$result );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AFPData $str
|
||||
* @param AFPData $regex
|
||||
* @param int $pos
|
||||
* @return AFPData
|
||||
*/
|
||||
public static function keywordRegexInsensitive( AFPData $str, AFPData $regex, $pos ) {
|
||||
return self::keywordRegex( $str, $regex, $pos, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AFPData
|
||||
*/
|
||||
|
|
|
@ -203,7 +203,7 @@ class AbuseFilterCachingParser extends AbuseFilterParser {
|
|||
$this->raiseCondCount();
|
||||
|
||||
// @phan-suppress-next-line PhanParamTooMany Not every function needs the position
|
||||
$result = AFPData::$func( $leftOperand, $rightOperand, $node->position );
|
||||
$result = $this->$func( $leftOperand, $rightOperand, $node->position );
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
|
|
@ -744,7 +744,7 @@ class AbuseFilterParser {
|
|||
$this->raiseCondCount();
|
||||
|
||||
// @phan-suppress-next-line PhanParamTooMany Not every function needs the position
|
||||
$result = AFPData::$func( $result, $r2, $this->mCur->pos );
|
||||
$result = $this->$func( $result, $r2, $this->mCur->pos );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1649,6 +1649,103 @@ class AbuseFilterParser {
|
|||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if $a contains $b
|
||||
*
|
||||
* @param AFPData $a
|
||||
* @param AFPData $b
|
||||
* @return AFPData
|
||||
*/
|
||||
protected function containmentKeyword( AFPData $a, AFPData $b ) {
|
||||
$a = $a->toString();
|
||||
$b = $b->toString();
|
||||
|
||||
if ( $a === '' || $b === '' ) {
|
||||
return new AFPData( AFPData::DBOOL, false );
|
||||
}
|
||||
|
||||
return new AFPData( AFPData::DBOOL, strpos( $a, $b ) !== false );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AFPData $a
|
||||
* @param AFPData $b
|
||||
* @return AFPData
|
||||
*/
|
||||
protected function keywordIn( AFPData $a, AFPData $b ) {
|
||||
return $this->containmentKeyword( $b, $a );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AFPData $a
|
||||
* @param AFPData $b
|
||||
* @return AFPData
|
||||
*/
|
||||
protected function keywordContains( AFPData $a, AFPData $b ) {
|
||||
return $this->containmentKeyword( $a, $b );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AFPData $str
|
||||
* @param AFPData $pattern
|
||||
* @return AFPData
|
||||
*/
|
||||
protected function keywordLike( AFPData $str, AFPData $pattern ) {
|
||||
$str = $str->toString();
|
||||
$pattern = '#^' . strtr( preg_quote( $pattern->toString(), '#' ), AFPData::$wildcardMap ) . '$#u';
|
||||
Wikimedia\suppressWarnings();
|
||||
$result = preg_match( $pattern, $str );
|
||||
Wikimedia\restoreWarnings();
|
||||
|
||||
return new AFPData( AFPData::DBOOL, (bool)$result );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AFPData $str
|
||||
* @param AFPData $regex
|
||||
* @param int $pos
|
||||
* @param bool $insensitive
|
||||
* @return AFPData
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function keywordRegex( AFPData $str, AFPData $regex, $pos, $insensitive = false ) {
|
||||
$str = $str->toString();
|
||||
$pattern = $regex->toString();
|
||||
|
||||
$pattern = preg_replace( '!(\\\\\\\\)*(\\\\)?/!', '$1\/', $pattern );
|
||||
$pattern = "/$pattern/u";
|
||||
|
||||
if ( $insensitive ) {
|
||||
$pattern .= 'i';
|
||||
}
|
||||
|
||||
Wikimedia\suppressWarnings();
|
||||
$result = preg_match( $pattern, $str );
|
||||
Wikimedia\restoreWarnings();
|
||||
if ( $result === false ) {
|
||||
throw new AFPUserVisibleException(
|
||||
'regexfailure',
|
||||
// Coverage bug
|
||||
// @codeCoverageIgnoreStart
|
||||
$pos,
|
||||
// @codeCoverageIgnoreEnd
|
||||
[ $pattern ]
|
||||
);
|
||||
}
|
||||
|
||||
return new AFPData( AFPData::DBOOL, (bool)$result );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AFPData $str
|
||||
* @param AFPData $regex
|
||||
* @param int $pos
|
||||
* @return AFPData
|
||||
*/
|
||||
protected function keywordRegexInsensitive( AFPData $str, AFPData $regex, $pos ) {
|
||||
return $this->keywordRegex( $str, $regex, $pos, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $args
|
||||
* @return AFPData
|
||||
|
|
|
@ -36,31 +36,6 @@
|
|||
* @covers AFPTreeNode
|
||||
*/
|
||||
class AFPDataTest extends AbuseFilterParserTestCase {
|
||||
/**
|
||||
* Test the 'regexfailure' exception
|
||||
*
|
||||
* @param string $expr The expression to test
|
||||
* @param string $caller The function where the exception is thrown
|
||||
* @covers AFPData::keywordRegex
|
||||
* @dataProvider regexFailure
|
||||
*/
|
||||
public function testRegexFailureException( $expr, $caller ) {
|
||||
$this->exceptionTest( 'regexfailure', $expr, $caller );
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testRegexFailureException
|
||||
* The second parameter is the function where the exception is raised.
|
||||
* One expression for each throw.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function regexFailure() {
|
||||
return [
|
||||
[ "'a' rlike '('", 'keywordRegex' ],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the 'dividebyzero' exception
|
||||
*
|
||||
|
|
|
@ -484,6 +484,7 @@ class AbuseFilterParserTest extends AbuseFilterParserTestCase {
|
|||
return [
|
||||
[ "rcount('(','a')", 'funcRCount' ],
|
||||
[ "get_matches('this (should fail', 'any haystack')", 'funcGetMatches' ],
|
||||
[ "'a' rlike '('", 'keywordRegex' ],
|
||||
];
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue