fix: Plugin missing provenance bypass

Signed-off-by: George Jenkins <gvjenkins@gmail.com>
release-4.1 v4.1.4
George Jenkins 1 month ago
parent 4e7994d446
commit 05fa37973d
No known key found for this signature in database
GPG Key ID: D79D67C9EC016739

@ -98,24 +98,23 @@ func InstallWithOptions(i Installer, opts Options) (*VerificationResult, error)
// Check if provenance data exists
if len(provData) == 0 {
// No .prov file found - emit warning but continue installation
fmt.Fprintf(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)
}
return nil, fmt.Errorf("plugin verification failed: no provenance file (.prov) found")
}
// Collect verification info
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)
}
// 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
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
import (
"bytes"
"crypto/sha256"
"fmt"
"io"
"os"
"path/filepath"
"strings"
@ -44,33 +42,49 @@ func TestInstallWithOptions_VerifyMissingProvenance(t *testing.T) {
}
defer os.RemoveAll(installer.Path())
// Capture stderr to check warning message
oldStderr := os.Stderr
r, w, _ := os.Pipe()
os.Stderr = w
// Install with verification enabled (should warn but succeed)
// Install with verification enabled should fail when .prov is missing
result, err := InstallWithOptions(installer, Options{Verify: true, Keyring: "dummy"})
// Restore stderr and read captured output
w.Close()
os.Stderr = oldStderr
var buf bytes.Buffer
io.Copy(&buf, r)
output := buf.String()
// 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)
// Should fail with a missing provenance error
if err == nil {
t.Fatal("Expected installation to fail when .prov file is missing and verification is enabled")
}
if !strings.Contains(err.Error(), "no provenance file") {
t.Errorf("Expected 'no provenance file' in error message, got: %v", err)
}
if result != nil {
t.Errorf("Expected nil verification result when .prov file is missing, got: %+v", result)
}
// Should contain warning message
expectedWarning := "WARNING: No provenance file found for plugin"
if !strings.Contains(output, expectedWarning) {
t.Errorf("Expected warning message '%s' in output, got: %s", expectedWarning, output)
// Plugin should NOT be installed
if _, err := os.Stat(installer.Path()); !os.IsNotExist(err) {
t.Errorf("Plugin should not be installed when verification fails due to missing .prov")
}
}
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

@ -50,11 +50,11 @@ const pluginInstallDesc = `
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
tarballs (.tgz or .tar.gz). This requires a corresponding .prov file to be available
alongside the tarball.
tarballs (.tgz or .tar.gz). A corresponding .prov file must be available alongside
the tarball; installation will fail if it is missing or invalid.
For local development, plugins installed from local directories are automatically
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 {

Loading…
Cancel
Save