From: Tim Starling Date: Mon, 23 Apr 2012 02:03:02 +0000 (+1000) Subject: (bug 35315) Detect circular references in strip tags X-Git-Tag: 1.31.0-rc.0~23677^2~1 X-Git-Url: http://git.cyclocoop.org/%24href?a=commitdiff_plain;h=3905be18fbd6d233f1517d6e7612a6acfb91fa82;p=lhc%2Fweb%2Fwiklou.git (bug 35315) Detect circular references in strip tags Explicitly detect circular references in strip tags and break the loop, similar to how we deal with circular references in templates. This is necessary to support Scribunto since we imagine we will provide an API that allows strip markers to be forged. The recursion depth limit is a consequence of changing the algorithm from iterative to recursive, it's required to protect the stack against deeply nested #tag invocations. Change-Id: Icc8dc4aedbced55ad75b3b5a5429a376d06d9b31 --- diff --git a/includes/parser/StripState.php b/includes/parser/StripState.php index 321fcd864e..b08aa148fe 100644 --- a/includes/parser/StripState.php +++ b/includes/parser/StripState.php @@ -31,6 +31,10 @@ class StripState { protected $regex; protected $tempType, $tempMergePrefix; + protected $circularRefGuard; + protected $recursionLevel = 0; + + const UNSTRIP_RECURSION_LIMIT = 20; /** * @param $prefix string @@ -42,6 +46,7 @@ class StripState { 'general' => array() ); $this->regex = "/{$this->prefix}([^\x7f]+)" . Parser::MARKER_SUFFIX . '/'; + $this->circularRefGuard = array(); } /** @@ -113,12 +118,10 @@ class StripState { } wfProfileIn( __METHOD__ ); + $oldType = $this->tempType; $this->tempType = $type; - do { - $oldText = $text; - $text = preg_replace_callback( $this->regex, array( $this, 'unstripCallback' ), $text ); - } while ( $text !== $oldText ); - $this->tempType = null; + $text = preg_replace_callback( $this->regex, array( $this, 'unstripCallback' ), $text ); + $this->tempType = $oldType; wfProfileOut( __METHOD__ ); return $text; } @@ -128,8 +131,22 @@ class StripState { * @return array */ protected function unstripCallback( $m ) { - if ( isset( $this->data[$this->tempType][$m[1]] ) ) { - return $this->data[$this->tempType][$m[1]]; + $marker = $m[1]; + if ( isset( $this->data[$this->tempType][$marker] ) ) { + if ( isset( $this->circularRefGuard[$marker] ) ) { + return '' . wfMsgForContent( 'parser-unstrip-loop-warning' ) . ''; + } + if ( $this->recursionLevel >= self::UNSTRIP_RECURSION_LIMIT ) { + return '' . + wfMsgForContent( 'parser-unstrip-recursion-limit', self::UNSTRIP_RECURSION_LIMIT ) . + ''; + } + $this->circularRefGuard[$marker] = true; + $this->recursionLevel++; + $ret = $this->unstripType( $this->tempType, $this->data[$this->tempType][$marker] ); + $this->recursionLevel--; + unset( $this->circularRefGuard[$marker] ); + return $ret; } else { return $m[0]; } diff --git a/languages/messages/MessagesEn.php b/languages/messages/MessagesEn.php index 34cb9f1499..3066e6cc28 100644 --- a/languages/messages/MessagesEn.php +++ b/languages/messages/MessagesEn.php @@ -1482,6 +1482,8 @@ These arguments have been omitted.", 'node-count-exceeded-warning' => 'Page exceeded the node-count', 'expansion-depth-exceeded-category' => 'Pages where expansion depth is exceeded', 'expansion-depth-exceeded-warning' => 'Page exceeded the expansion depth', +'parser-unstrip-loop-warning' => 'Unstrip loop detected', +'parser-unstrip-recursion-limit' => 'Unstrip recursion limit exceeded ($1)', # "Undo" feature 'undo-success' => 'The edit can be undone.