diff --git a/cmd/notation/blob/cmd.go b/cmd/notation/blob/cmd.go index caef64ea0..b65c053d3 100644 --- a/cmd/notation/blob/cmd.go +++ b/cmd/notation/blob/cmd.go @@ -25,11 +25,10 @@ func Cmd() *cobra.Command { Short: "Commands for blob", Long: "Sign, verify, inspect signatures of blob. Configure blob trust policy.", } - command.AddCommand( signCommand(nil), + verifyCommand(nil), policy.Cmd(), ) - return command } diff --git a/cmd/notation/blob/sign.go b/cmd/notation/blob/sign.go index 635c51c63..452fcbb1c 100644 --- a/cmd/notation/blob/sign.go +++ b/cmd/notation/blob/sign.go @@ -26,6 +26,7 @@ import ( "github.com/notaryproject/notation-go" "github.com/notaryproject/notation-go/log" "github.com/notaryproject/notation/cmd/notation/internal/cmdutil" + "github.com/notaryproject/notation/cmd/notation/internal/signer" "github.com/notaryproject/notation/internal/cmd" "github.com/notaryproject/notation/internal/envelope" "github.com/notaryproject/notation/internal/httputil" @@ -138,7 +139,7 @@ func runBlobSign(command *cobra.Command, cmdOpts *blobSignOpts) error { ctx := cmdOpts.LoggingFlagOpts.InitializeLogger(command.Context()) logger := log.GetLogger(ctx) - blobSigner, err := cmd.GetSigner(ctx, &cmdOpts.SignerFlagOpts) + blobSigner, err := signer.GetSigner(ctx, &cmdOpts.SignerFlagOpts) if err != nil { return err } diff --git a/cmd/notation/blob/verify.go b/cmd/notation/blob/verify.go new file mode 100644 index 000000000..131c05858 --- /dev/null +++ b/cmd/notation/blob/verify.go @@ -0,0 +1,170 @@ +// Copyright The Notary Project Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package blob + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/notaryproject/notation-go" + "github.com/notaryproject/notation/cmd/notation/internal/display" + "github.com/notaryproject/notation/cmd/notation/internal/option" + "github.com/notaryproject/notation/cmd/notation/internal/verifier" + "github.com/notaryproject/notation/internal/cmd" + "github.com/notaryproject/notation/internal/envelope" + "github.com/notaryproject/notation/internal/ioutil" + "github.com/spf13/cobra" +) + +type blobVerifyOpts struct { + cmd.LoggingFlagOpts + option.Common + blobPath string + signaturePath string + pluginConfig []string + userMetadata []string + policyStatementName string + blobMediaType string +} + +func verifyCommand(opts *blobVerifyOpts) *cobra.Command { + if opts == nil { + opts = &blobVerifyOpts{} + } + longMessage := `Verify a signature associated with a blob. + +Prerequisite: added a certificate into trust store and created a trust policy. + +Example - Verify a signature on a blob artifact: + notation blob verify --signature + +Example - Verify the signature on a blob artifact with user metadata: + notation blob verify --user-metadata --signature + +Example - Verify the signature on a blob artifact with media type: + notation blob verify --media-type --signature + +Example - Verify the signature on a blob artifact using a policy statement name: + notation blob verify --policy-name --signature +` + command := &cobra.Command{ + Use: "verify [flags] --signature ", + Short: "Verify a signature associated with a blob", + Long: longMessage, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errors.New("missing path to the blob artifact: use `notation blob verify --help` to see what parameters are required") + } + opts.blobPath = args[0] + return nil + }, + PreRunE: func(cmd *cobra.Command, args []string) error { + if opts.signaturePath == "" { + return errors.New("filepath of the signature cannot be empty") + } + if cmd.Flags().Changed("media-type") && opts.blobMediaType == "" { + return errors.New("--media-type is set but with empty value") + } + opts.Common.Parse(cmd) + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + return runVerify(cmd, opts) + }, + } + opts.LoggingFlagOpts.ApplyFlags(command.Flags()) + command.Flags().StringVar(&opts.signaturePath, "signature", "", "filepath of the signature to be verified") + 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") + command.Flags().StringVar(&opts.blobMediaType, "media-type", "", "media type of the blob to verify") + command.Flags().StringVar(&opts.policyStatementName, "policy-name", "", "policy name to verify against. If not provided, the global policy is used if exists") + cmd.SetPflagUserMetadata(command.Flags(), &opts.userMetadata, cmd.PflagUserMetadataVerifyUsage) + command.MarkFlagRequired("signature") + return command +} + +func runVerify(command *cobra.Command, cmdOpts *blobVerifyOpts) error { + // set log level + ctx := cmdOpts.LoggingFlagOpts.InitializeLogger(command.Context()) + + // initialize + displayHandler := display.NewBlobVerifyHandler(cmdOpts.Printer) + blobFile, err := os.Open(cmdOpts.blobPath) + if err != nil { + return err + } + defer blobFile.Close() + + signatureBytes, err := os.ReadFile(cmdOpts.signaturePath) + if err != nil { + return err + } + blobVerifier, err := verifier.GetBlobVerifier(ctx) + if err != nil { + return err + } + + // set up verification plugin config + pluginConfigs, err := cmd.ParseFlagMap(cmdOpts.pluginConfig, cmd.PflagPluginConfig.Name) + if err != nil { + return err + } + + // set up user metadata + userMetadata, err := cmd.ParseFlagMap(cmdOpts.userMetadata, cmd.PflagUserMetadata.Name) + if err != nil { + return err + } + signatureMediaType, err := parseSignatureMediaType(cmdOpts.signaturePath) + if err != nil { + return err + } + verifyBlobOpts := notation.VerifyBlobOptions{ + BlobVerifierVerifyOptions: notation.BlobVerifierVerifyOptions{ + SignatureMediaType: signatureMediaType, + PluginConfig: pluginConfigs, + UserMetadata: userMetadata, + TrustPolicyName: cmdOpts.policyStatementName, + }, + ContentMediaType: cmdOpts.blobMediaType, + } + _, outcome, err := notation.VerifyBlob(ctx, blobVerifier, blobFile, signatureBytes, verifyBlobOpts) + outcomes := []*notation.VerificationOutcome{outcome} + err = ioutil.ComposeBlobVerificationFailurePrintout(outcomes, cmdOpts.blobPath, err) + if err != nil { + return err + } + displayHandler.OnVerifySucceeded(outcomes, cmdOpts.blobPath) + return displayHandler.Render() +} + +// parseSignatureMediaType returns the media type of the signature file. +// `application/jose+json` and `application/cose` are supported. +func parseSignatureMediaType(signaturePath string) (string, error) { + signatureFileName := filepath.Base(signaturePath) + if strings.ToLower(filepath.Ext(signatureFileName)) != ".sig" { + return "", fmt.Errorf("invalid signature filename %s. The file extension must be .sig", signatureFileName) + } + sigFilenameArr := strings.Split(signatureFileName, ".") + + // a valid signature file name has at least 3 parts. + // for example, `myFile.jws.sig` + if len(sigFilenameArr) < 3 { + return "", fmt.Errorf("invalid signature filename %s. A valid signature file name must contain signature format and .sig file extension", signatureFileName) + } + sigFormat := sigFilenameArr[len(sigFilenameArr)-2] + return envelope.GetEnvelopeMediaType(strings.ToLower(sigFormat)) +} diff --git a/cmd/notation/blob/verify_test.go b/cmd/notation/blob/verify_test.go new file mode 100644 index 000000000..b3074ccfd --- /dev/null +++ b/cmd/notation/blob/verify_test.go @@ -0,0 +1,73 @@ +// Copyright The Notary Project Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package blob + +import ( + "reflect" + "testing" +) + +func TestVerifyCommand_BasicArgs(t *testing.T) { + opts := &blobVerifyOpts{} + command := verifyCommand(opts) + expected := &blobVerifyOpts{ + blobPath: "blob_path", + signaturePath: "sig_path", + } + if err := command.ParseFlags([]string{ + expected.blobPath, + "--signature", expected.signaturePath}); err != nil { + t.Fatalf("Parse Flag failed: %v", err) + } + if err := command.Args(command, command.Flags().Args()); err != nil { + t.Fatalf("Parse args failed: %v", err) + } + if !reflect.DeepEqual(*expected, *opts) { + t.Fatalf("Expect blob verify opts: %v, got: %v", expected, opts) + } +} + +func TestVerifyCommand_MoreArgs(t *testing.T) { + opts := &blobVerifyOpts{} + command := verifyCommand(opts) + expected := &blobVerifyOpts{ + blobPath: "blob_path", + signaturePath: "sig_path", + pluginConfig: []string{"key1=val1", "key2=val2"}, + } + if err := command.ParseFlags([]string{ + expected.blobPath, + "--signature", expected.signaturePath, + "--plugin-config", "key1=val1", + "--plugin-config", "key2=val2", + }); err != nil { + t.Fatalf("Parse Flag failed: %v", err) + } + if err := command.Args(command, command.Flags().Args()); err != nil { + t.Fatalf("Parse args failed: %v", err) + } + if !reflect.DeepEqual(*expected, *opts) { + t.Fatalf("Expect verify opts: %v, got: %v", expected, opts) + } +} + +func TestVerifyCommand_MissingArgs(t *testing.T) { + cmd := verifyCommand(nil) + if err := cmd.ParseFlags(nil); err != nil { + t.Fatalf("Parse Flag failed: %v", err) + } + if err := cmd.Args(cmd, cmd.Flags().Args()); err == nil { + t.Fatal("Parse Args expected error, but ok") + } +} diff --git a/cmd/notation/internal/display/handler.go b/cmd/notation/internal/display/handler.go index da43ca4e4..02cec17f1 100644 --- a/cmd/notation/internal/display/handler.go +++ b/cmd/notation/internal/display/handler.go @@ -43,7 +43,13 @@ func NewInpsectHandler(printer *output.Printer, format option.Format) (metadata. } // NewVerifyHandler creates a new metadata VerifyHandler for printing -// veriifcation result and warnings. +// verification result and warnings. func NewVerifyHandler(printer *output.Printer) metadata.VerifyHandler { return text.NewVerifyHandler(printer) } + +// NewBlobVerifyHandler creates a new metadata BlobVerifyHandler for printing +// blob verification result and warnings. +func NewBlobVerifyHandler(printer *output.Printer) metadata.BlobVerifyHandler { + return text.NewBlobVerifyHandler(printer) +} diff --git a/cmd/notation/internal/display/metadata/interface.go b/cmd/notation/internal/display/metadata/interface.go index 2eb18bd7c..fb1e342aa 100644 --- a/cmd/notation/internal/display/metadata/interface.go +++ b/cmd/notation/internal/display/metadata/interface.go @@ -54,3 +54,16 @@ type VerifyHandler interface { // outcomes must not be nil or empty. OnVerifySucceeded(outcomes []*notation.VerificationOutcome, digestReference string) } + +// BlobVerifyHandler is a handler for rendering metadata information of +// blob verification outcome. +// +// It only supports text format for now. +type BlobVerifyHandler interface { + Renderer + + // OnVerifySucceeded sets the successful verification result for the handler. + // + // outcomes must not be nil or empty. + OnVerifySucceeded(outcomes []*notation.VerificationOutcome, blobPath string) +} diff --git a/cmd/notation/internal/display/metadata/text/blobverify.go b/cmd/notation/internal/display/metadata/text/blobverify.go new file mode 100644 index 000000000..6160b3865 --- /dev/null +++ b/cmd/notation/internal/display/metadata/text/blobverify.go @@ -0,0 +1,48 @@ +// Copyright The Notary Project Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package text + +import ( + "github.com/notaryproject/notation-go" + "github.com/notaryproject/notation/cmd/notation/internal/display/output" +) + +// BlobVerifyHandler is a handler for rendering output for blob verify command +// in human-readable format. +// It implements metadata/BlobVerifyHandler. +type BlobVerifyHandler struct { + printer *output.Printer + outcome *notation.VerificationOutcome + blobPath string +} + +// NewBlobVerifyHandler creates a new BlobVerifyHandler. +func NewBlobVerifyHandler(printer *output.Printer) *BlobVerifyHandler { + return &BlobVerifyHandler{ + printer: printer, + } +} + +// OnVerifySucceeded sets the successful verification result for the handler. +// +// outcomes must not be nil or empty. +func (h *BlobVerifyHandler) OnVerifySucceeded(outcomes []*notation.VerificationOutcome, blobPath string) { + h.outcome = outcomes[0] + h.blobPath = blobPath +} + +// Render prints out the verification results in human-readable format. +func (h *BlobVerifyHandler) Render() error { + return printVerificationSuccess(h.printer, h.outcome, h.blobPath, false) +} diff --git a/cmd/notation/internal/display/metadata/text/verify.go b/cmd/notation/internal/display/metadata/text/verify.go index 838ad541f..743e17d4a 100644 --- a/cmd/notation/internal/display/metadata/text/verify.go +++ b/cmd/notation/internal/display/metadata/text/verify.go @@ -11,32 +11,23 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package text provides the text output in human-readable format for metadata -// information. package text import ( - "fmt" - "reflect" - "text/tabwriter" - "github.com/notaryproject/notation-go" - "github.com/notaryproject/notation-go/verifier/trustpolicy" "github.com/notaryproject/notation/cmd/notation/internal/display/output" ) // VerifyHandler is a handler for rendering output for verify command in // human-readable format. type VerifyHandler struct { - printer *output.Printer - + printer *output.Printer outcome *notation.VerificationOutcome digestReference string hasWarning bool } -// NewVerifyHandler creates a VerifyHandler to render verification results in -// human-readable format. +// NewVerifyHandler creates a new VerifyHandler. func NewVerifyHandler(printer *output.Printer) *VerifyHandler { return &VerifyHandler{ printer: printer, @@ -59,51 +50,5 @@ func (h *VerifyHandler) OnVerifySucceeded(outcomes []*notation.VerificationOutco // Render prints out the verification results in human-readable format. func (h *VerifyHandler) Render() error { - // write out on success - // print out warning for any failed result with logged verification action - for _, result := range h.outcome.VerificationResults { - if result.Error != nil { - // at this point, the verification action has to be logged and - // it's failed - h.printer.PrintErrorf("Warning: %v was set to %q and failed with error: %v\n", result.Type, result.Action, result.Error) - h.hasWarning = true - } - } - if h.hasWarning { - // print a newline to separate the warning from the final message - h.printer.Println() - } - if reflect.DeepEqual(h.outcome.VerificationLevel, trustpolicy.LevelSkip) { - h.printer.Println("Trust policy is configured to skip signature verification for", h.digestReference) - } else { - h.printer.Println("Successfully verified signature for", h.digestReference) - h.printMetadataIfPresent(h.outcome) - } - return nil -} - -func (h *VerifyHandler) 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() - - if len(metadata) > 0 { - h.printer.Println("\nThe artifact was signed with the following user metadata.") - h.printMetadataMap(metadata) - } -} - -// printMetadataMap prints out metadata given the metatdata map -// -// The metadata is additional information of text output. -func (h *VerifyHandler) printMetadataMap(metadata map[string]string) error { - tw := tabwriter.NewWriter(h.printer, 0, 0, 3, ' ', 0) - fmt.Fprintln(tw, "\nKEY\tVALUE\t") - - for k, v := range metadata { - fmt.Fprintf(tw, "%v\t%v\t\n", k, v) - } - - return tw.Flush() + return printVerificationSuccess(h.printer, h.outcome, h.digestReference, h.hasWarning) } diff --git a/cmd/notation/internal/display/metadata/text/verify_helper.go b/cmd/notation/internal/display/metadata/text/verify_helper.go new file mode 100644 index 000000000..2e5571648 --- /dev/null +++ b/cmd/notation/internal/display/metadata/text/verify_helper.go @@ -0,0 +1,73 @@ +// Copyright The Notary Project Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package text provides the text output in human-readable format for metadata +// information. +package text + +import ( + "fmt" + "reflect" + "text/tabwriter" + + "github.com/notaryproject/notation-go" + "github.com/notaryproject/notation-go/verifier/trustpolicy" + "github.com/notaryproject/notation/cmd/notation/internal/display/output" +) + +// printVerificationSuccess prints out messages when verification succeeds +func printVerificationSuccess(printer *output.Printer, outcome *notation.VerificationOutcome, artifact string, hasWarning bool) error { + // write out on success + // print out warning for any failed result with logged verification action + for _, result := range outcome.VerificationResults { + if result.Error != nil { + // at this point, the verification action has to be logged and + // it's failed + printer.PrintErrorf("Warning: %v was set to %q and failed with error: %v\n", result.Type, result.Action, result.Error) + hasWarning = true + } + } + if hasWarning { + // print a newline to separate the warning from the final message + printer.Println() + } + if reflect.DeepEqual(outcome.VerificationLevel, trustpolicy.LevelSkip) { + printer.Println("Trust policy is configured to skip signature verification for", artifact) + } else { + printer.Println("Successfully verified signature for", artifact) + printUserMetadataIfPresent(printer, outcome) + } + return nil +} + +// printUserMetadataIfPresent prints out user metadata if present +func printUserMetadataIfPresent(printer *output.Printer, 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() + if len(metadata) > 0 { + printer.Println("\nThe artifact was signed with the following user metadata.") + printUserMetadataMap(printer, metadata) + } +} + +// printUserMetadataMap prints out user metadata map +func printUserMetadataMap(printer *output.Printer, metadata map[string]string) error { + tw := tabwriter.NewWriter(printer, 0, 0, 3, ' ', 0) + fmt.Fprintln(tw, "\nKEY\tVALUE\t") + for k, v := range metadata { + fmt.Fprintf(tw, "%v\t%v\t\n", k, v) + } + return tw.Flush() +} diff --git a/cmd/notation/internal/display/metadata/text/verify_test.go b/cmd/notation/internal/display/metadata/text/verify_helper_test.go similarity index 97% rename from cmd/notation/internal/display/metadata/text/verify_test.go rename to cmd/notation/internal/display/metadata/text/verify_helper_test.go index 77caa475b..ce5abf7fe 100644 --- a/cmd/notation/internal/display/metadata/text/verify_test.go +++ b/cmd/notation/internal/display/metadata/text/verify_helper_test.go @@ -47,7 +47,7 @@ func TestPrintMetadataIfPresent(t *testing.T) { buf := bytes.Buffer{} printer := output.NewPrinter(&buf, &buf) h := NewVerifyHandler(printer) - h.printMetadataIfPresent(outcome) + printUserMetadataIfPresent(h.printer, outcome) got := buf.String() expected := "\nThe artifact was signed with the following user metadata.\n\nKEY VALUE \nfoo bar \n" if got != expected { diff --git a/internal/cmd/signer.go b/cmd/notation/internal/signer/signer.go similarity index 94% rename from internal/cmd/signer.go rename to cmd/notation/internal/signer/signer.go index c0c042026..942d419d7 100644 --- a/internal/cmd/signer.go +++ b/cmd/notation/internal/signer/signer.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package cmd +package signer import ( "context" @@ -21,6 +21,7 @@ import ( "github.com/notaryproject/notation-go/dir" "github.com/notaryproject/notation-go/plugin" "github.com/notaryproject/notation-go/signer" + "github.com/notaryproject/notation/internal/cmd" "github.com/notaryproject/notation/pkg/configutil" ) @@ -31,7 +32,7 @@ type Signer interface { } // GetSigner returns a Signer based on user opts. -func GetSigner(ctx context.Context, opts *SignerFlagOpts) (Signer, error) { +func GetSigner(ctx context.Context, opts *cmd.SignerFlagOpts) (Signer, error) { // Check if using on-demand key if opts.KeyID != "" && opts.PluginName != "" && opts.Key == "" { // Construct a signer from on-demand key diff --git a/internal/cmd/signer_test.go b/cmd/notation/internal/signer/signer_test.go similarity index 93% rename from internal/cmd/signer_test.go rename to cmd/notation/internal/signer/signer_test.go index f41b14e0c..57ed2b760 100644 --- a/internal/cmd/signer_test.go +++ b/cmd/notation/internal/signer/signer_test.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package cmd +package signer import ( "context" @@ -21,6 +21,7 @@ import ( "github.com/notaryproject/notation-go" "github.com/notaryproject/notation-go/dir" "github.com/notaryproject/notation-go/signer" + "github.com/notaryproject/notation/internal/cmd" ) func TestGenericSignerImpl(t *testing.T) { @@ -56,7 +57,7 @@ func TestGetSignerFromOpts(t *testing.T) { dir.UserLibexecDir = "./testdata/plugins" ctx := context.Background() - opts := &SignerFlagOpts{ + opts := &cmd.SignerFlagOpts{ KeyID: "testKeyId", PluginName: "testPlugin", } @@ -85,7 +86,7 @@ func TestGetSignerFromConfig(t *testing.T) { dir.UserLibexecDir = "./testdata/plugins" dir.UserConfigDir = "./testdata/valid_signingkeys" ctx := context.Background() - opts := &SignerFlagOpts{ + opts := &cmd.SignerFlagOpts{ Key: "test", } @@ -102,7 +103,7 @@ func TestGetSignerFromConfig(t *testing.T) { func TestGetFailed(t *testing.T) { ctx := context.Background() - opts := &SignerFlagOpts{} + opts := &cmd.SignerFlagOpts{} defer func(oldLibexeDir, oldConfigDir string) { dir.UserLibexecDir = oldLibexeDir @@ -126,7 +127,7 @@ func TestGetSignerFailed(t *testing.T) { }(dir.UserLibexecDir, dir.UserConfigDir) t.Run("get failed", func(t *testing.T) { - opts := &SignerFlagOpts{} + opts := &cmd.SignerFlagOpts{} dir.UserLibexecDir = "./testdata/plugins" dir.UserConfigDir = "./testdata/invalid_signingkeys" _, err := GetSigner(ctx, opts) @@ -142,7 +143,7 @@ func TestGetSignerFailed(t *testing.T) { dir.UserLibexecDir = "./testdata/plugins" dir.UserConfigDir = "./testdata/invalid_signingkeys" - opts := &SignerFlagOpts{ + opts := &cmd.SignerFlagOpts{ KeyID: "test", PluginName: "invalid", } @@ -156,7 +157,7 @@ func TestGetSignerFailed(t *testing.T) { t.Run("failed to resolve key", func(t *testing.T) { dir.UserConfigDir = "./testdata/valid_signingkeys" expectedErrMsg := `default signing key not set. Please set default signing key or specify a key name` - _, err := GetSigner(ctx, &SignerFlagOpts{}) + _, err := GetSigner(ctx, &cmd.SignerFlagOpts{}) if err == nil || err.Error() != expectedErrMsg { t.Fatalf("expected %s, but got %s", expectedErrMsg, err) } @@ -165,7 +166,7 @@ func TestGetSignerFailed(t *testing.T) { t.Run("keypath not specified", func(t *testing.T) { dir.UserConfigDir = "./testdata/invalid_signingkeys" expectedErrMsg := `key path not specified` - opts := &SignerFlagOpts{ + opts := &cmd.SignerFlagOpts{ Key: "invalid", } _, err := GetSigner(ctx, opts) @@ -177,7 +178,7 @@ func TestGetSignerFailed(t *testing.T) { t.Run("key not found", func(t *testing.T) { dir.UserConfigDir = "./testdata/valid_signingkeys" expectedErrMsg := `signing key not found` - opts := &SignerFlagOpts{ + opts := &cmd.SignerFlagOpts{ Key: "test2", } _, err := GetSigner(ctx, opts) @@ -194,7 +195,7 @@ func TestGetSignerFailed(t *testing.T) { dir.UserLibexecDir = "./testdata/plugins" dir.UserConfigDir = "./testdata/invalid_signingkeys" expectedErrMsg := `plugin executable file is either not found or inaccessible: stat testdata/plugins/plugins/invalid/notation-invalid: no such file or directory` - opts := &SignerFlagOpts{ + opts := &cmd.SignerFlagOpts{ Key: "invalidExternal", } _, err := GetSigner(ctx, opts) @@ -206,7 +207,7 @@ func TestGetSignerFailed(t *testing.T) { t.Run("empty key", func(t *testing.T) { dir.UserConfigDir = "./testdata/invalid_signingkeys" expectedErrMsg := `unsupported key, either provide a local key and certificate file paths, or a key name in config.json, check https://notaryproject.dev/docs/user-guides/how-to/notation-config-file/ for details` - opts := &SignerFlagOpts{ + opts := &cmd.SignerFlagOpts{ Key: "empty", } _, err := GetSigner(ctx, opts) diff --git a/internal/cmd/testdata/config.json b/cmd/notation/internal/signer/testdata/config.json similarity index 100% rename from internal/cmd/testdata/config.json rename to cmd/notation/internal/signer/testdata/config.json diff --git a/internal/cmd/testdata/invalid_signingkeys/signingkeys.json b/cmd/notation/internal/signer/testdata/invalid_signingkeys/signingkeys.json similarity index 100% rename from internal/cmd/testdata/invalid_signingkeys/signingkeys.json rename to cmd/notation/internal/signer/testdata/invalid_signingkeys/signingkeys.json diff --git a/internal/cmd/testdata/plugins/plugins/testPlugin/notation-testPlugin b/cmd/notation/internal/signer/testdata/plugins/plugins/testPlugin/notation-testPlugin similarity index 100% rename from internal/cmd/testdata/plugins/plugins/testPlugin/notation-testPlugin rename to cmd/notation/internal/signer/testdata/plugins/plugins/testPlugin/notation-testPlugin diff --git a/internal/cmd/testdata/valid_signingkeys/signingkeys.json b/cmd/notation/internal/signer/testdata/valid_signingkeys/signingkeys.json similarity index 100% rename from internal/cmd/testdata/valid_signingkeys/signingkeys.json rename to cmd/notation/internal/signer/testdata/valid_signingkeys/signingkeys.json diff --git a/cmd/notation/internal/verifier/verifier.go b/cmd/notation/internal/verifier/verifier.go new file mode 100644 index 000000000..f6417b535 --- /dev/null +++ b/cmd/notation/internal/verifier/verifier.go @@ -0,0 +1,85 @@ +// Copyright The Notary Project Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package verifier + +import ( + "context" + + "github.com/notaryproject/notation-core-go/revocation/purpose" + "github.com/notaryproject/notation-go" + "github.com/notaryproject/notation-go/dir" + "github.com/notaryproject/notation-go/plugin" + "github.com/notaryproject/notation-go/verifier" + "github.com/notaryproject/notation-go/verifier/trustpolicy" + "github.com/notaryproject/notation-go/verifier/truststore" + + clirev "github.com/notaryproject/notation/internal/revocation" +) + +// Verifier is embedded with notation.BlobVerifier and notation.Verifier. +type Verifier interface { + notation.BlobVerifier + notation.Verifier +} + +// GetVerifier creates a Verifier. +func GetVerifier(ctx context.Context) (Verifier, error) { + verifierOptions, err := newVerifierOptions(ctx) + if err != nil { + return nil, err + } + + // trust policy and trust store + x509TrustStore := truststore.NewX509TrustStore(dir.ConfigFS()) + policyDocument, err := trustpolicy.LoadOCIDocument() + if err != nil { + return nil, err + } + verifierOptions.OCITrustPolicy = policyDocument + return verifier.NewVerifierWithOptions(x509TrustStore, verifierOptions) +} + +// GetBlobVerifier creates a BlobVerifier. +func GetBlobVerifier(ctx context.Context) (Verifier, error) { + verifierOptions, err := newVerifierOptions(ctx) + if err != nil { + return nil, err + } + + // trust policy and trust store + x509TrustStore := truststore.NewX509TrustStore(dir.ConfigFS()) + blobPolicyDocument, err := trustpolicy.LoadBlobDocument() + if err != nil { + return nil, err + } + verifierOptions.BlobTrustPolicy = blobPolicyDocument + return verifier.NewVerifierWithOptions(x509TrustStore, verifierOptions) +} + +// newVerifierOptions creates a verifier.VerifierOptions. +func newVerifierOptions(ctx context.Context) (verifier.VerifierOptions, error) { + revocationCodeSigningValidator, err := clirev.NewRevocationValidator(ctx, purpose.CodeSigning) + if err != nil { + return verifier.VerifierOptions{}, err + } + revocationTimestampingValidator, err := clirev.NewRevocationValidator(ctx, purpose.Timestamping) + if err != nil { + return verifier.VerifierOptions{}, err + } + return verifier.VerifierOptions{ + RevocationCodeSigningValidator: revocationCodeSigningValidator, + RevocationTimestampingValidator: revocationTimestampingValidator, + PluginManager: plugin.NewCLIManager(dir.PluginFS()), + }, nil +} diff --git a/cmd/notation/internal/verifier/verifier_test.go b/cmd/notation/internal/verifier/verifier_test.go new file mode 100644 index 000000000..aee9dcc7d --- /dev/null +++ b/cmd/notation/internal/verifier/verifier_test.go @@ -0,0 +1,169 @@ +// Copyright The Notary Project Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package verifier + +import ( + "context" + "encoding/json" + "os" + "path/filepath" + "testing" + + "github.com/notaryproject/notation-go/dir" + "github.com/notaryproject/notation-go/verifier/trustpolicy" +) + +func TestGetVerifier(t *testing.T) { + defer func(oldConfiDir, oldCacheDir string) { + dir.UserConfigDir = oldConfiDir + dir.UserCacheDir = oldCacheDir + }(dir.UserConfigDir, dir.UserCacheDir) + + t.Run("oci success", func(t *testing.T) { + tempRoot := t.TempDir() + dir.UserConfigDir = tempRoot + path := filepath.Join(tempRoot, "trustpolicy.oci.json") + policyJson, _ := json.Marshal(dummyOCIPolicyDocument(false)) + if err := os.WriteFile(path, policyJson, 0600); err != nil { + t.Fatalf("write oci policy file failed. Error: %v", err) + } + t.Cleanup(func() { os.RemoveAll(tempRoot) }) + + _, err := GetVerifier(context.Background()) + if err != nil { + t.Fatal(err) + } + }) + + t.Run("non-existing oci trust policy", func(t *testing.T) { + dir.UserConfigDir = "/" + expectedErrMsg := "trust policy is not present. To create a trust policy, see: https://notaryproject.dev/docs/quickstart/#create-a-trust-policy" + _, err := GetVerifier(context.Background()) + if err == nil || err.Error() != expectedErrMsg { + t.Fatalf("expected %s, but got %s", expectedErrMsg, err) + } + }) + + t.Run("invalid oci trust policy", func(t *testing.T) { + tempRoot := t.TempDir() + dir.UserConfigDir = tempRoot + path := filepath.Join(tempRoot, "trustpolicy.oci.json") + policyJson, _ := json.Marshal(dummyOCIPolicyDocument(true)) + if err := os.WriteFile(path, policyJson, 0600); err != nil { + t.Fatalf("write oci policy file failed. Error: %v", err) + } + t.Cleanup(func() { os.RemoveAll(tempRoot) }) + + expectedErrMsg := "oci trust policy document has empty version, version must be specified" + _, err := GetVerifier(context.Background()) + if err == nil || err.Error() != expectedErrMsg { + t.Fatalf("expected %s, but got %s", expectedErrMsg, err) + } + }) +} + +func TestGetBlobVerifier(t *testing.T) { + defer func(oldConfiDir, oldCacheDir string) { + dir.UserConfigDir = oldConfiDir + dir.UserCacheDir = oldCacheDir + }(dir.UserConfigDir, dir.UserCacheDir) + + t.Run("blob success", func(t *testing.T) { + tempRoot := t.TempDir() + dir.UserConfigDir = tempRoot + path := filepath.Join(tempRoot, "trustpolicy.blob.json") + policyJson, _ := json.Marshal(dummyBlobPolicyDocument(false)) + if err := os.WriteFile(path, policyJson, 0600); err != nil { + t.Fatalf("write blob policy file failed. Error: %v", err) + } + t.Cleanup(func() { os.RemoveAll(tempRoot) }) + + _, err := GetBlobVerifier(context.Background()) + if err != nil { + t.Fatal(err) + } + }) + + t.Run("non-existing blob trust policy", func(t *testing.T) { + dir.UserConfigDir = "/" + expectedErrMsg := "trust policy is not present. To create a trust policy, see: https://notaryproject.dev/docs/quickstart/#create-a-trust-policy" + _, err := GetBlobVerifier(context.Background()) + if err == nil || err.Error() != expectedErrMsg { + t.Fatalf("expected %s, but got %s", expectedErrMsg, err) + } + }) + + t.Run("invalid blob trust policy", func(t *testing.T) { + tempRoot := t.TempDir() + dir.UserConfigDir = tempRoot + path := filepath.Join(tempRoot, "trustpolicy.blob.json") + policyJson, _ := json.Marshal(dummyBlobPolicyDocument(true)) + if err := os.WriteFile(path, policyJson, 0600); err != nil { + t.Fatalf("write blob policy file failed. Error: %v", err) + } + t.Cleanup(func() { os.RemoveAll(tempRoot) }) + + expectedErrMsg := "blob trust policy document has empty version, version must be specified" + _, err := GetBlobVerifier(context.Background()) + if err == nil || err.Error() != expectedErrMsg { + t.Fatalf("expected %s, but got %s", expectedErrMsg, err) + } + }) +} + +func dummyOCIPolicyDocument(invalid bool) trustpolicy.OCIDocument { + if invalid { + return trustpolicy.OCIDocument{ + Version: "", + } + } + return trustpolicy.OCIDocument{ + Version: "1.0", + TrustPolicies: []trustpolicy.OCITrustPolicy{ + { + Name: "test-oci-statement", + RegistryScopes: []string{"registry.acme-rockets.io/software/net-monitor"}, + SignatureVerification: trustpolicy.SignatureVerification{VerificationLevel: "strict"}, + TrustStores: []string{"ca:valid-trust-store", "signingAuthority:valid-trust-store"}, + TrustedIdentities: []string{"x509.subject:CN=Notation Test Root,O=Notation,L=Seattle,ST=WA,C=US"}, + }, + }, + } +} + +func dummyBlobPolicyDocument(invalid bool) trustpolicy.BlobDocument { + if invalid { + return trustpolicy.BlobDocument{ + Version: "", + } + } + return trustpolicy.BlobDocument{ + Version: "1.0", + TrustPolicies: []trustpolicy.BlobTrustPolicy{ + { + Name: "test-blob-statement", + SignatureVerification: trustpolicy.SignatureVerification{VerificationLevel: "strict"}, + TrustStores: []string{"ca:valid-trust-store", "signingAuthority:valid-trust-store"}, + TrustedIdentities: []string{"x509.subject:CN=Notation Test Root,O=Notation,L=Seattle,ST=WA,C=US"}, + }, + { + Name: "test-blob-statement-global", + SignatureVerification: trustpolicy.SignatureVerification{VerificationLevel: "strict"}, + TrustStores: []string{"ca:valid-trust-store", "signingAuthority:valid-trust-store"}, + TrustedIdentities: []string{"x509.subject:CN=Notation Test Root,O=Notation,L=Seattle,ST=WA,C=US"}, + GlobalPolicy: true, + }, + }, + } +} diff --git a/cmd/notation/main.go b/cmd/notation/main.go index be68016ff..7f33a1202 100644 --- a/cmd/notation/main.go +++ b/cmd/notation/main.go @@ -64,7 +64,6 @@ func main() { logoutCommand(nil), versionCommand(), inspectCommand(nil), - blob.Cmd(), ) if err := cmd.Execute(); err != nil { os.Exit(1) diff --git a/cmd/notation/sign.go b/cmd/notation/sign.go index 441d22947..215b759c2 100644 --- a/cmd/notation/sign.go +++ b/cmd/notation/sign.go @@ -26,6 +26,7 @@ import ( "github.com/notaryproject/notation-go" "github.com/notaryproject/notation-go/log" "github.com/notaryproject/notation/cmd/notation/internal/experimental" + "github.com/notaryproject/notation/cmd/notation/internal/signer" "github.com/notaryproject/notation/internal/cmd" "github.com/notaryproject/notation/internal/envelope" "github.com/notaryproject/notation/internal/httputil" @@ -162,7 +163,7 @@ func runSign(command *cobra.Command, cmdOpts *signOpts) error { ctx := cmdOpts.LoggingFlagOpts.InitializeLogger(command.Context()) // initialize - signer, err := cmd.GetSigner(ctx, &cmdOpts.SignerFlagOpts) + signer, err := signer.GetSigner(ctx, &cmdOpts.SignerFlagOpts) if err != nil { return err } diff --git a/cmd/notation/verify.go b/cmd/notation/verify.go index 42723f7fc..ecfec843a 100644 --- a/cmd/notation/verify.go +++ b/cmd/notation/verify.go @@ -14,27 +14,19 @@ package main import ( - "context" "errors" "fmt" - "io/fs" "os" - "github.com/notaryproject/notation-core-go/revocation/purpose" "github.com/notaryproject/notation-go" - "github.com/notaryproject/notation-go/dir" - "github.com/notaryproject/notation-go/plugin" - "github.com/notaryproject/notation-go/verifier" - "github.com/notaryproject/notation-go/verifier/trustpolicy" - "github.com/notaryproject/notation-go/verifier/truststore" "github.com/notaryproject/notation/cmd/notation/internal/display" "github.com/notaryproject/notation/cmd/notation/internal/experimental" "github.com/notaryproject/notation/cmd/notation/internal/option" + "github.com/notaryproject/notation/cmd/notation/internal/verifier" "github.com/notaryproject/notation/internal/cmd" + "github.com/notaryproject/notation/internal/ioutil" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/spf13/cobra" - - clirev "github.com/notaryproject/notation/internal/revocation" ) type verifyOpts struct { @@ -119,15 +111,14 @@ func runVerify(command *cobra.Command, opts *verifyOpts) error { // set log level ctx := opts.LoggingFlagOpts.InitializeLogger(command.Context()) - displayHandler := display.NewVerifyHandler(opts.Printer) - // initialize - sigVerifier, err := getVerifier(ctx) + displayHandler := display.NewVerifyHandler(opts.Printer) + sigVerifier, err := verifier.GetVerifier(ctx) if err != nil { return err } - // set up verification plugin config. + // set up verification plugin config configs, err := cmd.ParseFlagMap(opts.pluginConfig, cmd.PflagPluginConfig.Name) if err != nil { return err @@ -161,66 +152,10 @@ func runVerify(command *cobra.Command, opts *verifyOpts) error { UserMetadata: userMetadata, } _, outcomes, err := notation.Verify(ctx, sigVerifier, sigRepo, verifyOpts) - err = checkVerificationFailure(outcomes, resolvedRef, err) + err = ioutil.ComposeVerificationFailurePrintout(outcomes, resolvedRef, err) if err != nil { return err } displayHandler.OnVerifySucceeded(outcomes, resolvedRef) return displayHandler.Render() } - -func checkVerificationFailure(outcomes []*notation.VerificationOutcome, printOut string, err error) error { - // write out on failure - if err != nil || len(outcomes) == 0 { - if err != nil { - var errTrustStore truststore.TrustStoreError - if errors.As(err, &errTrustStore) { - if errors.Is(err, fs.ErrNotExist) { - return fmt.Errorf("%w. Use command 'notation cert add' to create and add trusted certificates to the trust store", errTrustStore) - } else { - return fmt.Errorf("%w. %w", errTrustStore, errTrustStore.InnerError) - } - } - - var errCertificate truststore.CertificateError - if errors.As(err, &errCertificate) { - if errors.Is(err, fs.ErrNotExist) { - return fmt.Errorf("%w. Use command 'notation cert add' to create and add trusted certificates to the trust store", errCertificate) - } else { - return fmt.Errorf("%w. %w", errCertificate, errCertificate.InnerError) - } - } - - var errorVerificationFailed notation.ErrorVerificationFailed - if !errors.As(err, &errorVerificationFailed) { - return fmt.Errorf("signature verification failed: %w", err) - } - } - return fmt.Errorf("signature verification failed for all the signatures associated with %s", printOut) - } - return nil -} - -func getVerifier(ctx context.Context) (notation.Verifier, error) { - // revocation check - revocationCodeSigningValidator, err := clirev.NewRevocationValidator(ctx, purpose.CodeSigning) - if err != nil { - return nil, err - } - revocationTimestampingValidator, err := clirev.NewRevocationValidator(ctx, purpose.Timestamping) - if err != nil { - return nil, err - } - - // trust policy and trust store - policyDocument, err := trustpolicy.LoadOCIDocument() - if err != nil { - return nil, err - } - x509TrustStore := truststore.NewX509TrustStore(dir.ConfigFS()) - - return verifier.NewVerifierWithOptions(policyDocument, nil, x509TrustStore, plugin.NewCLIManager(dir.PluginFS()), verifier.VerifierOptions{ - RevocationCodeSigningValidator: revocationCodeSigningValidator, - RevocationTimestampingValidator: revocationTimestampingValidator, - }) -} diff --git a/cmd/notation/verify_test.go b/cmd/notation/verify_test.go index b68796436..6ae49b8c8 100644 --- a/cmd/notation/verify_test.go +++ b/cmd/notation/verify_test.go @@ -14,15 +14,8 @@ package main import ( - "context" - "encoding/json" - "os" - "path/filepath" "reflect" "testing" - - "github.com/notaryproject/notation-go/dir" - "github.com/notaryproject/notation-go/verifier/trustpolicy" ) func TestVerifyCommand_BasicArgs(t *testing.T) { @@ -87,50 +80,3 @@ func TestVerifyCommand_MissingArgs(t *testing.T) { t.Fatal("Parse Args expected error, but ok") } } - -func TestGetVerifier(t *testing.T) { - defer func(oldConfiDir, oldCacheDir string) { - dir.UserConfigDir = oldConfiDir - dir.UserCacheDir = oldCacheDir - }(dir.UserConfigDir, dir.UserCacheDir) - - t.Run("success", func(t *testing.T) { - tempRoot := t.TempDir() - dir.UserConfigDir = tempRoot - path := filepath.Join(tempRoot, "trustpolicy.json") - policyJson, _ := json.Marshal(dummyOCIPolicyDocument()) - if err := os.WriteFile(path, policyJson, 0600); err != nil { - t.Fatalf("TestLoadOCIDocument write policy file failed. Error: %v", err) - } - t.Cleanup(func() { os.RemoveAll(tempRoot) }) - - _, err := getVerifier(context.Background()) - if err != nil { - t.Fatal(err) - } - }) - - t.Run("non-existing trust policy", func(t *testing.T) { - dir.UserConfigDir = "/" - expectedErrMsg := "trust policy is not present. To create a trust policy, see: https://notaryproject.dev/docs/quickstart/#create-a-trust-policy" - _, err := getVerifier(context.Background()) - if err == nil || err.Error() != expectedErrMsg { - t.Fatalf("expected %s, but got %s", expectedErrMsg, err) - } - }) -} - -func dummyOCIPolicyDocument() trustpolicy.OCIDocument { - return trustpolicy.OCIDocument{ - Version: "1.0", - TrustPolicies: []trustpolicy.OCITrustPolicy{ - { - Name: "test-statement-name", - RegistryScopes: []string{"registry.acme-rockets.io/software/net-monitor"}, - SignatureVerification: trustpolicy.SignatureVerification{VerificationLevel: "strict"}, - TrustStores: []string{"ca:valid-trust-store", "signingAuthority:valid-trust-store"}, - TrustedIdentities: []string{"x509.subject:CN=Notation Test Root,O=Notary,L=Seattle,ST=WA,C=US"}, - }, - }, - } -} diff --git a/go.mod b/go.mod index 53853d9b4..ba309938f 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.23 require ( github.com/notaryproject/notation-core-go v1.2.0 - github.com/notaryproject/notation-go v1.2.0-beta.1.0.20250115061529-96b71337184c + github.com/notaryproject/notation-go v1.2.0-beta.1.0.20250122072255-6eb53a50d69e github.com/notaryproject/tspclient-go v1.0.0 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.0 diff --git a/go.sum b/go.sum index 078c85232..70b63ef5e 100644 --- a/go.sum +++ b/go.sum @@ -38,8 +38,8 @@ github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZ github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= github.com/notaryproject/notation-core-go v1.2.0 h1:WElMG9X0YXJhBd0A4VOxLNalTLrTjvqtIAj7JHr5X08= github.com/notaryproject/notation-core-go v1.2.0/go.mod h1:+y3L1dOs2/ZwJIU5Imo7BBvZ/M3CFjXkydGGdK09EtA= -github.com/notaryproject/notation-go v1.2.0-beta.1.0.20250115061529-96b71337184c h1:P9GOhlDUtUXEWGikUDDIrnES2YtlYa15tTfg1U/E96Y= -github.com/notaryproject/notation-go v1.2.0-beta.1.0.20250115061529-96b71337184c/go.mod h1:ig6lhOPvLW4jrp6ZfaW+B3uNGKbcNW9pgIByvz/s31w= +github.com/notaryproject/notation-go v1.2.0-beta.1.0.20250122072255-6eb53a50d69e h1:1CyFPpzmL0PCRyj9UZLtvDKlGmTKBe9Q/Tx13lfoBVU= +github.com/notaryproject/notation-go v1.2.0-beta.1.0.20250122072255-6eb53a50d69e/go.mod h1:ig6lhOPvLW4jrp6ZfaW+B3uNGKbcNW9pgIByvz/s31w= github.com/notaryproject/notation-plugin-framework-go v1.0.0 h1:6Qzr7DGXoCgXEQN+1gTZWuJAZvxh3p8Lryjn5FaLzi4= github.com/notaryproject/notation-plugin-framework-go v1.0.0/go.mod h1:RqWSrTOtEASCrGOEffq0n8pSg2KOgKYiWqFWczRSics= github.com/notaryproject/tspclient-go v1.0.0 h1:AwQ4x0gX8IHnyiZB1tggpn5NFqHpTEm1SDX8YNv4Dg4= diff --git a/internal/ioutil/print.go b/internal/ioutil/print.go index 5f97f064b..c460aee10 100644 --- a/internal/ioutil/print.go +++ b/internal/ioutil/print.go @@ -14,12 +14,16 @@ package ioutil import ( + "errors" "fmt" "io" + "io/fs" "path/filepath" "text/tabwriter" + "github.com/notaryproject/notation-go" "github.com/notaryproject/notation-go/config" + "github.com/notaryproject/notation-go/verifier/truststore" ) func newTabWriter(w io.Writer) *tabwriter.Writer { @@ -66,3 +70,57 @@ func PrintCertMap(w io.Writer, certPaths []string) error { } return tw.Flush() } + +// ComposeVerificationFailurePrintout composes verification failure print out. +func ComposeVerificationFailurePrintout(outcomes []*notation.VerificationOutcome, reference string, err error) error { + if verificationErr := parseErrorOnVerificationFailure(err); verificationErr != nil { + return verificationErr + } + if len(outcomes) == 0 { + return fmt.Errorf("signature verification failed for all the signatures associated with %s", reference) + } + return nil +} + +// ComposeBlobVerificationFailurePrintout composes blob verification failure +// print out. +func ComposeBlobVerificationFailurePrintout(outcomes []*notation.VerificationOutcome, blobPath string, err error) error { + if verificationErr := parseErrorOnVerificationFailure(err); verificationErr != nil { + return verificationErr + } + if len(outcomes) == 0 { + return fmt.Errorf("provided signature verification failed against blob %s", blobPath) + } + return nil +} + +// parseErrorOnVerificationFailure parses error on verification failure. +func parseErrorOnVerificationFailure(err error) error { + if err == nil { + return nil + } + + var errTrustStore truststore.TrustStoreError + if errors.As(err, &errTrustStore) { + if errors.Is(err, fs.ErrNotExist) { + return fmt.Errorf("%w. Use command 'notation cert add' to create and add trusted certificates to the trust store", errTrustStore) + } else { + return fmt.Errorf("%w. %w", errTrustStore, errTrustStore.InnerError) + } + } + + var errCertificate truststore.CertificateError + if errors.As(err, &errCertificate) { + if errors.Is(err, fs.ErrNotExist) { + return fmt.Errorf("%w. Use command 'notation cert add' to create and add trusted certificates to the trust store", errCertificate) + } else { + return fmt.Errorf("%w. %w", errCertificate, errCertificate.InnerError) + } + } + + var errorVerificationFailed notation.ErrorVerificationFailed + if !errors.As(err, &errorVerificationFailed) { + return fmt.Errorf("signature verification failed: %w", err) + } + return nil +} diff --git a/internal/ioutil/print_test.go b/internal/ioutil/print_test.go new file mode 100644 index 000000000..f158eb097 --- /dev/null +++ b/internal/ioutil/print_test.go @@ -0,0 +1,29 @@ +// Copyright The Notary Project Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ioutil + +import ( + "testing" + + "github.com/notaryproject/notation-go" +) + +func TestBlobVerificateFailure(t *testing.T) { + var outcomes []*notation.VerificationOutcome + expectedErrMsg := "provided signature verification failed against blob myblob" + err := ComposeBlobVerificationFailurePrintout(outcomes, "myblob", nil) + if err == nil || err.Error() != expectedErrMsg { + t.Fatalf("expected %s, but got %s", expectedErrMsg, err) + } +} diff --git a/specs/commandline/blob.md b/specs/commandline/blob.md index 532d633f7..1e33048e4 100644 --- a/specs/commandline/blob.md +++ b/specs/commandline/blob.md @@ -4,13 +4,13 @@ Use `notation blob` command to sign, verify, and inspect signatures associated with arbitrary blobs. Notation can sign and verify any arbitrary bag of bits like zip files, documents, executables, etc. When a user signs a blob, `notation` produces a detached signature, which the user can transport/distribute using any medium that the user prefers along with the original blob. On the verification side, Notation can verify the blob's signature and assert that the blob has not been tampered with during its transmission. -Users can use `notation blob policy` command to manage trust policies for verifying a blob signature. The `notation blob policy` command provides a user-friendly way to manage trust policies for signed blobs. It allows users to show trust policy configuration, import/export a trust policy configuration file from/to a JSON file. For more details, see [blob trust policy specification and examples](https://github.com/notaryproject/specifications/blob/main/specs/trust-store-trust-policy.md#blob-trust-policy). +The `notation blob policy` command provides a user-friendly way to manage trust policies for signed blobs. It allows users to show blob trust policy configuration, import/export a blob trust policy configuration file from/to a JSON file. For more details, see [blob trust policy specification and examples](https://github.com/notaryproject/specifications/blob/main/specs/trust-store-trust-policy.md#blob-trust-policy). -The sample trust policy file (`trustpolicy.blob.json`) for verifying signed blobs is shown below. This sample trust policy file, contains three different statements for different usecases: +The sample trust policy file (`trustpolicy.blob.json`) for verifying signed blobs is shown below. This sample trust policy file, contains three different statements for different use cases: -- The Policy named "wabbit-networks-policy" is for verifying blob artifacts signed by Wabbit Networks. -- Policy named "skip-verification-policy" is for skipping verification on blob artifacts. -- Policy "global-verification-policy" is for auditing verification results when user does not provide `--policy-name` argument in `notation blob verify` command. +- The policy named "wabbit-networks-policy" is for verifying blob artifacts signed by Wabbit Networks. +- The policy named "skip-verification-policy" is for skipping verification on blob artifacts. +- The policy "global-verification-policy" is for auditing verification results when user does not set the `--policy-name` flag in `notation blob verify` command. ```jsonc { @@ -160,21 +160,20 @@ Usage: notation blob verify [flags] --signature Flags: - --signature string location of the blob signature file - --media-type string optional media type of the blob to verify - --policy-name string optional policy name to verify against. If not provided, notation verifies against the global policy if it exists. - -m, --user-metadata stringArray user defined {key}={value} pairs that must be present in the signature for successful verification if provided + -d, --debug debug mode + -h, --help help for verify + --media-type string media type of the blob to verify --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 - -o, --output string output format, options: 'json', 'text' (default "text") - -d, --debug debug mode - -v, --verbose verbose mode - -h, --help help for inspect + --policy-name string policy name to verify against. If not provided, the global policy is used if exists + --signature string filepath of the signature to be verified + -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 ``` ## Usage ## Produce blob signatures -The signature file will be written to the currently working directory with file name "{blob file name}.{signature format}.sig". For example, signature file name for "myBlob.bin" will be "myBlob.bin.jws.sig" for JWS signature format or "myBlob.bin.cose.sig" for COSE signature format. +The signature file will be written to the currently working directory with file name `{blob file name}.{signature format}.sig`. For example, signature file name for "myBlob.bin" will be "myBlob.bin.jws.sig" for JWS signature format or "myBlob.bin.cose.sig" for COSE signature format. ### Sign a blob by adding a new key @@ -379,10 +378,10 @@ The steps to update blob trust policy configuration: ``` ## Verify blob signatures -The `notation blob verify` command can be used to verify blob signatures. In order to verify signatures, user will need to setup a trust policy file `trustpolicy.blob.json` with Policies for blobs. Below are two examples of how a policy configuration file can be setup for verifying blob signatures. +The `notation blob verify` command can be used to verify blob signatures. In order to verify signatures, user will need to setup a trust policy file `trustpolicy.blob.json` with policies for blobs. Below are two examples of how a policy configuration file can be setup for verifying blob signatures. -- The Policy named "wabbit-networks-policy" is for verifying blob artifacts signed by Wabbit Networks. -- Policy named "global-verification-policy" is for auditing verification results when user doesn't not provide `--policy-name` argument in `notation blob verify` command. +- The policy named "wabbit-networks-policy" is for verifying blob artifacts signed by Wabbit Networks. +- The policy named "global-verification-policy" is for auditing verification results when user does not set the `--policy-name` flag in `notation blob verify` command. ```jsonc { @@ -432,7 +431,7 @@ notation blob verify --signature /tmp/my-blob.bin.jws.sig /tmp/my-blob.bin An example of output messages for a successful verification: ```text -Successfully verified signature /tmp/my-blob.bin.jws.sig +Successfully verified signature for /tmp/my-blob.bin ``` ### Verify the signature with user metadata @@ -447,7 +446,7 @@ notation blob verify --user-metadata io.wabbit-networks.buildId=123 --signature An example of output messages for a successful verification: ```text -Successfully verified signature /tmp/my-blob.bin.jws.sig +Successfully verified signature for /tmp/my-blob.bin The signature contains the following user metadata: @@ -458,7 +457,7 @@ io.wabbit-networks.buildId 123 An example of output messages for an unsuccessful verification: ```text -Error: signature verification failed: unable to find specified metadata in the given signature +Error: signature verification failed: unable to find specified metadata in the signature ``` ### Verify the signature with media type @@ -473,16 +472,13 @@ notation blob verify --media-type application/my-media-octet-stream --signature An example of output messages for a successful verification: ```text -Successfully verified signature /tmp/my-blob.bin.jws.sig - -The blob is of media type `application/my-media-octet-stream`. - +Successfully verified signature for /tmp/my-blob.bin ``` -An example of output messages for an unsuccessful verification: +An example of output messages for a failed verification: ```text -Error: Signature verification failed due to a mismatch in the blob's media type 'application/xyz' and the expected type 'application/my-media-octet-stream'. +Error: signature verification failed: integrity check failed. signature does not match the given blob ``` ### Verify the signature using a policy name @@ -496,11 +492,11 @@ notation blob verify --policy-name wabbit-networks-policy --signature ./sigs/my- An example of output messages for a successful verification: ```text -Successfully verified signature ./sigs/my-blob.bin.jws.sig using policy `wabbit-networks-policy` +Successfully verified signature for ./blobs/my-blob.bin ``` An example of output messages for an unsuccessful verification: ```text -Error: signature verification failed for policy `wabbit-networks-policy` +Error: signature verification failed: no applicable blob trust policy with name "wabbit-networks-policy" ``` diff --git a/test/e2e/go.mod b/test/e2e/go.mod index ac2516729..2d8cc118f 100644 --- a/test/e2e/go.mod +++ b/test/e2e/go.mod @@ -4,7 +4,7 @@ go 1.23 require ( github.com/notaryproject/notation-core-go v1.2.0 - github.com/notaryproject/notation-go v1.2.0-beta.1.0.20250115061529-96b71337184c + github.com/notaryproject/notation-go v1.2.0-beta.1.0.20250122072255-6eb53a50d69e github.com/onsi/ginkgo/v2 v2.22.2 github.com/onsi/gomega v1.36.2 github.com/opencontainers/image-spec v1.1.0 diff --git a/test/e2e/go.sum b/test/e2e/go.sum index bb8f7cee1..c2474b975 100644 --- a/test/e2e/go.sum +++ b/test/e2e/go.sum @@ -12,8 +12,8 @@ github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/Z github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/notaryproject/notation-core-go v1.2.0 h1:WElMG9X0YXJhBd0A4VOxLNalTLrTjvqtIAj7JHr5X08= github.com/notaryproject/notation-core-go v1.2.0/go.mod h1:+y3L1dOs2/ZwJIU5Imo7BBvZ/M3CFjXkydGGdK09EtA= -github.com/notaryproject/notation-go v1.2.0-beta.1.0.20250115061529-96b71337184c h1:P9GOhlDUtUXEWGikUDDIrnES2YtlYa15tTfg1U/E96Y= -github.com/notaryproject/notation-go v1.2.0-beta.1.0.20250115061529-96b71337184c/go.mod h1:ig6lhOPvLW4jrp6ZfaW+B3uNGKbcNW9pgIByvz/s31w= +github.com/notaryproject/notation-go v1.2.0-beta.1.0.20250122072255-6eb53a50d69e h1:1CyFPpzmL0PCRyj9UZLtvDKlGmTKBe9Q/Tx13lfoBVU= +github.com/notaryproject/notation-go v1.2.0-beta.1.0.20250122072255-6eb53a50d69e/go.mod h1:ig6lhOPvLW4jrp6ZfaW+B3uNGKbcNW9pgIByvz/s31w= github.com/notaryproject/tspclient-go v1.0.0 h1:AwQ4x0gX8IHnyiZB1tggpn5NFqHpTEm1SDX8YNv4Dg4= github.com/notaryproject/tspclient-go v1.0.0/go.mod h1:LGyA/6Kwd2FlM0uk8Vc5il3j0CddbWSHBj/4kxQDbjs= github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU= diff --git a/test/e2e/internal/notation/host.go b/test/e2e/internal/notation/host.go index 1d701d03e..28336b77e 100644 --- a/test/e2e/internal/notation/host.go +++ b/test/e2e/internal/notation/host.go @@ -140,14 +140,26 @@ func Opts(options ...utils.HostOption) []utils.HostOption { return options } -// BaseOptions returns a list of base Options for a valid notation. +// BaseOptions returns a list of base Options for a valid notation // testing environment. func BaseOptions() []utils.HostOption { return Opts( AuthOption("", ""), AddKeyOption(filepath.Join(NotationE2ELocalKeysDir, "e2e.key"), filepath.Join(NotationE2ELocalKeysDir, "e2e.crt")), AddTrustStoreOption("e2e", filepath.Join(NotationE2ELocalKeysDir, "e2e.crt")), - AddTrustPolicyOption("trustpolicy.json"), + AddTrustPolicyOption("trustpolicy.json", false), + ) +} + +// BaseBlobOptions returns a list of base blob options for a valid notation +// testing environment. +func BaseBlobOptions() []utils.HostOption { + return Opts( + AddKeyOption(filepath.Join(NotationE2ELocalKeysDir, "e2e.key"), filepath.Join(NotationE2ELocalKeysDir, "e2e.crt")), + AddTrustStoreOption("e2e", filepath.Join(NotationE2ELocalKeysDir, "e2e.crt")), + AddTrustPolicyOption("trustpolicy.blob.json", true), + AddTimestampTrustStoreOption("e2e", filepath.Join(NotationE2EConfigPath, "timestamp", "globalsignTSARoot.cer")), + AddTimestampTrustStoreOption("e2e", filepath.Join(NotationE2EConfigPath, "timestamp", "DigiCertTSARootSHA384.cer")), ) } @@ -156,9 +168,9 @@ func BaseOptions() []utils.HostOption { func TimestampOptions(verifyTimestamp string) []utils.HostOption { var trustPolicyOption utils.HostOption if verifyTimestamp == "afterCertExpiry" { - trustPolicyOption = AddTrustPolicyOption("timestamp_after_cert_expiry_trustpolicy.json") + trustPolicyOption = AddTrustPolicyOption("timestamp_after_cert_expiry_trustpolicy.json", false) } else { - trustPolicyOption = AddTrustPolicyOption("timestamp_trustpolicy.json") + trustPolicyOption = AddTrustPolicyOption("timestamp_trustpolicy.json", false) } return Opts( @@ -176,7 +188,7 @@ func CRLOptions() []utils.HostOption { AuthOption("", ""), AddKeyOption(filepath.Join(NotationE2EConfigPath, "crl", "leaf.key"), filepath.Join(NotationE2EConfigPath, "crl", "certchain_with_crl.pem")), AddTrustStoreOption("e2e", filepath.Join(NotationE2EConfigPath, "crl", "root.crt")), - AddTrustPolicyOption("trustpolicy.json"), + AddTrustPolicyOption("trustpolicy.json", false), ) } @@ -185,7 +197,7 @@ func BaseOptionsWithExperimental() []utils.HostOption { AuthOption("", ""), AddKeyOption(filepath.Join(NotationE2ELocalKeysDir, "e2e.key"), filepath.Join(NotationE2ELocalKeysDir, "e2e.crt")), AddTrustStoreOption("e2e", filepath.Join(NotationE2ELocalKeysDir, "e2e.crt")), - AddTrustPolicyOption("trustpolicy.json"), + AddTrustPolicyOption("trustpolicy.json", false), EnableExperimental(), ) } @@ -196,7 +208,7 @@ func TestLoginOptions() []utils.HostOption { return Opts( AddKeyOption(filepath.Join(NotationE2ELocalKeysDir, "e2e.key"), filepath.Join(NotationE2ELocalKeysDir, "e2e.crt")), AddTrustStoreOption("e2e", filepath.Join(NotationE2ELocalKeysDir, "e2e.crt")), - AddTrustPolicyOption("trustpolicy.json"), + AddTrustPolicyOption("trustpolicy.json", false), AddConfigJsonOption("pass_credential_helper_config.json"), ) } @@ -251,7 +263,15 @@ func AddTimestampTrustStoreOption(namedstore string, srcCertPath string) utils.H } // AddTrustPolicyOption adds a valid trust policy for testing. -func AddTrustPolicyOption(trustpolicyName string) utils.HostOption { +func AddTrustPolicyOption(trustpolicyName string, isBlob bool) utils.HostOption { + if isBlob { + return func(vhost *utils.VirtualHost) error { + return copyFile( + filepath.Join(BlobTrustPolicyPath, trustpolicyName), + vhost.AbsolutePath(NotationDirName, BlobTrustPolicyName), + ) + } + } return func(vhost *utils.VirtualHost) error { return copyFile( filepath.Join(NotationE2ETrustPolicyDir, trustpolicyName), diff --git a/test/e2e/internal/notation/init.go b/test/e2e/internal/notation/init.go index bf06f8a45..1bd92b9ac 100644 --- a/test/e2e/internal/notation/init.go +++ b/test/e2e/internal/notation/init.go @@ -24,8 +24,8 @@ import ( const ( NotationDirName = "notation" - TrustPolicyName = "trustpolicy.json" BlobTrustPolicyName = "trustpolicy.blob.json" + TrustPolicyName = "trustpolicy.json" TrustStoreDirName = "truststore" TrustStoreTypeCA = "ca" PluginDirName = "plugins" @@ -48,6 +48,7 @@ const ( envKeyTestRepo = "NOTATION_E2E_TEST_REPO" envKeyTestTag = "NOTATION_E2E_TEST_TAG" envKeyBlobPath = "NOTATION_E2E_BLOB_PATH" + envKeyNotationE2EBlobTrustPolicyPath = "NOTATION_E2E_BLOB_TRUST_POLICY_PATH" ) var ( @@ -72,6 +73,7 @@ var ( TestTag string RegistryStoragePath string BlobPath string + BlobTrustPolicyPath string ) func init() { @@ -88,6 +90,7 @@ func setUp() { setPathValue(envKeyOCILayoutPath, &OCILayoutPath) setPathValue(envKeyBlobPath, &BlobPath) + setPathValue(envKeyNotationE2EBlobTrustPolicyPath, &BlobTrustPolicyPath) setValue(envKeyTestRepo, &TestRepoUri) setValue(envKeyTestTag, &TestTag) } diff --git a/test/e2e/plugin/go.mod b/test/e2e/plugin/go.mod index 59e9b01f4..88c194ce1 100644 --- a/test/e2e/plugin/go.mod +++ b/test/e2e/plugin/go.mod @@ -5,7 +5,7 @@ go 1.23 require ( github.com/golang-jwt/jwt v3.2.2+incompatible github.com/notaryproject/notation-core-go v1.2.0 - github.com/notaryproject/notation-go v1.2.0-beta.1.0.20250115061529-96b71337184c + github.com/notaryproject/notation-go v1.2.0-beta.1.0.20250122072255-6eb53a50d69e github.com/notaryproject/notation-plugin-framework-go v1.0.0 github.com/spf13/cobra v1.8.1 ) diff --git a/test/e2e/plugin/go.sum b/test/e2e/plugin/go.sum index 5f12603c7..152aa3f42 100644 --- a/test/e2e/plugin/go.sum +++ b/test/e2e/plugin/go.sum @@ -40,8 +40,8 @@ github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZ github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= github.com/notaryproject/notation-core-go v1.2.0 h1:WElMG9X0YXJhBd0A4VOxLNalTLrTjvqtIAj7JHr5X08= github.com/notaryproject/notation-core-go v1.2.0/go.mod h1:+y3L1dOs2/ZwJIU5Imo7BBvZ/M3CFjXkydGGdK09EtA= -github.com/notaryproject/notation-go v1.2.0-beta.1.0.20250115061529-96b71337184c h1:P9GOhlDUtUXEWGikUDDIrnES2YtlYa15tTfg1U/E96Y= -github.com/notaryproject/notation-go v1.2.0-beta.1.0.20250115061529-96b71337184c/go.mod h1:ig6lhOPvLW4jrp6ZfaW+B3uNGKbcNW9pgIByvz/s31w= +github.com/notaryproject/notation-go v1.2.0-beta.1.0.20250122072255-6eb53a50d69e h1:1CyFPpzmL0PCRyj9UZLtvDKlGmTKBe9Q/Tx13lfoBVU= +github.com/notaryproject/notation-go v1.2.0-beta.1.0.20250122072255-6eb53a50d69e/go.mod h1:ig6lhOPvLW4jrp6ZfaW+B3uNGKbcNW9pgIByvz/s31w= github.com/notaryproject/notation-plugin-framework-go v1.0.0 h1:6Qzr7DGXoCgXEQN+1gTZWuJAZvxh3p8Lryjn5FaLzi4= github.com/notaryproject/notation-plugin-framework-go v1.0.0/go.mod h1:RqWSrTOtEASCrGOEffq0n8pSg2KOgKYiWqFWczRSics= github.com/notaryproject/tspclient-go v1.0.0 h1:AwQ4x0gX8IHnyiZB1tggpn5NFqHpTEm1SDX8YNv4Dg4= diff --git a/test/e2e/run.sh b/test/e2e/run.sh index df89d4dca..419367332 100755 --- a/test/e2e/run.sh +++ b/test/e2e/run.sh @@ -116,7 +116,8 @@ export NOTATION_E2E_TEST_TAG=v1 export NOTATION_E2E_PLUGIN_PATH=$CWD/plugin/bin/$PLUGIN_NAME export NOTATION_E2E_PLUGIN_TAR_GZ_PATH=$CWD/plugin/bin/$PLUGIN_NAME.tar.gz export NOTATION_E2E_MALICIOUS_PLUGIN_ARCHIVE_PATH=$CWD/testdata/malicious-plugin -export NOTATION_E2E_BLOB_PATH=$CWD/testdata/blob/blobFile +export NOTATION_E2E_BLOB_PATH=$CWD/testdata/blob/blobFile.txt +export NOTATION_E2E_BLOB_TRUST_POLICY_PATH=$CWD/testdata/blob/trustpolicies # run tests ginkgo -r -p -v diff --git a/test/e2e/suite/command/blob/sign.go b/test/e2e/suite/command/blob/sign.go index 3e5ab269c..19cd74bd0 100644 --- a/test/e2e/suite/command/blob/sign.go +++ b/test/e2e/suite/command/blob/sign.go @@ -76,7 +76,7 @@ var _ = Describe("notation blob sign", func() { HostWithBlob(BaseOptions(), func(notation *utils.ExecOpts, blobPath string, vhost *utils.VirtualHost) { notation.Exec("blob", "sign", "--signature-directory", vhost.AbsolutePath(), blobPath). MatchKeyWords(SignSuccessfully). - MatchKeyWords(fmt.Sprintf("Signature file written to %s", vhost.AbsolutePath("blobFile.jws.sig"))) + MatchKeyWords(fmt.Sprintf("Signature file written to %s", vhost.AbsolutePath("blobFile.txt.jws.sig"))) }) }) @@ -101,11 +101,11 @@ var _ = Describe("notation blob sign", func() { sigDir := vhost.AbsolutePath() notation.Exec("blob", "sign", "--signature-directory", sigDir, blobPath). MatchKeyWords(SignSuccessfully). - MatchKeyWords(fmt.Sprintf("Signature file written to %s", vhost.AbsolutePath("blobFile.jws.sig"))) + MatchKeyWords(fmt.Sprintf("Signature file written to %s", vhost.AbsolutePath("blobFile.txt.jws.sig"))) notation.Exec("blob", "sign", "--force", "--signature-directory", sigDir, blobPath). MatchKeyWords(SignSuccessfully). - MatchKeyWords(fmt.Sprintf("Signature file written to %s", vhost.AbsolutePath("blobFile.jws.sig"))) + MatchKeyWords(fmt.Sprintf("Signature file written to %s", vhost.AbsolutePath("blobFile.txt.jws.sig"))) }) }) diff --git a/test/e2e/suite/command/blob/verify.go b/test/e2e/suite/command/blob/verify.go new file mode 100644 index 000000000..b120e2735 --- /dev/null +++ b/test/e2e/suite/command/blob/verify.go @@ -0,0 +1,288 @@ +// Copyright The Notary Project Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package blob + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + . "github.com/notaryproject/notation/test/e2e/internal/notation" + "github.com/notaryproject/notation/test/e2e/internal/utils" + . "github.com/notaryproject/notation/test/e2e/suite/common" + . "github.com/onsi/ginkgo/v2" +) + +var _ = Describe("notation blob verify", func() { + // Success cases + It("with blob verify", func() { + HostWithBlob(BaseBlobOptions(), func(notation *utils.ExecOpts, blobPath string, vhost *utils.VirtualHost) { + workDir := vhost.AbsolutePath() + notation.WithWorkDir(workDir).Exec("blob", "sign", blobPath). + MatchKeyWords(SignSuccessfully). + MatchKeyWords("Signature file written to") + + signaturePath := signatureFilepath(workDir, blobPath, "jws") + notation.Exec("blob", "verify", "-d", "--signature", signaturePath, blobPath). + MatchKeyWords(VerifySuccessfully). + // debug log message outputs to stderr + MatchErrKeyWords( + "Verify signature of media type application/jose+json", + "Name:test-blob-global-statement", + ) + }) + }) + + It("with COSE signature", func() { + HostWithBlob(BaseBlobOptions(), func(notation *utils.ExecOpts, blobPath string, vhost *utils.VirtualHost) { + workDir := vhost.AbsolutePath() + notation.WithWorkDir(workDir).Exec("blob", "--signature-format", "cose", "sign", blobPath). + MatchKeyWords(SignSuccessfully). + MatchKeyWords("Signature file written to") + + signaturePath := signatureFilepath(workDir, blobPath, "cose") + notation.Exec("blob", "verify", "-d", "--signature", signaturePath, blobPath). + MatchKeyWords(VerifySuccessfully). + // debug log message outputs to stderr + MatchErrKeyWords( + "Verify signature of media type application/cose", + ) + }) + }) + + It("with policy name", func() { + HostWithBlob(BaseBlobOptions(), func(notation *utils.ExecOpts, blobPath string, vhost *utils.VirtualHost) { + workDir := vhost.AbsolutePath() + notation.WithWorkDir(workDir).Exec("blob", "sign", blobPath). + MatchKeyWords(SignSuccessfully). + MatchKeyWords("Signature file written to") + + signaturePath := signatureFilepath(workDir, blobPath, "jws") + notation.Exec("blob", "verify", "-d", "--policy-name", "test-blob-statement", "--signature", signaturePath, blobPath). + MatchKeyWords(VerifySuccessfully). + // debug log message outputs to stderr + MatchErrKeyWords( + "Name:test-blob-statement", + ) + }) + }) + + It("with media type", func() { + HostWithBlob(BaseBlobOptions(), func(notation *utils.ExecOpts, blobPath string, vhost *utils.VirtualHost) { + workDir := vhost.AbsolutePath() + notation.WithWorkDir(workDir).Exec("blob", "sign", "--media-type", "image/jpeg", blobPath). + MatchKeyWords(SignSuccessfully). + MatchKeyWords("Signature file written to") + + signaturePath := signatureFilepath(workDir, blobPath, "jws") + notation.Exec("blob", "verify", "--media-type", "image/jpeg", "--signature", signaturePath, blobPath). + MatchKeyWords(VerifySuccessfully) + }) + }) + + It("with timestamping", func() { + HostWithBlob(BaseBlobOptions(), func(notation *utils.ExecOpts, blobPath string, vhost *utils.VirtualHost) { + workDir := vhost.AbsolutePath() + notation.WithWorkDir(workDir).Exec("blob", "sign", "--timestamp-url", tsaURL, "--timestamp-root-cert", filepath.Join(NotationE2EConfigPath, "timestamp", "DigiCertTSARootSHA384.cer"), blobPath, "-d"). + MatchKeyWords(SignSuccessfully). + MatchKeyWords("Signature file written to") + + signaturePath := signatureFilepath(workDir, blobPath, "jws") + notation.Exec("blob", "verify", "-d", "--policy-name", "test-blob-with-timestamping", "--signature", signaturePath, blobPath). + MatchKeyWords(VerifySuccessfully). + // debug log message outputs to stderr + MatchErrKeyWords( + "Timestamp verification: Success", + ) + }) + }) + + It("with user metadata", func() { + HostWithBlob(BaseBlobOptions(), func(notation *utils.ExecOpts, blobPath string, vhost *utils.VirtualHost) { + workDir := vhost.AbsolutePath() + notation.WithWorkDir(workDir).Exec("blob", "sign", "--user-metadata", "k1=v1", blobPath). + MatchKeyWords(SignSuccessfully). + MatchKeyWords("Signature file written to") + + signaturePath := signatureFilepath(workDir, blobPath, "jws") + notation.Exec("blob", "verify", "--user-metadata", "k1=v1", "--signature", signaturePath, blobPath). + MatchKeyWords(VerifySuccessfully) + }) + }) + + // Failure cases + It("with missing --signature flag", func() { + HostWithBlob(BaseBlobOptions(), func(notation *utils.ExecOpts, blobPath string, vhost *utils.VirtualHost) { + workDir := vhost.AbsolutePath() + notation.WithWorkDir(workDir).Exec("blob", "sign", blobPath). + MatchKeyWords(SignSuccessfully). + MatchKeyWords("Signature file written to") + + notation.ExpectFailure().Exec("blob", "verify", blobPath). + MatchErrKeyWords("filepath of the signature cannot be empty") + }) + }) + + It("with no permission to read blob", func() { + HostWithBlob(BaseBlobOptions(), func(notation *utils.ExecOpts, blobPath string, vhost *utils.VirtualHost) { + workDir := vhost.AbsolutePath() + noPermissionBlobPath := filepath.Join(workDir, "noPermissionBlob") + newBlobFile, err := os.Create(noPermissionBlobPath) + if err != nil { + Fail(err.Error()) + } + defer newBlobFile.Close() + + notation.WithWorkDir(workDir).Exec("blob", "sign", noPermissionBlobPath). + MatchKeyWords(SignSuccessfully). + MatchKeyWords("Signature file written to") + if err := os.Chmod(noPermissionBlobPath, 0000); err != nil { + Fail(err.Error()) + } + defer os.Chmod(noPermissionBlobPath, 0700) + + signaturePath := signatureFilepath(workDir, noPermissionBlobPath, "jws") + notation.ExpectFailure().Exec("blob", "verify", "--signature", signaturePath, noPermissionBlobPath). + MatchErrKeyWords("permission denied") + }) + }) + + It("with no permission to read signature file", func() { + HostWithBlob(BaseBlobOptions(), func(notation *utils.ExecOpts, blobPath string, vhost *utils.VirtualHost) { + workDir := vhost.AbsolutePath() + notation.WithWorkDir(workDir).Exec("blob", "sign", blobPath). + MatchKeyWords(SignSuccessfully). + MatchKeyWords("Signature file written to") + noPermissionSignaturePath := signatureFilepath(workDir, blobPath, "jws") + if err := os.Chmod(noPermissionSignaturePath, 0000); err != nil { + Fail(err.Error()) + } + defer os.Chmod(noPermissionSignaturePath, 0700) + + notation.ExpectFailure().Exec("blob", "verify", "--signature", noPermissionSignaturePath, blobPath). + MatchErrKeyWords("permission denied") + }) + }) + + It("with invalid plugin-config", func() { + HostWithBlob(BaseBlobOptions(), func(notation *utils.ExecOpts, blobPath string, vhost *utils.VirtualHost) { + workDir := vhost.AbsolutePath() + notation.WithWorkDir(workDir).Exec("blob", "sign", blobPath). + MatchKeyWords(SignSuccessfully). + MatchKeyWords("Signature file written to") + + signaturePath := signatureFilepath(workDir, blobPath, "jws") + notation.ExpectFailure().Exec("blob", "verify", "--plugin-config", "invalid", "--signature", signaturePath, blobPath). + MatchErrKeyWords(`could not parse flag plugin-config: key-value pair requires "=" as separator`) + }) + }) + + It("with invalid user metadata", func() { + HostWithBlob(BaseBlobOptions(), func(notation *utils.ExecOpts, blobPath string, vhost *utils.VirtualHost) { + workDir := vhost.AbsolutePath() + notation.WithWorkDir(workDir).Exec("blob", "sign", blobPath). + MatchKeyWords(SignSuccessfully). + MatchKeyWords("Signature file written to") + + signaturePath := signatureFilepath(workDir, blobPath, "jws") + notation.ExpectFailure().Exec("blob", "verify", "--user-metadata", "invalid", "--signature", signaturePath, blobPath). + MatchErrKeyWords(`could not parse flag user-metadata: key-value pair requires "=" as separator`) + }) + }) + + It("with invalid signature file extension", func() { + HostWithBlob(BaseBlobOptions(), func(notation *utils.ExecOpts, blobPath string, vhost *utils.VirtualHost) { + workDir := vhost.AbsolutePath() + notation.WithWorkDir(workDir).Exec("blob", "sign", blobPath). + MatchKeyWords(SignSuccessfully). + MatchKeyWords("Signature file written to") + + signaturePath := signatureFilepath(workDir, blobPath, "jws") + invalidSignaturePath := strings.TrimSuffix(signaturePath, ".sig") + "." + "invalid" + if err := os.Rename(signaturePath, invalidSignaturePath); err != nil { + Fail(err.Error()) + } + notation.ExpectFailure().Exec("blob", "verify", "--signature", invalidSignaturePath, blobPath). + MatchErrKeyWords(`invalid signature filename blobFile.txt.jws.invalid. The file extension must be .sig`) + }) + }) + + It("with invalid signature file name", func() { + HostWithBlob(BaseBlobOptions(), func(notation *utils.ExecOpts, blobPath string, vhost *utils.VirtualHost) { + workDir := vhost.AbsolutePath() + notation.WithWorkDir(workDir).Exec("blob", "sign", blobPath). + MatchKeyWords(SignSuccessfully). + MatchKeyWords("Signature file written to") + + signaturePath := signatureFilepath(workDir, blobPath, "jws") + invalidSignaturePath := strings.TrimSuffix(signaturePath, ".txt.jws.sig") + ".sig" + if err := os.Rename(signaturePath, invalidSignaturePath); err != nil { + Fail(err.Error()) + } + notation.ExpectFailure().Exec("blob", "verify", "--signature", invalidSignaturePath, blobPath). + MatchErrKeyWords(`invalid signature filename blobFile.sig. A valid signature file name must contain signature format and .sig file extension`) + }) + }) + + It("with invalid signature format", func() { + HostWithBlob(BaseBlobOptions(), func(notation *utils.ExecOpts, blobPath string, vhost *utils.VirtualHost) { + workDir := vhost.AbsolutePath() + notation.WithWorkDir(workDir).Exec("blob", "sign", blobPath). + MatchKeyWords(SignSuccessfully). + MatchKeyWords("Signature file written to") + + signaturePath := signatureFilepath(workDir, blobPath, "jws") + invalidSignaturePath := signatureFilepath(workDir, blobPath, "invalid") + if err := os.Rename(signaturePath, invalidSignaturePath); err != nil { + Fail(err.Error()) + } + notation.ExpectFailure().Exec("blob", "verify", "--signature", invalidSignaturePath, blobPath). + MatchErrKeyWords(`signature format "invalid" not supported`). + MatchErrKeyWords(`Supported signature envelope formats are "jws" and "cose"`) + }) + }) + + It("with mismatch media type", func() { + HostWithBlob(BaseBlobOptions(), func(notation *utils.ExecOpts, blobPath string, vhost *utils.VirtualHost) { + workDir := vhost.AbsolutePath() + notation.WithWorkDir(workDir).Exec("blob", "sign", blobPath). + MatchKeyWords(SignSuccessfully). + MatchKeyWords("Signature file written to") + + signaturePath := signatureFilepath(workDir, blobPath, "jws") + notation.ExpectFailure().Exec("blob", "verify", "--media-type", "image/jpeg", "--signature", signaturePath, blobPath). + MatchErrKeyWords("integrity check failed. signature does not match the given blob") + }) + }) + + It("with no trust policy", func() { + HostWithBlob(BaseOptions(), func(notation *utils.ExecOpts, blobPath string, vhost *utils.VirtualHost) { + workDir := vhost.AbsolutePath() + notation.WithWorkDir(workDir).Exec("blob", "sign", blobPath). + MatchKeyWords(SignSuccessfully). + MatchKeyWords("Signature file written to") + + signaturePath := signatureFilepath(workDir, blobPath, "jws") + notation.ExpectFailure().Exec("blob", "verify", "--signature", signaturePath, blobPath). + MatchErrKeyWords(`trust policy is not present. To create a trust policy, see: https://notaryproject.dev/docs/quickstart/#create-a-trust-policy`) + }) + }) +}) + +func signatureFilepath(signatureDirectory, blobPath, signatureFormat string) string { + blobFilename := filepath.Base(blobPath) + signatureFilename := fmt.Sprintf("%s.%s.sig", blobFilename, signatureFormat) + return filepath.Join(signatureDirectory, signatureFilename) +} diff --git a/test/e2e/suite/command/policy.go b/test/e2e/suite/command/policy.go index 118939c58..c597228e9 100644 --- a/test/e2e/suite/command/policy.go +++ b/test/e2e/suite/command/policy.go @@ -35,7 +35,7 @@ var _ = Describe("trust policy maintainer", func() { }) It("should show error and hint if policy without read permission", func() { - Host(Opts(AddTrustPolicyOption(TrustPolicyName)), func(notation *utils.ExecOpts, artifact *Artifact, vhost *utils.VirtualHost) { + Host(Opts(AddTrustPolicyOption(TrustPolicyName, false)), func(notation *utils.ExecOpts, artifact *Artifact, vhost *utils.VirtualHost) { trustPolicyPath := vhost.AbsolutePath(NotationDirName, TrustPolicyName) os.Chmod(trustPolicyPath, 0200) notation.ExpectFailure(). @@ -47,7 +47,7 @@ var _ = Describe("trust policy maintainer", func() { It("should show exist policy", func() { content, err := os.ReadFile(filepath.Join(NotationE2ETrustPolicyDir, TrustPolicyName)) Expect(err).NotTo(HaveOccurred()) - Host(Opts(AddTrustPolicyOption(TrustPolicyName)), func(notation *utils.ExecOpts, artifact *Artifact, vhost *utils.VirtualHost) { + Host(Opts(AddTrustPolicyOption(TrustPolicyName, false)), func(notation *utils.ExecOpts, artifact *Artifact, vhost *utils.VirtualHost) { notation.Exec("policy", "show"). MatchContent(string(content)) }) @@ -57,7 +57,7 @@ var _ = Describe("trust policy maintainer", func() { policyName := "invalid_format_trustpolicy.json" content, err := os.ReadFile(filepath.Join(NotationE2ETrustPolicyDir, policyName)) Expect(err).NotTo(HaveOccurred()) - Host(Opts(AddTrustPolicyOption(policyName)), func(notation *utils.ExecOpts, artifact *Artifact, vhost *utils.VirtualHost) { + Host(Opts(AddTrustPolicyOption(policyName, false)), func(notation *utils.ExecOpts, artifact *Artifact, vhost *utils.VirtualHost) { notation.Exec("policy", "show"). MatchErrKeyWords("existing trust policy configuration is invalid"). MatchContent(string(content)) @@ -126,7 +126,7 @@ var _ = Describe("trust policy maintainer", func() { }) When("importing configuration with existing trust policy configuration", func() { - opts := Opts(AddTrustPolicyOption(TrustPolicyName)) + opts := Opts(AddTrustPolicyOption(TrustPolicyName, false)) It("should fail if no file path is provided", func() { Host(opts, func(notation *utils.ExecOpts, artifact *Artifact, vhost *utils.VirtualHost) { notation.ExpectFailure(). @@ -183,7 +183,7 @@ var _ = Describe("trust policy maintainer", func() { }) It("should skip confirmation if existing policy is malformed", func() { - Host(Opts(AddTrustPolicyOption("invalid_format_trustpolicy.json")), func(notation *utils.ExecOpts, artifact *Artifact, vhost *utils.VirtualHost) { + Host(Opts(AddTrustPolicyOption("invalid_format_trustpolicy.json", false)), func(notation *utils.ExecOpts, artifact *Artifact, vhost *utils.VirtualHost) { policyFileName := "skip_trustpolicy.json" notation.Exec("policy", "import", filepath.Join(NotationE2ETrustPolicyDir, policyFileName)).MatchKeyWords(). MatchKeyWords("Trust policy configuration imported successfully.") diff --git a/test/e2e/suite/command/sign.go b/test/e2e/suite/command/sign.go index d9cefd445..590c5a6ad 100644 --- a/test/e2e/suite/command/sign.go +++ b/test/e2e/suite/command/sign.go @@ -169,7 +169,7 @@ var _ = Describe("notation sign", func() { // copy the generated cert file and create the new trust policy for verify signature with generated new key. OldNotation(AuthOption("", ""), AddTrustStoreOption(keyName, vhost.AbsolutePath(NotationDirName, LocalKeysDirName, keyName+".crt")), - AddTrustPolicyOption("generate_test_trustpolicy.json"), + AddTrustPolicyOption("generate_test_trustpolicy.json", false), ).Exec("verify", artifact.ReferenceWithTag()). MatchKeyWords(VerifySuccessfully) }) diff --git a/test/e2e/suite/scenario/quickstart.go b/test/e2e/suite/scenario/quickstart.go index b04f5b22e..96161ecb4 100644 --- a/test/e2e/suite/scenario/quickstart.go +++ b/test/e2e/suite/scenario/quickstart.go @@ -85,7 +85,7 @@ var _ = Describe("notation quickstart E2E test", Ordered, func() { }) It("Create a trust policy", func() { - vhost.SetOption(AddTrustPolicyOption("quickstart_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("quickstart_trustpolicy.json", false)) validator.CheckFileExist(vhost.AbsolutePath(NotationDirName, TrustPolicyName)) }) diff --git a/test/e2e/suite/trustpolicy/multi_statements.go b/test/e2e/suite/trustpolicy/multi_statements.go index 6364123b6..b9ae2bcc6 100644 --- a/test/e2e/suite/trustpolicy/multi_statements.go +++ b/test/e2e/suite/trustpolicy/multi_statements.go @@ -25,7 +25,7 @@ var _ = Describe("notation trust policy multi-statements test", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { artifact := GenerateArtifact("", "test-repo8") // update trustpolicy.json - vhost.SetOption(AddTrustPolicyOption("multi_statements_with_the_same_registry_scope_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("multi_statements_with_the_same_registry_scope_trustpolicy.json", false)) // test localhost:5000/test-repo notation.Exec("sign", artifact.ReferenceWithDigest()).MatchKeyWords(SignSuccessfully) @@ -38,7 +38,7 @@ var _ = Describe("notation trust policy multi-statements test", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { artifact := GenerateArtifact("e2e-valid-signature", "test-repo9") // update trustpolicy.json - vhost.SetOption(AddTrustPolicyOption("multi_statements_with_wildcard_registry_scope_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("multi_statements_with_wildcard_registry_scope_trustpolicy.json", false)) // test localhost:5000/test-repo notation.Exec("sign", artifact.ReferenceWithDigest()).MatchKeyWords(SignSuccessfully) @@ -51,7 +51,7 @@ var _ = Describe("notation trust policy multi-statements test", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { artifact := GenerateArtifact("", "test-repo10") // update trustpolicy.json - vhost.SetOption(AddTrustPolicyOption("multi_statements_with_the_same_name_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("multi_statements_with_the_same_name_trustpolicy.json", false)) // test localhost:5000/test-repo notation.Exec("sign", artifact.ReferenceWithDigest()).MatchKeyWords(SignSuccessfully) @@ -63,7 +63,7 @@ var _ = Describe("notation trust policy multi-statements test", func() { It("multiple statements with multi-wildcard registry scopes", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, artifact *Artifact, vhost *utils.VirtualHost) { // update trustpolicy.json - vhost.SetOption(AddTrustPolicyOption("multi_statements_with_multi_wildcard_registry_scope_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("multi_statements_with_multi_wildcard_registry_scope_trustpolicy.json", false)) // test localhost:5000/test-repo notation.Exec("sign", artifact.ReferenceWithDigest()).MatchKeyWords(SignSuccessfully) diff --git a/test/e2e/suite/trustpolicy/registry_scope.go b/test/e2e/suite/trustpolicy/registry_scope.go index 7eb19a5f4..0e4cb0725 100644 --- a/test/e2e/suite/trustpolicy/registry_scope.go +++ b/test/e2e/suite/trustpolicy/registry_scope.go @@ -29,7 +29,7 @@ var _ = Describe("notation trust policy registryScope test", func() { It("empty registryScope", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, artifact *Artifact, vhost *utils.VirtualHost) { // update trustpolicy.json - vhost.SetOption(AddTrustPolicyOption("empty_registry_scope_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("empty_registry_scope_trustpolicy.json", false)) // test localhost:5000/test-repo OldNotation().Exec("sign", artifact.ReferenceWithDigest()).MatchKeyWords(SignSuccessfully) @@ -41,7 +41,7 @@ var _ = Describe("notation trust policy registryScope test", func() { It("malformed registryScope", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, artifact *Artifact, vhost *utils.VirtualHost) { // update trustpolicy.json - vhost.SetOption(AddTrustPolicyOption("malformed_registry_scope_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("malformed_registry_scope_trustpolicy.json", false)) OldNotation().Exec("sign", artifact.ReferenceWithDigest()).MatchKeyWords(SignSuccessfully) notation.ExpectFailure().Exec("verify", artifact.ReferenceWithDigest()). @@ -52,7 +52,7 @@ var _ = Describe("notation trust policy registryScope test", func() { It("registryScope with a repository", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { // update trustpolicy.json - vhost.SetOption(AddTrustPolicyOption("registry_scope_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("registry_scope_trustpolicy.json", false)) // generate an artifact with given repository name artifact := GenerateArtifact("", "test-repo") @@ -66,7 +66,7 @@ var _ = Describe("notation trust policy registryScope test", func() { It("registryScope with multiple repositories", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { // update trustpolicy.json - vhost.SetOption(AddTrustPolicyOption("multiple_registry_scope_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("multiple_registry_scope_trustpolicy.json", false)) // generate an artifact with given repository name artifact2 := GenerateArtifact("", "test-repo2") @@ -85,7 +85,7 @@ var _ = Describe("notation trust policy registryScope test", func() { It("registryScope with any(*) repository", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { // update trustpolicy.json - vhost.SetOption(AddTrustPolicyOption("any_registry_scope_trust_policy.json")) + vhost.SetOption(AddTrustPolicyOption("any_registry_scope_trust_policy.json", false)) // generate an artifact with given repository name artifact4 := GenerateArtifact("", "test-repo4") @@ -104,7 +104,7 @@ var _ = Describe("notation trust policy registryScope test", func() { It("overlapped registryScope", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { // update trustpolicy.json - vhost.SetOption(AddTrustPolicyOption("overlapped_registry_scope_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("overlapped_registry_scope_trustpolicy.json", false)) artifact := GenerateArtifact("", "test-repo6") @@ -118,7 +118,7 @@ var _ = Describe("notation trust policy registryScope test", func() { It("wildcard plus specific repo registryScope", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { // update trustpolicy.json - vhost.SetOption(AddTrustPolicyOption("wildcard_plus_other_registry_scope_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("wildcard_plus_other_registry_scope_trustpolicy.json", false)) artifact := GenerateArtifact("", "test-repo7") @@ -132,7 +132,7 @@ var _ = Describe("notation trust policy registryScope test", func() { It("invalid registryScope", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, artifact *Artifact, vhost *utils.VirtualHost) { // update trustpolicy.json - vhost.SetOption(AddTrustPolicyOption("invalid_registry_scope_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("invalid_registry_scope_trustpolicy.json", false)) // test localhost:5000/test-repo OldNotation().Exec("sign", artifact.ReferenceWithDigest()).MatchKeyWords(SignSuccessfully) diff --git a/test/e2e/suite/trustpolicy/trust_store.go b/test/e2e/suite/trustpolicy/trust_store.go index d01a2fae0..9f1a90ecd 100644 --- a/test/e2e/suite/trustpolicy/trust_store.go +++ b/test/e2e/suite/trustpolicy/trust_store.go @@ -25,7 +25,7 @@ import ( var _ = Describe("notation trust policy trust store test", func() { It("unset trust store", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - vhost.SetOption(AddTrustPolicyOption("unset_trust_store_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("unset_trust_store_trustpolicy.json", false)) artifact := GenerateArtifact("e2e-valid-signature", "") @@ -36,7 +36,7 @@ var _ = Describe("notation trust policy trust store test", func() { It("invalid trust store", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - vhost.SetOption(AddTrustPolicyOption("invalid_trust_store_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("invalid_trust_store_trustpolicy.json", false)) artifact := GenerateArtifact("e2e-valid-signature", "") @@ -47,7 +47,7 @@ var _ = Describe("notation trust policy trust store test", func() { It("malformed trust store", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - vhost.SetOption(AddTrustPolicyOption("malformed_trust_store_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("malformed_trust_store_trustpolicy.json", false)) artifact := GenerateArtifact("e2e-valid-signature", "") @@ -58,7 +58,7 @@ var _ = Describe("notation trust policy trust store test", func() { It("wildcard (malformed) trust store", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - vhost.SetOption(AddTrustPolicyOption("wildcard_trust_store_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("wildcard_trust_store_trustpolicy.json", false)) artifact := GenerateArtifact("e2e-valid-signature", "") @@ -79,7 +79,7 @@ var _ = Describe("notation trust policy trust store test", func() { // setup multiple trust store vhost.SetOption(AuthOption("", ""), - AddTrustPolicyOption("multiple_trust_store_trustpolicy.json"), + AddTrustPolicyOption("multiple_trust_store_trustpolicy.json", false), AddTrustStoreOption("e2e-new", filepath.Join(NotationE2ELocalKeysDir, "new_e2e.crt")), AddTrustStoreOption("e2e", filepath.Join(NotationE2ELocalKeysDir, "e2e.crt")), ) @@ -103,7 +103,7 @@ var _ = Describe("notation trust policy trust store test", func() { // setup overlapped trust store vhost.SetOption(AuthOption("", ""), - AddTrustPolicyOption("overlapped_trust_store_trustpolicy.json"), + AddTrustPolicyOption("overlapped_trust_store_trustpolicy.json", false), AddTrustStoreOption("e2e", filepath.Join(NotationE2ELocalKeysDir, "e2e.crt"))) notation.ExpectFailure().Exec("verify", artifact.ReferenceWithDigest(), "-v"). diff --git a/test/e2e/suite/trustpolicy/trusted_identity.go b/test/e2e/suite/trustpolicy/trusted_identity.go index e37637340..e822eb1ce 100644 --- a/test/e2e/suite/trustpolicy/trusted_identity.go +++ b/test/e2e/suite/trustpolicy/trusted_identity.go @@ -25,7 +25,7 @@ import ( var _ = Describe("notation trust policy trusted identity test", func() { It("with unset trusted identity", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - vhost.SetOption(AddTrustPolicyOption("unset_trusted_identity_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("unset_trusted_identity_trustpolicy.json", false)) artifact := GenerateArtifact("e2e-valid-signature", "") notation.ExpectFailure().Exec("verify", artifact.ReferenceWithDigest(), "-v"). @@ -35,7 +35,7 @@ var _ = Describe("notation trust policy trusted identity test", func() { It("with valid trusted identity", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - vhost.SetOption(AddTrustPolicyOption("valid_trusted_identity_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("valid_trusted_identity_trustpolicy.json", false)) artifact := GenerateArtifact("e2e-valid-signature", "") notation.Exec("verify", artifact.ReferenceWithDigest(), "-v"). @@ -45,7 +45,7 @@ var _ = Describe("notation trust policy trusted identity test", func() { It("with invalid trusted identity", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - vhost.SetOption(AddTrustPolicyOption("invalid_trusted_identity_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("invalid_trusted_identity_trustpolicy.json", false)) artifact := GenerateArtifact("e2e-valid-signature", "") notation.ExpectFailure().Exec("verify", artifact.ReferenceWithDigest(), "-v"). @@ -56,7 +56,7 @@ var _ = Describe("notation trust policy trusted identity test", func() { It("with malformed trusted identity", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - vhost.SetOption(AddTrustPolicyOption("malformed_trusted_identity_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("malformed_trusted_identity_trustpolicy.json", false)) artifact := GenerateArtifact("e2e-valid-signature", "") notation.ExpectFailure().Exec("verify", artifact.ReferenceWithDigest(), "-v"). @@ -66,7 +66,7 @@ var _ = Describe("notation trust policy trusted identity test", func() { It("with empty trusted identity", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - vhost.SetOption(AddTrustPolicyOption("empty_trusted_identity_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("empty_trusted_identity_trustpolicy.json", false)) artifact := GenerateArtifact("e2e-valid-signature", "") notation.ExpectFailure().Exec("verify", artifact.ReferenceWithDigest(), "-v"). @@ -86,7 +86,7 @@ var _ = Describe("notation trust policy trusted identity test", func() { // setup multiple trusted identity vhost.SetOption(AuthOption("", ""), - AddTrustPolicyOption("multiple_trusted_identity_trustpolicy.json"), + AddTrustPolicyOption("multiple_trusted_identity_trustpolicy.json", false), AddTrustStoreOption("e2e", filepath.Join(NotationE2ELocalKeysDir, "new_e2e.crt")), AddTrustStoreOption("e2e", filepath.Join(NotationE2ELocalKeysDir, "e2e.crt")), ) @@ -101,7 +101,7 @@ var _ = Describe("notation trust policy trusted identity test", func() { It("with overlapped trusted identities", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - vhost.SetOption(AddTrustPolicyOption("overlapped_trusted_identity_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("overlapped_trusted_identity_trustpolicy.json", false)) artifact := GenerateArtifact("e2e-valid-signature", "") notation.ExpectFailure().Exec("verify", artifact.ReferenceWithDigest(), "-v"). @@ -111,7 +111,7 @@ var _ = Describe("notation trust policy trusted identity test", func() { It("with wildcard plus other trusted identities", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - vhost.SetOption(AddTrustPolicyOption("wildcard_plus_other_trusted_identity_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("wildcard_plus_other_trusted_identity_trustpolicy.json", false)) artifact := GenerateArtifact("e2e-valid-signature", "") notation.ExpectFailure().Exec("verify", artifact.ReferenceWithDigest(), "-v"). @@ -121,7 +121,7 @@ var _ = Describe("notation trust policy trusted identity test", func() { It("with trusted identities missing organization", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - vhost.SetOption(AddTrustPolicyOption("missing_organization_trusted_identity_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("missing_organization_trusted_identity_trustpolicy.json", false)) artifact := GenerateArtifact("e2e-valid-signature", "") notation.ExpectFailure().Exec("verify", artifact.ReferenceWithDigest(), "-v"). @@ -131,7 +131,7 @@ var _ = Describe("notation trust policy trusted identity test", func() { It("with trusted identities missing state", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - vhost.SetOption(AddTrustPolicyOption("missing_state_trusted_identity_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("missing_state_trusted_identity_trustpolicy.json", false)) artifact := GenerateArtifact("e2e-valid-signature", "") notation.ExpectFailure().Exec("verify", artifact.ReferenceWithDigest(), "-v"). @@ -141,7 +141,7 @@ var _ = Describe("notation trust policy trusted identity test", func() { It("with trusted identities missing country", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - vhost.SetOption(AddTrustPolicyOption("missing_country_trusted_identity_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("missing_country_trusted_identity_trustpolicy.json", false)) artifact := GenerateArtifact("e2e-valid-signature", "") notation.ExpectFailure().Exec("verify", artifact.ReferenceWithDigest(), "-v"). diff --git a/test/e2e/suite/trustpolicy/verification_level.go b/test/e2e/suite/trustpolicy/verification_level.go index 3f6c1197c..0ebaf3ce7 100644 --- a/test/e2e/suite/trustpolicy/verification_level.go +++ b/test/e2e/suite/trustpolicy/verification_level.go @@ -38,7 +38,7 @@ var _ = Describe("notation trust policy verification level test", func() { artifact := GenerateArtifact("e2e-with-expired-cert", "") vhost.SetOption(AuthOption("", ""), - AddTrustPolicyOption("trustpolicy.json"), + AddTrustPolicyOption("trustpolicy.json", false), AddTrustStoreOption("e2e", filepath.Join(NotationE2EConfigPath, "localkeys", "expired_e2e.crt")), ) @@ -51,7 +51,7 @@ var _ = Describe("notation trust policy verification level test", func() { It("strict level with invalid authenticity", func() { Host(nil, func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { vhost.SetOption(AuthOption("", ""), - AddTrustPolicyOption("trustpolicy.json"), + AddTrustPolicyOption("trustpolicy.json", false), AddTrustStoreOption("e2e", filepath.Join(NotationE2ELocalKeysDir, "new_e2e.crt")), ) @@ -77,7 +77,7 @@ var _ = Describe("notation trust policy verification level test", func() { It("permissive level with expired signature", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - vhost.SetOption(AddTrustPolicyOption("permissive_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("permissive_trustpolicy.json", false)) artifact := GenerateArtifact("e2e-expired-signature", "") @@ -92,7 +92,7 @@ var _ = Describe("notation trust policy verification level test", func() { artifact := GenerateArtifact("e2e-with-expired-cert", "") vhost.SetOption(AuthOption("", ""), - AddTrustPolicyOption("permissive_trustpolicy.json"), + AddTrustPolicyOption("permissive_trustpolicy.json", false), AddTrustStoreOption("e2e", filepath.Join(NotationE2EConfigPath, "localkeys", "expired_e2e.crt")), ) @@ -106,7 +106,7 @@ var _ = Describe("notation trust policy verification level test", func() { It("permissive level with invalid authenticity", func() { Host(nil, func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { vhost.SetOption(AuthOption("", ""), - AddTrustPolicyOption("permissive_trustpolicy.json"), + AddTrustPolicyOption("permissive_trustpolicy.json", false), AddTrustStoreOption("e2e", filepath.Join(NotationE2ELocalKeysDir, "new_e2e.crt")), ) @@ -122,7 +122,7 @@ var _ = Describe("notation trust policy verification level test", func() { It("permissive level with invalid integrity", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - vhost.SetOption(AddTrustPolicyOption("permissive_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("permissive_trustpolicy.json", false)) artifact := GenerateArtifact("e2e-invalid-signature", "") @@ -134,7 +134,7 @@ var _ = Describe("notation trust policy verification level test", func() { It("audit level with expired signature", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - vhost.SetOption(AddTrustPolicyOption("audit_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("audit_trustpolicy.json", false)) artifact := GenerateArtifact("e2e-expired-signature", "") @@ -150,7 +150,7 @@ var _ = Describe("notation trust policy verification level test", func() { artifact := GenerateArtifact("e2e-with-expired-cert", "") vhost.SetOption(AuthOption("", ""), - AddTrustPolicyOption("audit_trustpolicy.json"), + AddTrustPolicyOption("audit_trustpolicy.json", false), AddTrustStoreOption("e2e", filepath.Join(NotationE2EConfigPath, "localkeys", "expired_e2e.crt")), ) @@ -164,7 +164,7 @@ var _ = Describe("notation trust policy verification level test", func() { It("audit level with invalid authenticity", func() { Host(nil, func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { vhost.SetOption(AuthOption("", ""), - AddTrustPolicyOption("audit_trustpolicy.json"), + AddTrustPolicyOption("audit_trustpolicy.json", false), AddTrustStoreOption("e2e", filepath.Join(NotationE2ELocalKeysDir, "new_e2e.crt")), ) @@ -181,7 +181,7 @@ var _ = Describe("notation trust policy verification level test", func() { It("audit level with invalid integrity", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - vhost.SetOption(AddTrustPolicyOption("audit_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("audit_trustpolicy.json", false)) artifact := GenerateArtifact("e2e-invalid-signature", "") @@ -193,7 +193,7 @@ var _ = Describe("notation trust policy verification level test", func() { It("skip level with invalid integrity", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - vhost.SetOption(AddTrustPolicyOption("skip_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("skip_trustpolicy.json", false)) artifact := GenerateArtifact("e2e-invalid-signature", "") @@ -204,7 +204,7 @@ var _ = Describe("notation trust policy verification level test", func() { It("strict level with Expiry overridden as log level", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - vhost.SetOption(AddTrustPolicyOption("override_strict_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("override_strict_trustpolicy.json", false)) artifact := GenerateArtifact("e2e-expired-signature", "") @@ -220,7 +220,7 @@ var _ = Describe("notation trust policy verification level test", func() { artifact := GenerateArtifact("e2e-with-expired-cert", "") vhost.SetOption(AuthOption("", ""), - AddTrustPolicyOption("override_strict_trustpolicy.json"), + AddTrustPolicyOption("override_strict_trustpolicy.json", false), AddTrustStoreOption("e2e", filepath.Join(NotationE2EConfigPath, "localkeys", "expired_e2e.crt")), ) @@ -234,7 +234,7 @@ var _ = Describe("notation trust policy verification level test", func() { It("strict level with Authenticity overridden as log level", func() { Host(nil, func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { vhost.SetOption(AuthOption("", ""), - AddTrustPolicyOption("override_strict_trustpolicy.json"), + AddTrustPolicyOption("override_strict_trustpolicy.json", false), AddTrustStoreOption("e2e", filepath.Join(NotationE2ELocalKeysDir, "new_e2e.crt")), ) @@ -251,7 +251,7 @@ var _ = Describe("notation trust policy verification level test", func() { It("permissive level with Expiry overridden as enforce level", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - vhost.SetOption(AddTrustPolicyOption("override_permissive_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("override_permissive_trustpolicy.json", false)) artifact := GenerateArtifact("e2e-expired-signature", "") @@ -263,12 +263,12 @@ var _ = Describe("notation trust policy verification level test", func() { It("permissive level with Authentic timestamp overridden as enforce level", func() { Host(nil, func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - vhost.SetOption(AddTrustPolicyOption("override_permissive_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("override_permissive_trustpolicy.json", false)) artifact := GenerateArtifact("e2e-with-expired-cert", "") vhost.SetOption(AuthOption("", ""), - AddTrustPolicyOption("trustpolicy.json"), + AddTrustPolicyOption("trustpolicy.json", false), AddTrustStoreOption("e2e", filepath.Join(NotationE2EConfigPath, "localkeys", "expired_e2e.crt")), ) @@ -281,7 +281,7 @@ var _ = Describe("notation trust policy verification level test", func() { It("permissive level with Authenticity overridden as log level", func() { Host(nil, func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { vhost.SetOption(AuthOption("", ""), - AddTrustPolicyOption("override_permissive_trustpolicy.json"), + AddTrustPolicyOption("override_permissive_trustpolicy.json", false), AddTrustStoreOption("e2e", filepath.Join(NotationE2ELocalKeysDir, "new_e2e.crt")), ) @@ -297,7 +297,7 @@ var _ = Describe("notation trust policy verification level test", func() { It("permissive level with Integrity overridden as log level", func() { Host(nil, func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { vhost.SetOption(AuthOption("", ""), - AddTrustPolicyOption("override_integrity_for_permissive_trustpolicy.json"), + AddTrustPolicyOption("override_integrity_for_permissive_trustpolicy.json", false), AddTrustStoreOption("e2e", filepath.Join(NotationE2ELocalKeysDir, "new_e2e.crt")), ) @@ -310,7 +310,7 @@ var _ = Describe("notation trust policy verification level test", func() { It("audit level with Expiry overridden as enforce level", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - vhost.SetOption(AddTrustPolicyOption("override_audit_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("override_audit_trustpolicy.json", false)) artifact := GenerateArtifact("e2e-expired-signature", "") @@ -322,12 +322,12 @@ var _ = Describe("notation trust policy verification level test", func() { It("audit level with Authentic timestamp overridden as enforce level", func() { Host(nil, func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - vhost.SetOption(AddTrustPolicyOption("override_audit_trustpolicy.json")) + vhost.SetOption(AddTrustPolicyOption("override_audit_trustpolicy.json", false)) artifact := GenerateArtifact("e2e-with-expired-cert", "") vhost.SetOption(AuthOption("", ""), - AddTrustPolicyOption("trustpolicy.json"), + AddTrustPolicyOption("trustpolicy.json", false), AddTrustStoreOption("e2e", filepath.Join(NotationE2EConfigPath, "localkeys", "expired_e2e.crt")), ) @@ -340,7 +340,7 @@ var _ = Describe("notation trust policy verification level test", func() { It("audit level with Authenticity overridden as enforce level", func() { Host(nil, func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { vhost.SetOption(AuthOption("", ""), - AddTrustPolicyOption("override_audit_trustpolicy.json"), + AddTrustPolicyOption("override_audit_trustpolicy.json", false), AddTrustStoreOption("e2e", filepath.Join(NotationE2ELocalKeysDir, "new_e2e.crt")), ) diff --git a/test/e2e/testdata/blob/blobFile b/test/e2e/testdata/blob/blobFile.txt similarity index 100% rename from test/e2e/testdata/blob/blobFile rename to test/e2e/testdata/blob/blobFile.txt diff --git a/test/e2e/testdata/blob/trustpolicies/trustpolicy.blob.json b/test/e2e/testdata/blob/trustpolicies/trustpolicy.blob.json new file mode 100644 index 000000000..9b90a704a --- /dev/null +++ b/test/e2e/testdata/blob/trustpolicies/trustpolicy.blob.json @@ -0,0 +1,37 @@ +{ + "version": "1.0", + "trustPolicies": [ + { + "name": "test-blob-statement", + "signatureVerification": { + "level" : "strict" + }, + "trustStores": [ "ca:e2e" ], + "trustedIdentities": [ + "*" + ] + }, + { + "name": "test-blob-global-statement", + "signatureVerification": { + "level" : "strict" + }, + "trustStores": [ "ca:e2e" ], + "trustedIdentities": [ + "*" + ], + "globalPolicy": true + }, + { + "name": "test-blob-with-timestamping", + "signatureVerification": { + "level" : "strict", + "verifyTimestamp": "always" + }, + "trustStores": [ "ca:e2e", "tsa:e2e" ], + "trustedIdentities": [ + "*" + ] + } + ] +}