Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert "feat: add support for json output for notation verify (#527)" #551

Merged
merged 1 commit into from
Feb 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 13 additions & 39 deletions cmd/notation/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,6 @@ type verifyOpts struct {
reference string
pluginConfig []string
userMetadata []string
outputFormat string
}

type verifyOutput struct {
Reference string `json:"reference"`
UserMetadata map[string]string `json:"userMetadata,omitempty"`
Result string `json:"result"`
}

func verifyCommand(opts *verifyOpts) *cobra.Command {
Expand All @@ -59,19 +52,14 @@ Example - Verify a signature on an OCI artifact identified by a tag (Notation w
opts.reference = args[0]
return nil
},
RunE: func(cmnd *cobra.Command, args []string) error {
if opts.outputFormat != cmd.OutputJson && opts.outputFormat != cmd.OutputPlaintext {
return fmt.Errorf("unrecognized output format: %v", opts.outputFormat)
}

return runVerify(cmnd, opts)
RunE: func(cmd *cobra.Command, args []string) error {
return runVerify(cmd, opts)
},
}
opts.LoggingFlagOpts.ApplyFlags(command.Flags())
opts.SecureFlagOpts.ApplyFlags(command.Flags())
command.Flags().StringArrayVar(&opts.pluginConfig, "plugin-config", nil, "{key}={value} pairs that are passed as it is to a plugin, if the verification is associated with a verification plugin, refer plugin documentation to set appropriate values")
cmd.SetPflagUserMetadata(command.Flags(), &opts.userMetadata, cmd.PflagUserMetadataVerifyUsage)
cmd.SetPflagOutput(command.Flags(), &opts.outputFormat, cmd.PflagOutputUsage)
return command
}

Expand Down Expand Up @@ -144,8 +132,13 @@ func runVerify(command *cobra.Command, opts *verifyOpts) error {
fmt.Fprintf(os.Stderr, "Warning: %v was set to %q and failed with error: %v\n", result.Type, result.Action, result.Error)
}
}

return printResult(opts.outputFormat, ref.String(), outcome)
if reflect.DeepEqual(outcome.VerificationLevel, trustpolicy.LevelSkip) {
fmt.Println("Trust policy is configured to skip signature verification for", ref.String())
} else {
fmt.Println("Successfully verified signature for", ref.String())
printMetadataIfPresent(outcome)
}
return nil
}

func resolveReference(ctx context.Context, opts *SecureFlagOpts, reference string, sigRepo notationregistry.Repository, fn func(registry.Reference, ocispec.Descriptor)) (registry.Reference, error) {
Expand All @@ -167,33 +160,14 @@ func resolveReference(ctx context.Context, opts *SecureFlagOpts, reference strin
return ref, nil
}

func printResult(outputFormat, reference string, outcome *notation.VerificationOutcome) error {
if reflect.DeepEqual(outcome.VerificationLevel, trustpolicy.LevelSkip) {
switch outputFormat {
case cmd.OutputJson:
output := verifyOutput{Reference: reference, Result: "SkippedByTrustPolicy", UserMetadata: map[string]string{}}
return ioutil.PrintObjectAsJSON(output)
default:
fmt.Println("Trust policy is configured to skip signature verification for", reference)
return nil
}
}

func printMetadataIfPresent(outcome *notation.VerificationOutcome) {
// the signature envelope is parsed as part of verification.
// since user metadata is only printed on successful verification,
// this error can be ignored
metadata, _ := outcome.UserMetadata()

switch outputFormat {
case cmd.OutputJson:
output := verifyOutput{Reference: reference, Result: "Success", UserMetadata: metadata}
return ioutil.PrintObjectAsJSON(output)
default:
fmt.Println("Successfully verified signature for", reference)
if len(metadata) > 0 {
fmt.Println("\nThe artifact was signed with the following user metadata.")
ioutil.PrintMetadataMap(os.Stdout, metadata)
}
return nil
if len(metadata) > 0 {
fmt.Println("\nThe artifact was signed with the following user metadata.")
ioutil.PrintMetadataMap(os.Stdout, metadata)
}
}
7 changes: 1 addition & 6 deletions cmd/notation/verify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package main
import (
"reflect"
"testing"

"github.com/notaryproject/notation/internal/cmd"
)

func TestVerifyCommand_BasicArgs(t *testing.T) {
Expand All @@ -17,7 +15,6 @@ func TestVerifyCommand_BasicArgs(t *testing.T) {
Password: "password",
},
pluginConfig: []string{"key1=val1"},
outputFormat: cmd.OutputPlaintext,
}
if err := command.ParseFlags([]string{
expected.reference,
Expand All @@ -43,14 +40,12 @@ func TestVerifyCommand_MoreArgs(t *testing.T) {
PlainHTTP: true,
},
pluginConfig: []string{"key1=val1", "key2=val2"},
outputFormat: cmd.OutputJson,
}
if err := command.ParseFlags([]string{
expected.reference,
"--plain-http",
"--plugin-config", "key1=val1",
"--plugin-config", "key2=val2",
"--output", "json"}); err != nil {
"--plugin-config", "key2=val2"}); err != nil {
t.Fatalf("Parse Flag failed: %v", err)
}
if err := command.Args(command, command.Flags().Args()); err != nil {
Expand Down
15 changes: 1 addition & 14 deletions internal/cmd/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,6 @@ import (
"github.com/spf13/pflag"
)

const (
OutputPlaintext = "text"
OutputJson = "json"
)

var (
PflagKey = &pflag.Flag{
Name: "key",
Expand Down Expand Up @@ -80,20 +75,12 @@ var (
Name: "user-metadata",
Shorthand: "m",
}

PflagUserMetadataSignUsage = "{key}={value} pairs that are added to the signature payload"
PflagUserMetadataVerifyUsage = "user defined {key}={value} pairs that must be present in the signature for successful verification if provided"
SetPflagUserMetadata = func(fs *pflag.FlagSet, p *[]string, usage string) {
fs.StringArrayVarP(p, PflagUserMetadata.Name, PflagUserMetadata.Shorthand, nil, usage)
}

PflagOutput = &pflag.Flag{
Name: "output",
Shorthand: "o",
}
PflagOutputUsage = fmt.Sprintf("output format, options: '%s', '%s'", OutputJson, OutputPlaintext)
SetPflagOutput = func(fs *pflag.FlagSet, p *string, usage string) {
fs.StringVarP(p, PflagOutput.Name, PflagOutput.Shorthand, OutputPlaintext, usage)
}
)

// KeyValueSlice is a flag with type int
Expand Down
16 changes: 1 addition & 15 deletions internal/ioutil/print.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package ioutil

import (
"encoding/json"
"fmt"
"io"
"text/tabwriter"
Expand Down Expand Up @@ -34,7 +33,6 @@ func PrintKeyMap(w io.Writer, target *string, v []config.KeySuite) error {
return tw.Flush()
}

// PrintMetadataMap prints a map to a given Writer as a table
func PrintMetadataMap(w io.Writer, metadata map[string]string) error {
tw := newTabWriter(w)
fmt.Fprintln(tw, "\nKEY\tVALUE\t")
Expand All @@ -44,16 +42,4 @@ func PrintMetadataMap(w io.Writer, metadata map[string]string) error {
}

return tw.Flush()
}

// PrintObjectAsJSON takes an interface and prints it as an indented JSON string
func PrintObjectAsJSON(i interface{}) error {
jsonBytes, err := json.MarshalIndent(i, "", " ")
if err != nil {
return err
}

fmt.Println(string(jsonBytes))

return nil
}
}
25 changes: 1 addition & 24 deletions specs/commandline/verify.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,11 @@ Usage:
Flags:
-d, --debug debug mode
-h, --help help for verify
-o, --output string output format, options: 'json', 'text' (default "text")
-p, --password string password for registry operations (default to $NOTATION_PASSWORD if not specified)
--plain-http registry access via plain HTTP
--plugin-config stringArray {key}={value} pairs that are passed as it is to a plugin, if the verification is associated with a verification plugin, refer plugin documentation to set appropriate values
-m, --user-metadata stringArray user defined {key}={value} pairs that must be present in the signature for successful verification if provided
-u, --username string username for registry operations (default to $NOTATION_USERNAME if not specified)
-m, --user-metadata stringArray user defined {key}={value} pairs that must be present in the signature for successful verification if provided
-v, --verbose verbose mode
```

Expand Down Expand Up @@ -169,25 +168,3 @@ An example of output messages for a successful verification:
Warning: Always verify the artifact using digest(@sha256:...) rather than a tag(:v1) because resolved digest may not point to the same signed artifact, as tags are mutable.
Successfully verified signature for localhost:5000/net-monitor@sha256:b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
```

### Verify signatures on an OCI artifact with json output

Use the `--output` flag to format successful verification output in json.

```shell
notation verify --output json localhost:5000/net-monitor@sha256:b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
```

An example of output messages for a successful verification:

```text
{
"reference": "localhost:5000/net-monitor@sha256:b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9",
"userMetadata": {
"io.wabbit-networks.buildId": "123"
},
"result": "Success"
}
```

On unsuccessful verification, nothing is written to `stdout`, and the failure is logged to `stderr`.
41 changes: 0 additions & 41 deletions test/e2e/suite/command/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,45 +49,4 @@ var _ = Describe("notation verify", func() {
MatchKeyWords(VerifySuccessfully)
})
})

It("with added user metadata", func() {
Host(BaseOptions(), func(notation *utils.ExecOpts, artifact *Artifact, vhost *utils.VirtualHost) {
notation.Exec("sign", artifact.ReferenceWithDigest(), "--user-metadata", "io.wabbit-networks.buildId=123").
MatchKeyWords(SignSuccessfully)

notation.Exec("verify", artifact.ReferenceWithTag()).
MatchKeyWords(
VerifySuccessfully,
"KEY",
"VALUE",
"io.wabbit-networks.buildId",
"123",
)

notation.Exec("verify", artifact.ReferenceWithDigest(), "--user-metadata", "io.wabbit-networks.buildId=123").
MatchKeyWords(
VerifySuccessfully,
"KEY",
"VALUE",
"io.wabbit-networks.buildId",
"123",
)

notation.ExpectFailure().Exec("verify", artifact.ReferenceWithDigest(), "--user-metadata", "io.wabbit-networks.buildId=321").
MatchErrKeyWords("unable to find specified metadata in the signature")
})
})

It("with json output", func() {
Host(BaseOptions(), func(notation *utils.ExecOpts, artifact *Artifact, vhost *utils.VirtualHost) {
notation.Exec("sign", artifact.ReferenceWithDigest(), "--user-metadata", "io.wabbit-networks.buildId=123").
MatchKeyWords(SignSuccessfully)

notation.Exec("verify", artifact.ReferenceWithDigest(), "--output", "json").
MatchContent(fmt.Sprintf("{\n \"reference\": \"%s\",\n \"userMetadata\": {\n \"io.wabbit-networks.buildId\": \"123\"\n },\n \"result\": \"Success\"\n}\n", artifact.ReferenceWithDigest()))

notation.ExpectFailure().Exec("verify", artifact.ReferenceWithDigest(), "--user-metadata", "io.wabbit-networks.buildId=321").
MatchErrKeyWords("unable to find specified metadata in the signature")
})
})
})