[Bug] Resolve context variables correctly if parameter isn't exist according to non-existence jmesPath check

Kyverno Version

1.12

Kubernetes Version

1.29

Kubernetes Platform

AKS

Description

We're using 1.13.4 Kyverno version in Argocd (couldn't find in dropdown). And we've configured Kyverno sidecar policy that injects container and initContainer to our Deployments. Additionally we've configured context variables to optionally overwrite default values from kyverno policy template like this - context: - name: tag variable: jmesPath: request.object.metadata.annotations."annotation1" || '{{ $.Values.image.tag }}'

My understanding is that if annotation(s) in application deployment template doesn't exist then Kyverno default values should be populated but it doesn't. Our current solution is to set annotations and add values.yaml with empty values in application Deployment helm template in such case it works.

I've read this documentation - https://kyverno.io/docs/writing-policies/jmespath/#non-existence-checks , where it's mentioned that "The resulting full expression which will correctly evaluate is {{request.object.metadata.labels.appns || ''}}. This expression reads, “take the value of the key request.object.metadata.labels.appns or, if it does not exist, set it to an empty string”.

So looks like if in our case annotation doesn't exists policy just stops working. I'd like to clarify is it some kind of bug or my query isn't properly formed?

Steps to reproduce

  1. Add argocd kyverno application -
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: kyverno
  namespace: argocd
  annotations:
    argocd.argoproj.io/compare-options: ServerSideDiff=true,IncludeMutationWebhook=true
    argocd.argoproj.io/sync-wave: "-1"
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  destination:
    name: in-cluster
    namespace: kyverno
  source:
    path: charts/kyverno
    repoURL: https://your_repo_URL
    targetRevision: HEAD
    helm:
      valueFiles:
        - values_dev.yaml
  project: infrastructure
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true
      - ServerSideApply=true
  revisionHistoryLimit: 2
  1. Add Kyverno policy
---
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: sidecar
spec:
  #for 1.12
  #mutateExistingOnPolicyUpdate: true
  validationFailureAction: {{ .Values.mode | default "Audit" }}
  background: true
  webhookTimeoutSeconds: 30
  failurePolicy: Fail
  rules:
  - name: sidecar-rule
    skipBackgroundRequests: true
    match:
      any:
      - resources:
          kinds:
          - Deployment
          selector:
            matchLabels:
              label1: "true"
    context:
    - name: tag
      variable:
        jmesPath: request.object.metadata.annotations."annotation1" || '{{ $.Values.image.tag }}'
    mutate:
      # Specify the mutate targets explicitly
      #For 1.13+
      mutateExistingOnPolicyUpdate: true  # Ensures existing resources are mutated
      targets:
        - apiVersion: apps/v1
          kind: Deployment
          name: {{`"{{request.object.metadata.name}}"`}}
          namespace: {{`"{{request.object.metadata.namespace}}"`}}
      patchStrategicMerge:
        metadata:
          labels:
            label1: "true"
        spec:
          template:
            spec:
              {{- with .Values.test }}
              initContainers:
              - name: init-container
                image: "{{ .image.registry }}/{{ .image.repository }}:{{`{{ tag }}`}}"
                imagePullPolicy: {{ .image.pullPolicy }}
                env: 
                - name: tag
                  value: "{{`{{ tag }}`}}"
                {{- if .resources }}
                resources:
                  {{- toYaml .resources | nindent 18 }}
                {{- end }}
                volumeMounts:
                - mountPath: /data
                  name: test-volume
              containers:
              - name: sidecar
                image: "{{ .image.registry }}/{{ .image.repository }}:{{`{{ tag }}`}}"
                imagePullPolicy: {{ .image.pullPolicy }}
                {{- if .resources }}
                resources: 
                  {{- toYaml .resources | nindent 18 }}
                {{- end }}
                livenessProbe:
                  {{- .livenessProbe | toYaml | nindent 18 }}
                volumeMounts:
                - mountPath: /data
                  name test-volume
              volumes:
              - emptyDir: {}
                name: test-volume
              {{- end }}
  1. Set some values in kyverno chart values.yaml.

  2. On application deployment don't set annotations and set only this label -

apiVersion: apps/v1 kind: Deployment metadata: name: {{ .fullnameOverride }} labels: label1: "true" ...

  1. Deploy kyverno and application in Argocd and you should observe that Kyverno policy isn't applied on application because it can't find annotations that are set in jmesPath queries. If annotations are set on application deployment helm template then policy works as expected

Expected behavior

When I'm using non-existence jmesPath check I'm expecting that if value in first condition part isn't exist, then second condition executes and populates with Kyverno default template values - context: - name: tag variable: jmesPath: request.object.metadata.annotations."annotation1" || '{{ $.Values.image.tag }}'

According to documentation it should but for some reason doesn't - https://kyverno.io/docs/writing-policies/jmespath/#non-existence-checks

Screenshots

No response

Kyverno logs

Actually I didn't find any errors in Kyverno admission and background controller logs

Slack discussion

No response

Troubleshooting

  • I have read and followed the documentation AND the troubleshooting guide.
  • I have searched other issues in this repository and mine is not recorded.