istio.io/istio@v0.0.0-20240520182934-d79c90f27776/tests/fuzz/regression_test.go (about)

     1  // Copyright Istio Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package fuzz
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  	"path/filepath"
    21  	"regexp"
    22  	"testing"
    23  
    24  	"istio.io/istio/pilot/pkg/util/runtime"
    25  	"istio.io/istio/pkg/test/env"
    26  	"istio.io/istio/pkg/util/sets"
    27  )
    28  
    29  // baseCases contains a few trivial test cases to do a very brief sanity check of a test
    30  var baseCases = [][]byte{
    31  	{},
    32  	[]byte("."),
    33  	[]byte(".............."),
    34  }
    35  
    36  // brokenCases contains test cases that are currently failing. These should only be added if the
    37  // failure is publicly disclosed!
    38  var brokenCases = map[string]string{}
    39  
    40  func runRegressionTest(t *testing.T, name string, fuzz func(data []byte) int) {
    41  	dir := filepath.Join("testdata", name)
    42  	cases, err := os.ReadDir(dir)
    43  	if err != nil && !os.IsNotExist(err) {
    44  		t.Fatal(err)
    45  	}
    46  	runfuzz := func(t *testing.T, name string, by []byte) {
    47  		defer func() {
    48  			if r := recover(); r != nil {
    49  				if _, broken := brokenCases[name]; broken {
    50  					t.Logf("expected broken case failed: %v", broken)
    51  				} else {
    52  					runtime.LogPanic(r)
    53  					t.Fatalf("panic encountered: %v", r)
    54  				}
    55  			} else {
    56  				// Ensure we update brokenCases when they are fixed
    57  				if _, broken := brokenCases[name]; broken {
    58  					t.Fatalf("expected broken case passed")
    59  				}
    60  			}
    61  		}()
    62  		fuzz(by)
    63  	}
    64  	for i, c := range baseCases {
    65  		t.Run(fmt.Sprintf("base case %d", i), func(t *testing.T) {
    66  			runfuzz(t, "", c)
    67  		})
    68  	}
    69  	for _, c := range cases {
    70  		t.Run(c.Name(), func(t *testing.T) {
    71  			by, err := os.ReadFile(filepath.Join(dir, c.Name()))
    72  			if err != nil {
    73  				t.Fatal(err)
    74  			}
    75  			runfuzz(t, c.Name(), by)
    76  		})
    77  	}
    78  }
    79  
    80  func walkMatch(root string, pattern *regexp.Regexp) ([]string, error) {
    81  	var matches []string
    82  	err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
    83  		if err != nil {
    84  			return err
    85  		}
    86  		if filepath.Base(path) == "regression_test.go" {
    87  			return nil
    88  		}
    89  		if info.IsDir() {
    90  			return nil
    91  		}
    92  		if filepath.Ext(path) != ".go" {
    93  			return nil
    94  		}
    95  		bytes, err := os.ReadFile(path)
    96  		if err != nil {
    97  			return err
    98  		}
    99  		matched := pattern.FindAllString(string(bytes), -1)
   100  		for _, m := range matched {
   101  			// Add the match, with trailing ( and previous `func ` stripped
   102  			matches = append(matches, m[5:len(m)-1])
   103  		}
   104  		return nil
   105  	})
   106  	if err != nil {
   107  		return nil, err
   108  	}
   109  	return matches, nil
   110  }
   111  
   112  func TestFuzzers(t *testing.T) {
   113  	testedFuzzers := sets.New[string]()
   114  	cases := []struct {
   115  		name   string
   116  		fuzzer func([]byte) int
   117  	}{
   118  		{"FuzzConfigValidation", FuzzConfigValidation},
   119  		{"FuzzParseInputs", FuzzParseInputs},
   120  		{"FuzzParseMeshNetworks", FuzzParseMeshNetworks},
   121  		{"FuzzValidateMeshConfig", FuzzValidateMeshConfig},
   122  		{"FuzzInitContext", FuzzInitContext},
   123  		{"FuzzXds", FuzzXds},
   124  		{"FuzzAnalyzer", FuzzAnalyzer},
   125  		{"FuzzCompareDiff", FuzzCompareDiff},
   126  		{"FuzzHelmReconciler", FuzzHelmReconciler},
   127  		{"FuzzIntoResourceFile", FuzzIntoResourceFile},
   128  		{"FuzzTranslateFromValueToSpec", FuzzTranslateFromValueToSpec},
   129  		{"FuzzConfigValidation2", FuzzConfigValidation2},
   130  		{"FuzzBNMUnmarshalJSON", FuzzBNMUnmarshalJSON},
   131  		{"FuzzValidateClusters", FuzzValidateClusters},
   132  		{"FuzzCheckIstioOperatorSpec", FuzzCheckIstioOperatorSpec},
   133  		{"FuzzV1Alpha1ValidateConfig", FuzzV1Alpha1ValidateConfig},
   134  		{"FuzzGetEnabledComponents", FuzzGetEnabledComponents},
   135  		{"FuzzUnmarshalAndValidateIOPS", FuzzUnmarshalAndValidateIOPS},
   136  		{"FuzzRenderManifests", FuzzRenderManifests},
   137  		{"FuzzOverlayIOP", FuzzOverlayIOP},
   138  		{"FuzzNewControlplane", FuzzNewControlplane},
   139  		{"FuzzResolveK8sConflict", FuzzResolveK8sConflict},
   140  		{"FuzzYAMLManifestPatch", FuzzYAMLManifestPatch},
   141  		{"FuzzGalleyDiag", FuzzGalleyDiag},
   142  		{"FuzzNewBootstrapServer", FuzzNewBootstrapServer},
   143  		{"FuzzGenCSR", FuzzGenCSR},
   144  		{"FuzzCreateCertE2EUsingClientCertAuthenticator", FuzzCreateCertE2EUsingClientCertAuthenticator},
   145  		{"FuzzConfigValidation3", FuzzConfigValidation3},
   146  		{"FuzzCidrRange", FuzzCidrRange},
   147  		{"FuzzHeaderMatcher", FuzzHeaderMatcher},
   148  		{"FuzzHostMatcherWithRegex", FuzzHostMatcherWithRegex},
   149  		{"FuzzHostMatcher", FuzzHostMatcher},
   150  		{"FuzzMetadataListMatcher", FuzzMetadataListMatcher},
   151  		{"FuzzGrpcGenGenerate", FuzzGrpcGenGenerate},
   152  		{"FuzzConvertIngressVirtualService", FuzzConvertIngressVirtualService},
   153  		{"FuzzConvertIngressV1alpha3", FuzzConvertIngressV1alpha3},
   154  		{"FuzzAggregateController", FuzzAggregateController},
   155  		{"FuzzKubeCRD", FuzzKubeCRD},
   156  		{"FuzzReconcileStatuses", FuzzReconcileStatuses},
   157  		{"FuzzWE", FuzzWE},
   158  		{"FuzzVerifyCertificate", FuzzVerifyCertificate},
   159  		{"FuzzExtractIDs", FuzzExtractIDs},
   160  		{"FuzzPemCertBytestoString", FuzzPemCertBytestoString},
   161  		{"FuzzParsePemEncodedCertificateChain", FuzzParsePemEncodedCertificateChain},
   162  		{"FuzzUpdateVerifiedKeyCertBundleFromFile", FuzzUpdateVerifiedKeyCertBundleFromFile},
   163  		{"FuzzJwtUtil", FuzzJwtUtil},
   164  		{"FuzzFindRootCertFromCertificateChainBytes", FuzzFindRootCertFromCertificateChainBytes},
   165  		{"FuzzCRDRoundtrip", FuzzCRDRoundtrip},
   166  	}
   167  	for _, tt := range cases {
   168  		if testedFuzzers.InsertContains(tt.name) {
   169  			t.Fatalf("dupliate fuzzer test %v", tt.name)
   170  		}
   171  		t.Run(tt.name, func(t *testing.T) {
   172  			runRegressionTest(t, tt.name, tt.fuzzer)
   173  		})
   174  	}
   175  	t.Run("completeness", func(t *testing.T) {
   176  		match := regexp.MustCompile(`func Fuzz.+\(`)
   177  		fuzzers, err := walkMatch(filepath.Join(env.IstioSrc, "tests/fuzz"), match)
   178  		if err != nil {
   179  			t.Fatal(err)
   180  		}
   181  		allFuzzers := sets.New(fuzzers...)
   182  		if !allFuzzers.Equals(testedFuzzers) {
   183  			t.Fatalf("Not all fuzzers are tested! Missing %v", allFuzzers.Difference(testedFuzzers))
   184  		}
   185  	})
   186  }