parser: Gracefully handle invalid ParsoidRenderID keys

Why:

- ParsoidRenderID::newFromKey() validates incoming keys and throws an
  InvalidArgumentException if a required key component was missing.
- It does so by eagerly destructuring the return value of explode(),
  which causes a PHP Notice for invalid inputs as the expected offsets
  won't exist then.

What:

- Check the count of key parts before destructuring.
- Add unit tests.

Bug: T385567
Change-Id: I1d936ae038f85ffa2e5d1d3d8a75fdc75e4c8ef8
(cherry picked from commit eec130925c081c2da1c475f9a9ce719e6838ca51)
This commit is contained in:
Máté Szabó 2025-02-04 13:17:43 +01:00 committed by Reedy
parent e751026153
commit b99dcc23bc
2 changed files with 36 additions and 2 deletions

View file

@ -5,6 +5,7 @@ namespace MediaWiki\Edit;
use InvalidArgumentException;
use MediaWiki\Parser\ParserOutput;
use Stringable;
use function count;
/**
* Represents the identity of a specific rendering of a specific revision
@ -37,12 +38,14 @@ class ParsoidRenderID implements Stringable {
*
*/
public static function newFromKey( string $key ): self {
[ $revisionID, $uniqueID ] = explode( '/', $key, 2 );
$parts = explode( '/', $key, 2 );
if ( $revisionID === null || $uniqueID === null ) {
if ( count( $parts ) < 2 ) {
throw new InvalidArgumentException( 'Bad key: ' . $key );
}
[ $revisionID, $uniqueID ] = $parts;
return new self( (int)$revisionID, $uniqueID );
}

View file

@ -2,6 +2,7 @@
namespace MediaWiki\Tests\Unit\Edit;
use InvalidArgumentException;
use MediaWiki\Edit\ParsoidRenderID;
use MediaWikiUnitTestCase;
@ -68,4 +69,34 @@ class ParsoidRenderIdTest extends MediaWikiUnitTestCase {
yield [ '"1/foo"XXX' ];
yield [ 'XXX"1/foo"' ];
}
/**
* @dataProvider provideNewFromKey
* @covers \MediaWiki\Edit\ParsoidRenderID::newFromKey
*/
public function testNewFromKey( string $key, ParsoidRenderID $expected ): void {
$actual = ParsoidRenderID::newFromKey( $key );
$this->assertSame( $expected->getKey(), $actual->getKey() );
}
public static function provideNewFromKey(): iterable {
yield [ '1/abc', new ParsoidRenderID( 1, 'abc' ) ];
yield [ '2/bar', new ParsoidRenderID( 2, 'bar' ) ];
}
/**
* @dataProvider provideBadKeys
* @covers \MediaWiki\Edit\ParsoidRenderID::newFromKey
*/
public function testBadNewFromKey( $key ): void {
$this->expectException( InvalidArgumentException::class );
$this->expectExceptionMessage( "Bad key: $key" );
ParsoidRenderID::newFromKey( $key );
}
public static function provideBadKeys(): iterable {
yield [ '' ];
yield [ '1' ];
}
}