/* Copyright The Helm Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package plugin import ( "os" "path/filepath" "strings" "testing" "helm.sh/helm/v4/pkg/provenance" ) const testKeyFile = "../../pkg/cmd/testdata/helm-test-key.secret" const testPubFile = "../../pkg/cmd/testdata/helm-test-key.pub" const testPluginYAML = `apiVersion: v1 name: test-plugin type: cli/v1 runtime: subprocess version: 1.0.0 runtimeConfig: platformCommand: - command: echo` func TestVerifyPlugin(t *testing.T) { // Create a test plugin and sign it tempDir := t.TempDir() // Create plugin directory pluginDir := filepath.Join(tempDir, "verify-test-plugin") if err := os.MkdirAll(pluginDir, 0755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(pluginDir, "plugin.yaml"), []byte(testPluginYAML), 0644); err != nil { t.Fatal(err) } // Create tarball tarballPath := filepath.Join(tempDir, "verify-test-plugin.tar.gz") tarFile, err := os.Create(tarballPath) if err != nil { t.Fatal(err) } if err := CreatePluginTarball(pluginDir, "test-plugin", tarFile); err != nil { tarFile.Close() t.Fatal(err) } tarFile.Close() // Sign the plugin with source directory signer, err := provenance.NewFromKeyring(testKeyFile, "helm-test") if err != nil { t.Fatal(err) } if err := signer.DecryptKey(func(_ string) ([]byte, error) { return []byte(""), nil }); err != nil { t.Fatal(err) } sig, err := SignPlugin(tarballPath, signer) if err != nil { t.Fatal(err) } // Write the signature to .prov file provFile := tarballPath + ".prov" if err := os.WriteFile(provFile, []byte(sig), 0644); err != nil { t.Fatal(err) } // Now verify the plugin verification, err := VerifyPlugin(tarballPath, testPubFile) if err != nil { t.Fatalf("Failed to verify plugin: %v", err) } // Check verification results if verification.SignedBy == nil { t.Error("SignedBy is nil") } if verification.FileName != "verify-test-plugin.tar.gz" { t.Errorf("Expected filename 'verify-test-plugin.tar.gz', got %s", verification.FileName) } if verification.FileHash == "" { t.Error("FileHash is empty") } } func TestVerifyPluginBadSignature(t *testing.T) { tempDir := t.TempDir() // Create a plugin tarball pluginDir := filepath.Join(tempDir, "bad-plugin") if err := os.MkdirAll(pluginDir, 0755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(pluginDir, "plugin.yaml"), []byte(testPluginYAML), 0644); err != nil { t.Fatal(err) } tarballPath := filepath.Join(tempDir, "bad-plugin.tar.gz") tarFile, err := os.Create(tarballPath) if err != nil { t.Fatal(err) } if err := CreatePluginTarball(pluginDir, "test-plugin", tarFile); err != nil { tarFile.Close() t.Fatal(err) } tarFile.Close() // Create a bad signature (just some text) badSig := `-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA512 This is not a real signature -----BEGIN PGP SIGNATURE----- InvalidSignatureData -----END PGP SIGNATURE-----` provFile := tarballPath + ".prov" if err := os.WriteFile(provFile, []byte(badSig), 0644); err != nil { t.Fatal(err) } // Try to verify - should fail _, err = VerifyPlugin(tarballPath, testPubFile) if err == nil { t.Error("Expected verification to fail with bad signature") } } func TestVerifyPluginMissingProvenance(t *testing.T) { tempDir := t.TempDir() tarballPath := filepath.Join(tempDir, "no-prov.tar.gz") // Create a minimal tarball if err := os.WriteFile(tarballPath, []byte("dummy"), 0644); err != nil { t.Fatal(err) } // Try to verify without .prov file _, err := VerifyPlugin(tarballPath, testPubFile) if err == nil { t.Error("Expected verification to fail without provenance file") } } func TestVerifyPluginDirectory(t *testing.T) { // Create a test plugin directory tempDir := t.TempDir() pluginDir := filepath.Join(tempDir, "test-plugin") if err := os.MkdirAll(pluginDir, 0755); err != nil { t.Fatal(err) } // Create a plugin.yaml file if err := os.WriteFile(filepath.Join(pluginDir, "plugin.yaml"), []byte(testPluginYAML), 0644); err != nil { t.Fatal(err) } // Attempt to verify the directory - should fail _, err := VerifyPlugin(pluginDir, testPubFile) if err == nil { t.Error("Expected directory verification to fail, but it succeeded") } expectedError := "directory verification not supported" if !containsString(err.Error(), expectedError) { t.Errorf("Expected error to contain %q, got %q", expectedError, err.Error()) } } func containsString(s, substr string) bool { return len(s) >= len(substr) && (s == substr || len(s) > len(substr) && (s[:len(substr)] == substr || s[len(s)-len(substr):] == substr || strings.Contains(s, substr))) }