k8s.io/kube-openapi@v0.0.0-20240228011516-70dd3763d340/pkg/internal/third_party/go-json-experiment/json/fuzz_test.go (about) 1 // Copyright 2020 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package json 6 7 import ( 8 "bytes" 9 "errors" 10 "io" 11 "math/rand" 12 "reflect" 13 "testing" 14 ) 15 16 func FuzzCoder(f *testing.F) { 17 // Add a number of inputs to the corpus including valid and invalid data. 18 for _, td := range coderTestdata { 19 f.Add(int64(0), []byte(td.in)) 20 } 21 for _, td := range decoderErrorTestdata { 22 f.Add(int64(0), []byte(td.in)) 23 } 24 for _, td := range encoderErrorTestdata { 25 f.Add(int64(0), []byte(td.wantOut)) 26 } 27 for _, td := range jsonTestdata() { 28 f.Add(int64(0), td.data) 29 } 30 31 f.Fuzz(func(t *testing.T, seed int64, b []byte) { 32 var tokVals []tokOrVal 33 rn := rand.NewSource(seed) 34 35 // Read a sequence of tokens or values. Skip the test for any errors 36 // since we expect this with randomly generated fuzz inputs. 37 src := bytes.NewReader(b) 38 dec := NewDecoder(src) 39 for { 40 if rn.Int63()%8 > 0 { 41 tok, err := dec.ReadToken() 42 if err != nil { 43 if err == io.EOF { 44 break 45 } 46 t.Skipf("Decoder.ReadToken error: %v", err) 47 } 48 tokVals = append(tokVals, tok.Clone()) 49 } else { 50 val, err := dec.ReadValue() 51 if err != nil { 52 expectError := dec.PeekKind() == '}' || dec.PeekKind() == ']' 53 if expectError && errors.As(err, new(*SyntacticError)) { 54 continue 55 } 56 if err == io.EOF { 57 break 58 } 59 t.Skipf("Decoder.ReadValue error: %v", err) 60 } 61 tokVals = append(tokVals, append(zeroValue, val...)) 62 } 63 } 64 65 // Write a sequence of tokens or values. Fail the test for any errors 66 // since the previous stage guarantees that the input is valid. 67 dst := new(bytes.Buffer) 68 enc := NewEncoder(dst) 69 for _, tokVal := range tokVals { 70 switch tokVal := tokVal.(type) { 71 case Token: 72 if err := enc.WriteToken(tokVal); err != nil { 73 t.Fatalf("Encoder.WriteToken error: %v", err) 74 } 75 case RawValue: 76 if err := enc.WriteValue(tokVal); err != nil { 77 t.Fatalf("Encoder.WriteValue error: %v", err) 78 } 79 } 80 } 81 82 // Encoded output and original input must decode to the same thing. 83 var got, want []Token 84 for dec := NewDecoder(bytes.NewReader(b)); dec.PeekKind() > 0; { 85 tok, err := dec.ReadToken() 86 if err != nil { 87 t.Fatalf("Decoder.ReadToken error: %v", err) 88 } 89 got = append(got, tok.Clone()) 90 } 91 for dec := NewDecoder(dst); dec.PeekKind() > 0; { 92 tok, err := dec.ReadToken() 93 if err != nil { 94 t.Fatalf("Decoder.ReadToken error: %v", err) 95 } 96 want = append(want, tok.Clone()) 97 } 98 if !equalTokens(got, want) { 99 t.Fatalf("mismatching output:\ngot %v\nwant %v", got, want) 100 } 101 }) 102 } 103 104 func FuzzResumableDecoder(f *testing.F) { 105 for _, td := range resumableDecoderTestdata { 106 f.Add(int64(0), []byte(td)) 107 } 108 109 f.Fuzz(func(t *testing.T, seed int64, b []byte) { 110 rn := rand.NewSource(seed) 111 112 // Regardless of how many bytes the underlying io.Reader produces, 113 // the provided tokens, values, and errors should always be identical. 114 t.Run("ReadToken", func(t *testing.T) { 115 decGot := NewDecoder(&FaultyBuffer{B: b, MaxBytes: 8, Rand: rn}) 116 decWant := NewDecoder(bytes.NewReader(b)) 117 gotTok, gotErr := decGot.ReadToken() 118 wantTok, wantErr := decWant.ReadToken() 119 if gotTok.String() != wantTok.String() || !reflect.DeepEqual(gotErr, wantErr) { 120 t.Errorf("Decoder.ReadToken = (%v, %v), want (%v, %v)", gotTok, gotErr, wantTok, wantErr) 121 } 122 }) 123 t.Run("ReadValue", func(t *testing.T) { 124 decGot := NewDecoder(&FaultyBuffer{B: b, MaxBytes: 8, Rand: rn}) 125 decWant := NewDecoder(bytes.NewReader(b)) 126 gotVal, gotErr := decGot.ReadValue() 127 wantVal, wantErr := decWant.ReadValue() 128 if !reflect.DeepEqual(gotVal, wantVal) || !reflect.DeepEqual(gotErr, wantErr) { 129 t.Errorf("Decoder.ReadValue = (%s, %v), want (%s, %v)", gotVal, gotErr, wantVal, wantErr) 130 } 131 }) 132 }) 133 } 134 135 func FuzzRawValueReformat(f *testing.F) { 136 for _, td := range rawValueTestdata { 137 f.Add([]byte(td.in)) 138 } 139 140 // isValid reports whether b is valid according to the specified options. 141 isValid := func(opts DecodeOptions, b []byte) bool { 142 d := opts.NewDecoder(bytes.NewReader(b)) 143 _, errVal := d.ReadValue() 144 _, errEOF := d.ReadToken() 145 return errVal == nil && errEOF == io.EOF 146 } 147 148 // stripWhitespace removes all JSON whitespace characters from the input. 149 stripWhitespace := func(in []byte) (out []byte) { 150 out = make([]byte, 0, len(in)) 151 for _, c := range in { 152 switch c { 153 case ' ', '\n', '\r', '\t': 154 default: 155 out = append(out, c) 156 } 157 } 158 return out 159 } 160 161 // unmarshal unmarshals the input into an any. 162 unmarshal := func(in []byte) (out any) { 163 if err := Unmarshal(in, &out); err != nil { 164 return nil // ignore invalid input 165 } 166 return out 167 } 168 169 f.Fuzz(func(t *testing.T, b []byte) { 170 validRFC7159 := isValid(DecodeOptions{AllowInvalidUTF8: true, AllowDuplicateNames: true}, b) 171 validRFC8259 := isValid(DecodeOptions{AllowInvalidUTF8: false, AllowDuplicateNames: true}, b) 172 validRFC7493 := isValid(DecodeOptions{AllowInvalidUTF8: false, AllowDuplicateNames: false}, b) 173 switch { 174 case !validRFC7159 && validRFC8259: 175 t.Errorf("invalid input per RFC 7159 implies invalid per RFC 8259") 176 case !validRFC8259 && validRFC7493: 177 t.Errorf("invalid input per RFC 8259 implies invalid per RFC 7493") 178 } 179 180 gotValid := RawValue(b).IsValid() 181 wantValid := validRFC7493 182 if gotValid != wantValid { 183 t.Errorf("RawValue.IsValid = %v, want %v", gotValid, wantValid) 184 } 185 186 gotCompacted := RawValue(string(b)) 187 gotCompactOk := gotCompacted.Compact() == nil 188 wantCompactOk := validRFC7159 189 if !bytes.Equal(stripWhitespace(gotCompacted), stripWhitespace(b)) { 190 t.Errorf("stripWhitespace(RawValue.Compact) = %s, want %s", stripWhitespace(gotCompacted), stripWhitespace(b)) 191 } 192 if !reflect.DeepEqual(unmarshal(gotCompacted), unmarshal(b)) { 193 t.Errorf("unmarshal(RawValue.Compact) = %s, want %s", unmarshal(gotCompacted), unmarshal(b)) 194 } 195 if gotCompactOk != wantCompactOk { 196 t.Errorf("RawValue.Compact success mismatch: got %v, want %v", gotCompactOk, wantCompactOk) 197 } 198 199 gotIndented := RawValue(string(b)) 200 gotIndentOk := gotIndented.Indent("", " ") == nil 201 wantIndentOk := validRFC7159 202 if !bytes.Equal(stripWhitespace(gotIndented), stripWhitespace(b)) { 203 t.Errorf("stripWhitespace(RawValue.Indent) = %s, want %s", stripWhitespace(gotIndented), stripWhitespace(b)) 204 } 205 if !reflect.DeepEqual(unmarshal(gotIndented), unmarshal(b)) { 206 t.Errorf("unmarshal(RawValue.Indent) = %s, want %s", unmarshal(gotIndented), unmarshal(b)) 207 } 208 if gotIndentOk != wantIndentOk { 209 t.Errorf("RawValue.Indent success mismatch: got %v, want %v", gotIndentOk, wantIndentOk) 210 } 211 212 gotCanonicalized := RawValue(string(b)) 213 gotCanonicalizeOk := gotCanonicalized.Canonicalize() == nil 214 wantCanonicalizeOk := validRFC7493 215 if !reflect.DeepEqual(unmarshal(gotCanonicalized), unmarshal(b)) { 216 t.Errorf("unmarshal(RawValue.Canonicalize) = %s, want %s", unmarshal(gotCanonicalized), unmarshal(b)) 217 } 218 if gotCanonicalizeOk != wantCanonicalizeOk { 219 t.Errorf("RawValue.Canonicalize success mismatch: got %v, want %v", gotCanonicalizeOk, wantCanonicalizeOk) 220 } 221 }) 222 } 223 224 func FuzzEqualFold(f *testing.F) { 225 for _, tt := range equalFoldTestdata { 226 f.Add([]byte(tt.in1), []byte(tt.in2)) 227 } 228 229 equalFoldSimple := func(x, y []byte) bool { 230 strip := func(b []byte) []byte { 231 return bytes.Map(func(r rune) rune { 232 if r == '_' || r == '-' { 233 return -1 // ignore underscores and dashes 234 } 235 return r 236 }, b) 237 } 238 return bytes.EqualFold(strip(x), strip(y)) 239 } 240 241 f.Fuzz(func(t *testing.T, s1, s2 []byte) { 242 // Compare the optimized and simplified implementations. 243 got := equalFold(s1, s2) 244 want := equalFoldSimple(s1, s2) 245 if got != want { 246 t.Errorf("equalFold(%q, %q) = %v, want %v", s1, s2, got, want) 247 } 248 }) 249 }