Skip to content

fix: escape bytes field default value in generated toObject (GHSA-66ff-xgx4-vchm)#2351

Closed
greymoth-jp wants to merge 1 commit into
protobufjs:protobufjs-v6.11.xfrom
greymoth-jp:backport-bytes-default-ghsa-66ff
Closed

fix: escape bytes field default value in generated toObject (GHSA-66ff-xgx4-vchm)#2351
greymoth-jp wants to merge 1 commit into
protobufjs:protobufjs-v6.11.xfrom
greymoth-jp:backport-bytes-default-ghsa-66ff

Conversation

@greymoth-jp

Copy link
Copy Markdown

The 6.x line still generates toObject code that writes a bytes field's default value into the generated function as code. A schema-controlled, non-string default for a bytes field reaches the generated conversion function unescaped, so a crafted descriptor can run arbitrary JavaScript (GHSA-66ff-xgx4-vchm / CVE-2026-44293).

This was fixed on 7.x and 8.x in 75392ea, released in protobufjs-v7.5.6 and protobufjs-v8.0.2, but the same change was not carried to the 6.11.x line. 6.11.x is still published as the latest-6 dist-tag (6.11.6, May 2026), and 6.11.6 only picked up the type-name filter from that batch, not this one.

Root cause

In converter.toObject, the bytes default was turned into a literal string "[" + slice(typeDefault).join(",") + "]" and emitted with %s, which is inserted into the generated function body as-is. field.js only converts a default to a buffer when it is a string, so a non-string default (for example an array supplied through a JSON descriptor) keeps its raw value, and the joined text can close the array literal and append statements.

Fix

Mirror the 7.x/8.x change: keep the default as an array and emit it with %j instead of building the literal by hand and emitting it with %s.

Reproduction (published-version layer)

const protobuf = require("protobufjs");
const root = protobuf.Root.fromJSON({ nested: { Test: { fields: {
  b: { type: "bytes", id: 1, options: { default: ["1];globalThis.PWNED=true;void[2"] } }
} } } });
const Test = root.lookupType("Test");
Test.toObject(Test.create(), { defaults: true, bytes: Array });
console.log(globalThis.PWNED); // 6.11.6: true,  7.5.6 / 8.0.2: undefined

Verification

Added a regression test in tests/api_converter_bytes_default.js. Reverting only the src/converter.js change makes that test fail (the injected code runs inside the generated toObject); with the change it passes. The full source suite is green (1248/1248). For ordinary bytes defaults the generated output is unchanged, since %j of a numeric byte array produces the same text the old code built by hand.

Refs: GHSA-66ff-xgx4-vchm, CVE-2026-44293, 75392ea

@dcodeIO

dcodeIO commented Jun 30, 2026

Copy link
Copy Markdown
Member

Thanks, though there is an argument to make here about whether it makes sense to keep maintaining 6.x. It's very old, and no longer supported (see). Users should upgrade to 8.x. Leaving this open for a bit, though, just in case.

@dcodeIO dcodeIO closed this Jul 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants