From 6f981e002ba6ee0b1e25b20315964d944fe5c0c8 Mon Sep 17 00:00:00 2001 From: "elvandlie@gmail.com" Date: Thu, 21 May 2026 02:46:15 +0700 Subject: [PATCH] fix(k8s): guard against nil vol.Path in ProcessVolumes ProcessVolumes dereferences vol.Path (*string) at line 925 without a nil check. If a Volume entry has a source type (secret, configMap, PVC) but no path field set, the nil pointer dereference crashes the process. None of the callers of ProcessVolumes (k8s deployer, knative deployer) gate on validateVolumes before calling ProcessVolumes, so a malformed func.yaml can trigger this crash. Added a nil guard that returns a descriptive error instead of panicking. Fixes #3796 --- pkg/k8s/deployer.go | 3 ++ pkg/k8s/deployer_test.go | 65 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/pkg/k8s/deployer.go b/pkg/k8s/deployer.go index 1341515ac8..f9f88879d6 100644 --- a/pkg/k8s/deployer.go +++ b/pkg/k8s/deployer.go @@ -922,6 +922,9 @@ func ProcessVolumes(volumes []fn.Volume, referencedSecrets, referencedConfigMaps } if volumeName != "" { + if vol.Path == nil { + return nil, nil, fmt.Errorf("volume %q is missing required path field", volumeName) + } if !usedPaths.Has(*vol.Path) { newVolumeMounts = append(newVolumeMounts, corev1.VolumeMount{ Name: volumeName, diff --git a/pkg/k8s/deployer_test.go b/pkg/k8s/deployer_test.go index ec48829493..61fb3658e1 100644 --- a/pkg/k8s/deployer_test.go +++ b/pkg/k8s/deployer_test.go @@ -2,6 +2,7 @@ package k8s import ( "os" + "strings" "testing" corev1 "k8s.io/api/core/v1" @@ -424,3 +425,67 @@ func TestGenerateTriggerName_DifferentBrokers(t *testing.T) { t.Errorf("Different brokers should produce different names: %s, %s, %s", name1, name2, name3) } } + +func Test_ProcessVolumes_NilPath(t *testing.T) { + secretName := "my-secret" + referencedSecrets := sets.New[string]() + referencedConfigMaps := sets.New[string]() + referencedPVCs := sets.New[string]() + + volumes := []fn.Volume{ + {Secret: &secretName, Path: nil}, + } + + _, _, err := ProcessVolumes(volumes, &referencedSecrets, &referencedConfigMaps, &referencedPVCs) + if err == nil { + t.Fatal("expected error for volume with nil path, got nil") + } + if !strings.Contains(err.Error(), "missing required path") { + t.Fatalf("unexpected error message: %s", err.Error()) + } +} + +func Test_ProcessVolumes_NilPathConfigMap(t *testing.T) { + configMapName := "my-config" + referencedSecrets := sets.New[string]() + referencedConfigMaps := sets.New[string]() + referencedPVCs := sets.New[string]() + + volumes := []fn.Volume{ + {ConfigMap: &configMapName, Path: nil}, + } + + _, _, err := ProcessVolumes(volumes, &referencedSecrets, &referencedConfigMaps, &referencedPVCs) + if err == nil { + t.Fatal("expected error for volume with nil path, got nil") + } + if !strings.Contains(err.Error(), "missing required path") { + t.Fatalf("unexpected error message: %s", err.Error()) + } +} + +func Test_ProcessVolumes_ValidPath(t *testing.T) { + secretName := "my-secret" + path := "/etc/secret" + referencedSecrets := sets.New[string]() + referencedConfigMaps := sets.New[string]() + referencedPVCs := sets.New[string]() + + volumes := []fn.Volume{ + {Secret: &secretName, Path: &path}, + } + + vols, mounts, err := ProcessVolumes(volumes, &referencedSecrets, &referencedConfigMaps, &referencedPVCs) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if len(vols) != 1 { + t.Fatalf("expected 1 volume, got %d", len(vols)) + } + if len(mounts) != 1 { + t.Fatalf("expected 1 mount, got %d", len(mounts)) + } + if mounts[0].MountPath != "/etc/secret" { + t.Errorf("expected mount path /etc/secret, got %s", mounts[0].MountPath) + } +}