clean up the code

This commit is contained in:
ejahnGithub 2025-05-20 18:35:40 -04:00
parent 3e5456827c
commit 0a6ce2bb74
5 changed files with 93 additions and 118 deletions

View file

@ -54,9 +54,8 @@ func normalizeReference(reference string, pathSeparator rune) (normalized string
return filepath.Clean(reference), fileArtifactType, nil
}
func NewDigestedArtifactForRelease(URL string, digest string, digestAlg string) (artifact *DigestedArtifact) {
func NewDigestedArtifactForRelease(digest string, digestAlg string) (artifact *DigestedArtifact) {
return &DigestedArtifact{
URL: URL,
digest: digest,
digestAlg: digestAlg,
}

View file

@ -239,9 +239,6 @@ func (v *LiveSigstoreVerifier) verify(attestation *api.Attestation, policy verif
result, err := verifier.Verify(attestation.Bundle, policy)
// if verification fails, create the error and exit verification early
if err != nil {
v.Logger.VerbosePrint(v.Logger.ColorScheme.Redf(
"Error is \"%s\"\n", err.Error(),
))
v.Logger.VerbosePrint(v.Logger.ColorScheme.Redf(
"Failed to verify against issuer \"%s\" \n\n", issuer,
))

View file

@ -9,7 +9,6 @@ import (
"github.com/cli/cli/v2/pkg/cmd/attestation/artifact"
"github.com/cli/cli/v2/pkg/cmd/attestation/verification"
att_io "github.com/cli/cli/v2/pkg/cmd/attestation/io"
v1 "github.com/in-toto/attestation/go/v1"
"google.golang.org/protobuf/encoding/protojson"
)
@ -61,30 +60,25 @@ func VerifyAttestations(art artifact.DigestedArtifact, att []*api.Attestation, s
return sigstoreVerified, "", nil
}
func FilterAttestationsByPURL(attestations []*api.Attestation, repo, tagName string, logger *att_io.Handler) []*api.Attestation {
func FilterAttestationsByTag(attestations []*api.Attestation, tagName string) ([]*api.Attestation, error) {
var filtered []*api.Attestation
expectedPURL := "pkg:github/" + repo + "@" + tagName
for _, att := range attestations {
statement := att.Bundle.Bundle.GetDsseEnvelope().Payload
var statementData v1.Statement
err := protojson.Unmarshal([]byte(statement), &statementData)
if err != nil {
logger.Println(logger.ColorScheme.Red("✗ Failed to unmarshal statement"))
continue
return nil, fmt.Errorf("failed to unmarshal statement: %w", err)
}
purlValue := statementData.Predicate.GetFields()["purl"]
var purl string
if purlValue != nil {
purl = purlValue.GetStringValue()
}
if purl == expectedPURL {
tagValue := statementData.Predicate.GetFields()["tag"].GetStringValue()
if tagValue == tagName {
filtered = append(filtered, att)
}
}
return filtered
return filtered, nil
}
func FilterAttestationsByFileDigest(attestations []*api.Attestation, repo, tagName, fileDigest string, logger *att_io.Handler) []*api.Attestation {
func FilterAttestationsByFileDigest(attestations []*api.Attestation, repo, tagName, fileDigest string) ([]*api.Attestation, error) {
var filtered []*api.Attestation
for _, att := range attestations {
statement := att.Bundle.Bundle.GetDsseEnvelope().Payload
@ -92,8 +86,7 @@ func FilterAttestationsByFileDigest(attestations []*api.Attestation, repo, tagNa
err := protojson.Unmarshal([]byte(statement), &statementData)
if err != nil {
logger.Println(logger.ColorScheme.Red("✗ Failed to unmarshal statement"))
continue
return nil, fmt.Errorf("failed to unmarshal statement: %w", err)
}
subjects := statementData.Subject
for _, subject := range subjects {
@ -107,5 +100,5 @@ func FilterAttestationsByFileDigest(attestations []*api.Attestation, repo, tagNa
}
}
return filtered
return filtered, nil
}

View file

@ -19,91 +19,76 @@ import (
"github.com/spf13/cobra"
)
func NewCmdVerifyAsset(f *cmdutil.Factory, runF func(*attestation.VerifyAssetOptions) error) *cobra.Command {
opts := &attestation.VerifyAssetOptions{
IO: f.IOStreams,
HttpClient: f.HttpClient,
}
func NewCmdVerifyAsset(f *cmdutil.Factory, runF func(*attestation.AttestOptions) error) *cobra.Command {
opts := &attestation.AttestOptions{}
cmd := &cobra.Command{
Use: "verify-asset <tag> <file-path>",
Short: "Verify that a given asset originated from a specific GitHub Release.",
Args: cobra.ExactArgs(2),
PreRunE: func(cmd *cobra.Command, args []string) error {
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
// support `-R, --repo` override
opts.BaseRepo = f.BaseRepo
opts.TagName = args[0]
opts.FilePath = args[1]
if len(args) > 0 {
opts.TagName = args[0]
}
if len(args) > 1 {
opts.FilePath = args[1]
}
if runF != nil {
return runF(opts)
}
httpClient, err := opts.HttpClient()
httpClient, err := f.HttpClient()
if err != nil {
return err
}
baseRepo, err := opts.BaseRepo()
baseRepo, err := f.BaseRepo()
if err != nil {
return err
}
logger := att_io.NewHandler(opts.IO)
logger := att_io.NewHandler(f.IOStreams)
hostname, _ := ghauth.DefaultHost()
option := attestation.AttestOptions{
*opts = attestation.AttestOptions{
TagName: opts.TagName,
FilePath: opts.FilePath,
Repo: baseRepo.RepoOwner() + "/" + baseRepo.RepoName(),
APIClient: api.NewLiveClient(httpClient, hostname, logger),
Limit: 10,
Owner: baseRepo.RepoOwner(),
PredicateType: "https://in-toto.io/attestation/release/v0.1",
Logger: logger,
HttpClient: httpClient,
BaseRepo: baseRepo,
IO: f.IOStreams,
Exporter: opts.Exporter,
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
if runF != nil {
return runF(opts)
}
option.HttpClient = httpClient
option.BaseRepo = baseRepo
option.IO = opts.IO
option.TagName = opts.TagName
option.Exporter = opts.Exporter
option.FilePath = opts.FilePath
td, err := option.APIClient.GetTrustDomain()
td, err := opts.APIClient.GetTrustDomain()
if err != nil {
logger.Println(logger.ColorScheme.Red("✗ Failed to get trust domain"))
opts.Logger.Println(opts.Logger.ColorScheme.Red("✗ Failed to get trust domain"))
return err
}
ec, err := attestation.NewEnforcementCriteria(&option, logger)
ec, err := attestation.NewEnforcementCriteria(opts, opts.Logger)
if err != nil {
logger.Println(logger.ColorScheme.Red("✗ Failed to build policy information"))
opts.Logger.Println(opts.Logger.ColorScheme.Red("✗ Failed to build policy information"))
return err
}
config := verification.SigstoreConfig{
TrustedRoot: "",
Logger: logger,
Logger: opts.Logger,
NoPublicGood: true,
TrustDomain: td,
}
sigstoreVerifier, err := verification.NewLiveSigstoreVerifier(config)
if err != nil {
logger.Println(logger.ColorScheme.Red("✗ Failed to create Sigstore verifier"))
opts.Logger.Println(opts.Logger.ColorScheme.Red("✗ Failed to create Sigstore verifier"))
return err
}
option.SigstoreVerifier = sigstoreVerifier
option.EC = ec
opts.SigstoreVerifier = sigstoreVerifier
opts.EC = ec
// output ec
return verifyAssetRun(&option)
return verifyAssetRun(opts)
},
}
@ -124,50 +109,56 @@ func verifyAssetRun(opts *attestation.AttestOptions) error {
opts.Logger.Printf("Loaded digest %s for %s\n", fileDigest.DigestWithAlg(), fileName)
sha, err := shared.FetchRefSHA(ctx, opts.HttpClient, opts.BaseRepo, opts.TagName)
ref, err := shared.FetchRefSHA(ctx, opts.HttpClient, opts.BaseRepo, opts.TagName)
if err != nil {
return err
}
releaseArtifact := artifact.NewDigestedArtifactForRelease(opts.TagName, sha, "sha1")
opts.Logger.Printf("Resolved %s to %s\n", opts.TagName, releaseArtifact.DigestWithAlg())
releaseRefDigest := artifact.NewDigestedArtifactForRelease(ref, "sha1")
opts.Logger.Printf("Resolved %s to %s\n", opts.TagName, releaseRefDigest.DigestWithAlg())
// Attestation fetching
attestations, logMsg, err := attestation.GetAttestations(opts, releaseArtifact.DigestWithAlg())
attestations, logMsg, err := attestation.GetAttestations(opts, releaseRefDigest.DigestWithAlg())
if err != nil {
if errors.Is(err, api.ErrNoAttestationsFound) {
opts.Logger.Printf(opts.Logger.ColorScheme.Red("✗ No attestations found for subject %s\n"), releaseArtifact.DigestWithAlg())
opts.Logger.Printf(opts.Logger.ColorScheme.Red("✗ No attestations found for subject %s\n"), releaseRefDigest.DigestWithAlg())
return err
}
opts.Logger.Println(opts.Logger.ColorScheme.Red(logMsg))
return err
}
// Filter attestations by predicate PURL
filteredAttestations := attestation.FilterAttestationsByPURL(attestations, opts.Repo, opts.TagName, opts.Logger)
filteredAttestations = attestation.FilterAttestationsByFileDigest(filteredAttestations, opts.Repo, opts.TagName, fileDigest.Digest(), opts.Logger)
// Filter attestations by tag
filteredAttestations, err := attestation.FilterAttestationsByTag(attestations, opts.TagName)
if err != nil {
opts.Logger.Println(opts.Logger.ColorScheme.Red(err.Error()))
return err
}
filteredAttestations, err = attestation.FilterAttestationsByFileDigest(filteredAttestations, opts.Repo, opts.TagName, fileDigest.Digest())
if err != nil {
opts.Logger.Println(opts.Logger.ColorScheme.Red(err.Error()))
return err
}
if len(filteredAttestations) == 0 {
opts.Logger.Printf(opts.Logger.ColorScheme.Red("✗ No attestations found for %s\n"), fileName)
return nil
}
opts.Logger.Printf("Loaded %s from GitHub API\n", text.Pluralize(len(filteredAttestations), "attestation"))
// Verify attestations
verified, errMsg, err := attestation.VerifyAttestations(*releaseArtifact, filteredAttestations, opts.SigstoreVerifier, opts.EC)
verified, errMsg, err := attestation.VerifyAttestations(*releaseRefDigest, filteredAttestations, opts.SigstoreVerifier, opts.EC)
if err != nil {
opts.Logger.Println(opts.Logger.ColorScheme.Red(errMsg))
opts.Logger.Println(opts.Logger.ColorScheme.Red("✗ Verification failed"))
// Release v1.0.0 does not contain bin-linux.tgz (sha256:0c2524c2b002fda89f8b766c7d3dd8e6ac1de183556728a83182c6137f19643d)
opts.Logger.Printf(opts.Logger.ColorScheme.Red("Release %s does not contain %s (%s)\n"), opts.TagName, opts.FilePath, fileDigest.DigestWithAlg())
return err
}
opts.Logger.Printf("The following %s matched the policy criteria\n\n", text.Pluralize(len(verified), "attestation"))
opts.Logger.Println(opts.Logger.ColorScheme.Green("✓ Verification succeeded!\n"))
opts.Logger.Printf("Attestation found matching release %s (%s)\n", opts.TagName, releaseArtifact.DigestWithAlg())
// bin-linux.tgz is present in release v1.0.0
opts.Logger.Printf("Attestation found matching release %s (%s)\n", opts.TagName, releaseRefDigest.DigestWithAlg())
opts.Logger.Printf("%s is present in release %s\n", fileName, opts.TagName)
return nil

View file

@ -28,10 +28,12 @@ func NewCmdVerify(f *cmdutil.Factory, runF func(*attestation.AttestOptions) erro
Short: "Verify the attestation for a GitHub Release.",
Args: cobra.ExactArgs(1),
PreRunE: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
opts.TagName = args[0]
if len(args) < 1 {
return cmdutil.FlagErrorf("You must specify a tag")
}
opts.TagName = args[0]
httpClient, err := f.HttpClient()
if err != nil {
return err
@ -41,29 +43,26 @@ func NewCmdVerify(f *cmdutil.Factory, runF func(*attestation.AttestOptions) erro
if err != nil {
return err
}
logger := att_io.NewHandler(f.IOStreams)
hostname, _ := ghauth.DefaultHost()
opts.Repo = baseRepo.RepoOwner() + "/" + baseRepo.RepoName()
opts.APIClient = api.NewLiveClient(httpClient, hostname, logger)
opts.Limit = 10
opts.Owner = baseRepo.RepoOwner()
opts.PredicateType = "https://in-toto.io/attestation/release/v0.1"
opts.Logger = logger
opts.HttpClient = httpClient
opts.BaseRepo = baseRepo
opts.HttpClient = httpClient
*opts = attestation.AttestOptions{
TagName: opts.TagName,
Repo: baseRepo.RepoOwner() + "/" + baseRepo.RepoName(),
APIClient: api.NewLiveClient(httpClient, hostname, logger),
Limit: 10,
Owner: baseRepo.RepoOwner(),
PredicateType: "https://in-toto.io/attestation/release/v0.1",
Logger: logger,
HttpClient: httpClient,
BaseRepo: baseRepo,
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
if runF != nil {
return runF(opts)
}
//
td, err := opts.APIClient.GetTrustDomain()
if err != nil {
@ -78,11 +77,11 @@ func NewCmdVerify(f *cmdutil.Factory, runF func(*attestation.AttestOptions) erro
}
config := verification.SigstoreConfig{
TrustedRoot: "",
Logger: opts.Logger,
NoPublicGood: true,
TrustDomain: td,
}
sigstoreVerifier, err := verification.NewLiveSigstoreVerifier(config)
if err != nil {
opts.Logger.Println(opts.Logger.ColorScheme.Red("✗ Failed to create Sigstore verifier"))
@ -92,7 +91,6 @@ func NewCmdVerify(f *cmdutil.Factory, runF func(*attestation.AttestOptions) erro
opts.SigstoreVerifier = sigstoreVerifier
opts.EC = ec
// output ec
return verifyRun(opts)
},
}
@ -105,38 +103,39 @@ func NewCmdVerify(f *cmdutil.Factory, runF func(*attestation.AttestOptions) erro
func verifyRun(opts *attestation.AttestOptions) error {
ctx := context.Background()
sha, err := shared.FetchRefSHA(ctx, opts.HttpClient, opts.BaseRepo, opts.TagName)
ref, err := shared.FetchRefSHA(ctx, opts.HttpClient, opts.BaseRepo, opts.TagName)
if err != nil {
return err
}
artifact := artifact.NewDigestedArtifactForRelease(opts.TagName, sha, "sha1")
opts.Logger.Printf("Resolved %s to %s\n", opts.TagName, artifact.DigestWithAlg())
releaseRefDigest := artifact.NewDigestedArtifactForRelease(ref, "sha1")
opts.Logger.Printf("Resolved %s to %s\n", opts.TagName, releaseRefDigest.DigestWithAlg())
// Attestation fetching
attestations, logMsg, err := attestation.GetAttestations(opts, artifact.DigestWithAlg())
attestations, logMsg, err := attestation.GetAttestations(opts, releaseRefDigest.DigestWithAlg())
if err != nil {
if errors.Is(err, api.ErrNoAttestationsFound) {
opts.Logger.Printf(opts.Logger.ColorScheme.Red("✗ No attestations found for subject %s\n"), artifact.DigestWithAlg())
opts.Logger.Printf(opts.Logger.ColorScheme.Red("✗ No attestations found for subject %s\n"), releaseRefDigest.DigestWithAlg())
return err
}
opts.Logger.Println(opts.Logger.ColorScheme.Red(logMsg))
return err
}
// Filter attestations by predicate PURL
filteredAttestations := attestation.FilterAttestationsByPURL(attestations, opts.Repo, opts.TagName, opts.Logger)
// Filter attestations by predicate tag
filteredAttestations, err := attestation.FilterAttestationsByTag(attestations, opts.TagName)
if err != nil {
opts.Logger.Println(opts.Logger.ColorScheme.Red(err.Error()))
return err
}
opts.Logger.Printf("Loaded %s from GitHub API\n", text.Pluralize(len(filteredAttestations), "attestation"))
// Verify attestations
verified, errMsg, err := attestation.VerifyAttestations(*artifact, filteredAttestations, opts.SigstoreVerifier, opts.EC)
verified, errMsg, err := attestation.VerifyAttestations(*releaseRefDigest, filteredAttestations, opts.SigstoreVerifier, opts.EC)
if err != nil {
opts.Logger.Println(opts.Logger.ColorScheme.Red(errMsg))
opts.Logger.Println(opts.Logger.ColorScheme.Red("✗ Verification failed"))
opts.Logger.Printf(opts.Logger.ColorScheme.Red("✗ Failed to find an attestation for release %s in %s\n"), opts.TagName, opts.Repo)
return err
}
@ -144,7 +143,7 @@ func verifyRun(opts *attestation.AttestOptions) error {
opts.Logger.Printf("The following %s matched the policy criteria\n\n", text.Pluralize(len(verified), "attestation"))
opts.Logger.Println(opts.Logger.ColorScheme.Green("✓ Verification succeeded!\n"))
opts.Logger.Printf("Attestation found matching release %s (%s)\n", opts.TagName, artifact.Digest())
opts.Logger.Printf("Attestation found matching release %s (%s)\n", opts.TagName, releaseRefDigest.Digest())
printVerifiedSubjects(verified, opts.Logger)
return nil
@ -164,14 +163,10 @@ func printVerifiedSubjects(verified []*verification.AttestationProcessingResult,
digest := s.Digest
if name != "" {
// digest is map[string]string and i want to be key:value
// so i need to iterate over the map and print key:value
digestStr := ""
for key, value := range digest {
digestStr += key + ":" + value
}
// output should like this
// bin-linux.tgz sha256:0c2524c2b002fda89f8b766c7d3dd8e6ac1de183556728a83182c6137f19643d
logger.Println(" " + name + " " + digestStr)
}
}