diff --git a/.gitignore b/.gitignore index fdcd5db..003a9b7 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,6 @@ main .swp main super-graph +*-fuzz.zip +crashers +suppressions \ No newline at end of file diff --git a/go.mod b/go.mod index 2188d09..d90b66b 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/dlclark/regexp2 v1.2.0 // indirect github.com/dop251/goja v0.0.0-20190912223329-aa89e6a4c733 + github.com/dvyukov/go-fuzz v0.0.0-20191022152526-8cb203812681 // indirect github.com/fsnotify/fsnotify v1.4.7 github.com/garyburd/redigo v1.6.0 github.com/go-sourcemap/sourcemap v2.1.2+incompatible // indirect diff --git a/go.sum b/go.sum index bdda6e9..848e404 100644 --- a/go.sum +++ b/go.sum @@ -57,6 +57,8 @@ github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dop251/goja v0.0.0-20190912223329-aa89e6a4c733 h1:cyNc40Dx5YNEO94idePU8rhVd3dn+sd04Arh0kDBAaw= github.com/dop251/goja v0.0.0-20190912223329-aa89e6a4c733/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= +github.com/dvyukov/go-fuzz v0.0.0-20191022152526-8cb203812681 h1:3WV5aRRj1ELP3RcLlBp/v0WJTuy47OQMkL9GIQq8QEE= +github.com/dvyukov/go-fuzz v0.0.0-20191022152526-8cb203812681/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/garyburd/redigo v1.6.0 h1:0VruCpn7yAIIu7pWVClQC8wxCJEcG3nyzpMSHKi1PQc= diff --git a/jsn/filter.go b/jsn/filter.go index c55097a..04f0cce 100644 --- a/jsn/filter.go +++ b/jsn/filter.go @@ -97,8 +97,7 @@ func Filter(w *bytes.Buffer, b []byte, keys []string) error { case state == expectNumClose && ((b[i] < '0' || b[i] > '9') && (b[i] != '.' && b[i] != 'e' && b[i] != 'E' && b[i] != '+' && b[i] != '-')): - i-- - e = i + e = i - 1 case state == expectValue && (b[i] == 'f' || b[i] == 'F' || b[i] == 't' || b[i] == 'T'): diff --git a/jsn/fuzz.go b/jsn/fuzz.go index 6a0b21f..b48fb6b 100644 --- a/jsn/fuzz.go +++ b/jsn/fuzz.go @@ -3,7 +3,7 @@ package jsn import "bytes" // FuzzerEntrypoint for Fuzzbuzz -func FuzzerEntryPoint(data []byte) int { +func Fuzz(data []byte) int { err1 := Validate(string(data)) var b1 bytes.Buffer @@ -28,8 +28,8 @@ func FuzzerEntryPoint(data []byte) int { Keys(data) if err1 != nil || err2 != nil || err3 != nil { - return -1 + return 0 } - return 0 + return 1 } diff --git a/jsn/fuzz_test.go b/jsn/fuzz_test.go new file mode 100644 index 0000000..2b66964 --- /dev/null +++ b/jsn/fuzz_test.go @@ -0,0 +1,59 @@ +package jsn + +import "testing" + +func TestFuzzCrashers(t *testing.T) { + + var crashers = []string{ + "00\"0000\"0{", + "6\",\n\t\t\t\"something\": " + + "null\n\t\t},\n\t\t{\n\t\t\t\"id" + + "\": 12,\n\t\t\t\"full_name" + + "\": \"Brenton Bauch Ph" + + "D\",\n\t\t\t\"email\": \"ren" + + "ee@miller.co\",\n\t\t\t\"_" + + "_twitter_id\": 1\n\t\t}," + + "\n\t\t{\n\t\t\t\"id\": 13,\n\t\t" + + "\t\"full_name\": \"Daine" + + " Gleichner\",\n\t\t\t\"ema" + + "il\": \"andrea@gmail.c" + + "om\",\n\t\t\t\"__twitter_i" + + "d\": \"\",\n\t\t\t\"id__twit" + + "ter_id\": \"NOOO\",\n\t\t\t" + + "\"work_email\": \"andre" + + "a@nienow.co\"\n\t\t}\n\t]}" + + "\n\t}", + "0000\"0000\"0{", + "0000\"\"{", + "0000\"000\"{", + "0\"\"{", + "\"0\"{", + "000\"0\"{", + "0\"0000\"0{", + "000\"\"{", + "0\"00\"{", + "000\"0000\"0{", + "000\"00\"{", + "\"\"{", + "0\"0000\"{", + "\"000\"00{", + "0000\"00\"{", + "00\"0\"{", + "0\"0\"{", + "000\"0000\"{", + "00\"0000\"{", + "0000\"0000\"{", + "\"000\"{", + "00\"00\"{", + "00\"0000\"00{", + "0\"0000\"00{", + "00\"\"{", + "0000\"0\"{", + "000\"000\"{", + "\"00000000\"{", + } + + for _, f := range crashers { + Fuzz([]byte(f)) + } +} diff --git a/jsn/get.go b/jsn/get.go index 29c37ac..86ba3b5 100644 --- a/jsn/get.go +++ b/jsn/get.go @@ -105,8 +105,7 @@ func Get(b []byte, keys [][]byte) []Field { case state == expectNumClose && ((b[i] < '0' || b[i] > '9') && (b[i] != '.' && b[i] != 'e' && b[i] != 'E' && b[i] != '+' && b[i] != '-')): - i-- - e = i + e = i - 1 case state == expectValue && (b[i] == 'f' || b[i] == 'F' || b[i] == 't' || b[i] == 'T'): diff --git a/jsn/keys.go b/jsn/keys.go index e1c0e1b..0ebf69f 100644 --- a/jsn/keys.go +++ b/jsn/keys.go @@ -12,6 +12,7 @@ func Keys(b []byte) [][]byte { ae := 0 for i := 0; i < len(b); i++ { + if state == expectObjClose || state == expectListClose { switch b[i] { case '{', '[': @@ -88,8 +89,7 @@ func Keys(b []byte) [][]byte { case state == expectNumClose && ((b[i] < '0' || b[i] > '9') && (b[i] != '.' && b[i] != 'e' && b[i] != 'E' && b[i] != '+' && b[i] != '-')): - i-- - e = i + e = i - 1 case state == expectValue && (b[i] == 'f' || b[i] == 'F' || b[i] == 't' || b[i] == 'T'): diff --git a/jsn/replace.go b/jsn/replace.go index 43389a5..0f5b67b 100644 --- a/jsn/replace.go +++ b/jsn/replace.go @@ -86,8 +86,7 @@ func Replace(w *bytes.Buffer, b []byte, from, to []Field) error { case state == expectNumClose && ((b[i] < '0' || b[i] > '9') && (b[i] != '.' && b[i] != 'e' && b[i] != 'E' && b[i] != '+' && b[i] != '-')): - i-- - e = i + e = i - 1 case state == expectValue && (b[i] == 'f' || b[i] == 'F' || b[i] == 't' || b[i] == 'T'): @@ -156,7 +155,7 @@ func Replace(w *bytes.Buffer, b []byte, from, to []Field) error { if ws == -1 || (ws == 0 && we == len(b)) { w.Write(b) - } else { + } else if ws < we { w.Write(b[ws:we]) } diff --git a/jsn/strip.go b/jsn/strip.go index 3b66fd0..e6dc64e 100644 --- a/jsn/strip.go +++ b/jsn/strip.go @@ -70,8 +70,7 @@ func Strip(b []byte, path [][]byte) []byte { case state == expectNumClose && ((b[i] < '0' || b[i] > '9') && (b[i] != '.' && b[i] != 'e' && b[i] != 'E' && b[i] != '+' && b[i] != '-')): - i-- - e = i + e = i - 1 case state == expectValue && (b[i] == 'f' || b[i] == 'F' || b[i] == 't' || b[i] == 'T'): diff --git a/qcode/corpus/001.gql b/qcode/corpus/001.gql new file mode 100644 index 0000000..ae381f8 --- /dev/null +++ b/qcode/corpus/001.gql @@ -0,0 +1,21 @@ +query { + products( + # returns only 30 items + limit: 30, + + # starts from item 10, commented out for now + # offset: 10, + + # orders the response items by highest price + order_by: { price: desc }, + + # no duplicate prices returned + distinct: [ price ] + + # only items with an id >= 30 and < 30 are returned + where: { id: { and: { greater_or_equals: 20, lt: 28 } } }) { + id + name + price + } +} \ No newline at end of file diff --git a/qcode/corpus/002.gql b/qcode/corpus/002.gql new file mode 100644 index 0000000..d0aa529 --- /dev/null +++ b/qcode/corpus/002.gql @@ -0,0 +1,14 @@ +query { + products( + where: { + or: { + not: { id: { is_null: true } }, + price: { gt: 10 }, + price: { lt: 20 } + } } + ) { + id + name + price + } +} \ No newline at end of file diff --git a/qcode/corpus/003.gql b/qcode/corpus/003.gql new file mode 100644 index 0000000..a57440b --- /dev/null +++ b/qcode/corpus/003.gql @@ -0,0 +1,12 @@ +query { + products( + where: { + and: { + not: { id: { is_null: true } }, + price: { gt: 10 } + }}) { + id + name + price + } +} \ No newline at end of file diff --git a/qcode/fuzz.go b/qcode/fuzz.go index 89a4a3c..25a8ca5 100644 --- a/qcode/fuzz.go +++ b/qcode/fuzz.go @@ -1,14 +1,12 @@ package qcode // FuzzerEntrypoint for Fuzzbuzz -func FuzzerEntrypoint(data []byte) int { - //testData := string(data) - +func Fuzz(data []byte) int { qcompile, _ := NewCompiler(Config{}) _, err := qcompile.Compile(data, "user") if err != nil { - return -1 + return 0 } - return 0 + return 1 }