The previous implementation was using an ad-hoc regular expression which
was matching inside the data-mw attribute of Parsoid output, eg:
<sup about="#mwt42" [...] typeof="mw:Extension/ref mw:Error" data-mw="{"name":"ref","attrs":{"name":"infobox_stats_ref_rail"},"body":{"html":"<style data-mw-deduplicate=\"TemplateStyles:r1133582631\" typeof=\"...">
After substitution, the <link> element inserted contained " instead of
" and so broke out of the attribute.
Instead use a proper HTML tokenizer (via wikimedia/remex-html) so that
we don't allow bogus matches inside attribute values.
To fix up tests:
* Don't deduplicate styles when parsing UX messages (also helps performance)
* Don't deduplicate styles in ContentHandler integration tests
* Don't deduplicate styles by default in parser tests
(unless explicit option is set)
Depends-On: Id9801a9ff540bd818a32bc6fa35c48a9cff12d3a
Depends-On: I5111f1fdb7140948b82113adbc774af286174ab3
Followup-To: Ic0b17e361bf6eb0e71c498abc17f5f67f82318f8
Change-Id: I32d3d1772243c3819e1e1486351d16871b6e21c4
116 lines
3.6 KiB
PHP
116 lines
3.6 KiB
PHP
<?php
|
|
|
|
use MediaWiki\Content\ValidationParams;
|
|
use MediaWiki\Page\PageIdentity;
|
|
use MediaWiki\Page\PageIdentityValue;
|
|
use MediaWiki\Title\Title;
|
|
|
|
class JsonContentHandlerIntegrationTest extends MediaWikiLangTestCase {
|
|
|
|
public static function provideDataAndParserText() {
|
|
return [
|
|
[
|
|
[],
|
|
'<table class="mw-json"><tbody><tr><td>' .
|
|
'<table class="mw-json"><tbody><tr><td class="mw-json-empty">Empty array</td></tr>'
|
|
. '</tbody></table></td></tr></tbody></table>'
|
|
],
|
|
[
|
|
(object)[],
|
|
'<table class="mw-json"><tbody><tr><td class="mw-json-empty">Empty object</td></tr>' .
|
|
'</tbody></table>'
|
|
],
|
|
[
|
|
(object)[ 'foo' ],
|
|
'<table class="mw-json"><tbody><tr><th><span>0</span></th>' .
|
|
'<td class="mw-json-value">"foo"</td></tr></tbody></table>'
|
|
],
|
|
[
|
|
(object)[ 'foo', 'bar' ],
|
|
'<table class="mw-json"><tbody><tr><th><span>0</span></th>' .
|
|
'<td class="mw-json-value">"foo"</td></tr><tr><th><span>1</span></th>' .
|
|
'<td class="mw-json-value">"bar"</td></tr></tbody></table>'
|
|
],
|
|
[
|
|
(object)[ 'baz' => 'foo', 'bar' ],
|
|
'<table class="mw-json"><tbody><tr><th><span>baz</span></th>' .
|
|
'<td class="mw-json-value">"foo"</td></tr><tr><th><span>0</span></th>' .
|
|
'<td class="mw-json-value">"bar"</td></tr></tbody></table>'
|
|
],
|
|
[
|
|
(object)[ 'baz' => 1000, 'bar' ],
|
|
'<table class="mw-json"><tbody><tr><th><span>baz</span></th>' .
|
|
'<td class="mw-json-value">1000</td></tr><tr><th><span>0</span></th>' .
|
|
'<td class="mw-json-value">"bar"</td></tr></tbody></table>'
|
|
],
|
|
[
|
|
(object)[ '<script>alert("evil!")</script>' ],
|
|
'<table class="mw-json"><tbody><tr><th><span>0</span></th><td class="mw-json-value">"' .
|
|
'<script>alert("evil!")</script>"' .
|
|
'</td></tr></tbody></table>',
|
|
],
|
|
[
|
|
'{ broken JSON ]',
|
|
'Invalid JSON: $1',
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideDataAndParserText
|
|
* @covers JsonContentHandler::fillParserOutput
|
|
*/
|
|
public function testFillParserOutput( $data, $expected ) {
|
|
if ( !is_string( $data ) ) {
|
|
$data = FormatJson::encode( $data );
|
|
}
|
|
|
|
$content = new JsonContent( $data );
|
|
$contentRenderer = $this->getServiceContainer()->getContentRenderer();
|
|
$parserOutput = $contentRenderer->getParserOutput(
|
|
$content,
|
|
$this->createMock( Title::class ),
|
|
null,
|
|
null,
|
|
true
|
|
);
|
|
$this->assertInstanceOf( ParserOutput::class, $parserOutput );
|
|
$this->assertEquals( $expected, $parserOutput->getText( [
|
|
'deduplicateStyles' => false,
|
|
] ) );
|
|
}
|
|
|
|
/**
|
|
* @covers JsonContentHandler::validateSave
|
|
*/
|
|
public function testValidateSave() {
|
|
$handler = new JsonContentHandler();
|
|
$validationParams = new ValidationParams(
|
|
PageIdentityValue::localIdentity( 123, NS_MEDIAWIKI, 'Config.json' ),
|
|
0
|
|
);
|
|
|
|
$validJson = new JsonContent( FormatJson::encode( [ 'test' => 'value' ] ) );
|
|
$invalidJson = new JsonContent( '{"key":' );
|
|
|
|
$this->assertStatusGood( $handler->validateSave( $validJson, $validationParams ) );
|
|
$this->assertStatusError( 'invalid-json-data',
|
|
$handler->validateSave( $invalidJson, $validationParams ) );
|
|
|
|
$this->setTemporaryHook(
|
|
'JsonValidateSave',
|
|
static function ( JsonContent $content, PageIdentity $pageIdentity, StatusValue $status )
|
|
{
|
|
if ( $pageIdentity->getDBkey() === 'Config.json' &&
|
|
!isset( $content->getData()->getValue()->foo ) ) {
|
|
$status->fatal( 'missing-key-foo' );
|
|
}
|
|
}
|
|
);
|
|
|
|
$this->assertStatusError( 'invalid-json-data',
|
|
$handler->validateSave( $invalidJson, $validationParams ) );
|
|
$this->assertStatusError( 'missing-key-foo',
|
|
$handler->validateSave( $validJson, $validationParams ) );
|
|
}
|
|
}
|