diff --git a/cmd/notation/verify.go b/cmd/notation/verify.go index 3caf7c6f3..3fea9e721 100644 --- a/cmd/notation/verify.go +++ b/cmd/notation/verify.go @@ -2,12 +2,11 @@ package main import ( "errors" - "fmt" "os" - "strings" "github.com/notaryproject/notation-go/registry" "github.com/notaryproject/notation-go/verification" + "github.com/notaryproject/notation/internal/cmd" "github.com/notaryproject/notation/internal/ioutil" orasregistry "oras.land/oras-go/v2/registry" @@ -18,7 +17,7 @@ import ( type verifyOpts struct { SecureFlagOpts reference string - config []string + config string } func verifyCommand(opts *verifyOpts) *cobra.Command { @@ -26,10 +25,10 @@ func verifyCommand(opts *verifyOpts) *cobra.Command { opts = &verifyOpts{} } command := &cobra.Command{ - Use: "verify ", + Use: "verify [flags] ", Short: "Verifies OCI Artifacts", Long: `Verifies OCI Artifacts: - notation verify [--config =] [--username ] [--password ] `, + notation verify [--config =,...] [--username ] [--password ] `, Args: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { return errors.New("missing reference") @@ -42,41 +41,38 @@ func verifyCommand(opts *verifyOpts) *cobra.Command { }, } opts.ApplyFlags(command.Flags()) - command.Flags().StringSliceVar(&opts.config, "config", nil, "verification plugin config (accepts multiple inputs)") + command.Flags().StringVarP(&opts.config, "config", "c", "", "list of comma-separated {key}={value} pairs that are passed as is to the plugin") return command } func runVerify(command *cobra.Command, opts *verifyOpts) error { - // initialize. - verifier, err := getVerifier(opts) + // resolve the given reference and set the digest. + ref, err := resolveReference(command, opts) + if err != nil { + return err + } + + // initialize verifier. + verifier, err := getVerifier(opts, ref) if err != nil { return err } // set up verification plugin config. - configs := make(map[string]string) - for _, c := range opts.config { - parts := strings.Split(c, "=") - if len(parts) != 2 { - return fmt.Errorf("invalid config option: %s", c) - } - configs[parts[0]] = parts[1] + configs, err := cmd.ParseFlagPluginConfig(opts.config) + if err != nil { + return err } - ctx := verification.WithPluginConfig(command.Context(), configs) // core verify process. - outcomes, err := verifier.Verify(ctx, opts.reference) + ctx := verification.WithPluginConfig(command.Context(), configs) + outcomes, err := verifier.Verify(ctx, ref.String()) // write out. - return ioutil.PrintVerificationResults(os.Stdout, outcomes, err) + return ioutil.PrintVerificationResults(os.Stdout, outcomes, err, ref.Reference) } -func getVerifier(opts *verifyOpts) (*verification.Verifier, error) { - ref, err := orasregistry.ParseReference(opts.reference) - if err != nil { - return nil, err - } - +func getVerifier(opts *verifyOpts, ref orasregistry.Reference) (*verification.Verifier, error) { authClient, plainHTTP, err := getAuthClient(&opts.SecureFlagOpts, ref) if err != nil { return nil, err @@ -86,3 +82,18 @@ func getVerifier(opts *verifyOpts) (*verification.Verifier, error) { return verification.NewVerifier(repo) } + +func resolveReference(command *cobra.Command, opts *verifyOpts) (orasregistry.Reference, error) { + ref, err := orasregistry.ParseReference(opts.reference) + if err != nil { + return orasregistry.Reference{}, err + } + + manifestDesc, err := getManifestDescriptorFromReference(command.Context(), &opts.SecureFlagOpts, opts.reference) + if err != nil { + return orasregistry.Reference{}, err + } + + ref.Reference = manifestDesc.Digest.String() + return ref, nil +} diff --git a/cmd/notation/verify_test.go b/cmd/notation/verify_test.go index 349f638eb..ff3de9c7a 100644 --- a/cmd/notation/verify_test.go +++ b/cmd/notation/verify_test.go @@ -37,13 +37,12 @@ func TestVerifyCommand_MoreArgs(t *testing.T) { SecureFlagOpts: SecureFlagOpts{ PlainHTTP: true, }, - config: []string{"key1=val1", "key2=val2"}, + config: "key1=val1,key2=val2", } if err := command.ParseFlags([]string{ expected.reference, "--plain-http", - "--config", expected.config[0], - "--config", expected.config[1]}); err != nil { + "--config", expected.config}); err != nil { t.Fatalf("Parse Flag failed: %v", err) } if err := command.Args(command, command.Flags().Args()); err != nil { diff --git a/internal/ioutil/print.go b/internal/ioutil/print.go index 90993858a..7156f9ed3 100644 --- a/internal/ioutil/print.go +++ b/internal/ioutil/print.go @@ -54,28 +54,29 @@ func PrintCertificateMap(w io.Writer, v []config.CertificateReference) error { return tw.Flush() } -func PrintVerificationResults(w io.Writer, v []*verification.SignatureVerificationOutcome, resultErr error) error { +func PrintVerificationResults(w io.Writer, v []*verification.SignatureVerificationOutcome, resultErr error, digest string) error { tw := newTabWriter(w) - overallResult := "success" - if resultErr != nil { - overallResult = "failure" + if resultErr == nil { + fmt.Fprintf(tw, "%s\n", digest) + return nil } - fmt.Fprintf(tw, "OVERALL RESULT: %s\n", overallResult) - if resultErr != nil { - fmt.Fprintf(tw, "ERROR: %s\n", resultErr.Error()) - printOutcomes(tw, v) - } + fmt.Fprintf(tw, "ERROR: %s\n\n", resultErr.Error()) + printOutcomes(tw, v) return tw.Flush() } func printOutcomes(tw *tabwriter.Writer, outcomes []*verification.SignatureVerificationOutcome) { - if len(outcomes) > 0 { - fmt.Fprintln(tw, "DETAILED ERRORS:") - for _, outcome := range outcomes { - fmt.Println(outcome.Error) - } + if len(outcomes) == 1 { + fmt.Println("1 signature failed verification, error is listed as below:") + } else { + fmt.Printf("%d signatures failed verification, errors are listed as below:\n", len(outcomes)) } -} \ No newline at end of file + + for _, outcome := range outcomes { + // TODO: print out the signature digest once the outcome contains it. + fmt.Printf("%s\n\n", outcome.Error.Error()) + } +}