clean up the code
This commit is contained in:
parent
3e5456827c
commit
0a6ce2bb74
5 changed files with 93 additions and 118 deletions
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue