Fix nil pointer dereference in selectable fields check When checking specVersion.SelectableFields, if specVersion is nil, a nil pointer dereference could occur. This change updates the conditional to use || instead of &&, ensuring that the check for specVersion being nil happens first, avoiding potential runtime panics.

Additionally, a new test case has been added to validate this condition, ensuring safe handling of nil values for specVersion and empty SelectableFields.
Signed-off-by: pirates <whalehunting.bob@gmail.com>
This commit is contained in:
jyh071116 2024-11-11 17:03:08 +09:00 committed by Joe Betz
parent 953a3a8aa6
commit 4fd0809717
2 changed files with 24 additions and 3 deletions

View File

@ -623,7 +623,7 @@ func buildSelectableFields(crd *apiextensionsv1.CustomResourceDefinition, versio
break break
} }
} }
if specVersion == nil && len(specVersion.SelectableFields) == 0 { if specVersion == nil || len(specVersion.SelectableFields) == 0 {
return nil return nil
} }
selectableFields := make([]any, len(specVersion.SelectableFields)) selectableFields := make([]any, len(specVersion.SelectableFields))

View File

@ -45,31 +45,41 @@ func TestNewBuilder(t *testing.T) {
wantedSchema string wantedSchema string
wantedItemsSchema string wantedItemsSchema string
v2 bool // produce OpenAPIv2 v2 bool // produce OpenAPIv2
includeSelectable bool // include selectable fields
version string
}{ }{
{ {
"nil", "nil",
"", "",
`{"type":"object","x-kubernetes-group-version-kind":[{"group":"bar.k8s.io","kind":"Foo","version":"v1"}]}`, `{"$ref":"#/definitions/io.k8s.bar.v1.Foo"}`, `{"type":"object","x-kubernetes-group-version-kind":[{"group":"bar.k8s.io","kind":"Foo","version":"v1"}]}`, `{"$ref":"#/definitions/io.k8s.bar.v1.Foo"}`,
true, true,
false,
"v1",
}, },
{"with properties", {"with properties",
`{"type":"object","properties":{"spec":{"type":"object"},"status":{"type":"object"}}}`, `{"type":"object","properties":{"spec":{"type":"object"},"status":{"type":"object"}}}`,
`{"type":"object","properties":{"apiVersion":{"type":"string"},"kind":{"type":"string"},"metadata":{"$ref":"#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"},"spec":{"type":"object"},"status":{"type":"object"}},"x-kubernetes-group-version-kind":[{"group":"bar.k8s.io","kind":"Foo","version":"v1"}]}`, `{"type":"object","properties":{"apiVersion":{"type":"string"},"kind":{"type":"string"},"metadata":{"$ref":"#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"},"spec":{"type":"object"},"status":{"type":"object"}},"x-kubernetes-group-version-kind":[{"group":"bar.k8s.io","kind":"Foo","version":"v1"}]}`,
`{"$ref":"#/definitions/io.k8s.bar.v1.Foo"}`, `{"$ref":"#/definitions/io.k8s.bar.v1.Foo"}`,
true, true,
false,
"v1",
}, },
{"type only", {"type only",
`{"type":"object"}`, `{"type":"object"}`,
`{"type":"object","properties":{"apiVersion":{"type":"string"},"kind":{"type":"string"},"metadata":{"$ref":"#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"}},"x-kubernetes-group-version-kind":[{"group":"bar.k8s.io","kind":"Foo","version":"v1"}]}`, `{"type":"object","properties":{"apiVersion":{"type":"string"},"kind":{"type":"string"},"metadata":{"$ref":"#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"}},"x-kubernetes-group-version-kind":[{"group":"bar.k8s.io","kind":"Foo","version":"v1"}]}`,
`{"$ref":"#/definitions/io.k8s.bar.v1.Foo"}`, `{"$ref":"#/definitions/io.k8s.bar.v1.Foo"}`,
true, true,
false,
"v1",
}, },
{"preserve unknown at root v2", {"preserve unknown at root v2",
`{"type":"object","x-kubernetes-preserve-unknown-fields":true}`, `{"type":"object","x-kubernetes-preserve-unknown-fields":true}`,
`{"type":"object","x-kubernetes-group-version-kind":[{"group":"bar.k8s.io","kind":"Foo","version":"v1"}]}`, `{"type":"object","x-kubernetes-group-version-kind":[{"group":"bar.k8s.io","kind":"Foo","version":"v1"}]}`,
`{"$ref":"#/definitions/io.k8s.bar.v1.Foo"}`, `{"$ref":"#/definitions/io.k8s.bar.v1.Foo"}`,
true, true,
false,
"v1",
}, },
{"with extensions", {"with extensions",
` `
@ -173,6 +183,17 @@ func TestNewBuilder(t *testing.T) {
}`, }`,
`{"$ref":"#/definitions/io.k8s.bar.v1.Foo"}`, `{"$ref":"#/definitions/io.k8s.bar.v1.Foo"}`,
true, true,
false,
"v1",
},
{
"include selectable fields with different version",
`{"type":"object","properties":{"spec":{"type":"object"},"status":{"type":"object"}}}`,
`{"type":"object","properties":{"apiVersion":{"type":"string"},"kind":{"type":"string"},"metadata":{"$ref":"#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"},"spec":{"type":"object"},"status":{"type":"object"}},"x-kubernetes-group-version-kind":[{"group":"bar.k8s.io","kind":"Foo","version":"v2"}]}`,
`{"$ref":"#/definitions/io.k8s.bar.v2.Foo"}`,
true,
true,
"v2",
}, },
} }
for _, tt := range tests { for _, tt := range tests {
@ -212,7 +233,7 @@ func TestNewBuilder(t *testing.T) {
}, },
Scope: apiextensionsv1.NamespaceScoped, Scope: apiextensionsv1.NamespaceScoped,
}, },
}, "v1", schema, Options{V2: tt.v2}) }, tt.version, schema, Options{V2: tt.v2, IncludeSelectableFields: tt.includeSelectable})
var wantedSchema, wantedItemsSchema spec.Schema var wantedSchema, wantedItemsSchema spec.Schema
if err := json.Unmarshal([]byte(tt.wantedSchema), &wantedSchema); err != nil { if err := json.Unmarshal([]byte(tt.wantedSchema), &wantedSchema); err != nil {