Merge commit from fork

fix: Plugin missing provenance bypass
pull/32025/head
George Jenkins 2 weeks ago committed by GitHub
commit bc215d8c19
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -98,24 +98,23 @@ func InstallWithOptions(i Installer, opts Options) (*VerificationResult, error)
// Check if provenance data exists // Check if provenance data exists
if len(provData) == 0 { if len(provData) == 0 {
// No .prov file found - emit warning but continue installation return nil, fmt.Errorf("plugin verification failed: no provenance file (.prov) found")
fmt.Fprint(os.Stderr, "WARNING: No provenance file found for plugin. Plugin is not signed and cannot be verified.\n") }
} else {
// Provenance data exists - verify the plugin
verification, err := plugin.VerifyPlugin(archiveData, provData, filename, opts.Keyring)
if err != nil {
return nil, fmt.Errorf("plugin verification failed: %w", err)
}
// Collect verification info // Provenance data exists - verify the plugin
result = &VerificationResult{ verification, err := plugin.VerifyPlugin(archiveData, provData, filename, opts.Keyring)
SignedBy: make([]string, 0), if err != nil {
Fingerprint: fmt.Sprintf("%X", verification.SignedBy.PrimaryKey.Fingerprint), return nil, fmt.Errorf("plugin verification failed: %w", err)
FileHash: verification.FileHash, }
}
for name := range verification.SignedBy.Identities { // Collect verification info
result.SignedBy = append(result.SignedBy, name) result = &VerificationResult{
} SignedBy: make([]string, 0),
Fingerprint: fmt.Sprintf("%X", verification.SignedBy.PrimaryKey.Fingerprint),
FileHash: verification.FileHash,
}
for name := range verification.SignedBy.Identities {
result.SignedBy = append(result.SignedBy, name)
} }
} }

@ -16,10 +16,8 @@ limitations under the License.
package installer package installer
import ( import (
"bytes"
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"io"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -44,33 +42,49 @@ func TestInstallWithOptions_VerifyMissingProvenance(t *testing.T) {
} }
defer os.RemoveAll(installer.Path()) defer os.RemoveAll(installer.Path())
// Capture stderr to check warning message // Install with verification enabled should fail when .prov is missing
oldStderr := os.Stderr
r, w, _ := os.Pipe()
os.Stderr = w
// Install with verification enabled (should warn but succeed)
result, err := InstallWithOptions(installer, Options{Verify: true, Keyring: "dummy"}) result, err := InstallWithOptions(installer, Options{Verify: true, Keyring: "dummy"})
// Restore stderr and read captured output // Should fail with a missing provenance error
w.Close() if err == nil {
os.Stderr = oldStderr t.Fatal("Expected installation to fail when .prov file is missing and verification is enabled")
var buf bytes.Buffer }
io.Copy(&buf, r) if !strings.Contains(err.Error(), "no provenance file") {
output := buf.String() t.Errorf("Expected 'no provenance file' in error message, got: %v", err)
// Should succeed with nil result (no verification performed)
if err != nil {
t.Fatalf("Expected installation to succeed despite missing .prov file, got error: %v", err)
} }
if result != nil { if result != nil {
t.Errorf("Expected nil verification result when .prov file is missing, got: %+v", result) t.Errorf("Expected nil verification result when .prov file is missing, got: %+v", result)
} }
// Should contain warning message // Plugin should NOT be installed
expectedWarning := "WARNING: No provenance file found for plugin" if _, err := os.Stat(installer.Path()); !os.IsNotExist(err) {
if !strings.Contains(output, expectedWarning) { t.Errorf("Plugin should not be installed when verification fails due to missing .prov")
t.Errorf("Expected warning message '%s' in output, got: %s", expectedWarning, output) }
}
func TestInstallWithOptions_NoVerifyMissingProvenance(t *testing.T) {
ensure.HelmHome(t)
// Create a temporary plugin tarball without .prov file
pluginDir := createTestPluginDir(t)
pluginTgz := createTarballFromPluginDir(t, pluginDir)
defer os.Remove(pluginTgz)
// Create local installer
installer, err := NewLocalInstaller(pluginTgz)
if err != nil {
t.Fatalf("Failed to create installer: %v", err)
}
defer os.RemoveAll(installer.Path())
// Install with verification explicitly disabled should succeed without .prov
result, err := InstallWithOptions(installer, Options{Verify: false})
if err != nil {
t.Fatalf("Expected installation to succeed with --verify=false, got error: %v", err)
}
if result != nil {
t.Errorf("Expected nil verification result when verification is disabled, got: %+v", result)
} }
// Plugin should be installed // Plugin should be installed

@ -51,11 +51,11 @@ const pluginInstallDesc = `
This command allows you to install a plugin from a url to a VCS repo or a local path. This command allows you to install a plugin from a url to a VCS repo or a local path.
By default, plugin signatures are verified before installation when installing from By default, plugin signatures are verified before installation when installing from
tarballs (.tgz or .tar.gz). This requires a corresponding .prov file to be available tarballs (.tgz or .tar.gz). A corresponding .prov file must be available alongside
alongside the tarball. the tarball; installation will fail if it is missing or invalid.
For local development, plugins installed from local directories are automatically For local development, plugins installed from local directories are automatically
treated as "local dev" and do not require signatures. treated as "local dev" and do not require signatures.
Use --verify=false to skip signature verification for remote plugins. Use --verify=false to explicitly skip signature verification (NOT recommended).
` `
func newPluginInstallCmd(out io.Writer) *cobra.Command { func newPluginInstallCmd(out io.Writer) *cobra.Command {

Loading…
Cancel
Save