From ccc4114812ae684e6f8bd555fcd74027864ecea7 Mon Sep 17 00:00:00 2001 From: "physikerwelt (Moritz Schubotz)" Date: Fri, 5 Sep 2014 21:59:13 -0400 Subject: [PATCH] Fine grained options for wgMathDisableTeXFilter Add new option to filter only new input. Now the complete list of possible settings is: MW_MATH_CHECK_ALWAYS backwards compatible to false MW_MATH_CHECK_NEVER backwards compatible to true MW_MATH_CHECK_NEW new option Change-Id: I455b41c8b8d918f4c34f6c115194d227a8394e0a --- Math.hooks.php | 13 ++-- Math.php | 27 ++++++-- MathRenderer.php | 19 ++++-- tests/MathRendererTest.php | 123 ++++++++++++++++++++++++++++++------- 4 files changed, 144 insertions(+), 38 deletions(-) diff --git a/Math.hooks.php b/Math.hooks.php index e315974..47bdf0c 100644 --- a/Math.hooks.php +++ b/Math.hooks.php @@ -92,7 +92,7 @@ class MathHooks { * @return array */ static function mathTagHook( $content, $attributes, $parser ) { - global $wgUseMathJax, $wgMathDisableTexFilter; + global $wgUseMathJax; if ( trim( $content ) === '' ) { // bug 8372 return ''; @@ -111,14 +111,13 @@ class MathHooks { $renderer = MathRenderer::getRenderer( $content, $attributes, $mode ); - if ( !$wgMathDisableTexFilter ) { - $checkResult = $renderer->checkTex(); + $checkResult = $renderer->checkTex(); - if ( $checkResult !== true ) { - // Returns the error message - return $renderer->getLastError(); - } + if ( $checkResult !== true ) { + // Returns the error message + return $renderer->getLastError(); } + if ( $renderer->render() ) { wfDebugLog( "Math" , "Rendering successful. Writing output" ); $renderedMath = $renderer->getHtmlOutput(); diff --git a/Math.php b/Math.php index 0314aaa..339810b 100644 --- a/Math.php +++ b/Math.php @@ -188,12 +188,29 @@ $wgMathDefaultLaTeXMLSetting = array( * The link to the texvccheck executable */ $wgMathTexvcCheckExecutable = __DIR__ . '/texvccheck/texvccheck'; -/** - * Option to disable the tex filter. If set to true any LaTeX espression is parsed - * this can be a potential security risk. If set to false only a subset of the TeX - * commands is allowed. See the wikipedia page Help:Math for details. + +/**@{ + * Math check constants */ -$wgMathDisableTexFilter = false; +define( 'MW_MATH_CHECK_ALWAYS', 0 ); /// backwards compatible to false +define( 'MW_MATH_CHECK_NEVER' , 1 ); /// backwards compatible to true +define( 'MW_MATH_CHECK_NEW' , 2 ); +/**@}*/ +/** + * Option to disable the TeX security filter: + * In general every math object, which is rendered by the math extension has its rendering cached in + * a database. + * MW_MATH_CHECK_ALWAYS: If set to MW_MATH_CHECK_ALWAYS only a subset of the TeX commands is allowed. + * See the Wikipedia page Help:Math for details about the allowed commands. + * MW_MATH_CHECK_NONE: If set to MW_MATH_CHECK_NONE any TeX expression is parsed. + * This can be a potential security risk. + * MW_MATH_CHECK_NEW checks only new equations. If the database does not yet contain the given math object, + * then it is passed through texvccheck. + * Please make sure to truncate the database tables (math, mathoid, mathlatexml) when switching from + * MW_MATH_CHECK_NONE to MW_MATH_CHECK_NEW. Otherwise, unchecked content contained in the database + * will be displayed. + */ +$wgMathDisableTexFilter = MW_MATH_CHECK_NEW; /** Stores debug information in the database and provides more detailed debug output */ $wgMathDebug = false; diff --git a/MathRenderer.php b/MathRenderer.php index 8e46583..c214a23 100644 --- a/MathRenderer.php +++ b/MathRenderer.php @@ -262,7 +262,7 @@ abstract class MathRenderer { wfProfileIn( __METHOD__ ); /** @var DatabaseBase */ $dbr = wfGetDB( DB_SLAVE ); - /** @var ResultWrapper asdf */ + /** @var ResultWrapper */ $rpage = $dbr->selectRow( $this->getMathTableName(), $this->dbInArray(), array( 'math_inputhash' => $this->getInputHash() ), @@ -600,8 +600,21 @@ abstract class MathRenderer { return $this->texSecure; } + /** + * @global $wgMathDisableTexFilter + * @return bool + */ public function checkTex() { - if ( !$this->texSecure ) { + global $wgMathDisableTexFilter; + if ( $this->texSecure || (int) $wgMathDisableTexFilter == MW_MATH_CHECK_NEVER ) { + // equation was already checked or checking is disabled + return true; + } else { + if( (int) $wgMathDisableTexFilter == MW_MATH_CHECK_NEW ){ + if( $this->readFromDatabase() ){ + return true; + } + } $checker = new MathInputCheckTexvc( $this->userInputTex ); if ( $checker->isValid() ) { $this->setTex( $checker->getValidTex() ); @@ -611,8 +624,6 @@ abstract class MathRenderer { $this->lastError = $checker->getError(); return false; } - } else { - return true; } } diff --git a/tests/MathRendererTest.php b/tests/MathRendererTest.php index 5110a1e..8d90420 100644 --- a/tests/MathRendererTest.php +++ b/tests/MathRendererTest.php @@ -6,19 +6,18 @@ */ class MathRendererTest extends MediaWikiTestCase { const SOME_TEX = "a+b"; + const TEXVCCHECK_INPUT = '\forall \epsilon \exist \delta'; + const TEXVCCHECK_OUTPUT = '\forall \epsilon \exists \delta '; // be aware of the s at exists /** * Checks the tex and hash functions * @covers MathRenderer::getTex() * @covers MathRenderer::__construct() */ public function testBasics() { - $renderer = $this->getMockForAbstractClass( 'MathRenderer' - , array ( self::SOME_TEX ) ); + $renderer = $this->getMockForAbstractClass( 'MathRenderer', array( self::SOME_TEX ) ); // check if the TeX input was corretly passed to the class - $this->assertEquals( self::SOME_TEX, $renderer->getTex() - , "test getTex" ); - $this->assertEquals( $renderer->isChanged(), false - , "test if changed is initially false" ); + $this->assertEquals( self::SOME_TEX, $renderer->getTex(), "test getTex" ); + $this->assertEquals( $renderer->isChanged(), false, "test if changed is initially false" ); } /** @@ -26,12 +25,14 @@ class MathRendererTest extends MediaWikiTestCase { * @covers MathRenderer::writeCache() */ public function testWriteCacheSkip() { - $renderer = $this->getMockBuilder( 'MathRenderer' ) - ->setMethods( array( 'writeToDatabase' , 'render', 'getMathTableName', 'getHtmlOutput' ) ) - ->disableOriginalConstructor() - ->getMock(); - $renderer->expects( $this->never() ) - ->method( 'writeToDatabase' ); + $renderer = + $this->getMockBuilder( 'MathRenderer' )->setMethods( array( + 'writeToDatabase', + 'render', + 'getMathTableName', + 'getHtmlOutput' + ) )->disableOriginalConstructor()->getMock(); + $renderer->expects( $this->never() )->method( 'writeToDatabase' ); $renderer->writeCache(); } @@ -40,22 +41,100 @@ class MathRendererTest extends MediaWikiTestCase { * @covers MathRenderer::writeCache() */ public function testWriteCache() { - $renderer = $this->getMockBuilder( 'MathRenderer' ) - ->setMethods( array( 'writeToDatabase' , 'render', 'getMathTableName', 'getHtmlOutput' ) ) - ->disableOriginalConstructor() - ->getMock(); - $renderer->expects( $this->never() ) - ->method( 'writeToDatabase' ); + $renderer = + $this->getMockBuilder( 'MathRenderer' )->setMethods( array( + 'writeToDatabase', + 'render', + 'getMathTableName', + 'getHtmlOutput' + ) )->disableOriginalConstructor()->getMock(); + $renderer->expects( $this->never() )->method( 'writeToDatabase' ); $renderer->writeCache(); } public function testSetPurge() { - $renderer = $this->getMockBuilder( 'MathRenderer' ) - ->setMethods( array( 'render', 'getMathTableName', 'getHtmlOutput' ) ) - ->disableOriginalConstructor() - ->getMock(); + $renderer = + $this->getMockBuilder( 'MathRenderer' )->setMethods( array( + 'render', + 'getMathTableName', + 'getHtmlOutput' + ) )->disableOriginalConstructor()->getMock(); $renderer->setPurge(); $this->assertEquals( $renderer->isPurge(), true, "Test purge." ); } + + public function testCheckingAlways() { + $this->setMwGlobals( "wgMathDisableTexFilter", MW_MATH_CHECK_ALWAYS ); + $renderer = + $this->getMockBuilder( 'MathRenderer' )->setMethods( array( + 'render', + 'getMathTableName', + 'getHtmlOutput', + 'readFromDatabase', + 'setTex' + ) )->setConstructorArgs( array( self::TEXVCCHECK_INPUT ) )->getMock(); + $renderer->expects( $this->never() )->method( 'readFromDatabase' ); + $renderer->expects( $this->once() )->method( 'setTex' )->with( self::TEXVCCHECK_OUTPUT ); + + $this->assertEquals( $renderer->checkTex(), true ); + // now setTex sould not be called again + $this->assertEquals( $renderer->checkTex(), true ); + + } + + public function testCheckingNever() { + $this->setMwGlobals( "wgMathDisableTexFilter", MW_MATH_CHECK_NEVER ); + $renderer = + $this->getMockBuilder( 'MathRenderer' )->setMethods( array( + 'render', + 'getMathTableName', + 'getHtmlOutput', + 'readFromDatabase', + 'setTex' + ) )->setConstructorArgs( array( self::TEXVCCHECK_INPUT ) )->getMock(); + $renderer->expects( $this->never() )->method( 'readFromDatabase' ); + $renderer->expects( $this->never() )->method( 'setTex' ); + + $this->assertEquals( $renderer->checkTex(), true ); + } + + public function testCheckingNewUnknown() { + $this->setMwGlobals( "wgMathDisableTexFilter", MW_MATH_CHECK_NEW ); + $renderer = + $this->getMockBuilder( 'MathRenderer' )->setMethods( array( + 'render', + 'getMathTableName', + 'getHtmlOutput', + 'readFromDatabase', + 'setTex' + ) )->setConstructorArgs( array( self::TEXVCCHECK_INPUT ) )->getMock(); + $renderer->expects( $this->once() )->method( 'readFromDatabase' ) + ->will( $this->returnValue( false ) ); + $renderer->expects( $this->once() )->method( 'setTex' )->with( self::TEXVCCHECK_OUTPUT ); + + $this->assertEquals( $renderer->checkTex(), true ); + // now setTex sould not be called again + $this->assertEquals( $renderer->checkTex(), true ); + } + + public function testCheckingNewKnown() { + $this->setMwGlobals( "wgMathDisableTexFilter", MW_MATH_CHECK_NEW ); + $renderer = + $this->getMockBuilder( 'MathRenderer' )->setMethods( array( + 'render', + 'getMathTableName', + 'getHtmlOutput', + 'readFromDatabase', + 'setTex' + ) )->setConstructorArgs( array( self::TEXVCCHECK_INPUT ) )->getMock(); + $renderer->expects( $this->exactly( 2 ) )->method( 'readFromDatabase' ) + ->will( $this->returnValue( true ) ); + $renderer->expects( $this->never() )->method( 'setTex' ); + + $this->assertEquals( $renderer->checkTex(), true ); + // we don't mark a object as checked even though we rely on the database cache + // so readFromDatabase will be called again + $this->assertEquals( $renderer->checkTex(), true ); + } } \ No newline at end of file