The vulnerability, identified as GHSA-75px-5xx7-5xc7, is a prototype pollution issue in protobuf.js. The root cause is the library's use of plain JavaScript objects ({}) as internal lookup tables and for data representation without proper safeguards. In JavaScript, when a property is accessed on an object, the runtime will traverse the prototype chain if the property is not found on the object itself. An attacker who can first achieve a prototype pollution primitive (i.e., add properties to Object.prototype) can exploit this behavior.
Several key functions in protobuf.js were vulnerable because they accessed properties on objects without checking if the property was its own or inherited. For example, functions like Namespace.get would look up a type name in a nested object. If the name wasn't found, JavaScript would look at Object.prototype, and if the attacker had polluted it with a property of the same name, the attacker's value would be returned and treated as a valid type definition.
This becomes critical in functions that dynamically generate JavaScript code, such as converter.fromObject and decoder. These functions build new functions as strings based on the structure of protobuf messages. By feeding a polluted reflection object into these code generators, an attacker could cause malicious strings to be written into the generated code, leading to arbitrary code execution when that code is later run.
The patches address this by:
- Creating lookup tables with
Object.create(null), which creates objects with no prototype, preventing them from being affected by Object.prototype pollution.
- Using
Object.prototype.hasOwnProperty.call(obj, key) to ensure that a property is owned by the object itself before using it.
- Explicitly checking for and ignoring keys named
__proto__, constructor, or prototype when iterating over keys from user-supplied objects or when creating new objects.