-
Type:
Bug
-
Resolution: Unresolved
-
Priority:
Minor - P4
-
None
-
Affects Version/s: None
-
Component/s: None
-
2
-
None
-
None
-
None
-
None
-
None
-
None
-
None
-
- Summary
While auditing `mongodb/js-bson` we found three independent input-validation
issues that all reduce to the same root pattern: a BSON invariant that is
enforced on one code path is missing on a neighbouring one. None of them is a
memory-safety problem — js-bson stays within JS/`Uint8Array` bounds throughout —
but each lets malformed or oversized input reach an outcome the library is
otherwise designed to reject (a non-terminating scan, a malformed serialized
vector, or stack exhaustion).
We are reporting them together because they share a pinned ref, can be fixed in
one pass, and illustrate the same "validation enforced here but not there" gap.
We want to be upfront about reachability: these are library-level defects with
real triggers, but exploitability depends on how an embedding application uses
the affected API. The per-issue "Conditions required" notes below state what an
application must be doing for the defect to matter; we are not claiming every
js-bson consumer is exposed.
- Affected
- Summary
- Project: `mongodb/js-bson`
- Pinned ref: `5b42c5a1535d45ec89ab9f1ed3bb249d09730e3c`
—
- 1. `onDemand.parseToElements` — non-terminating scan on zero-length string
- 1. `onDemand.parseToElements` — non-terminating scan on zero-length string
- *What*: A 10-byte BSON document with a string element declaring length `0`
passes the on-demand parser's size/terminator guards, advances the cursor to
the declared document end, and then makes `findNull` scan from past the
buffer. `findNull` has no bounds check, and out-of-range `Uint8Array` reads
return `undefined` (`undefined !== 0x00` is always true), so the loop never
terminates — synchronous CPU hang. - *Root cause*: `findNull` in `src/parser/on_demand/parse_to_elements.ts`
assumes the document is well-formed and omits a `< bytes.length` guard. The
ordinary deserializer rejects `stringSize <= 0` (`src/parser/deserializer.ts`),
but that check is not mirrored on the on-demand path. - *Conditions required: an application must call the **experimental*
`BSON.onDemand.parseToElements` API directly on untrusted bytes, without a
surrounding timeout / worker isolation. Applications using the standard
`BSON.deserialize` path are *not* affected. We acknowledge `onDemand` is not
a common entry point today. - *Details & PoC*: `int-mongodb-js-bson-ondemand-zero-length-string-hang/README.md`
and `int-mongodb-js-bson-ondemand-zero-length-string-hang/poc/` (run `bash poc/run.sh`).
- 2. `validateBinaryVector` — zero-length subtype-9 Binary serialized as malformed vector
- 2. `validateBinaryVector` — zero-length subtype-9 Binary serialized as malformed vector
- *What*: `new Binary(Buffer.alloc(0), Binary.SUBTYPE_VECTOR)` produces a
subtype-9 (Vector) value with no dtype/padding metadata, yet both
`BSON.serialize` and `EJSON.stringify` emit it without complaint. A one-byte
vector is correctly rejected, so this is specifically a missing
minimum-length invariant, not a fully disabled validator. - *Root cause*: `validateBinaryVector` in `src/binary.ts` reads
`vector.buffer[0]` (dtype) and `vector.buffer[1]` (padding) without first
checking that those two bytes exist. With a zero-length payload both reads are
`undefined`, so every dtype-specific branch is skipped and validation passes.
The typed helper constructors (`fromInt8Array`, etc.) always allocate
`byteLength + 2`, so the invariant is "every subtype-9 value carries 2 metadata
bytes" — just not enforced on the raw constructor path. - *Conditions required*: an application must wrap caller-controlled bytes in
the raw `Binary(buf, SUBTYPE_VECTOR)` constructor (rather than the typed
helpers) and then treat js-bson serialization as its validation boundary
before persisting/exporting. The impact is integrity (malformed vector data
emitted), not memory safety or a server-side acceptance claim. - *Details & PoC*: `int-mongodb-js-bson-empty-vector-serialization-bypass/README.md`
and `int-mongodb-js-bson-empty-vector-serialization-bypass/poc/` (run `bash poc/run.sh`).
- 3. `BSON.serialize` — stack exhaustion on deeply nested documents
- 3. `BSON.serialize` — stack exhaustion on deeply nested documents
- *What: A deeply nested *acyclic plain-object document
(`{a:{a:{...{leaf:1}}}}`) drives unbounded recursion in serialization until V8
throws `RangeError: Maximum call stack size exceeded`. The existing
circular-reference guard does not catch this because each level is a fresh
object. - *Root cause*: `serializeObject` → `serializeInto` in
`src/parser/serializer.ts` threads a `depth` argument but never compares it
against a limit. There is no `BSON_MAX_NESTING_DEPTH` enforcement, unlike the
MongoDB server's own document nesting cap. - *Conditions required*: an application must serialize request-derived
documents (e.g. a JSON request body) with `BSON.serialize` /
`serializeWithBufferAndIndex` *without its own depth guard first*. This is
the most realistic of the three: standard JSON body parsers enforce size but
not depth, so a small (~1 KB) but deeply nested payload reaches the encoder.
Practical impact still depends on the app's error handling — a caught
`RangeError` degrades to a failed request rather than a process kill in most
setups. - *Details & PoC*: `int-mongodb-js-bson-serialize-depth-dos/README.md`
and `int-mongodb-js-bson-serialize-depth-dos/poc/` (run `bash poc/run.sh`).
—
- Suggested direction
All three are addressable in a single change set:
1. Add a `< bytes.length` bound to `findNull` (and treat the overrun as the
existing "null terminator not found" error).
2. Reject subtype-9 `Binary` with fewer than 2 bytes in `validateBinaryVector`.
3. Introduce a `BSON_MAX_NESTING_DEPTH` limit (the server uses 100) checked in
the serialize/size paths.
Concrete diffs are included in the per-issue `README.md` files referenced above.
- Reproduction
Each PoC is self-contained: it clones js-bson at the pinned ref, builds against
that exact source, and prints a `TRIGGERED:` fingerprint only on the vulnerable
behaviour. See each issue's `poc/run.sh`.
- Suggested direction
- is related to
-
GODRIVER-3931 Go driver bson.Raw.Validate panic on zero-length BSON document
-
- Needs Triage
-
- related to
-
JAVA-6220 Java driver RawBsonDocument accessors accept malformed zero-length string field
-
- In Code Review
-