Compare commits

...

10 commits

Author SHA1 Message Date
anterdc99
4da53e4e00
Fix indentations of special page aliases in Trad. Chinese
Change-Id: If502ebe94f3b6414f51c7238df4c49e756393d07
(cherry picked from commit b6ecdfb087106abf405691f48a222ae628c2ff14)
2025-02-06 21:06:56 +08:00
anterdc99
41e8b8d5e7
Add special page aliases for Simp. & Trad. Chinese
Change-Id: I1d30fe0ca4c1956201e465a32c9ca1aa5919f931
(cherry picked from commit 076db709a912ce23c922e4e10f8e9e94712201ac)
2025-02-06 21:02:36 +08:00
jenkins-bot
637e4121dc Merge "rest: Return a 400 for invalid render IDs" into REL1_43 2025-02-04 16:01:04 +00:00
jenkins-bot
ce24650bfa Merge "parser: Gracefully handle invalid ParsoidRenderID keys" into REL1_43 2025-02-04 15:58:58 +00:00
Reedy
a973a62478 Update git submodules
* Update extensions/Poem from branch 'REL1_43'
  to 8c1e853e2e7f08d20e0c78309509c90b46f73bae
  - Poem: Null coalescence $in
    
    Bug: T385588
    Change-Id: I64c660cd4518e12c4a1c543bf3b1732dbd7066a2
    (cherry picked from commit 88dead04dcbb01df42c1397df3dde3fcfdb80b52)
2025-02-04 15:43:21 +00:00
Máté Szabó
c4e9f987f1 rest: Return a 400 for invalid render IDs
Why:

- The REST API takes an optional renderid param when converting HTML
  back to source wikitext, which is user-provided and may be invalid.
- Invalid render IDs cause an InvalidArgumentException to be thrown that
  causes a 500 response.

What:

- Introduce a new error message for invalid render IDs in the REST API.
- Return a 400 with this new error message for HTML reverse-parses with
  an invalid render ID.

Bug: T385568
Change-Id: I062419fe8952329a39781a49cdca2e94c3996447
(cherry picked from commit cd1d42a5066e4bcb9b9d4ed9b4f7714fd428fea3)
2025-02-04 14:54:50 +00:00
Máté Szabó
b99dcc23bc 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)
2025-02-04 14:54:31 +00:00
Reedy
e751026153 Update git submodules
* Update extensions/Echo from branch 'REL1_43'
  to 3ef08eb81ec11e009cf79bc92ba172b59c073001
  - Hooks: Check for null option in onSpecialMuteModifyFormFields
    
    Bug: T385169
    Change-Id: I38c5614745abeb2fa66e881b586cd1660ff8ef9d
    (cherry picked from commit 35519f33b5c1bcdf344d689c356c32f4fd7f118f)
2025-02-04 12:15:50 +00:00
Translation updater bot
f4bd2c03a4 Update git submodules
* Update skins/Vector from branch 'REL1_43'
  to e3a1e3b3fc73f4c5257a2774a68105899952c307
  - Localisation updates from https://translatewiki.net.
    
    Change-Id: Ib448290cc2f37ae2a15409fffbe0e080aec93323
2025-02-04 06:22:21 +00:00
Translation updater bot
b9ae5c0b22 Update git submodules
* Update skins/Timeless from branch 'REL1_43'
  to 4fb4f38f2a69dd525898a64acef6924ef5c45d67
  - Localisation updates from https://translatewiki.net.
    
    Change-Id: Ic2928071e06c6535a0190d0aa1dde10108da3bf3
2025-02-04 06:22:18 +00:00
12 changed files with 205 additions and 119 deletions

@ -1 +1 @@
Subproject commit 5cb6026104f0a622b82721edf8510e75597ecc6c
Subproject commit 3ef08eb81ec11e009cf79bc92ba172b59c073001

@ -1 +1 @@
Subproject commit e7201baf478d38b0ddf5b8e5f625e83f69c9c3bf
Subproject commit 8c1e853e2e7f08d20e0c78309509c90b46f73bae

View file

@ -368,7 +368,14 @@ class HtmlInputTransformHelper {
throw new LocalizedHttpException( new MessageValue( "rest-bad-etag", [ $key ] ), 400 );
}
} else {
try {
$originalRendering = ParsoidRenderID::newFromKey( $key );
} catch ( InvalidArgumentException $e ) {
throw new LocalizedHttpException(
new MessageValue( 'rest-parsoid-bad-render-id', [ $key ] ),
400
);
}
}
} elseif ( !empty( $original['html'] ) || !empty( $original['data-parsoid'] ) ) {
// NOTE: We might have an incomplete PageBundle here, with no HTML but with data-parsoid!

View file

@ -65,6 +65,7 @@
"rest-unsupported-target-format": "The requested target format is not supported.",
"rest-parsoid-resource-exceeded": "Resource limit exceeded",
"rest-parsoid-error": "Parsoid error.",
"rest-parsoid-bad-render-id": "Bad Parsoid render ID: $1",
"rest-bad-stash-key": "Bad stash key.",
"rest-html-key-expected": "Expected <var>html</var> key in body",
"rest-invalid-transform": "Invalid transform: $1 to $2",

View file

@ -69,6 +69,7 @@
"rest-transform-missing-title": "Error message for REST API debugging, shown when no title or wikitext is provided",
"rest-unsupported-target-format": "Error message for REST API debugging, shown when the requested target format is not supported",
"rest-parsoid-resource-exceeded": "Error message for REST API debugging, shown when a resource limit is exceeded while converting between wikitext and HTML.",
"rest-parsoid-bad-render-id": "Error message for REST API debugging, shown when a ParsoidRenderID object cannot be created from the provided key. Parameters:\n* $1: The key",
"rest-parsoid-error": "Error message for REST API debugging, indicating an unspecified backend error occurred while converting between wikitext and HTML.\n\n[[:mw:Parsoid|Parsoid]] is the name of a software library; do not translate that name.",
"rest-bad-stash-key": "Error message for REST API debugging, shown when When the rednerid or etag given in the request is not a valid stash key.",
"rest-html-key-expected": "Error message for REST API debugging, shown when when the \"html\" key is missing from the request body",

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

@ -231,6 +231,7 @@ $specialPageAliases = [
'Mypage' => [ '我的用户页' ],
'Mytalk' => [ '我的讨论页', '我的对话页' ],
'Myuploads' => [ '我上传的文件', '我的文件' ],
'NamespaceInfo' => [ '命名空间信息' ],
'Newimages' => [ '新建文件', '新建图像' ],
'Newpages' => [ '新建页面' ],
'NewSection' => [ '新章节' ],

View file

@ -144,24 +144,35 @@ $specialPageAliases = [
'AllMyUploads' => [ '所有我的上傳', '所有我的檔案', '所有本人上載', '所有本人檔案' ],
'Allpages' => [ '所有頁面' ],
'Ancientpages' => [ '最舊頁面', '最早頁面' ],
'ApiHelp' => [ 'Api使用說明' ],
'ApiHelp' => [ 'API說明', 'API使用說明' ],
'ApiSandbox' => [ 'API沙盒' ],
'AuthenticationPopupSuccess' => [ '認證成功彈窗' ],
'AutoblockList' => [ '自動封鎖清單', '列出自動封鎖' ],
'Badtitle' => [ '無效標題' ],
'Blankpage' => [ '空白頁面' ],
'Block' => [ '封鎖', '封鎖IP', '封鎖使用者', '封禁', '封禁IP', '封禁使用者' ],
'BlockList' => [ '封鎖清單', 'IP封鎖清單', '封禁列表', 'IP封禁列表' ],
'Booksources' => [ '書籍來源', '網路書源' ],
'BotPasswords' => [ '機器人密碼' ],
'BrokenRedirects' => [ '損壞的重新導向', '損壞的重定向頁' ],
'Categories' => [ '分類', '頁面分類' ],
'ChangeEmail' => [ '更改信箱', '修改郵箱' ],
'ChangePassword' => [ '更改密碼', '修改密碼', '密碼重設' ],
'ChangeContentModel' => [ '變更內容模型' ],
'ChangeCredentials' => [ '變更憑證' ],
'ChangeEmail' => [ '變更信箱', '修改郵箱' ],
'ChangePassword' => [ '變更密碼', '修改密碼', '密碼重設' ],
'ComparePages' => [ '頁面比較' ],
'Confirmemail' => [ '確認信箱', '確認電郵' ],
'Contribute' => [ '做出貢獻' ],
'Contributions' => [ '使用者貢獻', '用戶貢獻' ],
'CreateAccount' => [ '建立帳號', '建立帳戶' ],
'Deadendpages' => [ '無連結頁面', '斷鏈頁面' ],
'DeletedContributions' => [ '已刪除的貢獻', '已刪除的用戶貢獻' ],
'DeletePage' => [ '刪除頁面', '刪除' ],
'Diff' => [ '編輯差異' ],
'DoubleRedirects' => [ '雙重的重新導向', '雙重重定向頁面' ],
'EditPage' => [ '編輯頁面', '編輯' ],
'EditRecovery' => [ '編輯恢復' ],
'EditTags' => [ '編輯標籤' ],
'EditWatchlist' => [ '編輯監視清單', '編輯監視列表' ],
'Emailuser' => [ '寄信給使用者', '寄信', '電郵使用者' ],
'ExpandTemplates' => [ '展開模板' ],
@ -169,14 +180,17 @@ $specialPageAliases = [
'Fewestrevisions' => [ '最少修訂頁面' ],
'FileDuplicateSearch' => [ '重複檔案搜尋', '搜尋重複檔案' ],
'Filepath' => [ '檔案路徑' ],
'GoToInterwiki' => [ '前往跨wiki頁面' ],
'Import' => [ '匯入', '匯入頁面' ],
'Invalidateemail' => [ '無效的信箱' ],
'JavaScriptTest' => [ 'JavaScript測試' ],
'LinkAccounts' => [ '連結帳號' ],
'LinkSearch' => [ '連結搜尋', '搜尋網頁連結' ],
'Listadmins' => [ '管理員清單', '管理員列表' ],
'Listbots' => [ '機器人清單', '機械人列表' ],
'ListDuplicatedFiles' => [ '重複檔案清單', '重複檔案列表' ],
'Listfiles' => [ '檔案清單', '圖片清單', '檔案列表', '圖像列表' ],
'Listgrants' => [ '列出授權' ],
'Listgrouprights' => [ '群組權限清單', '使用者群組權限', '群組權限列表' ],
'Listredirects' => [ '重新導向清單', '重定向頁面列表' ],
'Listusers' => [ '使用者清單', '使用者列表' ],
@ -189,27 +203,37 @@ $specialPageAliases = [
'MIMEsearch' => [ 'MIME搜尋' ],
'Mostcategories' => [ '最多分類的頁面', '最多分類頁面' ],
'Mostimages' => [ '被連結最多的檔案', '最多連結檔案' ],
'Mostinterwikis' => [ '最多_Interwiki_連結的頁面', '最多跨wiki連結' ],
'Mostinterwikis' => [ '最多跨wiki連結的頁面', '最多_Interwiki_連結的頁面', '最多跨wiki連結' ],
'Mostlinked' => [ '被連結最多的頁面', '最多連結頁面' ],
'Mostlinkedcategories' => [ '被連結最多的分類', '最多連結分類' ],
'Mostlinkedtemplates' => [ '被引用最多的頁面', '被連結最多的模板', '被使用最多的模板' ],
'Mostrevisions' => [ '最多修訂的頁面', '最多修訂頁面' ],
'Movepage' => [ '移動頁面' ],
'Mute' => [ '靜音' ],
'Mycontributions' => [ '我的貢獻' ],
'MyLanguage' => [ '我的語言' ],
'Mylog' => [ '我的日誌' ],
'Mypage' => [ '我的使用者頁面', '我的用戶頁' ],
'Mytalk' => [ '我的對話', '我的討論頁' ],
'Myuploads' => [ '我的上傳', '我的上載', '我的檔案' ],
'NamespaceInfo' => [ '命名空間資訊' ],
'Newimages' => [ '新增檔案', '新增圖片' ],
'Newpages' => [ '新增頁面', '新頁面' ],
'NewSection' => [ '新章節' ],
'PageData' => [ '頁面資料' ],
'PageHistory' => [ '頁面歷史', '歷史' ],
'PageInfo' => [ '頁面資訊', '資訊' ],
'PageLanguage' => [ '頁面語言' ],
'PagesWithProp' => [ '擁有屬性的頁面', '帶屬性頁面' ],
'PasswordPolicies' => [ '密碼原則' ],
'PasswordReset' => [ '重設密碼' ],
'PermanentLink' => [ '靜態連結', '永久連結' ],
'PermanentLink' => [ '固定連結', '靜態連結', '永久連結' ],
'Preferences' => [ '偏好設定' ],
'Prefixindex' => [ '字首索引', '前綴索引' ],
'Prefixindex' => [ '前綴索引', '字首索引' ],
'Protectedpages' => [ '受保護頁面', '已保護頁面' ],
'Protectedtitles' => [ '受保護標題', '已保護標題' ],
'ProtectPage' => [ '保護頁面', '保護' ],
'Purge' => [ '更新快取' ],
'RandomInCategory' => [ '隨機分類頁面', '於分類中隨機' ],
'Randompage' => [ '隨機頁面' ],
'Randomredirect' => [ '隨機重新導向', '隨機重定向頁面' ],
@ -217,8 +241,10 @@ $specialPageAliases = [
'Recentchanges' => [ '最近變更', '最近更改' ],
'Recentchangeslinked' => [ '已連結的最近變更', '相關變更', '連出更改' ],
'Redirect' => [ '重新導向', '重定向' ],
'RemoveCredentials' => [ '移除憑證' ],
'Renameuser' => [ '重新命名使用者' ],
'ResetTokens' => [ '重設密鑰', '覆寫令牌' ],
'RestSandbox' => [ 'REST沙盒' ],
'Revisiondelete' => [ '修訂刪除', '刪除或恢復版本' ],
'RunJobs' => [ '執行作業', '運行工作' ],
'Search' => [ '搜尋' ],
@ -226,6 +252,7 @@ $specialPageAliases = [
'Specialpages' => [ '特殊頁面' ],
'Statistics' => [ '統計資訊' ],
'Tags' => [ '標籤' ],
'TalkPage' => [ '討論頁' ],
'TrackingCategories' => [ '追蹤分類', '跟蹤分類' ],
'Unblock' => [ '解除封鎖', '解除封禁', '解禁' ],
'Uncategorizedcategories' => [ '未分類的分類', '未歸類分類' ],
@ -233,6 +260,7 @@ $specialPageAliases = [
'Uncategorizedpages' => [ '未分類的頁面', '未歸類頁面' ],
'Uncategorizedtemplates' => [ '未分類的模板', '未歸類模板' ],
'Undelete' => [ '取消刪除' ],
'UnlinkAccounts' => [ '解除連結帳號' ],
'Unlockdb' => [ '解除鎖定資料庫', '解除資料庫鎖定' ],
'Unusedcategories' => [ '未使用的分類', '未使用分類' ],
'Unusedimages' => [ '未使用的檔案', '未使用檔案' ],

@ -1 +1 @@
Subproject commit 3cc5ea9debd9ecf3fd4a0b87a7d98dd69f816262
Subproject commit 4fb4f38f2a69dd525898a64acef6924ef5c45d67

@ -1 +1 @@
Subproject commit b8febf782b5291d85c0d7a5f97fca9fd1b21abac
Subproject commit e3a1e3b3fc73f4c5257a2774a68105899952c307

View file

@ -1188,6 +1188,20 @@ class HtmlInputTransformHelperTest extends MediaWikiIntegrationTestCase {
$helper->getContent();
}
public function testHandlesInvalidRenderID(): void {
$page = $this->getExistingTestPage( __METHOD__ );
$body = [ 'html' => 'hi', 'original' => [ 'renderid' => 'foo' ] ];
$params = [];
$this->expectExceptionObject( new LocalizedHttpException(
new MessageValue( 'rest-parsoid-bad-render-id', [ 'foo' ] ),
400
) );
$this->newHelper( [], StatsFactory::newNull(), $page, $body, $params );
}
private function newHtmlToContentTransform( $html, $methodOverrides = [] ): HtmlToContentTransform {
$transform = $this->getMockBuilder( HtmlToContentTransform::class )
->onlyMethods( array_keys( $methodOverrides ) )

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' ];
}
}