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  }