Malformed `json.Number` causes panic
chanced opened this issue · 7 comments
package main
import (
"encoding/json"
"log"
"github.com/santhosh-tekuri/jsonschema/v5"
)
func main() {
schema := `{"type": "integer"}`
instance := json.Number("abc")
sch, err := jsonschema.CompileString("schema.json", schema)
if err != nil {
log.Fatalf("%#v", err)
}
if err = sch.Validate(instance); err != nil {
log.Fatalf("%#v", err)
}
}
https://go.dev/play/p/TWAWZJPb0iL
results in:
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x28 pc=0x102f157cc]
goroutine 1 [running]:
github.com/santhosh-tekuri/jsonschema/v5.(*Schema).validateValue.func1()
/Users/chance/go/pkg/mod/github.com/santhosh-tekuri/jsonschema/v5@v5.0.0/schema.go:151 +0xd4
panic({0x102f67220, 0x1030350c0})
/opt/homebrew/Cellar/go/1.18.5/libexec/src/runtime/panic.go:838 +0x204
math/big.(*Rat).IsInt(...)
/opt/homebrew/Cellar/go/1.18.5/libexec/src/math/big/rat.go:402
github.com/santhosh-tekuri/jsonschema/v5.(*Schema).validate(0x14000271880, {0x0, 0x0, 0x0}, 0x0, {0x0, 0x0}, {0x102f69140, 0x140001edb90?}, {0x0, ...})
/Users/chance/go/pkg/mod/github.com/santhosh-tekuri/jsonschema/v5@v5.0.0/schema.go:242 +0x696c
github.com/santhosh-tekuri/jsonschema/v5.(*Schema).validateValue(0x14000271880, {0x102f69140?, 0x140001edb90?}, {0x0, 0x0})
/Users/chance/go/pkg/mod/github.com/santhosh-tekuri/jsonschema/v5@v5.0.0/schema.go:155 +0x98
github.com/santhosh-tekuri/jsonschema/v5.(*Schema).Validate(...)
/Users/chance/go/pkg/mod/github.com/santhosh-tekuri/jsonschema/v5@v5.0.0/schema.go:141
main.main()
/Users/chance/dev/spike/go/main.go:21 +0x9c
exit status 2
The issue is here:
Lines 241 to 245 in e298789
The second return of Rat.SetString
is a bool
which indicates whether or not it was successful. If unsuccessful, num
is nil
.
as per doc, json.Number
must be valid json number literal. so the library assumes it is valid number. so it is fine to panic if invalid json.Number
is given.
InvalidJSONTypeError
is used in examples like instance = time.Now()
. here instance is of type time.Time
which is not valid json type
These docs? It doesn't mention that it must be a valid number. In fact, the accessor / parser methods return an error (as they are just using strconv under the hood) if unable to parse.
// Float64 returns the number as a float64.
func (n Number) Float64() (float64, error) {
return strconv.ParseFloat(string(n), 64)
}
// Int64 returns the number as an int64.
func (n Number) Int64() (int64, error) {
return strconv.ParseInt(string(n), 10, 64)
}
if json.Number
is float then int64
returns error
also json.Number
may not fit into 64 bit size, so it can return error. we are using big.Rat
to handle that.
Understood and I recognize that in a normal decoding flow, it would be caught with the isValidNumber
function in the json package.
However, I'm not unmarshaling the data first. There's no reason to attempt to do so before validating. This also doesn't work:
package main
import (
"encoding/json"
"log"
"github.com/santhosh-tekuri/jsonschema/v5"
)
func main() {
schema := `{"type": "integer"}`
instance := json.RawMessage("0")
sch, err := jsonschema.CompileString("schema.json", schema)
if err != nil {
log.Fatalf("%v", err)
}
if err = sch.Validate(instance); err != nil {
log.Fatalf("%v", err)
}
}
https://go.dev/play/p/-ozZtjEAoIW
as it is considered invalid json by this package so I have to use json.Number
in this scenario.
Arguably I could validate that it is a number but then that number is going to be checked at least 3 times (by me, by big.Rat, and by json.Unmarshal)
Besides that, I still think it makes sense to clean up the edge-case and prevent a nil-pointer panic. I've already done so in #79
the library works only for valid json types like string number boolean nil.
it does not work for json.RawMessage
or any type like time.Time
even through they implement encoding.BinaryUnmarshaler
interface.
for example, you cannot validate a struct object, even though you have unmarshalled json into struct object.
Arg. I must have been thinking of a different json schema library which operates on []byte
or perhaps I'm just incredibly tired (its rather late/early here).
Sorry about that! 🤦♂️