Summary: Merging two lexemes (via API or special page) makes two edits; only the second edit is rate limited and run though edit filters.
If the title of this task sounds familiar, that’s because I based it on T345064: CVE-2023-45371: Merging items is not rate limited and only partially protected by AbuseFilter. In that task, we saw that Wikibase PHP code has two interfaces to save edits: the higher-level EditEntity, and the lower-level EntityStore. EditEntity, as implemented by MediaWikiEditEntity, handles permission checks, rate limits, edit filter hooks (e.g. AbuseFilter), and more; you really want to be using it if you can. However, it’s not quite suited for saving redirects. To solve the problem of missing rate limits and edit filters for merges and redirects in T345064, we added code to explicitly handle those things to ItemMergeInteractor and EntityRedirectCreationInteractor. (Side note: We’ll have to add more custom code to them in T356149 to handle temporary users correctly.)
Enter WikibaseLexeme. WikibaseLexeme also has the ability to merge entities, implemented in MergeLexemesInteractor (and exposed via a special page and an API). To perform the redirect, MergeLexemesInteractor uses an EntityRedirectCreationInteractor (implemented by MediaWikiLexemeRedirector), so it gets rate limit and edit filter handling from there. However, before redirecting the “source” lexeme to the “target” lexeme, it also edits the “target” lexeme to add data from the “source” lexeme; and this edit is done using the EntityStore interface (wrapped in a MediaWikiLexemeRepository), bypassing all the checks at the EditEntity level, and doesn’t implement those checks anywhere else either. Therefore, this first edit is not rate limited nor checked against AbuseFilter. (It is still permission-checked; that said, on Wikidata all users have the right to merge lexemes as far as I’m aware, so the permission check adds no little protection in production [edit: actually, it should at least protect against blocked users].)
Steps to reproduce:
- create two lexemes
- (they need to be compatible for merging, so they should not both have a lemma in the same language)
- create and enable an AbuseFilter that would block the first edit, but not the second edit, such as:
- conditions !(added_lines contains "#REDIRECT")
- prevent the user from performing the action in question, with message abusefilter-disallowed
- try to merge one lexeme into the other on Special:MergeLexemes
Expected result: an error from the AbuseFilter; actual result: both edits go through.