Skip to content

Commit 93e724c

Browse files
author
Divjot Arora
committed
Error if BSON cstrings contain null bytes
1 parent ea02175 commit 93e724c

File tree

3 files changed

+55
-1
lines changed

3 files changed

+55
-1
lines changed

bson/bsonrw/value_writer.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"io"
1313
"math"
1414
"strconv"
15+
"strings"
1516
"sync"
1617

1718
"go.mongodb.org/mongo-driver/bson/bsontype"
@@ -247,7 +248,12 @@ func (vw *valueWriter) invalidTransitionError(destination mode, name string, mod
247248
func (vw *valueWriter) writeElementHeader(t bsontype.Type, destination mode, callerName string, addmodes ...mode) error {
248249
switch vw.stack[vw.frame].mode {
249250
case mElement:
250-
vw.buf = bsoncore.AppendHeader(vw.buf, t, vw.stack[vw.frame].key)
251+
key := vw.stack[vw.frame].key
252+
if !isValidCString(key) {
253+
return errors.New("BSON element key cannot contain null bytes")
254+
}
255+
256+
vw.buf = bsoncore.AppendHeader(vw.buf, t, key)
251257
case mValue:
252258
// TODO: Do this with a cache of the first 1000 or so array keys.
253259
vw.buf = bsoncore.AppendHeader(vw.buf, t, strconv.Itoa(vw.stack[vw.frame].arrkey))
@@ -430,6 +436,9 @@ func (vw *valueWriter) WriteObjectID(oid primitive.ObjectID) error {
430436
}
431437

432438
func (vw *valueWriter) WriteRegex(pattern string, options string) error {
439+
if !isValidCString(pattern) || !isValidCString(options) {
440+
return errors.New("BSON regex values cannot contain null bytes")
441+
}
433442
if err := vw.writeElementHeader(bsontype.Regex, mode(0), "WriteRegex"); err != nil {
434443
return err
435444
}
@@ -602,3 +611,7 @@ func (vw *valueWriter) writeLength() error {
602611
vw.buf[start+3] = byte(length >> 24)
603612
return nil
604613
}
614+
615+
func isValidCString(cs string) bool {
616+
return !strings.ContainsRune(cs, '\x00')
617+
}

bson/extjson_prose_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,11 @@ func TestExtJSON(t *testing.T) {
4545
})
4646
}
4747
}
48+
49+
func TestExtJSONNullBytes(t *testing.T) {
50+
t.Run("element keys", func(t *testing.T) {
51+
doc := D{{"a\x00", "foo"}}
52+
res, err := MarshalExtJSON(doc, false, false)
53+
assert.NotNil(t, err, "expected MarshalExtJSON error but got nil with result %v", string(res))
54+
})
55+
}

bson/marshal_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package bson
88

99
import (
1010
"bytes"
11+
"errors"
1112
"fmt"
1213
"reflect"
1314
"testing"
@@ -267,3 +268,35 @@ func TestCachingEncodersNotSharedAcrossRegistries(t *testing.T) {
267268
})
268269
})
269270
}
271+
272+
func TestNullBytes(t *testing.T) {
273+
t.Run("element keys", func(t *testing.T) {
274+
doc := D{{"a\x00", "foobar"}}
275+
res, err := Marshal(doc)
276+
want := errors.New("BSON element key cannot contain null bytes")
277+
assert.Equal(t, want, err, "expected Marshal error %v, got error %v with result %q", want, err, Raw(res))
278+
})
279+
280+
t.Run("regex values", func(t *testing.T) {
281+
wantErr := errors.New("BSON regex values cannot contain null bytes")
282+
283+
testCases := []struct {
284+
name string
285+
pattern string
286+
options string
287+
}{
288+
{"null bytes in pattern", "a\x00", "i"},
289+
{"null bytes in options", "pattern", "i\x00"},
290+
}
291+
for _, tc := range testCases {
292+
t.Run(tc.name, func(t *testing.T) {
293+
regex := primitive.Regex{
294+
Pattern: tc.pattern,
295+
Options: tc.options,
296+
}
297+
res, err := Marshal(D{{"foo", regex}})
298+
assert.Equal(t, wantErr, err, "expected Marshal error %v, got error %v with result %q", wantErr, err, Raw(res))
299+
})
300+
}
301+
})
302+
}

0 commit comments

Comments
 (0)