-
Notifications
You must be signed in to change notification settings - Fork 86
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Patrick Zheng <[email protected]>
- Loading branch information
1 parent
3657482
commit 4808e08
Showing
46 changed files
with
1,209 additions
and
312 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <signature_path> <blob_path> | ||
Example - Verify the signature on a blob artifact with user metadata: | ||
notation blob verify --user-metadata <metadata> --signature <signature_path> <blob_path> | ||
Example - Verify the signature on a blob artifact with media type: | ||
notation blob verify --media-type <media_type> --signature <signature_path> <blob_path> | ||
Example - Verify the signature on a blob artifact using a policy statement name: | ||
notation blob verify --policy-name <policy_name> --signature <signature_path> <blob_path> | ||
` | ||
command := &cobra.Command{ | ||
Use: "verify [flags] --signature <signature_path> <blob_path>", | ||
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)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} |
Oops, something went wrong.