You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
helm/pkg/cmd/plugin_verify_test.go

265 lines
6.7 KiB

/*
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 cmd
import (
"bytes"
"crypto/sha256"
"fmt"
"os"
"path/filepath"
"strings"
"testing"
"helm.sh/helm/v4/internal/plugin"
"helm.sh/helm/v4/internal/test/ensure"
)
func TestPluginVerifyCmd_NoArgs(t *testing.T) {
ensure.HelmHome(t)
out := &bytes.Buffer{}
cmd := newPluginVerifyCmd(out)
cmd.SetArgs([]string{})
err := cmd.Execute()
if err == nil {
t.Error("expected error when no arguments provided")
}
if !strings.Contains(err.Error(), "requires 1 argument") {
t.Errorf("expected 'requires 1 argument' error, got: %v", err)
}
}
func TestPluginVerifyCmd_TooManyArgs(t *testing.T) {
ensure.HelmHome(t)
out := &bytes.Buffer{}
cmd := newPluginVerifyCmd(out)
cmd.SetArgs([]string{"plugin1", "plugin2"})
err := cmd.Execute()
if err == nil {
t.Error("expected error when too many arguments provided")
}
if !strings.Contains(err.Error(), "requires 1 argument") {
t.Errorf("expected 'requires 1 argument' error, got: %v", err)
}
}
func TestPluginVerifyCmd_NonexistentFile(t *testing.T) {
ensure.HelmHome(t)
out := &bytes.Buffer{}
cmd := newPluginVerifyCmd(out)
cmd.SetArgs([]string{"/nonexistent/plugin.tgz"})
err := cmd.Execute()
if err == nil {
t.Error("expected error when plugin file doesn't exist")
}
}
func TestPluginVerifyCmd_MissingProvenance(t *testing.T) {
ensure.HelmHome(t)
// Create a plugin tarball without .prov file
pluginTgz := createTestPluginTarball(t)
defer os.Remove(pluginTgz)
out := &bytes.Buffer{}
cmd := newPluginVerifyCmd(out)
cmd.SetArgs([]string{pluginTgz})
err := cmd.Execute()
if err == nil {
t.Error("expected error when .prov file is missing")
}
if !strings.Contains(err.Error(), "could not find provenance file") {
t.Errorf("expected 'could not find provenance file' error, got: %v", err)
}
}
func TestPluginVerifyCmd_InvalidProvenance(t *testing.T) {
ensure.HelmHome(t)
// Create a plugin tarball with invalid .prov file
pluginTgz := createTestPluginTarball(t)
defer os.Remove(pluginTgz)
// Create invalid .prov file
provFile := pluginTgz + ".prov"
if err := os.WriteFile(provFile, []byte("invalid provenance"), 0644); err != nil {
t.Fatal(err)
}
defer os.Remove(provFile)
out := &bytes.Buffer{}
cmd := newPluginVerifyCmd(out)
cmd.SetArgs([]string{pluginTgz})
err := cmd.Execute()
if err == nil {
t.Error("expected error when .prov file is invalid")
}
}
func TestPluginVerifyCmd_DirectoryNotSupported(t *testing.T) {
ensure.HelmHome(t)
// Create a plugin directory
pluginDir := createTestPluginDir(t)
out := &bytes.Buffer{}
cmd := newPluginVerifyCmd(out)
cmd.SetArgs([]string{pluginDir})
err := cmd.Execute()
if err == nil {
t.Error("expected error when verifying directory")
}
if !strings.Contains(err.Error(), "directory verification not supported") {
t.Errorf("expected 'directory verification not supported' error, got: %v", err)
}
}
func TestPluginVerifyCmd_KeyringFlag(t *testing.T) {
ensure.HelmHome(t)
// Create a plugin tarball with .prov file
pluginTgz := createTestPluginTarball(t)
defer os.Remove(pluginTgz)
// Create .prov file
provFile := pluginTgz + ".prov"
createProvFile(t, provFile, pluginTgz, "")
defer os.Remove(provFile)
// Create empty keyring file
keyring := createTestKeyring(t)
defer os.Remove(keyring)
out := &bytes.Buffer{}
cmd := newPluginVerifyCmd(out)
cmd.SetArgs([]string{"--keyring", keyring, pluginTgz})
// Should fail with keyring error but command parsing should work
err := cmd.Execute()
if err == nil {
t.Error("expected error with empty keyring")
}
// The important thing is that the keyring flag was parsed and used
}
func TestPluginVerifyOptions_Run_Success(t *testing.T) {
// Skip this test as it would require real PGP keys and valid signatures
// The core verification logic is thoroughly tested in internal/plugin/verify_test.go
t.Skip("Success case requires real PGP keys - core logic tested in internal/plugin/verify_test.go")
}
// Helper functions for test setup
func createTestPluginDir(t *testing.T) string {
t.Helper()
// Create temporary directory with plugin structure
tmpDir := t.TempDir()
pluginDir := filepath.Join(tmpDir, "test-plugin")
if err := os.MkdirAll(pluginDir, 0755); err != nil {
t.Fatalf("Failed to create plugin directory: %v", err)
}
// Use the same plugin YAML as other cmd tests
if err := os.WriteFile(filepath.Join(pluginDir, "plugin.yaml"), []byte(testPluginYAML), 0644); err != nil {
t.Fatalf("Failed to create plugin.yaml: %v", err)
}
return pluginDir
}
func createTestPluginTarball(t *testing.T) string {
t.Helper()
pluginDir := createTestPluginDir(t)
// Create tarball using the plugin package helper
tmpDir := filepath.Dir(pluginDir)
tgzPath := filepath.Join(tmpDir, "test-plugin-1.0.0.tgz")
tarFile, err := os.Create(tgzPath)
if err != nil {
t.Fatalf("Failed to create tarball file: %v", err)
}
defer tarFile.Close()
if err := plugin.CreatePluginTarball(pluginDir, "test-plugin", tarFile); err != nil {
t.Fatalf("Failed to create tarball: %v", err)
}
return tgzPath
}
func createProvFile(t *testing.T, provFile, pluginTgz, hash string) {
t.Helper()
var hashStr string
if hash == "" {
// Calculate actual hash of the tarball
data, err := os.ReadFile(pluginTgz)
if err != nil {
t.Fatalf("Failed to read tarball for hashing: %v", err)
}
hashSum := sha256.Sum256(data)
hashStr = fmt.Sprintf("sha256:%x", hashSum)
} else {
// Use provided hash
hashStr = hash
}
// Create properly formatted provenance file with specified hash
provContent := fmt.Sprintf(`-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
name: test-plugin
version: 1.0.0
description: Test plugin for verification
files:
test-plugin-1.0.0.tgz: %s
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
iQEcBAEBCAAGBQJktest...
-----END PGP SIGNATURE-----
`, hashStr)
if err := os.WriteFile(provFile, []byte(provContent), 0644); err != nil {
t.Fatalf("Failed to create provenance file: %v", err)
}
}
func createTestKeyring(t *testing.T) string {
t.Helper()
// Create a temporary keyring file
tmpDir := t.TempDir()
keyringPath := filepath.Join(tmpDir, "pubring.gpg")
// Create empty keyring for testing
if err := os.WriteFile(keyringPath, []byte{}, 0644); err != nil {
t.Fatalf("Failed to create test keyring: %v", err)
}
return keyringPath
}