CVE-2023-36665 is a critical prototype pollution vulnerability in protobuf.js (the protobufjs npm package) that carries a CVSS score of 9.8 and can lead to remote code execution or denial of service. A user-controlled protobuf message can be crafted to pollute Object.prototype, adding or overwriting properties and functions that every object in the running Node.js process then inherits. If your service parses protobuf data from any untrusted source, this is a patch-now finding.
What prototype pollution means here
In JavaScript, almost every object shares a prototype chain that ultimately reaches Object.prototype. Prototype pollution is a class of bug where an attacker manipulates that shared prototype through a normal-looking data path. Once Object.prototype is polluted, the injected properties appear on objects that never explicitly set them, which lets an attacker change application logic, bypass checks, or in the worst case reach a code execution sink.
For CVE-2023-36665 the entry point is protobuf.js's util.setProperty function. It sets nested object properties based on a path string. Before the fix, it blocked the __proto__ key but did not block prototype. An attacker who controls the path could therefore reach and modify the prototype chain through the prototype property instead.
How an attacker reaches the flaw
The GitHub advisory (GHSA-h755-8qp9-cq85) describes several triggers. Exploitation can happen when an application:
- uses
parseto parse protobuf messages on the fly, - loads
.protofiles with theloadorloadSyncfunctions, or - passes untrusted input to
ReflectionObject.setParsedOptionorutil.setProperty.
The common thread is untrusted input flowing into protobuf.js's parsing or reflection paths. If your protobuf definitions and messages are entirely internal and never influenced by an outside caller, your practical exposure is lower — but protobuf is frequently used precisely at service boundaries where messages arrive from clients or peers, which is exactly where the danger lives.
Affected versions and the fix
The vulnerability affects protobuf.js:
- 6.10.0 up to (but not including) 6.11.4
- 7.0.0 up to (but not including) 7.2.4
The patched releases are 6.11.4 and 7.2.4, which add prototype to the blocklist in util.setProperty. Note the subtlety many teams tripped over: there was follow-up discussion (see googleapis/gax-nodejs issue #1586) about whether the fix fully landed, and the practical guidance settled on moving to 7.2.5 or later on the 7.x line to be safe. Upgrade to the latest 7.x release rather than pinning to the exact minimum patched version.
Check what you actually have resolved:
npm ls protobufjs
Because protobuf.js is a workhorse dependency, you will very often find it transitively rather than in your own package.json. Google's @grpc/grpc-js, google-gax, Firebase libraries, and many others depend on it. The npm ls output shows you the full tree and which parent brought it in.
Remediating it
The direct fix is a version bump:
npm install protobufjs@latest
# or pin a known-good version
npm install protobufjs@7.2.5
When protobuf.js is transitive, upgrade the parent package that owns it — that is the durable fix. If the parent has not yet moved to a patched protobuf.js, you can force resolution while you wait, but treat overrides as temporary:
{
"overrides": {
"protobufjs": ">=7.2.5"
}
}
The overrides field (npm 8.3+) pins the transitive version across the whole tree. Yarn uses resolutions for the equivalent behavior. After applying it, run npm ls protobufjs again and confirm every instance resolves to a patched version — nested duplicates are common and a single stale copy still leaves you exposed.
Reducing your blast radius
Patching is necessary but the deeper habit is not trusting the deserialization boundary. A few defensive practices help against this whole class of bug:
- Validate and constrain protobuf input against a known schema before feeding it to reflection-heavy code paths.
- Avoid passing externally-influenced strings into
setParsedOptionorutil.setProperty. - On modern Node.js, consider freezing prototypes in security-sensitive services, though test carefully since it can break libraries that mutate prototypes intentionally.
Prototype pollution is not unique to protobuf.js — it has hit lodash, minimist, and many others. Treating deserialization of untrusted data as a trust boundary, rather than a formality, is what actually reduces recurrence.
Finding it across your fleet
The reason CVE-2023-36665 caught so many teams off guard is that almost nobody depends on protobuf.js directly. It arrives four layers deep under a gRPC or cloud SDK dependency. Manual package.json review will never catch that; you need to resolve the full lockfile graph. Continuous software composition analysis does exactly this — an SCA tool such as Safeguard can flag a vulnerable transitive protobufjs and point to the parent package pulling it in, so you know whether to bump the library or apply an override. For a broader look at how these tools stack up, see our comparison with Snyk.
FAQ
How severe is CVE-2023-36665?
It is rated critical, CVSS 9.8. Successful exploitation can lead to remote code execution or denial of service, because polluting Object.prototype affects every object in the running Node.js process.
Which protobuf.js versions are safe?
The fix landed in 6.11.4 and 7.2.4, but because of follow-up concerns about completeness on the 7.x line, upgrade to 7.2.5 or later to be safe. Always confirm with npm ls protobufjs after upgrading.
I don't import protobufjs directly — am I still affected?
Very possibly. protobuf.js is a common transitive dependency of gRPC and cloud SDK libraries. Run npm ls protobufjs to see whether it appears anywhere in your tree and which parent package brought it in.
Can I fix it without waiting for my dependencies to update?
Yes, temporarily. Use the npm overrides field (or Yarn resolutions) to force protobuf.js to a patched version across the tree. Treat it as a stopgap and remove the override once the parent package ships a fixed release.