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 }