Is there an existing issue for this?
Is this a regression?
No
Current behavior
@jsverse/transloco-keys-manager@8.1.0 appears to allow prototype pollution during the public CLI extract workflow when it merges existing translation JSON files.
The issue is reachable through the public CLI:
transloco-keys-manager extract -i src -o i18n -l en
When an existing translation file such as i18n/en.json contains an own __proto__ property, the CLI reads it as the current translation and later merges it through mergeDeep({}, translation, currentTranslation).
Relevant distributed code:
// keys-builder/utils/get-current-translation.js
function parseJson(path) {
return fs.readJsonSync(path, { throws: false }) || {};
}
// keys-builder/utils/create-translation.js
function resolveTranslation({ currentTranslation, translation, replace, removeExtraKeys: removeExtraKeysParam }) {
if (replace) {
return mergeDeep({}, translation);
}
if (removeExtraKeysParam) {
currentTranslation = removeExtraKeys(currentTranslation, translation);
}
return mergeDeep({}, translation, currentTranslation);
}
// utils/object.utils.js
export function mergeDeep(target, ...sources) {
if (!sources.length) return target;
const source = sources.shift();
if (isObject(target) && isObject(source)) {
for (const key in source) {
if (isObject(source[key])) {
if (!target[key]) Object.assign(target, { [key]: {} });
mergeDeep(target[key], source[key]);
} else {
Object.assign(target, { [key]: source[key] });
}
}
}
return mergeDeep(target, ...sources);
}
There does not appear to be a guard for prototype-pollution keys such as:
__proto__
constructor
prototype
When key === "__proto__", target[key] resolves to Object.prototype for a normal object target. The recursive call then writes attacker-controlled properties onto Object.prototype.
Minimal reproduction:
Create these files:
package.json
{
"name": "transloco-keys-manager-pp-repro",
"private": true,
"type": "module",
"scripts": {
"test": "NODE_OPTIONS='--import ./observe.mjs' transloco-keys-manager extract -i src -o i18n -l en"
},
"dependencies": {
"@jsverse/transloco-keys-manager": "8.1.0"
}
}
src/app.ts
import { marker } from "@jsverse/transloco-keys-manager";
marker("safe.key");
i18n/en.json
{
"__proto__": {
"translocoPolluted": "yes"
}
}
observe.mjs
process.on("exit", () => {
console.log("Object.prototype.translocoPolluted:", Object.prototype.translocoPolluted);
console.log("plain object inherited:", ({}).translocoPolluted);
delete Object.prototype.translocoPolluted;
});
Then run:
Observed output:
Object.prototype.translocoPolluted: yes
plain object inherited: yes
The NODE_OPTIONS=--import ./observe.mjs hook is only used to observe the CLI process before it exits. The vulnerable behavior is triggered by the public transloco-keys-manager extract command, not by importing internal package paths.
Expected behavior
Translation files should be treated as data only. Existing translation JSON containing keys such as __proto__, constructor, or prototype should not modify Object.prototype or other inherited prototypes during extract.
The expected output for the reproduction should be:
Object.prototype.translocoPolluted: undefined
plain object inherited: undefined
Please provide a link to a minimal reproduction of the bug
Minimal reproduction files are shown above. If required, I can also provide them as a small public repository or attached zip.
Transloco Config
No custom Transloco configuration is required.
Debug Logs
The reproduction can be run with debug mode as well. The core issue is visible from the final observed output:
Object.prototype.translocoPolluted: yes
plain object inherited: yes
Please provide the environment you discovered this bug in
Transloco: not required for this reproduction
Transloco Keys Manager: `@jsverse/transloco-keys-manager@8.1.0`
Angular: not required for this reproduction
Node: `v24.3.0`
Package Manager: `npm`
OS: macOS
Additional context
The direct impact is prototype pollution inside the transloco-keys-manager CLI process.
This is not a standalone remote code execution issue by itself. The practical impact depends on whether a project or CI job runs the keys manager against untrusted or partially trusted translation files, for example from a pull request, localization contribution, generated translation artifact, or compromised project file.
Prototype pollution can cause application-specific effects such as unexpected option changes, denial of service, or unsafe behavior if polluted properties are later consumed by other code in the same process. In my local test, installing prettier caused the polluted prototype state to affect the prettier step with:
Failed to run prettier Getter must be a function: yes
Suggested fix:
Reject prototype-pollution keys before reading from or assigning to dynamic object keys in mergeDeep(), including:
__proto__
constructor
prototype
It would also be safer to avoid for...in for untrusted objects and use own-property iteration only, for example Object.keys() / Object.entries(), combined with the dangerous-key guard.
GitHub currently shows no security policy detected for this repository:
https://github.com/jsverse/transloco-keys-manager/security
Would you consider adding a SECURITY.md file and enabling GitHub Private Vulnerability Reporting so future security-sensitive reports can be submitted privately?
I would like to make a pull request for this bug:
Not immediately. I am happy to validate a patch or help with a small follow-up if useful.
I would like to make a pull request for this bug
Yes 🚀
Is there an existing issue for this?
Is this a regression?
No
Current behavior
@jsverse/transloco-keys-manager@8.1.0appears to allow prototype pollution during the public CLIextractworkflow when it merges existing translation JSON files.The issue is reachable through the public CLI:
When an existing translation file such as
i18n/en.jsoncontains an own__proto__property, the CLI reads it as the current translation and later merges it throughmergeDeep({}, translation, currentTranslation).Relevant distributed code:
There does not appear to be a guard for prototype-pollution keys such as:
When
key === "__proto__",target[key]resolves toObject.prototypefor a normal object target. The recursive call then writes attacker-controlled properties ontoObject.prototype.Minimal reproduction:
Create these files:
package.json{ "name": "transloco-keys-manager-pp-repro", "private": true, "type": "module", "scripts": { "test": "NODE_OPTIONS='--import ./observe.mjs' transloco-keys-manager extract -i src -o i18n -l en" }, "dependencies": { "@jsverse/transloco-keys-manager": "8.1.0" } }src/app.tsi18n/en.json{ "__proto__": { "translocoPolluted": "yes" } }observe.mjsThen run:
npm install npm testObserved output:
The
NODE_OPTIONS=--import ./observe.mjshook is only used to observe the CLI process before it exits. The vulnerable behavior is triggered by the publictransloco-keys-manager extractcommand, not by importing internal package paths.Expected behavior
Translation files should be treated as data only. Existing translation JSON containing keys such as
__proto__,constructor, orprototypeshould not modifyObject.prototypeor other inherited prototypes duringextract.The expected output for the reproduction should be:
Please provide a link to a minimal reproduction of the bug
Minimal reproduction files are shown above. If required, I can also provide them as a small public repository or attached zip.
Transloco Config
Debug Logs
Please provide the environment you discovered this bug in
Additional context
The direct impact is prototype pollution inside the
transloco-keys-managerCLI process.This is not a standalone remote code execution issue by itself. The practical impact depends on whether a project or CI job runs the keys manager against untrusted or partially trusted translation files, for example from a pull request, localization contribution, generated translation artifact, or compromised project file.
Prototype pollution can cause application-specific effects such as unexpected option changes, denial of service, or unsafe behavior if polluted properties are later consumed by other code in the same process. In my local test, installing
prettiercaused the polluted prototype state to affect the prettier step with:Suggested fix:
Reject prototype-pollution keys before reading from or assigning to dynamic object keys in
mergeDeep(), including:It would also be safer to avoid
for...infor untrusted objects and use own-property iteration only, for exampleObject.keys()/Object.entries(), combined with the dangerous-key guard.GitHub currently shows no security policy detected for this repository:
https://github.com/jsverse/transloco-keys-manager/security
Would you consider adding a
SECURITY.mdfile and enabling GitHub Private Vulnerability Reporting so future security-sensitive reports can be submitted privately?I would like to make a pull request for this bug:
Not immediately. I am happy to validate a patch or help with a small follow-up if useful.
I would like to make a pull request for this bug
Yes 🚀