From 0f400ec690d17c2c736a54a4b8e03fbafa1be65c Mon Sep 17 00:00:00 2001 From: Vincent Shen Date: Wed, 19 Aug 2026 12:08:46 -0700 Subject: [PATCH 1/2] CMP-4605: Guard kubevirt NAD rules against NADs without a spec kubevirt-localnet-vlan-required and kubevirt-bridge-mac-spoof-filtering guarded only has(n.spec.config). A NetworkAttachmentDefinition with no spec at all is legitimate (e.g. the istio-cni NAD), but it makes has(n.spec.config) raise 'no such key: spec' at evaluation time, and the scanner maps missing-key errors to FAIL - so both rules false-FAIL on any cluster carrying such a NAD. Prepend !has(n.spec) to both expressions and add a spec-less NAD regression fixture: it fails 5/6 and 6/7 against the unfixed expressions and all cases pass with the fix (celctl, the operator's scanner engine). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../cel/shared.yml | 1 + .../cel/tests/cases.yaml | 12 ++++++++++++ .../kubevirt-localnet-vlan-required/cel/shared.yml | 1 + .../cel/tests/cases.yaml | 12 ++++++++++++ 4 files changed, 26 insertions(+) diff --git a/applications/openshift-virtualization/kubevirt-bridge-mac-spoof-filtering/cel/shared.yml b/applications/openshift-virtualization/kubevirt-bridge-mac-spoof-filtering/cel/shared.yml index d801641cab47..7329890e08f6 100644 --- a/applications/openshift-virtualization/kubevirt-bridge-mac-spoof-filtering/cel/shared.yml +++ b/applications/openshift-virtualization/kubevirt-bridge-mac-spoof-filtering/cel/shared.yml @@ -13,6 +13,7 @@ inputs: expression: | nads.items.all(n, + !has(n.spec) || !has(n.spec.config) || !("type" in parseJSON(n.spec.config)) || parseJSON(n.spec.config).type != "bridge" || diff --git a/applications/openshift-virtualization/kubevirt-bridge-mac-spoof-filtering/cel/tests/cases.yaml b/applications/openshift-virtualization/kubevirt-bridge-mac-spoof-filtering/cel/tests/cases.yaml index d52df86994e5..21a385573faa 100644 --- a/applications/openshift-virtualization/kubevirt-bridge-mac-spoof-filtering/cel/tests/cases.yaml +++ b/applications/openshift-virtualization/kubevirt-bridge-mac-spoof-filtering/cel/tests/cases.yaml @@ -90,3 +90,15 @@ cases: spec: config: '{"cniVersion": "0.3.1", "type": "bridge", "bridge": "br3", "macspoofchk": false}' + - name: NAD with no spec at all is compliant (regression - istio-cni style) + expect: true + inputs: + nads: + apiVersion: v1 + kind: List + items: + - apiVersion: k8s.cni.cncf.io/v1 + kind: NetworkAttachmentDefinition + metadata: + name: istio-cni + namespace: default diff --git a/applications/openshift-virtualization/kubevirt-localnet-vlan-required/cel/shared.yml b/applications/openshift-virtualization/kubevirt-localnet-vlan-required/cel/shared.yml index 769908fd78d5..50e14f833941 100644 --- a/applications/openshift-virtualization/kubevirt-localnet-vlan-required/cel/shared.yml +++ b/applications/openshift-virtualization/kubevirt-localnet-vlan-required/cel/shared.yml @@ -13,6 +13,7 @@ inputs: expression: | nads.items.all(n, + !has(n.spec) || !has(n.spec.config) || !("topology" in parseJSON(n.spec.config)) || parseJSON(n.spec.config).topology != "localnet" || diff --git a/applications/openshift-virtualization/kubevirt-localnet-vlan-required/cel/tests/cases.yaml b/applications/openshift-virtualization/kubevirt-localnet-vlan-required/cel/tests/cases.yaml index ca7c3f6b7e28..df6ec64ff11d 100644 --- a/applications/openshift-virtualization/kubevirt-localnet-vlan-required/cel/tests/cases.yaml +++ b/applications/openshift-virtualization/kubevirt-localnet-vlan-required/cel/tests/cases.yaml @@ -75,3 +75,15 @@ cases: spec: config: '{"cniVersion": "0.3.1", "type": "ovn-k8s-cni-overlay", "topology": "localnet", "netAttachDefName": "default/localnet-untagged"}' + - name: NAD with no spec at all is compliant (regression - istio-cni style) + expect: true + inputs: + nads: + apiVersion: v1 + kind: List + items: + - apiVersion: k8s.cni.cncf.io/v1 + kind: NetworkAttachmentDefinition + metadata: + name: istio-cni + namespace: default From d32be1219f5e226fb8f53f4a9a92cf5bc16b2d58 Mon Sep 17 00:00:00 2001 From: Vincent Shen Date: Mon, 24 Aug 2026 10:43:00 -0700 Subject: [PATCH 2/2] Also guard against empty-string NAD config A NetworkAttachmentDefinition with spec.config set to an empty string passes both has() guards but makes parseJSON crash with 'unexpected end of JSON input', which the scanner surfaces as ERROR. Seen on a large CNV engineering cluster during profile validation. Treat an empty config as compliant, like an absent one, and add regression fixtures. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../cel/shared.yml | 1 + .../cel/tests/cases.yaml | 14 ++++++++++++++ .../kubevirt-localnet-vlan-required/cel/shared.yml | 1 + .../cel/tests/cases.yaml | 14 ++++++++++++++ 4 files changed, 30 insertions(+) diff --git a/applications/openshift-virtualization/kubevirt-bridge-mac-spoof-filtering/cel/shared.yml b/applications/openshift-virtualization/kubevirt-bridge-mac-spoof-filtering/cel/shared.yml index 7329890e08f6..fe0667544448 100644 --- a/applications/openshift-virtualization/kubevirt-bridge-mac-spoof-filtering/cel/shared.yml +++ b/applications/openshift-virtualization/kubevirt-bridge-mac-spoof-filtering/cel/shared.yml @@ -15,6 +15,7 @@ expression: | nads.items.all(n, !has(n.spec) || !has(n.spec.config) || + n.spec.config == "" || !("type" in parseJSON(n.spec.config)) || parseJSON(n.spec.config).type != "bridge" || ("macspoofchk" in parseJSON(n.spec.config) && parseJSON(n.spec.config).macspoofchk == true) diff --git a/applications/openshift-virtualization/kubevirt-bridge-mac-spoof-filtering/cel/tests/cases.yaml b/applications/openshift-virtualization/kubevirt-bridge-mac-spoof-filtering/cel/tests/cases.yaml index 21a385573faa..4ed9950bf0c9 100644 --- a/applications/openshift-virtualization/kubevirt-bridge-mac-spoof-filtering/cel/tests/cases.yaml +++ b/applications/openshift-virtualization/kubevirt-bridge-mac-spoof-filtering/cel/tests/cases.yaml @@ -102,3 +102,17 @@ cases: metadata: name: istio-cni namespace: default + - name: NAD with an empty-string config is compliant (regression - parseJSON("")) + expect: true + inputs: + nads: + apiVersion: v1 + kind: List + items: + - apiVersion: k8s.cni.cncf.io/v1 + kind: NetworkAttachmentDefinition + metadata: + name: empty-config + namespace: default + spec: + config: "" diff --git a/applications/openshift-virtualization/kubevirt-localnet-vlan-required/cel/shared.yml b/applications/openshift-virtualization/kubevirt-localnet-vlan-required/cel/shared.yml index 50e14f833941..c6f372540262 100644 --- a/applications/openshift-virtualization/kubevirt-localnet-vlan-required/cel/shared.yml +++ b/applications/openshift-virtualization/kubevirt-localnet-vlan-required/cel/shared.yml @@ -15,6 +15,7 @@ expression: | nads.items.all(n, !has(n.spec) || !has(n.spec.config) || + n.spec.config == "" || !("topology" in parseJSON(n.spec.config)) || parseJSON(n.spec.config).topology != "localnet" || ("vlanID" in parseJSON(n.spec.config) && parseJSON(n.spec.config).vlanID > 0) diff --git a/applications/openshift-virtualization/kubevirt-localnet-vlan-required/cel/tests/cases.yaml b/applications/openshift-virtualization/kubevirt-localnet-vlan-required/cel/tests/cases.yaml index df6ec64ff11d..7c9edd5ef71a 100644 --- a/applications/openshift-virtualization/kubevirt-localnet-vlan-required/cel/tests/cases.yaml +++ b/applications/openshift-virtualization/kubevirt-localnet-vlan-required/cel/tests/cases.yaml @@ -87,3 +87,17 @@ cases: metadata: name: istio-cni namespace: default + - name: NAD with an empty-string config is compliant (regression - parseJSON("")) + expect: true + inputs: + nads: + apiVersion: v1 + kind: List + items: + - apiVersion: k8s.cni.cncf.io/v1 + kind: NetworkAttachmentDefinition + metadata: + name: empty-config + namespace: default + spec: + config: ""