Fix mismatched provenance

Signed-off-by: Scott Rigby <scott@r6by.com>
pull/31176/head
Scott Rigby 4 weeks ago
parent f4ded700ca
commit aef38ea221
No known key found for this signature in database
GPG Key ID: C7C6FBB5B91C1155

@ -16,9 +16,12 @@ limitations under the License.
package plugin package plugin
import ( import (
"crypto/sha256"
"fmt" "fmt"
"io"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"golang.org/x/crypto/openpgp/clearsign" //nolint "golang.org/x/crypto/openpgp/clearsign" //nolint
@ -31,8 +34,8 @@ type SigningInfo struct {
// - "local dev": Plugin is a symlink (development mode) // - "local dev": Plugin is a symlink (development mode)
// - "unsigned": No provenance file found // - "unsigned": No provenance file found
// - "invalid provenance": Provenance file is malformed // - "invalid provenance": Provenance file is malformed
// - "mismatched provenance": Provenance file does not exactly match plugin // - "mismatched provenance": Provenance file does not match the installed tarball
// - "signed": Valid signature exists for this exact plugin // - "signed": Valid signature exists for the installed tarball
Status string Status string
IsSigned bool // True if plugin has a valid signature (even if not verified against keyring) IsSigned bool // True if plugin has a valid signature (even if not verified against keyring)
} }
@ -88,9 +91,9 @@ func GetPluginSigningInfo(metadata Metadata) (*SigningInfo, error) {
}, nil }, nil
} }
// Check if provenance is well-formed // Check if provenance matches the actual tarball
blockContent := string(block.Plaintext) blockContent := string(block.Plaintext)
if !validateProvenanceHash(blockContent) { if !validateProvenanceHash(blockContent, tarballPath) {
return &SigningInfo{ return &SigningInfo{
Status: "mismatched provenance", Status: "mismatched provenance",
IsSigned: false, IsSigned: false,
@ -108,15 +111,48 @@ func GetPluginSigningInfo(metadata Metadata) (*SigningInfo, error) {
}, nil }, nil
} }
func validateProvenanceHash(blockContent string) bool { func validateProvenanceHash(blockContent string, tarballPath string) bool {
// Parse provenance to check if it's well-formed // Parse provenance to get the expected hash
// (No need to compare hashes since we can't recreate the original tarball)
_, sums, err := parsePluginMessageBlock([]byte(blockContent)) _, sums, err := parsePluginMessageBlock([]byte(blockContent))
if err != nil { if err != nil {
return false return false
} }
// Valid if provenance contains file checksums
return len(sums.Files) > 0 // Must have file checksums
if len(sums.Files) == 0 {
return false
}
// Calculate actual hash of the tarball
actualHash, err := calculateFileHash(tarballPath)
if err != nil {
return false
}
// Check if the actual hash matches the expected hash in the provenance
for filename, expectedHash := range sums.Files {
if strings.Contains(filename, filepath.Base(tarballPath)) && expectedHash == actualHash {
return true
}
}
return false
}
// calculateFileHash calculates the SHA256 hash of a file
func calculateFileHash(filePath string) (string, error) {
file, err := os.Open(filePath)
if err != nil {
return "", err
}
defer file.Close()
hasher := sha256.New()
if _, err := io.Copy(hasher, file); err != nil {
return "", err
}
return fmt.Sprintf("sha256:%x", hasher.Sum(nil)), nil
} }
// GetSigningInfoForPlugins returns signing info for multiple plugins // GetSigningInfoForPlugins returns signing info for multiple plugins

Loading…
Cancel
Save