1
16
17 package fuzzer
18
19 import (
20 "fmt"
21 "math/rand"
22 "sort"
23 "strconv"
24 "strings"
25
26 fuzz "github.com/google/gofuzz"
27
28 apitesting "k8s.io/apimachinery/pkg/api/apitesting"
29 "k8s.io/apimachinery/pkg/api/apitesting/fuzzer"
30 "k8s.io/apimachinery/pkg/api/resource"
31 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32 metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
33 "k8s.io/apimachinery/pkg/runtime"
34 runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
35 "k8s.io/apimachinery/pkg/types"
36 )
37
38 func genericFuzzerFuncs(codecs runtimeserializer.CodecFactory) []interface{} {
39 return []interface{}{
40 func(q *resource.Quantity, c fuzz.Continue) {
41 *q = *resource.NewQuantity(c.Int63n(1000), resource.DecimalExponent)
42 },
43 func(j *int, c fuzz.Continue) {
44 *j = int(c.Int31())
45 },
46 func(j **int, c fuzz.Continue) {
47 if c.RandBool() {
48 i := int(c.Int31())
49 *j = &i
50 } else {
51 *j = nil
52 }
53 },
54 func(j *runtime.TypeMeta, c fuzz.Continue) {
55
56
57 j.APIVersion = ""
58 j.Kind = ""
59 },
60 func(j *runtime.Object, c fuzz.Continue) {
61
62 if true {
63 *j = &runtime.Unknown{
64
65 Raw: []byte(`{"apiVersion":"unknown.group/unknown","kind":"Something","someKey":"someValue"}`),
66 ContentType: runtime.ContentTypeJSON,
67 }
68 } else {
69 types := []runtime.Object{&metav1.Status{}, &metav1.APIGroup{}}
70 t := types[c.Rand.Intn(len(types))]
71 c.Fuzz(t)
72 *j = t
73 }
74 },
75 func(r *runtime.RawExtension, c fuzz.Continue) {
76
77 types := []runtime.Object{&metav1.Status{}, &metav1.APIGroup{}}
78 obj := types[c.Rand.Intn(len(types))]
79 c.Fuzz(obj)
80
81
82
83 var codec = apitesting.TestCodec(codecs, metav1.SchemeGroupVersion)
84
85
86 bytes, err := runtime.Encode(codec, obj)
87 if err != nil {
88 panic(fmt.Sprintf("Failed to encode object: %v", err))
89 }
90
91
92 for len(bytes) >= 1 && bytes[len(bytes)-1] == 10 {
93 bytes = bytes[:len(bytes)-1]
94 }
95
96
97 r.Raw = bytes
98 },
99 }
100 }
101
102
103 type charRange struct {
104 first, last rune
105 }
106
107 func (c *charRange) choose(r *rand.Rand) rune {
108 count := int64(c.last - c.first + 1)
109 ch := c.first + rune(r.Int63n(count))
110
111 return ch
112 }
113
114
115
116 func randomLabelPart(c fuzz.Continue, canBeEmpty bool) string {
117 validStartEnd := []charRange{{'0', '9'}, {'a', 'z'}, {'A', 'Z'}}
118 validMiddle := []charRange{{'0', '9'}, {'a', 'z'}, {'A', 'Z'},
119 {'.', '.'}, {'-', '-'}, {'_', '_'}}
120
121 partLen := c.Rand.Intn(64)
122 if !canBeEmpty {
123 partLen = c.Rand.Intn(63) + 1
124 }
125
126 runes := make([]rune, partLen)
127 if partLen == 0 {
128 return string(runes)
129 }
130
131 runes[0] = validStartEnd[c.Rand.Intn(len(validStartEnd))].choose(c.Rand)
132 for i := range runes[1:] {
133 runes[i+1] = validMiddle[c.Rand.Intn(len(validMiddle))].choose(c.Rand)
134 }
135 runes[len(runes)-1] = validStartEnd[c.Rand.Intn(len(validStartEnd))].choose(c.Rand)
136
137 return string(runes)
138 }
139
140 func randomDNSLabel(c fuzz.Continue) string {
141 validStartEnd := []charRange{{'0', '9'}, {'a', 'z'}}
142 validMiddle := []charRange{{'0', '9'}, {'a', 'z'}, {'-', '-'}}
143
144 partLen := c.Rand.Intn(63) + 1
145 runes := make([]rune, partLen)
146
147 runes[0] = validStartEnd[c.Rand.Intn(len(validStartEnd))].choose(c.Rand)
148 for i := range runes[1:] {
149 runes[i+1] = validMiddle[c.Rand.Intn(len(validMiddle))].choose(c.Rand)
150 }
151 runes[len(runes)-1] = validStartEnd[c.Rand.Intn(len(validStartEnd))].choose(c.Rand)
152
153 return string(runes)
154 }
155
156 func randomLabelKey(c fuzz.Continue) string {
157 namePart := randomLabelPart(c, false)
158 prefixPart := ""
159
160 usePrefix := c.RandBool()
161 if usePrefix {
162
163 prefixPartsLen := c.Rand.Intn(2) + 1
164 prefixParts := make([]string, prefixPartsLen)
165 for i := range prefixParts {
166 prefixParts[i] = randomDNSLabel(c)
167 }
168 prefixPart = strings.Join(prefixParts, ".") + "/"
169 }
170
171 return prefixPart + namePart
172 }
173
174 func v1FuzzerFuncs(codecs runtimeserializer.CodecFactory) []interface{} {
175
176 return []interface{}{
177 func(j *metav1.TypeMeta, c fuzz.Continue) {
178
179
180 j.APIVersion = ""
181 j.Kind = ""
182 },
183 func(j *metav1.ObjectMeta, c fuzz.Continue) {
184 c.FuzzNoCustom(j)
185
186 j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10)
187 j.UID = types.UID(c.RandString())
188
189
190
191 var sec, nsec uint32
192 c.Fuzz(&sec)
193 c.Fuzz(&nsec)
194 j.CreationTimestamp = metav1.Unix(int64(sec), int64(nsec)).Rfc3339Copy()
195
196 if j.DeletionTimestamp != nil {
197 c.Fuzz(&sec)
198 c.Fuzz(&nsec)
199 t := metav1.Unix(int64(sec), int64(nsec)).Rfc3339Copy()
200 j.DeletionTimestamp = &t
201 }
202
203 if len(j.Labels) == 0 {
204 j.Labels = nil
205 } else {
206 delete(j.Labels, "")
207 }
208 if len(j.Annotations) == 0 {
209 j.Annotations = nil
210 } else {
211 delete(j.Annotations, "")
212 }
213 if len(j.OwnerReferences) == 0 {
214 j.OwnerReferences = nil
215 }
216 if len(j.Finalizers) == 0 {
217 j.Finalizers = nil
218 }
219 },
220 func(j *metav1.ResourceVersionMatch, c fuzz.Continue) {
221 matches := []metav1.ResourceVersionMatch{"", metav1.ResourceVersionMatchExact, metav1.ResourceVersionMatchNotOlderThan}
222 *j = matches[c.Rand.Intn(len(matches))]
223 },
224 func(j *metav1.ListMeta, c fuzz.Continue) {
225 j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10)
226 j.SelfLink = c.RandString()
227 },
228 func(j *metav1.LabelSelector, c fuzz.Continue) {
229 c.FuzzNoCustom(j)
230
231
232 if len(j.MatchLabels) == 0 && len(j.MatchExpressions) == 0 {
233 j.MatchExpressions = make([]metav1.LabelSelectorRequirement, c.Rand.Intn(2)+1)
234 }
235
236 if j.MatchLabels != nil {
237 fuzzedMatchLabels := make(map[string]string, len(j.MatchLabels))
238 for i := 0; i < len(j.MatchLabels); i++ {
239 fuzzedMatchLabels[randomLabelKey(c)] = randomLabelPart(c, true)
240 }
241 j.MatchLabels = fuzzedMatchLabels
242 }
243
244 validOperators := []metav1.LabelSelectorOperator{
245 metav1.LabelSelectorOpIn,
246 metav1.LabelSelectorOpNotIn,
247 metav1.LabelSelectorOpExists,
248 metav1.LabelSelectorOpDoesNotExist,
249 }
250
251 if j.MatchExpressions != nil {
252
253
254
255
256 for i := range j.MatchExpressions {
257 req := metav1.LabelSelectorRequirement{}
258 c.Fuzz(&req)
259 req.Key = randomLabelKey(c)
260 req.Operator = validOperators[c.Rand.Intn(len(validOperators))]
261 if req.Operator == metav1.LabelSelectorOpIn || req.Operator == metav1.LabelSelectorOpNotIn {
262 if len(req.Values) == 0 {
263
264 req.Values = make([]string, c.Rand.Intn(2)+1)
265 }
266 for i := range req.Values {
267 req.Values[i] = randomLabelPart(c, true)
268 }
269 sort.Strings(req.Values)
270 } else {
271 req.Values = nil
272 }
273 j.MatchExpressions[i] = req
274 }
275
276 sort.Slice(j.MatchExpressions, func(a, b int) bool { return j.MatchExpressions[a].Key < j.MatchExpressions[b].Key })
277 }
278 },
279 func(j *metav1.ManagedFieldsEntry, c fuzz.Continue) {
280 c.FuzzNoCustom(j)
281 j.FieldsV1 = nil
282 },
283 }
284 }
285
286 func v1beta1FuzzerFuncs(codecs runtimeserializer.CodecFactory) []interface{} {
287 return []interface{}{
288 func(r *metav1beta1.TableOptions, c fuzz.Continue) {
289 c.FuzzNoCustom(r)
290
291
292 r.NoHeaders = false
293 },
294 func(r *metav1beta1.TableRow, c fuzz.Continue) {
295 c.Fuzz(&r.Object)
296 c.Fuzz(&r.Conditions)
297 if len(r.Conditions) == 0 {
298 r.Conditions = nil
299 }
300 n := c.Intn(10)
301 if n > 0 {
302 r.Cells = make([]interface{}, n)
303 }
304 for i := range r.Cells {
305 t := c.Intn(6)
306 switch t {
307 case 0:
308 r.Cells[i] = c.RandString()
309 case 1:
310 r.Cells[i] = c.Int63()
311 case 2:
312 r.Cells[i] = c.RandBool()
313 case 3:
314 x := map[string]interface{}{}
315 for j := c.Intn(10) + 1; j >= 0; j-- {
316 x[c.RandString()] = c.RandString()
317 }
318 r.Cells[i] = x
319 case 4:
320 x := make([]interface{}, c.Intn(10))
321 for i := range x {
322 x[i] = c.Int63()
323 }
324 r.Cells[i] = x
325 default:
326 r.Cells[i] = nil
327 }
328 }
329 },
330 }
331 }
332
333 var Funcs = fuzzer.MergeFuzzerFuncs(
334 genericFuzzerFuncs,
335 v1FuzzerFuncs,
336 v1beta1FuzzerFuncs,
337 )
338
View as plain text