Conversation
The number decoders delimit a number token by greedily consuming digit/
sign/dot/exponent bytes and then validate only with strconv.ParseFloat,
which is more permissive than the JSON grammar (RFC 8259). As a result
go-json accepted literals encoding/json rejects: a leading zero (01, 007,
00.5), a bare trailing dot (1., 1.e5), and a fraction with no integer part
after a sign (-.5). This breaks the drop-in encoding/json compatibility the
package targets, and diverges Valid() from encoding/json.Valid().
Add validNumber (a port of encoding/json's isValidNumber) and check it in the
float and json.Number decoders after ParseFloat has already accepted the
token, so no previously-rejected input changes behavior; the check only closes
the gap where ParseFloat is looser than JSON. Covers Unmarshal into
interface{}/float/json.Number and, through the interface stream decoder, Valid.
…idation These five encoding/json invalidTests entries (01, 1., 012, 01.2, 1.e1) were commented out when the test was added (c9f1d00) because go-json accepted them. The number-validation fix now rejects them, so restore the cases.
Signed-off-by: Sueun Cho <sueun.dev@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
go-json is a drop-in replacement for encoding/json, but value decoding accepted several malformed number literals that encoding/json rejects:
The same grammar gap also showed up while skipping unknown struct fields. In #448,
{"fake": 0.}unmarshaled intostruct{}returned nil in go-json while encoding/json rejected it.The parser was delimiting a candidate number by greedily consuming digit/sign/dot/exponent bytes and then relying on
strconv.ParseFloat, which is looser than the JSON grammar. ParseFloat accepts leading zeros (01,007,00.5), bare trailing dots (0.,1.,1.e5), and a fraction with no integer part after a sign (-.5).Fix: add
validNumber(a port of encoding/json'sisValidNumber) and check it after ParseFloat in the float and json.Number decoders. Also use the same check while skipping numeric values in unknown fields. The skipped array/object path now parses value boundaries instead of only balancing delimiters, so malformed numeric values are not hidden inside skipped composites.I left
Decoder.Token()unchanged; it is a separate token-stream API, andencoding/json.Decoder.Token()can also return the first token for inputs like00.This also re-enables five cases in the existing
TestNumberIsValidthat were commented out in the commit that added the test (c9f1d00) because go-json accepted them:01,1.,012,01.2,1.e1.Fixes #448.
Testing:
go test ./... -run 'TestDecodeFirstWin|TestUnmarshal(Rejects|Accepts).*NumberLiterals|TestNumberIsValid|TestDecoderValidatesSkippedNumberAcrossBuffer' -count=1 -vgo test ./... -count=1GOGC=1 go test ./... -count=1go test -race ./... -count=1GOTOOLCHAIN=go1.19.13 CGO_ENABLED=0 go test ./... -count=1GOTOOLCHAIN=go1.20.14 CGO_ENABLED=0 go test ./... -count=1GOTOOLCHAIN=go1.21.13 CGO_ENABLED=0 go test ./... -count=1make covercd benchmarks && go test -run '^$' -bench GoJson -benchtime=1xgolangci-lintv1.54.2:golangci-lint run --concurrency=1 --timeout=5mgofmt -l internal/decoder/context.go internal/decoder/stream.go number_validation_test.goproduced no output