A translation plugin wrote an admin password reset link into a database table that anyone could read
CVE-2026-19632 is a 9.8 in TranslatePress, a WordPress plugin on more than 400,000 sites. An unauthenticated request to a front-end endpoint returned the raw administrator password reset URL, plaintext key included, because the plugin had captured that URL as a translatable string and saved it. Patched in 3.3.2 on August 13, published on August 26. The specific bug is a five-minute fix. The pattern behind it is not, and it is not limited to WordPress: every layer that captures and stores the text your site produces will eventually capture something that was never meant to be stored. Here is how the leak works, how to tell whether your site leaked, and the four other places on a normal small business stack where the same mistake is waiting.
The interesting part of this bug is not that a WordPress plugin had a critical vulnerability. That happens most weeks. The interesting part is the mechanism, which is one of the cleanest examples we have seen of a failure mode that has nothing to do with WordPress and is sitting in a great many stacks right now.
Here it is in one sentence. WordPress sent an administrator a password reset email. A translation plugin, doing exactly what it was designed to do, noticed the text of that email, decided it was a string that might need translating, and wrote it into a database table. A different part of the same plugin serves that table to anybody who asks, without authentication, because translations are public by nature. So the one-time reset link that was supposed to exist only in one person’s inbox became a value you could fetch over HTTP.
The specifics, with dates
CVE-2026-19632, in the plugin TranslatePress: Translate Multilingual sites with AI Translation by Cozmoslabs.
| CVSS 3.1 base score | 9.8 critical (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) |
| Affected versions | all versions up to and including 3.3.1 |
| Fixed in | 3.3.2 |
| Current version | 3.3.4, released August 25, 2026 |
| Active installs | 400,000+ |
| Attack requirements | none: unauthenticated, no user interaction |
The vector is the trp_get_translations_regular AJAX action. NVD’s entry states that unauthenticated attackers can extract the raw administrator password reset URL, including the plaintext reset key and login parameters, from the translation dictionary table, which is enough for full administrator account takeover.
The disclosure timeline was quick, and to Cozmoslabs’ credit the patch was quicker:
| Date | Event |
|---|---|
| August 11, 2026 | Researcher momopon1415 reports it through the Wordfence Bug Bounty Program |
| August 12, 2026 | Wordfence discloses to Cozmoslabs |
| August 13, 2026 | TranslatePress 3.3.2 ships with the fix |
| August 25, 2026 | 3.3.4 released, carrying a separate security fix |
| August 26, 2026 | CVE published to NVD, trade press coverage begins |
Two days from report to patch is genuinely good vendor behaviour. The bounty was $975.
How a password reset link ends up in a translation table
TranslatePress works by intercepting the text WordPress produces, matching it against a dictionary, and substituting the translated version. To have a dictionary in the first place, it needs to discover strings, and the way it discovers strings is by watching the site run and saving what it sees. That behaviour has a name in the plugin’s own settings. The Advanced tab has an option called Manual Translation Only, described by TranslatePress as follows: the plugin “pro-actively scans and saves strings in the database when users access translated pages,” and the setting “disables this functionality and only allows translation and string saving when inside the Translation Editor.”
That option is off by default. Automatic string saving is on by default, and for a translation plugin that is the correct default. Nobody wants to hand-enter every string on a 300 page site.
Now add the second ingredient. WordPress lets each user set a profile language, independent of the site language. If an administrator’s profile locale is set to one of the site’s published secondary languages, then the emails WordPress sends that administrator get rendered in that locale, which routes them through the translation layer.
Put those together and the password reset email becomes, from the plugin’s point of view, an ordinary piece of site text in a secondary language. It gets captured. It gets written to the dictionary table for that language pair, which on a default install is named something like wp_trp_dictionary_en_us_fr_fr. And the reset URL, with its one-time key sitting in a query parameter, is part of that text.
The third ingredient is that dictionary tables are meant to be readable. Translations are shown to visitors. The plugin exposes a front-end AJAX endpoint so a page can ask for the strings it needs, and that endpoint does not require a login, because visitors do not have logins. The bug is not that the endpoint was public. The bug is what had been allowed into the table it reads from.
So the exploit is: ask the public endpoint for translations, read back the reset URL, use it, become an administrator.
NVD is explicit that both conditions have to hold: automatic string saving must be enabled, which is the default, and the target administrator’s profile locale must be set to a published secondary language. That second condition is the one that decides whether your particular site leaked. It sounds narrow. On a bilingual site it is not narrow at all, because the person most likely to have set their own WordPress profile to French or Spanish or Simplified Chinese is the person who set up the translation plugin, and that person is usually an administrator.
For a Toronto business running an English and French site, this is close to the default configuration.
The pattern is bigger than this plugin
Here is the part worth keeping after you have updated.
TranslatePress is a string capture layer. It sits in the path of text your application generates, decides that text is data worth keeping, and persists it somewhere with different access rules than the place it came from. The password reset email had exactly one intended reader and a lifetime of a few hours. The dictionary table has an unbounded lifetime and a public reader.
That is not a translation problem. That is the shape of the problem, and this shape recurs:
- Session replay and heatmap tools. They record what was rendered in the browser, which includes anything the page displayed. The mitigation is field masking, and field masking is opt-in, which means it protects the fields somebody remembered.
- Error trackers that capture request context. A 500 error on a form submission is enormously useful to debug with the request body attached. The request body on a login form is a password. Most tools scrub common field names by default. Most tools do not know your field is called
pw_confirm_2. - Full-page caches. A cache keyed on URL rather than on session serves one user’s rendered page to the next user. This is how “logged in as someone else” bugs happen, and it is a configuration mistake rather than a code one, which is why it survives code review.
- Application logs. Logging the full URL of every request is standard. One-time tokens travel in URLs. Password reset links, magic login links, signed download URLs, and invitation links all end up in a log file with a retention policy nobody set and a set of readers nobody enumerated.
- AI features, which are the newest instance. Anything that sends page content or user input to a model for summarisation, translation, or search is a capture layer with a retention policy at the far end that you do not control. We wrote about the trust boundary side of this in our post on AI agents and prompt injection; this is the same boundary viewed from the storage side.
The question that catches all of these is not “is this tool secure.” It is: what does this tool store, where does it store it, and who can read that place? Ask it about every integration that sees your site’s text. It takes about a minute per tool and it is the only question in this article that keeps working after August 2026.
What TranslatePress’s own changelog shows
We pulled the plugin’s full changelog from the WordPress.org plugin repository. Read as a sequence, it makes the point better than we can:
| Version | Security fix |
|---|---|
| 3.2.6 | XSS via gettext wrappers; multisite add-on action security |
| 3.3 | XSS via gettext markers in comments; XSS in Translation Editor strings dropdown |
| 3.3.1 | none (database optimisation fixes) |
| 3.3.2 | sensitive information exposure, CVE-2026-19632 |
| 3.3.3 | XSS in Translation Editor via gettext markers in a search query parameter |
| 3.3.4 | unauthenticated stored XSS via comment noise-key injection |
Five of the last six releases patched a security issue, and every one of them is in the same subsystem: the machinery that captures strings, marks them, stores them, and hands them back. That is not a plugin with sloppy code. TranslatePress is a mature product, it has been on the repository since 2017, it has been downloaded more than 20 million times, and it holds a 94 out of 100 rating from over 1,600 reviews. It is a plugin whose core job places it directly in the path of every piece of text the site produces, which means its bug surface is the size of your content.
We would say the same about any caching, logging, or personalisation layer with the same reach. Reach is the risk. It is worth knowing which of your plugins have it.
What to do, in order
1. Check your version. WordPress admin, Plugins, find TranslatePress. 3.3.2 or later is patched. Go to 3.3.4 while you are there, because 3.3.3 and 3.3.4 each fixed a separate XSS issue. If you are on 3.3.1 or below, you are exposed right now and the rest of this list is urgent rather than precautionary.
2. Work out whether you met the conditions. Under Users, open each administrator account and look at the Language field on their profile. If every administrator’s language is the site default, or blank, the reset URL was never routed through the secondary-language path and you almost certainly did not leak. If any administrator’s profile language is set to one of your published translation languages, and you were running 3.3.1 or earlier, treat the account as potentially exposed.
3. If you were exposed, assume the link leaked and act accordingly. A leaked reset URL is only useful until it is used or expires, but you have no way to know whether somebody used it during the window. Force a password reset on every administrator account. List the administrators and confirm you recognise every one, because the first thing an attacker does with admin access is create a second, less interesting looking account. Rotate the authentication keys and salts in wp-config.php, which invalidates every existing session including the attacker’s. Then read our wp2shell post from last week, specifically the section on checking for persistence, because the checks are the same and the logic is the same: patching closes the door, it does not remove anyone already inside.
4. Move administrator profile languages back to the site default. This is defence in depth rather than a fix, and it costs nothing. The site’s language and an individual’s admin interface language do not need to match, and the secondary-language profile setting is the precondition that turned a design decision into a critical CVE. On a bilingual site, let the front end be bilingual and let the admin panel be one language.
5. Consider turning on Manual Translation Only once the site is translated. Under TranslatePress, Settings, Advanced. It stops the plugin from scanning and saving strings on front-end page loads and restricts capture to the Translation Editor. On a site whose content is already translated and does not change daily, the automatic behaviour is doing very little and capturing a great deal. On a site that publishes constantly, leave it on and rely on being patched.
6. Look in the tables, if you want to be certain. In phpMyAdmin or whatever your host provides, browse the wp_trp_dictionary_* tables and search the original-string column for key= or action=rp or wp-login. Anything that comes back is a captured URL that should never have been captured. This is also a good habit for auditing any capture layer: do not ask what it should have stored, look at what it did store.
The uncomfortable question about your other plugins
Everything above takes twenty minutes and resolves one CVE. The question underneath it takes longer and matters more.
Go to your Plugins page and read the list as an inventory of things with access to your site’s text and database, rather than as a list of features. Most entries will be narrow: a contact form sees form submissions, a gallery sees images. A few will be broad: caching, translation, SEO, analytics, membership, anything that says “optimisation” or “personalisation.” The broad ones are where the reach is, and reach is what turns an ordinary bug into a 9.8.
You do not need to remove them. You need to know which ones they are, keep those specific ones current on a short timeline rather than a monthly one, and be able to answer the storage question for each. If you cannot name your broad plugins right now, that is the finding, and it is the same finding we keep arriving at from different directions: the cost of a WordPress site is not the build, it is knowing what is running on it. We made the same argument in Astro vs Next.js vs WordPress and this week’s CVE is another data point for it.
If you want a second opinion on a site you own, send us the URL. We will tell you what is running, which plugins have the reach to matter, and whether any of them are currently unpatched. It takes us about ten minutes and it does not cost anything.
Sources
- CVE-2026-19632, National Vulnerability Database, published August 26, 2026
- TranslatePress plugin page and changelog, WordPress.org plugin repository, retrieved August 27, 2026
- WordPress.org plugin information API, retrieved August 27, 2026, for version, release date and download figures
- Advanced Settings: Manual Translation Only, TranslatePress documentation
- WordPress Plugin Vulnerability Exposes 400,000 Sites to Account Takeover Attacks, Cyber Security News, August 26, 2026
- TranslatePress plugin allows takeover of 400,000 installations, heise online, August 26, 2026