summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2023-02-24 09:20:15 -0700
committerLuke Shumaker <lukeshu@lukeshu.com>2023-02-25 20:59:23 -0700
commit3b4ca665fbe89bdf44560454684bb6829070d7f4 (patch)
tree3a3ccc02a39edad8aa4116890e6070bd18d921ef
parent0fa9f8b14f04f4b0099f038cc43e4cef57a155a1 (diff)
jsonparse: Fix a bug allowing a trailing comma in objects
-rw-r--r--ReleaseNotes.md3
-rw-r--r--compat/json/compat_test.go1
-rw-r--r--compat/json/testdata/fuzz/FuzzEquiv/f9afef161f73f7cf2
-rw-r--r--internal/jsonparse/parse.go33
-rw-r--r--internal/jsonparse/parse_test.go14
5 files changed, 35 insertions, 18 deletions
diff --git a/ReleaseNotes.md b/ReleaseNotes.md
index 71973aa..be5b8ff 100644
--- a/ReleaseNotes.md
+++ b/ReleaseNotes.md
@@ -37,6 +37,9 @@
now reflect the `InvalidUTF8` setting, rather than simply saying
"unflushed unicode garbage".
+ - Bugfix: No longer allows a comma after the last key:value pair in
+ an object.
+
# v0.3.7 (2023-02-20)
Theme: Fixes from fuzzing (part 1?)
diff --git a/compat/json/compat_test.go b/compat/json/compat_test.go
index 3de48f7..af92093 100644
--- a/compat/json/compat_test.go
+++ b/compat/json/compat_test.go
@@ -196,6 +196,7 @@ func TestCompatUnmarshal(t *testing.T) {
"syntax-01": {In: `{}x`, ExpErr: `invalid character 'x' after top-level value`},
"syntax-02": {In: `x`, ExpErr: `invalid character 'x' looking for beginning of value`},
"syntax-03": {In: `{x`, ExpErr: `invalid character 'x' looking for beginning of object key string`},
+ "syntax-18": {In: `{"":0,}`, ExpErr: `invalid character '}' looking for beginning of object key string`},
"syntax-04": {In: `{""x`, ExpErr: `invalid character 'x' after object key`},
"syntax-05": {In: `{"":0x`, ExpErr: `invalid character 'x' after object key:value pair`},
"syntax-06": {In: `[0x`, ExpErr: `invalid character 'x' after array element`},
diff --git a/compat/json/testdata/fuzz/FuzzEquiv/f9afef161f73f7cf b/compat/json/testdata/fuzz/FuzzEquiv/f9afef161f73f7cf
new file mode 100644
index 0000000..6618d0f
--- /dev/null
+++ b/compat/json/testdata/fuzz/FuzzEquiv/f9afef161f73f7cf
@@ -0,0 +1,2 @@
+go test fuzz v1
+[]byte("{\"\":{},}")
diff --git a/internal/jsonparse/parse.go b/internal/jsonparse/parse.go
index 5547df4..ce71a65 100644
--- a/internal/jsonparse/parse.go
+++ b/internal/jsonparse/parse.go
@@ -279,7 +279,8 @@ type Parser struct {
// rule; they need some special assignments:
//
// { object: waiting for key to start or '}'
- // » object: reading key / waiting for colon
+ // } object: waiting for key to start
+ // : object: reading key / waiting for colon
// o object: reading value / waiting for ',' or '}'
//
// [ array: waiting for item to start or ']'
@@ -298,17 +299,17 @@ type Parser struct {
// stack processed
// ?
// { {
- // »" {"
- // »" {"x
- // » {"x"
+ // :" {"
+ // :" {"x
+ // : {"x"
// o? {"x":
// o" {"x":"
// o" {"x":"y
// o {"x":"y"
- // { {"x":"y",
- // »" {"x":"y","
- // »" {"x":"y","a
- // » {"x":"y","a"
+ // } {"x":"y",
+ // :" {"x":"y","
+ // :" {"x":"y","a
+ // : {"x":"y","a"
// o? {"x":"y","a":
// o" {"x":"y","a":"
// o" {"x":"y","a":"b
@@ -627,7 +628,7 @@ func (par *Parser) HandleRune(c rune, isRune bool) (RuneType, error) {
case 0x0020, 0x000A, 0x000D, 0x0009:
return RuneTypeSpace, nil
case '"':
- par.replaceState(RuneTypeStringEnd)
+ par.replaceState(RuneTypeObjectColon)
return par.pushState(RuneTypeStringBeg), nil
case '}':
par.popState()
@@ -635,7 +636,17 @@ func (par *Parser) HandleRune(c rune, isRune bool) (RuneType, error) {
default:
return RuneTypeError, &InvalidCharacterError{c, isRune, "looking for beginning of object key string"}
}
- case RuneTypeStringEnd: // waiting for ':'
+ case RuneTypeObjectEnd: // waiting for key to start
+ switch c {
+ case 0x0020, 0x000A, 0x000D, 0x0009:
+ return RuneTypeSpace, nil
+ case '"':
+ par.replaceState(RuneTypeObjectColon)
+ return par.pushState(RuneTypeStringBeg), nil
+ default:
+ return RuneTypeError, &InvalidCharacterError{c, isRune, "looking for beginning of object key string"}
+ }
+ case RuneTypeObjectColon: // waiting for ':'
switch c {
case 0x0020, 0x000A, 0x000D, 0x0009:
return RuneTypeSpace, nil
@@ -651,7 +662,7 @@ func (par *Parser) HandleRune(c rune, isRune bool) (RuneType, error) {
case 0x0020, 0x000A, 0x000D, 0x0009:
return RuneTypeSpace, nil
case ',':
- par.replaceState(RuneTypeObjectBeg)
+ par.replaceState(RuneTypeObjectEnd)
return RuneTypeObjectComma, nil
case '}':
par.popState()
diff --git a/internal/jsonparse/parse_test.go b/internal/jsonparse/parse_test.go
index acb43e8..fe94c58 100644
--- a/internal/jsonparse/parse_test.go
+++ b/internal/jsonparse/parse_test.go
@@ -24,17 +24,17 @@ func TestParserHandleRune(t *testing.T) {
// st,// processed
`?`,
`{`, // {
- `»"`, // {"
- `»"`, // {"x
- `»`, // {"x"
+ `:"`, // {"
+ `:"`, // {"x
+ `:`, // {"x"
`o?`, // {"x":
`o"`, // {"x":"
`o"`, // {"x":"y
`o`, // {"x":"y"
- `{`, // {"x":"y",
- `»"`, // {"x":"y","
- `»"`, // {"x":"y","a
- `»`, // {"x":"y","a"
+ `}`, // {"x":"y",
+ `:"`, // {"x":"y","
+ `:"`, // {"x":"y","a
+ `:`, // {"x":"y","a"
`o?`, // {"x":"y","a":
`o"`, // {"x":"y","a":"
`o"`, // {"x":"y","a":"b