Skip to content

Commit

Permalink
GODRIVER-1923 Error if BSON cstrings contain null bytes
Browse files Browse the repository at this point in the history
  • Loading branch information
Divjot Arora committed Mar 25, 2021
1 parent ea02175 commit 78b349d
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 1 deletion.
15 changes: 14 additions & 1 deletion bson/bsonrw/value_writer.go
Expand Up @@ -12,6 +12,7 @@ import (
"io"
"math"
"strconv"
"strings"
"sync"

"go.mongodb.org/mongo-driver/bson/bsontype"
Expand Down Expand Up @@ -247,7 +248,12 @@ func (vw *valueWriter) invalidTransitionError(destination mode, name string, mod
func (vw *valueWriter) writeElementHeader(t bsontype.Type, destination mode, callerName string, addmodes ...mode) error {
switch vw.stack[vw.frame].mode {
case mElement:
vw.buf = bsoncore.AppendHeader(vw.buf, t, vw.stack[vw.frame].key)
key := vw.stack[vw.frame].key
if !isValidCString(key) {
return errors.New("BSON element key cannot contain null bytes")
}

vw.buf = bsoncore.AppendHeader(vw.buf, t, key)
case mValue:
// TODO: Do this with a cache of the first 1000 or so array keys.
vw.buf = bsoncore.AppendHeader(vw.buf, t, strconv.Itoa(vw.stack[vw.frame].arrkey))
Expand Down Expand Up @@ -430,6 +436,9 @@ func (vw *valueWriter) WriteObjectID(oid primitive.ObjectID) error {
}

func (vw *valueWriter) WriteRegex(pattern string, options string) error {
if !isValidCString(pattern) || !isValidCString(options) {
return errors.New("BSON regex values cannot contain null bytes")
}
if err := vw.writeElementHeader(bsontype.Regex, mode(0), "WriteRegex"); err != nil {
return err
}
Expand Down Expand Up @@ -602,3 +611,7 @@ func (vw *valueWriter) writeLength() error {
vw.buf[start+3] = byte(length >> 24)
return nil
}

func isValidCString(cs string) bool {
return !strings.ContainsRune(cs, '\x00')
}
8 changes: 8 additions & 0 deletions bson/extjson_prose_test.go
Expand Up @@ -45,3 +45,11 @@ func TestExtJSON(t *testing.T) {
})
}
}

func TestExtJSONNullBytes(t *testing.T) {
t.Run("element keys", func(t *testing.T) {
doc := D{{"a\x00", "foo"}}
res, err := MarshalExtJSON(doc, false, false)
assert.NotNil(t, err, "expected MarshalExtJSON error but got nil with result %v", string(res))
})
}
33 changes: 33 additions & 0 deletions bson/marshal_test.go
Expand Up @@ -8,6 +8,7 @@ package bson

import (
"bytes"
"errors"
"fmt"
"reflect"
"testing"
Expand Down Expand Up @@ -267,3 +268,35 @@ func TestCachingEncodersNotSharedAcrossRegistries(t *testing.T) {
})
})
}

func TestNullBytes(t *testing.T) {
t.Run("element keys", func(t *testing.T) {
doc := D{{"a\x00", "foobar"}}
res, err := Marshal(doc)
want := errors.New("BSON element key cannot contain null bytes")
assert.Equal(t, want, err, "expected Marshal error %v, got error %v with result %q", want, err, Raw(res))
})

t.Run("regex values", func(t *testing.T) {
wantErr := errors.New("BSON regex values cannot contain null bytes")

testCases := []struct {
name string
pattern string
options string
}{
{"null bytes in pattern", "a\x00", "i"},
{"null bytes in options", "pattern", "i\x00"},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
regex := primitive.Regex{
Pattern: tc.pattern,
Options: tc.options,
}
res, err := Marshal(D{{"foo", regex}})
assert.Equal(t, wantErr, err, "expected Marshal error %v, got error %v with result %q", wantErr, err, Raw(res))
})
}
})
}

0 comments on commit 78b349d

Please sign in to comment.