From 89adf15dfb2b29e8e9980803cdec6bf7b30e7827 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Thu, 16 Jan 2025 09:21:02 +0000 Subject: [PATCH 01/32] refactor: add inpect display handler Signed-off-by: Junjie Gao --- cmd/notation/inspect.go | 268 ++---------------- cmd/notation/inspect_test.go | 43 ++- cmd/notation/internal/display/handler.go | 21 ++ .../internal/display/metadata/interface.go | 17 ++ .../internal/display/metadata/json/inspect.go | 66 +++++ .../display/metadata/model/inspect.go | 148 ++++++++++ .../internal/display/metadata/tree/inspect.go | 139 +++++++++ cmd/notation/internal/option/format.go | 66 +++++ cmd/notation/internal/output/json.go | 28 ++ cmd/notation/internal/output/json_test.go | 35 +++ cmd/notation/internal/output/print.go | 77 +++++ cmd/notation/internal/output/print_test.go | 104 +++++++ internal/tree/tree.go | 17 +- internal/tree/tree_test.go | 7 +- 14 files changed, 754 insertions(+), 282 deletions(-) create mode 100644 cmd/notation/internal/display/handler.go create mode 100644 cmd/notation/internal/display/metadata/interface.go create mode 100644 cmd/notation/internal/display/metadata/json/inspect.go create mode 100644 cmd/notation/internal/display/metadata/model/inspect.go create mode 100644 cmd/notation/internal/display/metadata/tree/inspect.go create mode 100644 cmd/notation/internal/option/format.go create mode 100644 cmd/notation/internal/output/json.go create mode 100644 cmd/notation/internal/output/json_test.go create mode 100644 cmd/notation/internal/output/print.go create mode 100644 cmd/notation/internal/output/print_test.go diff --git a/cmd/notation/inspect.go b/cmd/notation/inspect.go index a55595c51..03e6a7a97 100644 --- a/cmd/notation/inspect.go +++ b/cmd/notation/inspect.go @@ -14,26 +14,17 @@ package main import ( - "crypto/sha256" - "crypto/x509" - "encoding/hex" "errors" "fmt" "os" - "strconv" - "strings" - "time" "github.com/notaryproject/notation-core-go/signature" - "github.com/notaryproject/notation-go/plugin/proto" - "github.com/notaryproject/notation-go/registry" + "github.com/notaryproject/notation/cmd/notation/internal/display" cmderr "github.com/notaryproject/notation/cmd/notation/internal/errors" "github.com/notaryproject/notation/cmd/notation/internal/experimental" + "github.com/notaryproject/notation/cmd/notation/internal/option" + "github.com/notaryproject/notation/cmd/notation/internal/output" "github.com/notaryproject/notation/internal/cmd" - "github.com/notaryproject/notation/internal/envelope" - "github.com/notaryproject/notation/internal/ioutil" - "github.com/notaryproject/notation/internal/tree" - "github.com/notaryproject/tspclient-go" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/spf13/cobra" ) @@ -41,41 +32,12 @@ import ( type inspectOpts struct { cmd.LoggingFlagOpts SecureFlagOpts + option.Format reference string - outputFormat string allowReferrersAPI bool maxSignatures int } -type inspectOutput struct { - MediaType string `json:"mediaType"` - Signatures []signatureOutput -} - -type signatureOutput struct { - MediaType string `json:"mediaType"` - Digest string `json:"digest"` - SignatureAlgorithm string `json:"signatureAlgorithm"` - SignedAttributes map[string]string `json:"signedAttributes"` - UserDefinedAttributes map[string]string `json:"userDefinedAttributes"` - UnsignedAttributes map[string]any `json:"unsignedAttributes"` - Certificates []certificateOutput `json:"certificates"` - SignedArtifact ocispec.Descriptor `json:"signedArtifact"` -} - -type certificateOutput struct { - SHA256Fingerprint string `json:"SHA256Fingerprint"` - IssuedTo string `json:"issuedTo"` - IssuedBy string `json:"issuedBy"` - Expiry string `json:"expiry"` -} - -type timestampOutput struct { - Timestamp string `json:"timestamp,omitempty"` - Certificates []certificateOutput `json:"certificates,omitempty"` - Error string `json:"error,omitempty"` -} - func inspectCommand(opts *inspectOpts) *cobra.Command { if opts == nil { opts = &inspectOpts{} @@ -103,6 +65,9 @@ Example - Inspect signatures on an OCI artifact identified by a digest and outpu return nil }, PreRunE: func(cmd *cobra.Command, args []string) error { + if err := opts.Format.Parse(cmd); err != nil { + return err + } return experimental.CheckFlagsAndWarn(cmd, "allow-referrers-api") }, RunE: func(cmd *cobra.Command, args []string) error { @@ -118,18 +83,22 @@ Example - Inspect signatures on an OCI artifact identified by a digest and outpu opts.LoggingFlagOpts.ApplyFlags(command.Flags()) opts.SecureFlagOpts.ApplyFlags(command.Flags()) - cmd.SetPflagOutput(command.Flags(), &opts.outputFormat, cmd.PflagOutputUsage) command.Flags().IntVar(&opts.maxSignatures, "max-signatures", 100, "maximum number of signatures to evaluate or examine") cmd.SetPflagReferrersAPI(command.Flags(), &opts.allowReferrersAPI, fmt.Sprintf(cmd.PflagReferrersUsageFormat, "inspect")) + // set format + opts.SetTypes(option.FormatTypeText, option.FormatTypeJSON) + opts.Format.ApplyFlags(command.Flags()) return command } func runInspect(command *cobra.Command, opts *inspectOpts) error { // set log level ctx := opts.LoggingFlagOpts.InitializeLogger(command.Context()) + printer := output.NewPrinter(command.OutOrStdout(), command.OutOrStderr()) - if opts.outputFormat != cmd.OutputJSON && opts.outputFormat != cmd.OutputPlaintext { - return fmt.Errorf("unrecognized output format %s", opts.outputFormat) + displayHandler, err := display.NewInpsectHandler(printer, opts.Format) + if err != nil { + return err } // initialize @@ -144,7 +113,11 @@ func runInspect(command *cobra.Command, opts *inspectOpts) error { if err != nil { return err } - output := inspectOutput{MediaType: manifestDesc.MediaType, Signatures: []signatureOutput{}} + + // set output headers + displayHandler.SetMediaType(manifestDesc.MediaType) + displayHandler.SetReference(resolvedRef) + skippedSignatures := false err = listSignatures(ctx, sigRepo, manifestDesc, opts.maxSignatures, func(sigManifestDesc ocispec.Descriptor) error { sigBlob, sigDesc, err := sigRepo.FetchSignatureBlob(ctx, sigManifestDesc) @@ -161,44 +134,11 @@ func runInspect(command *cobra.Command, opts *inspectOpts) error { return nil } - envelopeContent, err := sigEnvelope.Content() - if err != nil { - logSkippedSignature(sigManifestDesc, err) - skippedSignatures = true - return nil - } - - signedArtifactDesc, err := envelope.DescriptorFromSignaturePayload(&envelopeContent.Payload) - if err != nil { + if err := displayHandler.AddSignature(sigManifestDesc.Digest.String(), sigDesc.MediaType, sigEnvelope); err != nil { logSkippedSignature(sigManifestDesc, err) skippedSignatures = true return nil } - - signatureAlgorithm, err := proto.EncodeSigningAlgorithm(envelopeContent.SignerInfo.SignatureAlgorithm) - if err != nil { - logSkippedSignature(sigManifestDesc, err) - skippedSignatures = true - return nil - } - - sig := signatureOutput{ - MediaType: sigDesc.MediaType, - Digest: sigManifestDesc.Digest.String(), - SignatureAlgorithm: string(signatureAlgorithm), - SignedAttributes: getSignedAttributes(opts.outputFormat, envelopeContent), - UserDefinedAttributes: signedArtifactDesc.Annotations, - UnsignedAttributes: getUnsignedAttributes(opts.outputFormat, envelopeContent), - Certificates: getCertificates(opts.outputFormat, envelopeContent.SignerInfo.CertificateChain), - SignedArtifact: *signedArtifactDesc, - } - - // clearing annotations from the SignedArtifact field since they're already - // displayed as UserDefinedAttributes - sig.SignedArtifact.Annotations = nil - - output.Signatures = append(output.Signatures, sig) - return nil }) var errorExceedMaxSignatures cmderr.ErrorExceedMaxSignatures @@ -206,7 +146,7 @@ func runInspect(command *cobra.Command, opts *inspectOpts) error { return err } - if err := printOutput(opts.outputFormat, resolvedRef, output); err != nil { + if err := displayHandler.Print(); err != nil { return err } @@ -224,169 +164,3 @@ func runInspect(command *cobra.Command, opts *inspectOpts) error { func logSkippedSignature(sigDesc ocispec.Descriptor, err error) { fmt.Fprintf(os.Stderr, "Warning: Skipping signature %s because of error: %v\n", sigDesc.Digest.String(), err) } - -func getSignedAttributes(outputFormat string, envContent *signature.EnvelopeContent) map[string]string { - signedAttributes := map[string]string{ - "signingScheme": string(envContent.SignerInfo.SignedAttributes.SigningScheme), - "signingTime": formatTimestamp(outputFormat, envContent.SignerInfo.SignedAttributes.SigningTime), - } - expiry := envContent.SignerInfo.SignedAttributes.Expiry - if !expiry.IsZero() { - signedAttributes["expiry"] = formatTimestamp(outputFormat, expiry) - } - - for _, attribute := range envContent.SignerInfo.SignedAttributes.ExtendedAttributes { - signedAttributes[fmt.Sprint(attribute.Key)] = fmt.Sprint(attribute.Value) - } - - return signedAttributes -} - -func getUnsignedAttributes(outputFormat string, envContent *signature.EnvelopeContent) map[string]any { - unsignedAttributes := make(map[string]any) - - if envContent.SignerInfo.UnsignedAttributes.TimestampSignature != nil { - unsignedAttributes["timestampSignature"] = parseTimestamp(outputFormat, envContent.SignerInfo) - } - - if envContent.SignerInfo.UnsignedAttributes.SigningAgent != "" { - unsignedAttributes["signingAgent"] = envContent.SignerInfo.UnsignedAttributes.SigningAgent - } - - return unsignedAttributes -} - -func formatTimestamp(outputFormat string, t time.Time) string { - switch outputFormat { - case cmd.OutputJSON: - return t.Format(time.RFC3339) - default: - return t.Format(time.ANSIC) - } -} - -func getCertificates(outputFormat string, certChain []*x509.Certificate) []certificateOutput { - certificates := []certificateOutput{} - - for _, cert := range certChain { - h := sha256.Sum256(cert.Raw) - fingerprint := strings.ToLower(hex.EncodeToString(h[:])) - - certificate := certificateOutput{ - SHA256Fingerprint: fingerprint, - IssuedTo: cert.Subject.String(), - IssuedBy: cert.Issuer.String(), - Expiry: formatTimestamp(outputFormat, cert.NotAfter), - } - - certificates = append(certificates, certificate) - } - - return certificates -} - -func printOutput(outputFormat string, ref string, output inspectOutput) error { - if outputFormat == cmd.OutputJSON { - return ioutil.PrintObjectAsJSON(output) - } - - if len(output.Signatures) == 0 { - fmt.Printf("%s has no associated signature\n", ref) - return nil - } - - fmt.Println("Inspecting all signatures for signed artifact") - root := tree.New(ref) - cncfSigNode := root.Add(registry.ArtifactTypeNotation) - - for _, signature := range output.Signatures { - sigNode := cncfSigNode.Add(signature.Digest) - sigNode.AddPair("media type", signature.MediaType) - sigNode.AddPair("signature algorithm", signature.SignatureAlgorithm) - - signedAttributesNode := sigNode.Add("signed attributes") - addMapToTree(signedAttributesNode, signature.SignedAttributes) - - userDefinedAttributesNode := sigNode.Add("user defined attributes") - addMapToTree(userDefinedAttributesNode, signature.UserDefinedAttributes) - - unsignedAttributesNode := sigNode.Add("unsigned attributes") - for k, v := range signature.UnsignedAttributes { - switch value := v.(type) { - case string: - unsignedAttributesNode.AddPair(k, value) - case timestampOutput: - timestampNode := unsignedAttributesNode.Add("timestamp signature") - if value.Error != "" { - timestampNode.AddPair("error", value.Error) - break - } - timestampNode.AddPair("timestamp", value.Timestamp) - addCertificatesToTree(timestampNode, "certificates", value.Certificates) - } - } - - addCertificatesToTree(sigNode, "certificates", signature.Certificates) - - artifactNode := sigNode.Add("signed artifact") - artifactNode.AddPair("media type", signature.SignedArtifact.MediaType) - artifactNode.AddPair("digest", signature.SignedArtifact.Digest.String()) - artifactNode.AddPair("size", strconv.FormatInt(signature.SignedArtifact.Size, 10)) - } - - root.Print() - return nil -} - -func addMapToTree(node *tree.Node, m map[string]string) { - if len(m) > 0 { - for k, v := range m { - node.AddPair(k, v) - } - } else { - node.Add("(empty)") - } -} - -func addCertificatesToTree(node *tree.Node, name string, certs []certificateOutput) { - certListNode := node.Add(name) - for _, cert := range certs { - certNode := certListNode.AddPair("SHA256 fingerprint", cert.SHA256Fingerprint) - certNode.AddPair("issued to", cert.IssuedTo) - certNode.AddPair("issued by", cert.IssuedBy) - certNode.AddPair("expiry", cert.Expiry) - } -} - -func parseTimestamp(outputFormat string, signerInfo signature.SignerInfo) timestampOutput { - signedToken, err := tspclient.ParseSignedToken(signerInfo.UnsignedAttributes.TimestampSignature) - if err != nil { - return timestampOutput{ - Error: fmt.Sprintf("failed to parse timestamp countersignature: %s", err.Error()), - } - } - info, err := signedToken.Info() - if err != nil { - return timestampOutput{ - Error: fmt.Sprintf("failed to parse timestamp countersignature: %s", err.Error()), - } - } - timestamp, err := info.Validate(signerInfo.Signature) - if err != nil { - return timestampOutput{ - Error: fmt.Sprintf("failed to parse timestamp countersignature: %s", err.Error()), - } - } - certificates := getCertificates(outputFormat, signedToken.Certificates) - var formatTimestamp string - switch outputFormat { - case cmd.OutputJSON: - formatTimestamp = timestamp.Format(time.RFC3339) - default: - formatTimestamp = timestamp.Format(time.ANSIC) - } - return timestampOutput{ - Timestamp: formatTimestamp, - Certificates: certificates, - } -} diff --git a/cmd/notation/inspect_test.go b/cmd/notation/inspect_test.go index a6c20f596..72584ed2e 100644 --- a/cmd/notation/inspect_test.go +++ b/cmd/notation/inspect_test.go @@ -14,15 +14,19 @@ package main import ( + "reflect" "testing" - "github.com/notaryproject/notation-core-go/signature" - "github.com/notaryproject/notation/internal/cmd" + "github.com/notaryproject/notation/cmd/notation/internal/option" ) func TestInspectCommand_SecretsFromArgs(t *testing.T) { opts := &inspectOpts{} command := inspectCommand(opts) + format := option.Format{ + FormatFlag: "text", + } + format.SetTypes(option.FormatTypeText, option.FormatTypeJSON) expected := &inspectOpts{ reference: "ref", SecureFlagOpts: SecureFlagOpts{ @@ -30,7 +34,7 @@ func TestInspectCommand_SecretsFromArgs(t *testing.T) { InsecureRegistry: true, Username: "user", }, - outputFormat: cmd.OutputPlaintext, + Format: format, maxSignatures: 100, } if err := command.ParseFlags([]string{ @@ -44,8 +48,8 @@ func TestInspectCommand_SecretsFromArgs(t *testing.T) { if err := command.Args(command, command.Flags().Args()); err != nil { t.Fatalf("Parse Args failed: %v", err) } - if *opts != *expected { - t.Fatalf("Expect inspect opts: %v, got: %v", expected, opts) + if !reflect.DeepEqual(opts, expected) { + t.Fatalf("Expect opts: %v, got: %v", expected, opts) } } @@ -53,13 +57,17 @@ func TestInspectCommand_SecretsFromEnv(t *testing.T) { t.Setenv(defaultUsernameEnv, "user") t.Setenv(defaultPasswordEnv, "password") opts := &inspectOpts{} + + format := option.Format{} + format.SetTypes(option.FormatTypeText, option.FormatTypeJSON) + format.FormatFlag = "json" expected := &inspectOpts{ reference: "ref", SecureFlagOpts: SecureFlagOpts{ Password: "password", Username: "user", }, - outputFormat: cmd.OutputJSON, + Format: format, maxSignatures: 100, } command := inspectCommand(opts) @@ -71,8 +79,8 @@ func TestInspectCommand_SecretsFromEnv(t *testing.T) { if err := command.Args(command, command.Flags().Args()); err != nil { t.Fatalf("Parse Args failed: %v", err) } - if *opts != *expected { - t.Fatalf("Expect inspect opts: %v, got: %v", expected, opts) + if !reflect.DeepEqual(opts, expected) { + t.Fatalf("Expect opts: %v, got: %v", expected, opts) } } @@ -85,22 +93,3 @@ func TestInspectCommand_MissingArgs(t *testing.T) { t.Fatal("Parse Args expected error, but ok") } } - -func TestGetUnsignedAttributes(t *testing.T) { - envContent := &signature.EnvelopeContent{ - SignerInfo: signature.SignerInfo{ - UnsignedAttributes: signature.UnsignedAttributes{ - TimestampSignature: []byte("invalid"), - }, - }, - } - expectedErrMsg := "failed to parse timestamp countersignature: cms: syntax error: invalid signed data: failed to convert from BER to DER: asn1: syntax error: decoding BER length octets: short form length octets value should be less or equal to the subsequent octets length" - unsignedAttr := getUnsignedAttributes(cmd.OutputPlaintext, envContent) - val, ok := unsignedAttr["timestampSignature"].(timestampOutput) - if !ok { - t.Fatal("expected to have timestampSignature") - } - if val.Error != expectedErrMsg { - t.Fatalf("expected %s, but got %s", expectedErrMsg, val.Error) - } -} diff --git a/cmd/notation/internal/display/handler.go b/cmd/notation/internal/display/handler.go new file mode 100644 index 000000000..31af48c31 --- /dev/null +++ b/cmd/notation/internal/display/handler.go @@ -0,0 +1,21 @@ +package display + +import ( + "fmt" + + "github.com/notaryproject/notation/cmd/notation/internal/display/metadata" + "github.com/notaryproject/notation/cmd/notation/internal/display/metadata/json" + "github.com/notaryproject/notation/cmd/notation/internal/display/metadata/tree" + "github.com/notaryproject/notation/cmd/notation/internal/option" + "github.com/notaryproject/notation/cmd/notation/internal/output" +) + +func NewInpsectHandler(printer *output.Printer, format option.Format) (metadata.InspectHandler, error) { + switch option.FormatType(format.FormatFlag) { + case option.FormatTypeJSON: + return json.NewInspectHandler(printer), nil + case option.FormatTypeText: + return tree.NewInspectHandler(printer), nil + } + return nil, fmt.Errorf("unrecognized output format %s", format.FormatFlag) +} diff --git a/cmd/notation/internal/display/metadata/interface.go b/cmd/notation/internal/display/metadata/interface.go new file mode 100644 index 000000000..ce0b970e0 --- /dev/null +++ b/cmd/notation/internal/display/metadata/interface.go @@ -0,0 +1,17 @@ +package metadata + +import "github.com/notaryproject/notation-core-go/signature" + +type InspectHandler interface { + // SetReference sets the artifact reference for the handler. + SetReference(reference string) + + // SetMediaType sets the media type for the handler. + SetMediaType(mediaType string) + + // AddSignature adds a signature to the handler. + AddSignature(digest string, envelopeMediaType string, sigEnvelope signature.Envelope) error + + // Print prints the metadata. + Print() error +} diff --git a/cmd/notation/internal/display/metadata/json/inspect.go b/cmd/notation/internal/display/metadata/json/inspect.go new file mode 100644 index 000000000..ea82bbd92 --- /dev/null +++ b/cmd/notation/internal/display/metadata/json/inspect.go @@ -0,0 +1,66 @@ +package json + +import ( + "fmt" + "time" + + "github.com/notaryproject/notation-core-go/signature" + "github.com/notaryproject/notation/cmd/notation/internal/display/metadata/model" + "github.com/notaryproject/notation/cmd/notation/internal/output" + "github.com/notaryproject/tspclient-go" +) + +type inspectOutput struct { + MediaType string `json:"mediaType"` + Signatures []model.Signature +} + +type InspectHandler struct { + output inspectOutput + + // printer is the printer for output. + printer *output.Printer +} + +// NewInspectHandler creates a new InspectHandler. +func NewInspectHandler(printer *output.Printer) *InspectHandler { + return &InspectHandler{ + printer: printer, + } +} + +// SetReference sets the artifact reference for the handler. It is a no-op for this +// handler. +func (h *InspectHandler) SetReference(_ string) {} + +// SetMediaType sets the media type for the handler. +func (h *InspectHandler) SetMediaType(mediaType string) { + if h.output.MediaType == "" { + h.output.MediaType = mediaType + } +} + +// AddSignature adds a signature to the handler. +func (h *InspectHandler) AddSignature(digest string, envelopeMediaType string, sigEnvelope signature.Envelope) error { + sig, err := model.NewSignature(digest, envelopeMediaType, sigEnvelope, formatter) + if err != nil { + return err + } + h.output.Signatures = append(h.output.Signatures, *sig) + return nil +} + +func (h *InspectHandler) Print() error { + return output.PrintPrettyJSON(h.printer, h.output) +} + +// formatter is the function for formatting the time.Time and tspclient.Timestamp. +func formatter(v any) string { + switch v := v.(type) { + case time.Time: + return v.Format(time.RFC3339) + case tspclient.Timestamp: + return v.Format(time.RFC3339) + } + return fmt.Sprintf("%v", v) +} diff --git a/cmd/notation/internal/display/metadata/model/inspect.go b/cmd/notation/internal/display/metadata/model/inspect.go new file mode 100644 index 000000000..4bf7e02fa --- /dev/null +++ b/cmd/notation/internal/display/metadata/model/inspect.go @@ -0,0 +1,148 @@ +package model + +import ( + "crypto/sha256" + "crypto/x509" + "encoding/hex" + "fmt" + "strings" + + "github.com/notaryproject/notation-core-go/signature" + "github.com/notaryproject/notation-go/plugin/proto" + "github.com/notaryproject/notation/internal/envelope" + "github.com/notaryproject/tspclient-go" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" +) + +// Signature is the signature envelope for printing in human readable format. +type Signature struct { + MediaType string `json:"mediaType"` + Digest string `json:"digest,omitempty"` + SignatureAlgorithm string `json:"signatureAlgorithm"` + SignedAttributes map[string]string `json:"signedAttributes"` + UserDefinedAttributes map[string]string `json:"userDefinedAttributes"` + UnsignedAttributes map[string]any `json:"unsignedAttributes"` + Certificates []Certificate `json:"certificates"` + SignedArtifact ocispec.Descriptor `json:"signedArtifact"` +} + +// Certificate is the certificate information for printing in human readable +// format. +type Certificate struct { + SHA256Fingerprint string `json:"SHA256Fingerprint"` + IssuedTo string `json:"issuedTo"` + IssuedBy string `json:"issuedBy"` + Expiry string `json:"expiry"` +} + +// Timestamp is the timestamp information for printing in human readable. +type Timestamp struct { + Timestamp string `json:"timestamp,omitempty"` + Certificates []Certificate `json:"certificates,omitempty"` + Error string `json:"error,omitempty"` +} + +// formatter is the function to format the value to string. +type formatter func(any) string + +// NewSignature parses the signature blob and returns a Signature object. +func NewSignature(digest string, envelopeMediaType string, sigEnvelope signature.Envelope, formatter formatter) (*Signature, error) { + envelopeContent, err := sigEnvelope.Content() + if err != nil { + return nil, err + } + + signedArtifactDesc, err := envelope.DescriptorFromSignaturePayload(&envelopeContent.Payload) + if err != nil { + return nil, err + } + + signatureAlgorithm, err := proto.EncodeSigningAlgorithm(envelopeContent.SignerInfo.SignatureAlgorithm) + if err != nil { + return nil, err + } + sig := &Signature{ + MediaType: envelopeMediaType, + Digest: digest, + SignatureAlgorithm: string(signatureAlgorithm), + SignedAttributes: getSignedAttributes(envelopeContent, formatter), + UserDefinedAttributes: signedArtifactDesc.Annotations, + UnsignedAttributes: getUnsignedAttributes(envelopeContent, formatter), + Certificates: getCertificates(envelopeContent.SignerInfo.CertificateChain, formatter), + SignedArtifact: *signedArtifactDesc, + } + + // clearing annotations from the SignedArtifact field since they're already + // displayed as UserDefinedAttributes + sig.SignedArtifact.Annotations = nil + + return sig, nil +} + +func getSignedAttributes(envContent *signature.EnvelopeContent, formatter formatter) map[string]string { + signedAttributes := map[string]string{ + "signingScheme": string(envContent.SignerInfo.SignedAttributes.SigningScheme), + "signingTime": formatter(envContent.SignerInfo.SignedAttributes.SigningTime), + } + if expiry := envContent.SignerInfo.SignedAttributes.Expiry; !expiry.IsZero() { + signedAttributes["expiry"] = formatter(expiry) + } + + for _, attribute := range envContent.SignerInfo.SignedAttributes.ExtendedAttributes { + signedAttributes[fmt.Sprint(attribute.Key)] = fmt.Sprint(attribute.Value) + } + return signedAttributes +} + +func getUnsignedAttributes(envContent *signature.EnvelopeContent, formatter formatter) map[string]any { + unsignedAttributes := make(map[string]any) + + if envContent.SignerInfo.UnsignedAttributes.TimestampSignature != nil { + unsignedAttributes["timestampSignature"] = parseTimestamp(envContent.SignerInfo, formatter) + } + + if envContent.SignerInfo.UnsignedAttributes.SigningAgent != "" { + unsignedAttributes["signingAgent"] = envContent.SignerInfo.UnsignedAttributes.SigningAgent + } + return unsignedAttributes +} + +func getCertificates(certChain []*x509.Certificate, formatter formatter) []Certificate { + certificates := []Certificate{} + + for _, cert := range certChain { + hash := sha256.Sum256(cert.Raw) + certificates = append(certificates, Certificate{ + SHA256Fingerprint: strings.ToLower(hex.EncodeToString(hash[:])), + IssuedTo: cert.Subject.String(), + IssuedBy: cert.Issuer.String(), + Expiry: formatter(cert.NotAfter), + }) + } + return certificates +} + +func parseTimestamp(signerInfo signature.SignerInfo, formatter formatter) Timestamp { + signedToken, err := tspclient.ParseSignedToken(signerInfo.UnsignedAttributes.TimestampSignature) + if err != nil { + return Timestamp{ + Error: fmt.Sprintf("failed to parse timestamp countersignature: %s", err), + } + } + info, err := signedToken.Info() + if err != nil { + return Timestamp{ + Error: fmt.Sprintf("failed to parse timestamp countersignature: %s", err), + } + } + timestamp, err := info.Validate(signerInfo.Signature) + if err != nil { + return Timestamp{ + Error: fmt.Sprintf("failed to parse timestamp countersignature: %s", err), + } + } + return Timestamp{ + Timestamp: formatter(*timestamp), + Certificates: getCertificates(signedToken.Certificates, formatter), + } +} diff --git a/cmd/notation/internal/display/metadata/tree/inspect.go b/cmd/notation/internal/display/metadata/tree/inspect.go new file mode 100644 index 000000000..c6ba4ea2d --- /dev/null +++ b/cmd/notation/internal/display/metadata/tree/inspect.go @@ -0,0 +1,139 @@ +package tree + +import ( + "fmt" + "slices" + "strconv" + "time" + + "github.com/notaryproject/notation-core-go/signature" + "github.com/notaryproject/notation-go/registry" + "github.com/notaryproject/notation/cmd/notation/internal/display/metadata/model" + "github.com/notaryproject/notation/cmd/notation/internal/output" + "github.com/notaryproject/notation/internal/tree" + "github.com/notaryproject/tspclient-go" +) + +type InspectHandler struct { + root *tree.Node + cncfSigNode *tree.Node + printer *output.Printer +} + +func NewInspectHandler(printer *output.Printer) *InspectHandler { + return &InspectHandler{ + printer: printer, + } +} + +// SetReference sets the artifact reference for the handler. +func (h *InspectHandler) SetReference(reference string) { + if h.root == nil { + h.root = tree.New(reference) + h.cncfSigNode = h.root.Add(registry.ArtifactTypeNotation) + } +} + +// SetMediaType sets the media type for the handler. It is a no-op for this +// handler. +func (h *InspectHandler) SetMediaType(_ string) {} + +func (h *InspectHandler) AddSignature(digest string, envelopeMediaType string, sigEnvelope signature.Envelope) error { + sig, err := model.NewSignature(digest, envelopeMediaType, sigEnvelope, formatter) + if err != nil { + return err + } + + if h.root == nil || h.cncfSigNode == nil { + return fmt.Errorf("artifact reference is not set") + } + + sigNode := toTreeNode(sig) + h.cncfSigNode.Children = append(h.cncfSigNode.Children, sigNode) + return nil +} + +func (h *InspectHandler) Print() error { + if h.root == nil { + return fmt.Errorf("artifact reference is not set") + } + return h.root.Print(h.printer) +} + +func toTreeNode(s *model.Signature) *tree.Node { + sigNode := tree.New(s.Digest) + sigNode.AddPair("signature algorithm", s.SignatureAlgorithm) + sigNode.AddPair("signature envelope type", s.MediaType) + + signedAttributesNode := sigNode.Add("signed attributes") + addMapToTree(signedAttributesNode, s.SignedAttributes) + + userDefinedAttributesNode := sigNode.Add("user defined attributes") + addMapToTree(userDefinedAttributesNode, s.UserDefinedAttributes) + + unsignedAttributesNode := sigNode.Add("unsigned attributes") + for _, k := range orderedKeys(s.UnsignedAttributes) { + v := s.UnsignedAttributes[k] + switch value := v.(type) { + case string: + unsignedAttributesNode.AddPair(k, value) + case model.Timestamp: + timestampNode := unsignedAttributesNode.Add("timestamp signature") + if value.Error != "" { + timestampNode.AddPair("error", value.Error) + continue + } + timestampNode.AddPair("timestamp", value.Timestamp) + addCertificatesToTree(timestampNode, "certificates", value.Certificates) + } + } + + addCertificatesToTree(sigNode, "certificates", s.Certificates) + + artifactNode := sigNode.Add("signed artifact") + artifactNode.AddPair("media type", s.SignedArtifact.MediaType) + artifactNode.AddPair("digest", s.SignedArtifact.Digest.String()) + artifactNode.AddPair("size", strconv.FormatInt(s.SignedArtifact.Size, 10)) + return sigNode +} + +func addMapToTree(node *tree.Node, m map[string]string) { + if len(m) == 0 { + node.Add("(empty)") + return + } + + // Add each entry in sorted order + for _, k := range orderedKeys(m) { + node.AddPair(k, m[k]) + } +} + +func orderedKeys[T any](m map[string]T) []string { + keys := make([]string, 0, len(m)) + for k := range m { + keys = append(keys, k) + } + slices.Sort(keys) + return keys +} + +func addCertificatesToTree(node *tree.Node, name string, certs []model.Certificate) { + certListNode := node.Add(name) + for _, cert := range certs { + certNode := certListNode.AddPair("SHA256 fingerprint", cert.SHA256Fingerprint) + certNode.AddPair("issued to", cert.IssuedTo) + certNode.AddPair("issued by", cert.IssuedBy) + certNode.AddPair("expiry", cert.Expiry) + } +} + +func formatter(v any) string { + switch v := v.(type) { + case time.Time: + return v.Format(time.ANSIC) + case tspclient.Timestamp: + return v.Format(time.ANSIC) + } + return fmt.Sprint(v) +} diff --git a/cmd/notation/internal/option/format.go b/cmd/notation/internal/option/format.go new file mode 100644 index 000000000..5c2a8f280 --- /dev/null +++ b/cmd/notation/internal/option/format.go @@ -0,0 +1,66 @@ +/* +Copyright The ORAS 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 option + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +type FormatType string + +// format types +var ( + FormatTypeJSON FormatType = "json" + FormatTypeText FormatType = "text" +) + +// Format contains input and parsed options for formatted output flags. +type Format struct { + FormatFlag string + allowedTypes []FormatType +} + +// SetTypes sets the default format type and allowed format types. +func (f *Format) SetTypes(defaultType FormatType, otherTypes ...FormatType) { + f.FormatFlag = string(defaultType) + f.allowedTypes = append(otherTypes, defaultType) +} + +// ApplyFlags implements FlagProvider.ApplyFlag. +func (f *Format) ApplyFlags(fs *pflag.FlagSet) { + var quotedAllowedTypes []string + for _, t := range f.allowedTypes { + quotedAllowedTypes = append(quotedAllowedTypes, fmt.Sprintf("'%s'", t)) + } + usage := fmt.Sprintf("output format, options: %s", strings.Join(quotedAllowedTypes, ", ")) + // apply flags + fs.StringVar(&f.FormatFlag, "output", f.FormatFlag, usage) +} + +// Parse parses the input format flag. +func (opts *Format) Parse(_ *cobra.Command) error { + for _, t := range opts.allowedTypes { + if opts.FormatFlag == string(t) { + // type validation passed + return nil + } + } + return fmt.Errorf("invalid format type: %q", opts.FormatFlag) +} diff --git a/cmd/notation/internal/output/json.go b/cmd/notation/internal/output/json.go new file mode 100644 index 000000000..184e1f7bd --- /dev/null +++ b/cmd/notation/internal/output/json.go @@ -0,0 +1,28 @@ +/* +Copyright The ORAS 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 output + +import ( + "encoding/json" + "io" +) + +// PrintPrettyJSON prints the object to the writer in JSON format. +func PrintPrettyJSON(out io.Writer, object any) error { + encoder := json.NewEncoder(out) + encoder.SetIndent("", " ") + return encoder.Encode(object) +} diff --git a/cmd/notation/internal/output/json_test.go b/cmd/notation/internal/output/json_test.go new file mode 100644 index 000000000..50b6d0c93 --- /dev/null +++ b/cmd/notation/internal/output/json_test.go @@ -0,0 +1,35 @@ +/* +Copyright The ORAS 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 output + +import ( + "strings" + "testing" +) + +func Test_PrintPrettyJSON(t *testing.T) { + builder := &strings.Builder{} + given := map[string]int{"bob": 5} + expected := "{\n \"bob\": 5\n}\n" + err := PrintPrettyJSON(builder, given) + if err != nil { + t.Error("Expected no error got <" + err.Error() + ">") + } + actual := builder.String() + if expected != actual { + t.Error("Expected <" + expected + "> not equal to actual <" + actual + ">") + } +} diff --git a/cmd/notation/internal/output/print.go b/cmd/notation/internal/output/print.go new file mode 100644 index 000000000..287f0831f --- /dev/null +++ b/cmd/notation/internal/output/print.go @@ -0,0 +1,77 @@ +/* +Copyright The ORAS 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 output + +import ( + "fmt" + "io" + "sync" +) + +// Printer prints for status handlers. +type Printer struct { + Verbose bool + + out io.Writer + err io.Writer + lock sync.Mutex +} + +// NewPrinter creates a new Printer. +func NewPrinter(out io.Writer, err io.Writer) *Printer { + return &Printer{out: out, err: err} +} + +// Write implements the io.Writer interface. +func (p *Printer) Write(b []byte) (int, error) { + p.lock.Lock() + defer p.lock.Unlock() + return p.out.Write(b) +} + +// Println prints objects concurrent-safely with newline. +func (p *Printer) Println(a ...any) error { + p.lock.Lock() + defer p.lock.Unlock() + _, err := fmt.Fprintln(p.out, a...) + if err != nil { + err = fmt.Errorf("display output error: %w", err) + _, _ = fmt.Fprint(p.err, err) + return err + } + return nil +} + +// Printf prints objects concurrent-safely with newline. +func (p *Printer) Printf(format string, a ...any) error { + p.lock.Lock() + defer p.lock.Unlock() + _, err := fmt.Fprintf(p.out, format, a...) + if err != nil { + err = fmt.Errorf("display output error: %w", err) + _, _ = fmt.Fprint(p.err, err) + } + // Errors are handled above, so return nil + return nil +} + +// PrintVerbose prints when verbose is true. +func (p *Printer) PrintVerbose(a ...any) error { + if !p.Verbose { + return nil + } + return p.Println(a...) +} diff --git a/cmd/notation/internal/output/print_test.go b/cmd/notation/internal/output/print_test.go new file mode 100644 index 000000000..38466f20e --- /dev/null +++ b/cmd/notation/internal/output/print_test.go @@ -0,0 +1,104 @@ +/* +Copyright The ORAS 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 output + +import ( + "fmt" + "os" + "strconv" + "strings" + "testing" +) + +type mockWriter struct { + errorCount int + written string +} + +func (mw *mockWriter) Write(p []byte) (n int, err error) { + mw.written += string(p) + if strings.TrimSpace(string(p)) != "boom" { + return len(string(p)), nil + } + mw.errorCount++ + return 0, fmt.Errorf("boom %s", string(p)) +} + +func (mw *mockWriter) String() string { + return mw.written +} + +func TestPrinter_Println(t *testing.T) { + mockWriter := &mockWriter{} + printer := NewPrinter(mockWriter, os.Stderr) + err := printer.Println("boom") + if mockWriter.errorCount != 1 { + t.Error("Expected one error actual <" + strconv.Itoa(mockWriter.errorCount) + ">") + } + if err == nil { + t.Error("Expected error got ") + } + err = printer.Printf("boom") + if mockWriter.errorCount != 2 { + t.Error("Expected two errors actual <" + strconv.Itoa(mockWriter.errorCount) + ">") + } + if err != nil { + t.Error("Expected error to be ignored") + } +} + +func TestPrinter_PrintVerbose_noError(t *testing.T) { + builder := &strings.Builder{} + printer := NewPrinter(builder, os.Stderr) + + expected := "normal\nthing one\n" + err := printer.Println("normal") + if err != nil { + t.Error("Expected no error got <" + err.Error() + ">") + } + err = printer.Printf("thing %s\n", "one") + if err != nil { + t.Error("Expected no error got <" + err.Error() + ">") + } + err = printer.PrintVerbose("verbose") + if err != nil { + t.Error("Expected no error got <" + err.Error() + ">") + } + actual := builder.String() + if expected != actual { + t.Error("Expected <" + expected + "> not equal to actual <" + actual + ">") + } +} + +func TestPrinter_PrintVerbose(t *testing.T) { + builder := &strings.Builder{} + printer := NewPrinter(builder, os.Stderr) + printer.Verbose = true + + expected := "normal\nverbose\n" + err := printer.Println("normal") + if err != nil { + t.Error("Expected no error got <" + err.Error() + ">") + } + err = printer.PrintVerbose("verbose") + if err != nil { + t.Error("Expected no error got <" + err.Error() + ">") + } + actual := builder.String() + if expected != actual { + t.Error("Expected <" + expected + "> not equal to actual <" + actual + ">") + } +} diff --git a/internal/tree/tree.go b/internal/tree/tree.go index 616cfee1d..6ee3080ba 100644 --- a/internal/tree/tree.go +++ b/internal/tree/tree.go @@ -15,6 +15,7 @@ package tree import ( "fmt" + "io" ) const ( @@ -48,12 +49,14 @@ func (parent *Node) AddPair(key string, value string) *Node { } // prints the tree represented by the root node -func (root *Node) Print() { - print("", "", "", root) +func (root *Node) Print(w io.Writer) error { + return print(w, "", "", "", root) } -func print(prefix string, itemMarker string, nextPrefix string, n *Node) { - fmt.Println(prefix + itemMarker + n.Value) +func print(w io.Writer, prefix string, itemMarker string, nextPrefix string, n *Node) error { + if _, err := fmt.Fprintln(w, prefix+itemMarker+n.Value); err != nil { + return err + } nextItemPrefix := treeItemPrefix nextSubTreePrefix := subTreePrefix @@ -64,7 +67,11 @@ func print(prefix string, itemMarker string, nextPrefix string, n *Node) { nextItemPrefix = treeItemPrefixLast nextSubTreePrefix = subTreePrefixLast } - print(nextPrefix, nextItemPrefix, nextPrefix+nextSubTreePrefix, child) + if err := print(w, nextPrefix, nextItemPrefix, nextPrefix+nextSubTreePrefix, child); err != nil { + return err + } } } + + return nil } diff --git a/internal/tree/tree_test.go b/internal/tree/tree_test.go index 1408e19fc..99067acf3 100644 --- a/internal/tree/tree_test.go +++ b/internal/tree/tree_test.go @@ -14,6 +14,7 @@ package tree import ( + "os" "reflect" "testing" ) @@ -49,7 +50,7 @@ func TestNodeAddPair(t *testing.T) { func ExampleRootPrint() { root := New("root") - root.Print() + root.Print(os.Stdout) // Output: // root @@ -59,7 +60,7 @@ func ExampleSingleLayerPrint() { root := New("root") root.Add("child1") root.Add("child2") - root.Print() + root.Print(os.Stdout) // Output: // root @@ -74,7 +75,7 @@ func ExampleMultiLayerPrint() { child2 := root.Add("child2") child2.Add("child2.1") child2.Add("child2.2") - root.Print() + root.Print(os.Stdout) // Output: // root From f0756a8405461e54202566e533bbb87a42c16603 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Thu, 16 Jan 2025 10:21:57 +0000 Subject: [PATCH 02/32] fix: update code Signed-off-by: Junjie Gao --- .../internal/display/metadata/tree/inspect.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/cmd/notation/internal/display/metadata/tree/inspect.go b/cmd/notation/internal/display/metadata/tree/inspect.go index c6ba4ea2d..358ed7453 100644 --- a/cmd/notation/internal/display/metadata/tree/inspect.go +++ b/cmd/notation/internal/display/metadata/tree/inspect.go @@ -39,17 +39,16 @@ func (h *InspectHandler) SetReference(reference string) { func (h *InspectHandler) SetMediaType(_ string) {} func (h *InspectHandler) AddSignature(digest string, envelopeMediaType string, sigEnvelope signature.Envelope) error { + if h.root == nil || h.cncfSigNode == nil { + return fmt.Errorf("artifact reference is not set") + } + sig, err := model.NewSignature(digest, envelopeMediaType, sigEnvelope, formatter) if err != nil { return err } - if h.root == nil || h.cncfSigNode == nil { - return fmt.Errorf("artifact reference is not set") - } - - sigNode := toTreeNode(sig) - h.cncfSigNode.Children = append(h.cncfSigNode.Children, sigNode) + h.cncfSigNode.Children = append(h.cncfSigNode.Children, toTreeNode(sig)) return nil } From 578dea2cb0fe51282a2701d68dab83908fe4d0cc Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Fri, 17 Jan 2025 06:18:10 +0000 Subject: [PATCH 03/32] test: add unit test Signed-off-by: Junjie Gao --- cmd/notation/inspect.go | 3 +- .../model/{inspect.go => signature.go} | 0 .../display/metadata/model/signature_test.go | 105 ++++++++++++++++++ .../TimeStampTokenWithInvalidSignature.p7s | Bin 0 -> 6595 bytes 4 files changed, 107 insertions(+), 1 deletion(-) rename cmd/notation/internal/display/metadata/model/{inspect.go => signature.go} (100%) create mode 100644 cmd/notation/internal/display/metadata/model/signature_test.go create mode 100644 cmd/notation/internal/display/metadata/model/testdata/TimeStampTokenWithInvalidSignature.p7s diff --git a/cmd/notation/inspect.go b/cmd/notation/inspect.go index 03e6a7a97..15fcfa7d4 100644 --- a/cmd/notation/inspect.go +++ b/cmd/notation/inspect.go @@ -85,7 +85,8 @@ Example - Inspect signatures on an OCI artifact identified by a digest and outpu opts.SecureFlagOpts.ApplyFlags(command.Flags()) command.Flags().IntVar(&opts.maxSignatures, "max-signatures", 100, "maximum number of signatures to evaluate or examine") cmd.SetPflagReferrersAPI(command.Flags(), &opts.allowReferrersAPI, fmt.Sprintf(cmd.PflagReferrersUsageFormat, "inspect")) - // set format + + // set output format opts.SetTypes(option.FormatTypeText, option.FormatTypeJSON) opts.Format.ApplyFlags(command.Flags()) return command diff --git a/cmd/notation/internal/display/metadata/model/inspect.go b/cmd/notation/internal/display/metadata/model/signature.go similarity index 100% rename from cmd/notation/internal/display/metadata/model/inspect.go rename to cmd/notation/internal/display/metadata/model/signature.go diff --git a/cmd/notation/internal/display/metadata/model/signature_test.go b/cmd/notation/internal/display/metadata/model/signature_test.go new file mode 100644 index 000000000..c329989c5 --- /dev/null +++ b/cmd/notation/internal/display/metadata/model/signature_test.go @@ -0,0 +1,105 @@ +// 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 model + +import ( + "fmt" + "os" + "testing" + "time" + + "github.com/notaryproject/notation-core-go/signature" +) + +func dummyFormatter(v any) string { + return fmt.Sprintf("%v", v) +} + +func TestGetUnsignedAttributes(t *testing.T) { + envContent := &signature.EnvelopeContent{ + SignerInfo: signature.SignerInfo{ + UnsignedAttributes: signature.UnsignedAttributes{ + TimestampSignature: []byte("invalid"), + }, + }, + } + expectedErrMsg := "failed to parse timestamp countersignature: cms: syntax error: invalid signed data: failed to convert from BER to DER: asn1: syntax error: decoding BER length octets: short form length octets value should be less or equal to the subsequent octets length" + unsignedAttr := getUnsignedAttributes(envContent, dummyFormatter) + val, ok := unsignedAttr["timestampSignature"].(Timestamp) + if !ok { + t.Fatal("expected to have timestampSignature") + } + if val.Error != expectedErrMsg { + t.Fatalf("expected %s, but got %s", expectedErrMsg, val.Error) + } +} + +func TestGetSignedAttributes(t *testing.T) { + expiry := time.Now() + envContent := &signature.EnvelopeContent{ + SignerInfo: signature.SignerInfo{ + SignedAttributes: signature.SignedAttributes{ + Expiry: expiry, + ExtendedAttributes: []signature.Attribute{ + { + Key: "keyName", + Value: "value", + }, + }, + }, + }, + } + signedAttr := getSignedAttributes(envContent, dummyFormatter) + if signedAttr["expiry"] != dummyFormatter(expiry) { + t.Fatalf("expected %s, but got %s", dummyFormatter(expiry), signedAttr["expiry"]) + } + + if signedAttr["keyName"] != "value" { + t.Fatalf("expected value, but got %s", signedAttr["keyName"]) + } +} + +func TestParseTimestamp(t *testing.T) { + t.Run("invalid timestamp signature", func(t *testing.T) { + signerInfo := signature.SignerInfo{ + UnsignedAttributes: signature.UnsignedAttributes{ + TimestampSignature: []byte("invalid"), + }, + } + val := parseTimestamp(signerInfo, dummyFormatter) + expectedErrMsg := "failed to parse timestamp countersignature: cms: syntax error: invalid signed data: failed to convert from BER to DER: asn1: syntax error: decoding BER length octets: short form length octets value should be less or equal to the subsequent octets length" + if val.Error != expectedErrMsg { + t.Fatalf("expected %s, but got %s", expectedErrMsg, val.Error) + } + }) + + t.Run("timestamp validation error", func(t *testing.T) { + tsaToken, err := os.ReadFile("./testdata/TimeStampTokenWithInvalidSignature.p7s") + if err != nil { + t.Fatal(err) + } + + signerInfo := signature.SignerInfo{ + UnsignedAttributes: signature.UnsignedAttributes{ + TimestampSignature: tsaToken, + }, + } + val := parseTimestamp(signerInfo, dummyFormatter) + expectedErrMsg := "failed to parse timestamp countersignature: invalid TSTInfo: mismatched message" + + if val.Error != expectedErrMsg { + t.Fatalf("expected %s, but got %s", expectedErrMsg, val.Error) + } + }) +} diff --git a/cmd/notation/internal/display/metadata/model/testdata/TimeStampTokenWithInvalidSignature.p7s b/cmd/notation/internal/display/metadata/model/testdata/TimeStampTokenWithInvalidSignature.p7s new file mode 100644 index 0000000000000000000000000000000000000000..5da09e0b8962084ffe2be77916440b4eb2a22676 GIT binary patch literal 6595 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 literal 0 HcmV?d00001 From 24dea8b0db4a0713036435e28ce5fa6795b84a6e Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Fri, 17 Jan 2025 07:07:12 +0000 Subject: [PATCH 04/32] test: add e2e test for tree and json Signed-off-by: Junjie Gao --- test/e2e/suite/command/inspect.go | 100 ++++++++++++++++++ ...7e98cb328de9731f23783c546bea1aa7580a4a6a37 | 1 + ...16f7e8649e94fb4fc21fe77e8310c060f61caaff8a | 1 + ...1d15b62c870bf359d912aba86622cfc735337ae4fa | 1 + ...c2ee6a8d7759503a29f64624fa52970516d9ec45b2 | 1 + ...01f8397dcacad35e17fb3b7a9f0fa77881ec3cef1c | 1 + ...b3c02f0ab6aa2430c52c96b5e127513259309002d4 | 1 + ...eb4433c937a22c241f6f61ccc5ce84172509bf78ae | 1 + ...cc672269e3228cdaaf38140f81e97f2ed29a714d70 | 1 + .../e2e-with-timestamped-signature/index.json | 1 + .../e2e-with-timestamped-signature/oci-layout | 1 + 11 files changed, 110 insertions(+) create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/123ec9fa46aad212010c907e98cb328de9731f23783c546bea1aa7580a4a6a37 create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/54eab65f9262feac4ea9f31d15b62c870bf359d912aba86622cfc735337ae4fa create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/7c52011daa0bf2983c4687c2ee6a8d7759503a29f64624fa52970516d9ec45b2 create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa77881ec3cef1c create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/cacefba75b1771fa028faeb3c02f0ab6aa2430c52c96b5e127513259309002d4 create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/d7b441acd8ea27f3fa22afeb4433c937a22c241f6f61ccc5ce84172509bf78ae create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/f27085b685cfed009862f7cc672269e3228cdaaf38140f81e97f2ed29a714d70 create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/index.json create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/oci-layout diff --git a/test/e2e/suite/command/inspect.go b/test/e2e/suite/command/inspect.go index 8d04aa9e4..0bb477fec 100644 --- a/test/e2e/suite/command/inspect.go +++ b/test/e2e/suite/command/inspect.go @@ -172,4 +172,104 @@ var _ = Describe("notation inspect", func() { MatchKeyWords(inspectSuccessfullyWithTimestamp...) }) }) + + It("with timestamped oci layout", func() { + Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { + artifact := GenerateArtifact("e2e-with-timestamped-signature", "e2e") + expectedOutput := `localhost:5000/e2e@sha256:99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa77881ec3cef1c +└── application/vnd.cncf.notary.signature + └── sha256:54eab65f9262feac4ea9f31d15b62c870bf359d912aba86622cfc735337ae4fa + ├── signature algorithm: RSASSA-PSS-SHA-256 + ├── signature envelope type: application/jose+json + ├── signed attributes + │ ├── signingScheme: notary.x509 + │ └── signingTime: Fri Jan 17 06:36:19 2025 + ├── user defined attributes + │ └── (empty) + ├── unsigned attributes + │ ├── signingAgent: notation-go/1.3.0+unreleased + │ └── timestamp signature + │ ├── timestamp: [Fri Jan 17 06:36:19 2025, Fri Jan 17 06:36:20 2025] + │ └── certificates + │ ├── SHA256 fingerprint: 36e731cfa9bfd69dafb643809f6dec500902f7197daeaad86ea0159a2268a2b8 + │ │ ├── issued to: CN=Microsoft Public RSA Timestamping CA 2020,O=Microsoft Corporation,C=US + │ │ ├── issued by: CN=Microsoft Identity Verification Root Certificate Authority 2020,O=Microsoft Corporation,C=US + │ │ └── expiry: Mon Nov 19 20:42:31 2035 + │ └── SHA256 fingerprint: 2be4c1670d176be2b0e56081a7b6523487c528a7ea092febbb84ae9db03ceb9a + │ ├── issued to: CN=Microsoft Public RSA Time Stamping Authority,OU=Microsoft Ireland Operations Limited+OU=Thales TSS ESN:F5D6-96D6-909E,O=Microsoft Corporation,L=Redmond,ST=Washington,C=US + │ ├── issued by: CN=Microsoft Public RSA Timestamping CA 2020,O=Microsoft Corporation,C=US + │ └── expiry: Thu Apr 17 17:59:12 2025 + ├── certificates + │ └── SHA256 fingerprint: 36b3e0e0fee117d33d5664a2f56b147ddbbe8b7ca3ad2ae56498703fd782a56e + │ ├── issued to: CN=testcert5,O=Notary,L=Seattle,ST=WA,C=US + │ ├── issued by: CN=testcert5,O=Notary,L=Seattle,ST=WA,C=US + │ └── expiry: Sat Jan 18 06:34:29 2025 + └── signed artifact + ├── media type: application/vnd.oci.image.manifest.v1+json + ├── digest: sha256:99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa77881ec3cef1c + └── size: 582 +` + + notation.Exec("inspect", artifact.ReferenceWithDigest()). + MatchContent(expectedOutput) + }) + }) + + It("with timestamped oci layout and output in JSON", func() { + Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { + artifact := GenerateArtifact("e2e-with-timestamped-signature", "e2e") + expectedOutput := `{ + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "Signatures": [ + { + "mediaType": "application/jose+json", + "digest": "sha256:54eab65f9262feac4ea9f31d15b62c870bf359d912aba86622cfc735337ae4fa", + "signatureAlgorithm": "RSASSA-PSS-SHA-256", + "signedAttributes": { + "signingScheme": "notary.x509", + "signingTime": "2025-01-17T06:36:19Z" + }, + "userDefinedAttributes": null, + "unsignedAttributes": { + "signingAgent": "notation-go/1.3.0+unreleased", + "timestampSignature": { + "timestamp": "[2025-01-17T06:36:19Z, 2025-01-17T06:36:20Z]", + "certificates": [ + { + "SHA256Fingerprint": "36e731cfa9bfd69dafb643809f6dec500902f7197daeaad86ea0159a2268a2b8", + "issuedTo": "CN=Microsoft Public RSA Timestamping CA 2020,O=Microsoft Corporation,C=US", + "issuedBy": "CN=Microsoft Identity Verification Root Certificate Authority 2020,O=Microsoft Corporation,C=US", + "expiry": "2035-11-19T20:42:31Z" + }, + { + "SHA256Fingerprint": "2be4c1670d176be2b0e56081a7b6523487c528a7ea092febbb84ae9db03ceb9a", + "issuedTo": "CN=Microsoft Public RSA Time Stamping Authority,OU=Microsoft Ireland Operations Limited+OU=Thales TSS ESN:F5D6-96D6-909E,O=Microsoft Corporation,L=Redmond,ST=Washington,C=US", + "issuedBy": "CN=Microsoft Public RSA Timestamping CA 2020,O=Microsoft Corporation,C=US", + "expiry": "2025-04-17T17:59:12Z" + } + ] + } + }, + "certificates": [ + { + "SHA256Fingerprint": "36b3e0e0fee117d33d5664a2f56b147ddbbe8b7ca3ad2ae56498703fd782a56e", + "issuedTo": "CN=testcert5,O=Notary,L=Seattle,ST=WA,C=US", + "issuedBy": "CN=testcert5,O=Notary,L=Seattle,ST=WA,C=US", + "expiry": "2025-01-18T06:34:29Z" + } + ], + "signedArtifact": { + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "digest": "sha256:99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa77881ec3cef1c", + "size": 582 + } + } + ] +} +` + + notation.Exec("inspect", artifact.ReferenceWithDigest(), "--output", "json"). + MatchContent(expectedOutput) + }) + }) }) diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/123ec9fa46aad212010c907e98cb328de9731f23783c546bea1aa7580a4a6a37 b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/123ec9fa46aad212010c907e98cb328de9731f23783c546bea1aa7580a4a6a37 new file mode 100644 index 000000000..5cb670ffd --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/123ec9fa46aad212010c907e98cb328de9731f23783c546bea1aa7580a4a6a37 @@ -0,0 +1 @@ +{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.cncf.notary.signature","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2},"layers":[{"mediaType":"application/jose+json","digest":"sha256:cacefba75b1771fa028faeb3c02f0ab6aa2430c52c96b5e127513259309002d4","size":2089}],"subject":{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:d7b441acd8ea27f3fa22afeb4433c937a22c241f6f61ccc5ce84172509bf78ae","size":582},"annotations":{"io.cncf.notary.x509chain.thumbprint#S256":"[\"36b3e0e0fee117d33d5664a2f56b147ddbbe8b7ca3ad2ae56498703fd782a56e\"]","org.opencontainers.image.created":"2025-01-17T06:34:56Z"}} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/54eab65f9262feac4ea9f31d15b62c870bf359d912aba86622cfc735337ae4fa b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/54eab65f9262feac4ea9f31d15b62c870bf359d912aba86622cfc735337ae4fa new file mode 100644 index 000000000..394caad4c --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/54eab65f9262feac4ea9f31d15b62c870bf359d912aba86622cfc735337ae4fa @@ -0,0 +1 @@ +{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.cncf.notary.signature","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2},"layers":[{"mediaType":"application/jose+json","digest":"sha256:f27085b685cfed009862f7cc672269e3228cdaaf38140f81e97f2ed29a714d70","size":10208}],"subject":{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa77881ec3cef1c","size":582},"annotations":{"io.cncf.notary.x509chain.thumbprint#S256":"[\"36b3e0e0fee117d33d5664a2f56b147ddbbe8b7ca3ad2ae56498703fd782a56e\"]","org.opencontainers.image.created":"2025-01-17T06:36:19Z"}} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/7c52011daa0bf2983c4687c2ee6a8d7759503a29f64624fa52970516d9ec45b2 b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/7c52011daa0bf2983c4687c2ee6a8d7759503a29f64624fa52970516d9ec45b2 new file mode 100644 index 000000000..022ea9754 --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/7c52011daa0bf2983c4687c2ee6a8d7759503a29f64624fa52970516d9ec45b2 @@ -0,0 +1 @@ +testFile diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa77881ec3cef1c b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa77881ec3cef1c new file mode 100644 index 000000000..f20214f45 --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa77881ec3cef1c @@ -0,0 +1 @@ +{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","artifactType":"application/vnd.unknown.artifact.v1","config":{"mediaType":"application/vnd.oci.empty.v1+json","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2,"data":"e30="},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar","digest":"sha256:7c52011daa0bf2983c4687c2ee6a8d7759503a29f64624fa52970516d9ec45b2","size":9,"annotations":{"org.opencontainers.image.title":"file"}}],"annotations":{"org.opencontainers.image.created":"2025-01-17T06:35:54Z"}} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/cacefba75b1771fa028faeb3c02f0ab6aa2430c52c96b5e127513259309002d4 b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/cacefba75b1771fa028faeb3c02f0ab6aa2430c52c96b5e127513259309002d4 new file mode 100644 index 000000000..0e5a4c0c7 --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/cacefba75b1771fa028faeb3c02f0ab6aa2430c52c96b5e127513259309002d4 @@ -0,0 +1 @@ +{"payload":"eyJ0YXJnZXRBcnRpZmFjdCI6eyJkaWdlc3QiOiJzaGEyNTY6ZDdiNDQxYWNkOGVhMjdmM2ZhMjJhZmViNDQzM2M5MzdhMjJjMjQxZjZmNjFjY2M1Y2U4NDE3MjUwOWJmNzhhZSIsIm1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5vY2kuaW1hZ2UubWFuaWZlc3QudjEranNvbiIsInNpemUiOjU4Mn19","protected":"eyJhbGciOiJQUzI1NiIsImNyaXQiOlsiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSJdLCJjdHkiOiJhcHBsaWNhdGlvbi92bmQuY25jZi5ub3RhcnkucGF5bG9hZC52MStqc29uIiwiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSI6Im5vdGFyeS54NTA5IiwiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1RpbWUiOiIyMDI1LTAxLTE3VDA2OjM0OjU2WiJ9","header":{"x5c":["MIIDRDCCAiygAwIBAgIBDzANBgkqhkiG9w0BAQsFADBRMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTESMBAGA1UEAxMJdGVzdGNlcnQ1MB4XDTI1MDExNzA2MzQyOVoXDTI1MDExODA2MzQyOVowUTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZOb3RhcnkxEjAQBgNVBAMTCXRlc3RjZXJ0NTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPRXF8lqb67w7AjvuI3IB3VTwtJ9sZEP61PoAbvc/qt1WTgSuiI3VCTUbi8GMIIxLDRs+0xsfCRWZFKodbWrw6YXrYpWwwEW7ba2GN63XL0jiFgbbDWB/QylAzd5JSCX8OgBDFR+KiTSzTbfUgQ5+q8hLbR6TfiJOR0pLoSUQ96Y/oPty/aFv8hfcjp0+pbiHeNwZlNAU2uDfxnYZK+uhhOEznF/Jzd98rh9FMnZdM4AGrvg73rNjaY2JQSWWEwIgK6Fl1qCBH9zqrpmyQdC20NrtinOsRi1CNVz6b0GG558bVliETYTGH1KOMEXX4f6VAbvzrJAI24PSm+vZA5zFeECAwEAAaMnMCUwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA0GCSqGSIb3DQEBCwUAA4IBAQDNHCmlY2z93TLnO8ukW3d1q3/Yiddy+YDy31zVrofOd4WBUTemKlFlj8GpiVbTmWmHiX5F/9CgDeTBDgZFzxBOqUZbyIrsyVktpv7vuFXyAkMcC68Ztz8H5EepEFnfo4u1GpWlsmi9PE/TV0MP8KNCj3hVvh70xEiEuzHzk1xQqAt+0MxjKQMJdOaC+bao224j2pS+yZ4hMeMCF8qmZwjn3SEfCRghvzee02XmEAWDOVGcg1gCkFyq2oLp4RwdWBnc4/n7lbmg05+nrUDM0tq22FO2HPWrdGMf/hWlowaa2UZBJe3x4sJsMRQLMVgo29Zhie2KgbNSByu9C/5llapS"],"io.cncf.notary.signingAgent":"notation-go/1.3.0+unreleased"},"signature":"PvOhkuI6YHKU2hRzTeqIR23KpP3RBFSjwYJgh5e2nty2BoinvbHnZl6HQgkrS3brmhGDsjRvB98puX3QprJUcF5ElRmz290ICcBCMiPYTNLhrTomVOmahfeVfdykyamoi6hISoRw-sWA1IXSuAr0jXwDDPqbqmAkh1wmgWRnWwlOjT8yTQ7606CNEN05A9qOgTSiZJA30IoORrQFSWbfC8c6_mtCc2yL4U9RLnl21YCIDEFCEI0kv-UmeFL5vtaaiSl5uLt-4I2myMrn0AiNj9DlGSJnN7rHIKUqM31rgKYdKK5HqUtTNONI2607I_Yfv8WjxskrT5n1K--uAlWNPw"} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/d7b441acd8ea27f3fa22afeb4433c937a22c241f6f61ccc5ce84172509bf78ae b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/d7b441acd8ea27f3fa22afeb4433c937a22c241f6f61ccc5ce84172509bf78ae new file mode 100644 index 000000000..19f9776b8 --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/d7b441acd8ea27f3fa22afeb4433c937a22c241f6f61ccc5ce84172509bf78ae @@ -0,0 +1 @@ +{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","artifactType":"application/vnd.unknown.artifact.v1","config":{"mediaType":"application/vnd.oci.empty.v1+json","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2,"data":"e30="},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar","digest":"sha256:7c52011daa0bf2983c4687c2ee6a8d7759503a29f64624fa52970516d9ec45b2","size":9,"annotations":{"org.opencontainers.image.title":"file"}}],"annotations":{"org.opencontainers.image.created":"2025-01-17T06:32:32Z"}} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/f27085b685cfed009862f7cc672269e3228cdaaf38140f81e97f2ed29a714d70 b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/f27085b685cfed009862f7cc672269e3228cdaaf38140f81e97f2ed29a714d70 new file mode 100644 index 000000000..ee20e5006 --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/f27085b685cfed009862f7cc672269e3228cdaaf38140f81e97f2ed29a714d70 @@ -0,0 +1 @@ +{"payload":"eyJ0YXJnZXRBcnRpZmFjdCI6eyJkaWdlc3QiOiJzaGEyNTY6OTk5NTA4Njg2MjhlZDc5ZWJjMjk1ZTAxZjgzOTdkY2FjYWQzNWUxN2ZiM2I3YTlmMGZhNzc4ODFlYzNjZWYxYyIsIm1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5vY2kuaW1hZ2UubWFuaWZlc3QudjEranNvbiIsInNpemUiOjU4Mn19","protected":"eyJhbGciOiJQUzI1NiIsImNyaXQiOlsiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSJdLCJjdHkiOiJhcHBsaWNhdGlvbi92bmQuY25jZi5ub3RhcnkucGF5bG9hZC52MStqc29uIiwiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSI6Im5vdGFyeS54NTA5IiwiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1RpbWUiOiIyMDI1LTAxLTE3VDA2OjM2OjE5WiJ9","header":{"io.cncf.notary.timestampSignature":"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","x5c":["MIIDRDCCAiygAwIBAgIBDzANBgkqhkiG9w0BAQsFADBRMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTESMBAGA1UEAxMJdGVzdGNlcnQ1MB4XDTI1MDExNzA2MzQyOVoXDTI1MDExODA2MzQyOVowUTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZOb3RhcnkxEjAQBgNVBAMTCXRlc3RjZXJ0NTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPRXF8lqb67w7AjvuI3IB3VTwtJ9sZEP61PoAbvc/qt1WTgSuiI3VCTUbi8GMIIxLDRs+0xsfCRWZFKodbWrw6YXrYpWwwEW7ba2GN63XL0jiFgbbDWB/QylAzd5JSCX8OgBDFR+KiTSzTbfUgQ5+q8hLbR6TfiJOR0pLoSUQ96Y/oPty/aFv8hfcjp0+pbiHeNwZlNAU2uDfxnYZK+uhhOEznF/Jzd98rh9FMnZdM4AGrvg73rNjaY2JQSWWEwIgK6Fl1qCBH9zqrpmyQdC20NrtinOsRi1CNVz6b0GG558bVliETYTGH1KOMEXX4f6VAbvzrJAI24PSm+vZA5zFeECAwEAAaMnMCUwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA0GCSqGSIb3DQEBCwUAA4IBAQDNHCmlY2z93TLnO8ukW3d1q3/Yiddy+YDy31zVrofOd4WBUTemKlFlj8GpiVbTmWmHiX5F/9CgDeTBDgZFzxBOqUZbyIrsyVktpv7vuFXyAkMcC68Ztz8H5EepEFnfo4u1GpWlsmi9PE/TV0MP8KNCj3hVvh70xEiEuzHzk1xQqAt+0MxjKQMJdOaC+bao224j2pS+yZ4hMeMCF8qmZwjn3SEfCRghvzee02XmEAWDOVGcg1gCkFyq2oLp4RwdWBnc4/n7lbmg05+nrUDM0tq22FO2HPWrdGMf/hWlowaa2UZBJe3x4sJsMRQLMVgo29Zhie2KgbNSByu9C/5llapS"],"io.cncf.notary.signingAgent":"notation-go/1.3.0+unreleased"},"signature":"HPR6idroqimVvWszvmX4v3dF6YpktvoYyOVRPnjzfkQJDAw3AJ9x7G__2Jb1hhEL53jnwJMe8Ojnm_5z0x2ebqtl9Ndr_kAYymT4gf29Qxmr_e9U0nWpkjOi_MoYIXWo5udPsTy3oNbP9K-CX_4FQ2bRDCI8476nJ6vc6-uREJJg6HhexeQJ1nYeltbvc7hBmkYe9TX1IkbSVkOLuSYMVFU2T9S-P92Q5l22Qwpw4MG4CXxp7GlLjR1_wq0Hc71ARZZFz8olWv03AQZtR_QJqhdZHBch4MSUJ1cTJMs9NNaQQnU63I7C_k4Ca0-bvlF3JD7SN7Wiz3cP-IqiJKFHcw"} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/index.json b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/index.json new file mode 100644 index 000000000..947cbf68f --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/index.json @@ -0,0 +1 @@ +{"schemaVersion":2,"manifests":[{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa77881ec3cef1c","size":582,"annotations":{"org.opencontainers.image.created":"2025-01-17T06:35:54Z","org.opencontainers.image.ref.name":"v1"},"artifactType":"application/vnd.unknown.artifact.v1"},{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:d7b441acd8ea27f3fa22afeb4433c937a22c241f6f61ccc5ce84172509bf78ae","size":582,"annotations":{"org.opencontainers.image.created":"2025-01-17T06:32:32Z"},"artifactType":"application/vnd.unknown.artifact.v1"},{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:123ec9fa46aad212010c907e98cb328de9731f23783c546bea1aa7580a4a6a37","size":728,"annotations":{"io.cncf.notary.x509chain.thumbprint#S256":"[\"36b3e0e0fee117d33d5664a2f56b147ddbbe8b7ca3ad2ae56498703fd782a56e\"]","org.opencontainers.image.created":"2025-01-17T06:34:56Z"}},{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:54eab65f9262feac4ea9f31d15b62c870bf359d912aba86622cfc735337ae4fa","size":729,"annotations":{"io.cncf.notary.x509chain.thumbprint#S256":"[\"36b3e0e0fee117d33d5664a2f56b147ddbbe8b7ca3ad2ae56498703fd782a56e\"]","org.opencontainers.image.created":"2025-01-17T06:36:19Z"}}]} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/oci-layout b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/oci-layout new file mode 100644 index 000000000..1343d370f --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/oci-layout @@ -0,0 +1 @@ +{"imageLayoutVersion":"1.0.0"} \ No newline at end of file From 4031faf86e686b6c9d442c9d555a99de9e27d4a0 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Fri, 17 Jan 2025 09:08:16 +0000 Subject: [PATCH 05/32] refactor code Signed-off-by: Junjie Gao --- cmd/notation/inspect.go | 2 +- .../internal/display/metadata/interface.go | 4 +- .../internal/display/metadata/json/inspect.go | 152 ++++++++++++++++-- .../inspect_test.go} | 19 +-- .../TimeStampTokenWithInvalidSignature.p7s | Bin .../display/metadata/model/signature.go | 148 ----------------- .../internal/display/metadata/tree/inspect.go | 148 ++++++++++------- .../display/metadata/tree/inspect_test.go | 46 ++++++ 8 files changed, 282 insertions(+), 237 deletions(-) rename cmd/notation/internal/display/metadata/{model/signature_test.go => json/inspect_test.go} (86%) rename cmd/notation/internal/display/metadata/{model => json}/testdata/TimeStampTokenWithInvalidSignature.p7s (100%) delete mode 100644 cmd/notation/internal/display/metadata/model/signature.go create mode 100644 cmd/notation/internal/display/metadata/tree/inspect_test.go diff --git a/cmd/notation/inspect.go b/cmd/notation/inspect.go index 15fcfa7d4..2c51f0f23 100644 --- a/cmd/notation/inspect.go +++ b/cmd/notation/inspect.go @@ -135,7 +135,7 @@ func runInspect(command *cobra.Command, opts *inspectOpts) error { return nil } - if err := displayHandler.AddSignature(sigManifestDesc.Digest.String(), sigDesc.MediaType, sigEnvelope); err != nil { + if err := displayHandler.InspectSignature(sigManifestDesc.Digest.String(), sigDesc.MediaType, sigEnvelope); err != nil { logSkippedSignature(sigManifestDesc, err) skippedSignatures = true return nil diff --git a/cmd/notation/internal/display/metadata/interface.go b/cmd/notation/internal/display/metadata/interface.go index ce0b970e0..215c887a5 100644 --- a/cmd/notation/internal/display/metadata/interface.go +++ b/cmd/notation/internal/display/metadata/interface.go @@ -9,8 +9,8 @@ type InspectHandler interface { // SetMediaType sets the media type for the handler. SetMediaType(mediaType string) - // AddSignature adds a signature to the handler. - AddSignature(digest string, envelopeMediaType string, sigEnvelope signature.Envelope) error + // InspectSignature inspects a signature to get it ready to be rendered. + InspectSignature(digest string, envelopeMediaType string, sigEnvelope signature.Envelope) error // Print prints the metadata. Print() error diff --git a/cmd/notation/internal/display/metadata/json/inspect.go b/cmd/notation/internal/display/metadata/json/inspect.go index ea82bbd92..9d2b4401d 100644 --- a/cmd/notation/internal/display/metadata/json/inspect.go +++ b/cmd/notation/internal/display/metadata/json/inspect.go @@ -1,18 +1,53 @@ package json import ( + "crypto/sha256" + "crypto/x509" + "encoding/hex" "fmt" + "strings" "time" "github.com/notaryproject/notation-core-go/signature" - "github.com/notaryproject/notation/cmd/notation/internal/display/metadata/model" + "github.com/notaryproject/notation-go/plugin/proto" "github.com/notaryproject/notation/cmd/notation/internal/output" + "github.com/notaryproject/notation/internal/envelope" "github.com/notaryproject/tspclient-go" + + ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) type inspectOutput struct { MediaType string `json:"mediaType"` - Signatures []model.Signature + Signatures []Signature +} + +// Signature is the signature envelope for printing in human readable format. +type Signature struct { + MediaType string `json:"mediaType"` + Digest string `json:"digest,omitempty"` + SignatureAlgorithm string `json:"signatureAlgorithm"` + SignedAttributes map[string]any `json:"signedAttributes"` + UserDefinedAttributes map[string]string `json:"userDefinedAttributes"` + UnsignedAttributes map[string]any `json:"unsignedAttributes"` + Certificates []Certificate `json:"certificates"` + SignedArtifact ocispec.Descriptor `json:"signedArtifact"` +} + +// Certificate is the certificate information for printing in human readable +// format. +type Certificate struct { + SHA256Fingerprint string `json:"SHA256Fingerprint"` + IssuedTo string `json:"issuedTo"` + IssuedBy string `json:"issuedBy"` + Expiry time.Time `json:"expiry"` +} + +// Timestamp is the timestamp information for printing in human readable. +type Timestamp struct { + Timestamp string `json:"timestamp,omitempty"` + Certificates []Certificate `json:"certificates,omitempty"` + Error string `json:"error,omitempty"` } type InspectHandler struct { @@ -40,9 +75,9 @@ func (h *InspectHandler) SetMediaType(mediaType string) { } } -// AddSignature adds a signature to the handler. -func (h *InspectHandler) AddSignature(digest string, envelopeMediaType string, sigEnvelope signature.Envelope) error { - sig, err := model.NewSignature(digest, envelopeMediaType, sigEnvelope, formatter) +// InspectSignature inspects a signature to get it ready to be rendered. +func (h *InspectHandler) InspectSignature(digest string, envelopeMediaType string, sigEnvelope signature.Envelope) error { + sig, err := newSignature(digest, envelopeMediaType, sigEnvelope) if err != nil { return err } @@ -54,13 +89,104 @@ func (h *InspectHandler) Print() error { return output.PrintPrettyJSON(h.printer, h.output) } -// formatter is the function for formatting the time.Time and tspclient.Timestamp. -func formatter(v any) string { - switch v := v.(type) { - case time.Time: - return v.Format(time.RFC3339) - case tspclient.Timestamp: - return v.Format(time.RFC3339) +// newSignature parses the signature blob and returns a Signature object. +func newSignature(digest string, envelopeMediaType string, sigEnvelope signature.Envelope) (*Signature, error) { + envelopeContent, err := sigEnvelope.Content() + if err != nil { + return nil, err + } + + signedArtifactDesc, err := envelope.DescriptorFromSignaturePayload(&envelopeContent.Payload) + if err != nil { + return nil, err + } + + signatureAlgorithm, err := proto.EncodeSigningAlgorithm(envelopeContent.SignerInfo.SignatureAlgorithm) + if err != nil { + return nil, err + } + sig := &Signature{ + MediaType: envelopeMediaType, + Digest: digest, + SignatureAlgorithm: string(signatureAlgorithm), + SignedAttributes: getSignedAttributes(envelopeContent), + UserDefinedAttributes: signedArtifactDesc.Annotations, + UnsignedAttributes: getUnsignedAttributes(envelopeContent), + Certificates: getCertificates(envelopeContent.SignerInfo.CertificateChain), + SignedArtifact: *signedArtifactDesc, + } + + // clearing annotations from the SignedArtifact field since they're already + // displayed as UserDefinedAttributes + sig.SignedArtifact.Annotations = nil + + return sig, nil +} + +func getSignedAttributes(envelopeContent *signature.EnvelopeContent) map[string]any { + signedAttributes := map[string]any{ + "signingScheme": string(envelopeContent.SignerInfo.SignedAttributes.SigningScheme), + "signingTime": envelopeContent.SignerInfo.SignedAttributes.SigningTime, + } + if expiry := envelopeContent.SignerInfo.SignedAttributes.Expiry; !expiry.IsZero() { + signedAttributes["expiry"] = expiry + } + + for _, attribute := range envelopeContent.SignerInfo.SignedAttributes.ExtendedAttributes { + signedAttributes[fmt.Sprint(attribute.Key)] = fmt.Sprint(attribute.Value) + } + return signedAttributes +} + +func getUnsignedAttributes(envelopeContent *signature.EnvelopeContent) map[string]any { + unsignedAttributes := make(map[string]any) + + if envelopeContent.SignerInfo.UnsignedAttributes.TimestampSignature != nil { + unsignedAttributes["timestampSignature"] = parseTimestamp(envelopeContent.SignerInfo) + } + + if envelopeContent.SignerInfo.UnsignedAttributes.SigningAgent != "" { + unsignedAttributes["signingAgent"] = envelopeContent.SignerInfo.UnsignedAttributes.SigningAgent + } + return unsignedAttributes +} + +func getCertificates(certChain []*x509.Certificate) []Certificate { + certificates := []Certificate{} + + for _, cert := range certChain { + hash := sha256.Sum256(cert.Raw) + certificates = append(certificates, Certificate{ + SHA256Fingerprint: strings.ToLower(hex.EncodeToString(hash[:])), + IssuedTo: cert.Subject.String(), + IssuedBy: cert.Issuer.String(), + Expiry: cert.NotAfter, + }) + } + return certificates +} + +func parseTimestamp(signerInfo signature.SignerInfo) Timestamp { + signedToken, err := tspclient.ParseSignedToken(signerInfo.UnsignedAttributes.TimestampSignature) + if err != nil { + return Timestamp{ + Error: fmt.Sprintf("failed to parse timestamp countersignature: %s", err), + } + } + info, err := signedToken.Info() + if err != nil { + return Timestamp{ + Error: fmt.Sprintf("failed to parse timestamp countersignature: %s", err), + } + } + timestamp, err := info.Validate(signerInfo.Signature) + if err != nil { + return Timestamp{ + Error: fmt.Sprintf("failed to parse timestamp countersignature: %s", err), + } + } + return Timestamp{ + Timestamp: timestamp.Format(time.RFC3339Nano), + Certificates: getCertificates(signedToken.Certificates), } - return fmt.Sprintf("%v", v) } diff --git a/cmd/notation/internal/display/metadata/model/signature_test.go b/cmd/notation/internal/display/metadata/json/inspect_test.go similarity index 86% rename from cmd/notation/internal/display/metadata/model/signature_test.go rename to cmd/notation/internal/display/metadata/json/inspect_test.go index c329989c5..1295e6b07 100644 --- a/cmd/notation/internal/display/metadata/model/signature_test.go +++ b/cmd/notation/internal/display/metadata/json/inspect_test.go @@ -11,10 +11,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -package model +package json import ( - "fmt" "os" "testing" "time" @@ -22,10 +21,6 @@ import ( "github.com/notaryproject/notation-core-go/signature" ) -func dummyFormatter(v any) string { - return fmt.Sprintf("%v", v) -} - func TestGetUnsignedAttributes(t *testing.T) { envContent := &signature.EnvelopeContent{ SignerInfo: signature.SignerInfo{ @@ -35,7 +30,7 @@ func TestGetUnsignedAttributes(t *testing.T) { }, } expectedErrMsg := "failed to parse timestamp countersignature: cms: syntax error: invalid signed data: failed to convert from BER to DER: asn1: syntax error: decoding BER length octets: short form length octets value should be less or equal to the subsequent octets length" - unsignedAttr := getUnsignedAttributes(envContent, dummyFormatter) + unsignedAttr := getUnsignedAttributes(envContent) val, ok := unsignedAttr["timestampSignature"].(Timestamp) if !ok { t.Fatal("expected to have timestampSignature") @@ -60,9 +55,9 @@ func TestGetSignedAttributes(t *testing.T) { }, }, } - signedAttr := getSignedAttributes(envContent, dummyFormatter) - if signedAttr["expiry"] != dummyFormatter(expiry) { - t.Fatalf("expected %s, but got %s", dummyFormatter(expiry), signedAttr["expiry"]) + signedAttr := getSignedAttributes(envContent) + if signedAttr["expiry"] != expiry { + t.Fatalf("expected %s, but got %s", expiry, signedAttr["expiry"]) } if signedAttr["keyName"] != "value" { @@ -77,7 +72,7 @@ func TestParseTimestamp(t *testing.T) { TimestampSignature: []byte("invalid"), }, } - val := parseTimestamp(signerInfo, dummyFormatter) + val := parseTimestamp(signerInfo) expectedErrMsg := "failed to parse timestamp countersignature: cms: syntax error: invalid signed data: failed to convert from BER to DER: asn1: syntax error: decoding BER length octets: short form length octets value should be less or equal to the subsequent octets length" if val.Error != expectedErrMsg { t.Fatalf("expected %s, but got %s", expectedErrMsg, val.Error) @@ -95,7 +90,7 @@ func TestParseTimestamp(t *testing.T) { TimestampSignature: tsaToken, }, } - val := parseTimestamp(signerInfo, dummyFormatter) + val := parseTimestamp(signerInfo) expectedErrMsg := "failed to parse timestamp countersignature: invalid TSTInfo: mismatched message" if val.Error != expectedErrMsg { diff --git a/cmd/notation/internal/display/metadata/model/testdata/TimeStampTokenWithInvalidSignature.p7s b/cmd/notation/internal/display/metadata/json/testdata/TimeStampTokenWithInvalidSignature.p7s similarity index 100% rename from cmd/notation/internal/display/metadata/model/testdata/TimeStampTokenWithInvalidSignature.p7s rename to cmd/notation/internal/display/metadata/json/testdata/TimeStampTokenWithInvalidSignature.p7s diff --git a/cmd/notation/internal/display/metadata/model/signature.go b/cmd/notation/internal/display/metadata/model/signature.go deleted file mode 100644 index 4bf7e02fa..000000000 --- a/cmd/notation/internal/display/metadata/model/signature.go +++ /dev/null @@ -1,148 +0,0 @@ -package model - -import ( - "crypto/sha256" - "crypto/x509" - "encoding/hex" - "fmt" - "strings" - - "github.com/notaryproject/notation-core-go/signature" - "github.com/notaryproject/notation-go/plugin/proto" - "github.com/notaryproject/notation/internal/envelope" - "github.com/notaryproject/tspclient-go" - ocispec "github.com/opencontainers/image-spec/specs-go/v1" -) - -// Signature is the signature envelope for printing in human readable format. -type Signature struct { - MediaType string `json:"mediaType"` - Digest string `json:"digest,omitempty"` - SignatureAlgorithm string `json:"signatureAlgorithm"` - SignedAttributes map[string]string `json:"signedAttributes"` - UserDefinedAttributes map[string]string `json:"userDefinedAttributes"` - UnsignedAttributes map[string]any `json:"unsignedAttributes"` - Certificates []Certificate `json:"certificates"` - SignedArtifact ocispec.Descriptor `json:"signedArtifact"` -} - -// Certificate is the certificate information for printing in human readable -// format. -type Certificate struct { - SHA256Fingerprint string `json:"SHA256Fingerprint"` - IssuedTo string `json:"issuedTo"` - IssuedBy string `json:"issuedBy"` - Expiry string `json:"expiry"` -} - -// Timestamp is the timestamp information for printing in human readable. -type Timestamp struct { - Timestamp string `json:"timestamp,omitempty"` - Certificates []Certificate `json:"certificates,omitempty"` - Error string `json:"error,omitempty"` -} - -// formatter is the function to format the value to string. -type formatter func(any) string - -// NewSignature parses the signature blob and returns a Signature object. -func NewSignature(digest string, envelopeMediaType string, sigEnvelope signature.Envelope, formatter formatter) (*Signature, error) { - envelopeContent, err := sigEnvelope.Content() - if err != nil { - return nil, err - } - - signedArtifactDesc, err := envelope.DescriptorFromSignaturePayload(&envelopeContent.Payload) - if err != nil { - return nil, err - } - - signatureAlgorithm, err := proto.EncodeSigningAlgorithm(envelopeContent.SignerInfo.SignatureAlgorithm) - if err != nil { - return nil, err - } - sig := &Signature{ - MediaType: envelopeMediaType, - Digest: digest, - SignatureAlgorithm: string(signatureAlgorithm), - SignedAttributes: getSignedAttributes(envelopeContent, formatter), - UserDefinedAttributes: signedArtifactDesc.Annotations, - UnsignedAttributes: getUnsignedAttributes(envelopeContent, formatter), - Certificates: getCertificates(envelopeContent.SignerInfo.CertificateChain, formatter), - SignedArtifact: *signedArtifactDesc, - } - - // clearing annotations from the SignedArtifact field since they're already - // displayed as UserDefinedAttributes - sig.SignedArtifact.Annotations = nil - - return sig, nil -} - -func getSignedAttributes(envContent *signature.EnvelopeContent, formatter formatter) map[string]string { - signedAttributes := map[string]string{ - "signingScheme": string(envContent.SignerInfo.SignedAttributes.SigningScheme), - "signingTime": formatter(envContent.SignerInfo.SignedAttributes.SigningTime), - } - if expiry := envContent.SignerInfo.SignedAttributes.Expiry; !expiry.IsZero() { - signedAttributes["expiry"] = formatter(expiry) - } - - for _, attribute := range envContent.SignerInfo.SignedAttributes.ExtendedAttributes { - signedAttributes[fmt.Sprint(attribute.Key)] = fmt.Sprint(attribute.Value) - } - return signedAttributes -} - -func getUnsignedAttributes(envContent *signature.EnvelopeContent, formatter formatter) map[string]any { - unsignedAttributes := make(map[string]any) - - if envContent.SignerInfo.UnsignedAttributes.TimestampSignature != nil { - unsignedAttributes["timestampSignature"] = parseTimestamp(envContent.SignerInfo, formatter) - } - - if envContent.SignerInfo.UnsignedAttributes.SigningAgent != "" { - unsignedAttributes["signingAgent"] = envContent.SignerInfo.UnsignedAttributes.SigningAgent - } - return unsignedAttributes -} - -func getCertificates(certChain []*x509.Certificate, formatter formatter) []Certificate { - certificates := []Certificate{} - - for _, cert := range certChain { - hash := sha256.Sum256(cert.Raw) - certificates = append(certificates, Certificate{ - SHA256Fingerprint: strings.ToLower(hex.EncodeToString(hash[:])), - IssuedTo: cert.Subject.String(), - IssuedBy: cert.Issuer.String(), - Expiry: formatter(cert.NotAfter), - }) - } - return certificates -} - -func parseTimestamp(signerInfo signature.SignerInfo, formatter formatter) Timestamp { - signedToken, err := tspclient.ParseSignedToken(signerInfo.UnsignedAttributes.TimestampSignature) - if err != nil { - return Timestamp{ - Error: fmt.Sprintf("failed to parse timestamp countersignature: %s", err), - } - } - info, err := signedToken.Info() - if err != nil { - return Timestamp{ - Error: fmt.Sprintf("failed to parse timestamp countersignature: %s", err), - } - } - timestamp, err := info.Validate(signerInfo.Signature) - if err != nil { - return Timestamp{ - Error: fmt.Sprintf("failed to parse timestamp countersignature: %s", err), - } - } - return Timestamp{ - Timestamp: formatter(*timestamp), - Certificates: getCertificates(signedToken.Certificates, formatter), - } -} diff --git a/cmd/notation/internal/display/metadata/tree/inspect.go b/cmd/notation/internal/display/metadata/tree/inspect.go index 358ed7453..b443f16c6 100644 --- a/cmd/notation/internal/display/metadata/tree/inspect.go +++ b/cmd/notation/internal/display/metadata/tree/inspect.go @@ -1,15 +1,20 @@ package tree import ( + "crypto/sha256" + "crypto/x509" + "encoding/hex" "fmt" "slices" "strconv" + "strings" "time" "github.com/notaryproject/notation-core-go/signature" + "github.com/notaryproject/notation-go/plugin/proto" "github.com/notaryproject/notation-go/registry" - "github.com/notaryproject/notation/cmd/notation/internal/display/metadata/model" "github.com/notaryproject/notation/cmd/notation/internal/output" + "github.com/notaryproject/notation/internal/envelope" "github.com/notaryproject/notation/internal/tree" "github.com/notaryproject/tspclient-go" ) @@ -38,18 +43,12 @@ func (h *InspectHandler) SetReference(reference string) { // handler. func (h *InspectHandler) SetMediaType(_ string) {} -func (h *InspectHandler) AddSignature(digest string, envelopeMediaType string, sigEnvelope signature.Envelope) error { +func (h *InspectHandler) InspectSignature(digest string, envelopeMediaType string, sigEnvelope signature.Envelope) error { if h.root == nil || h.cncfSigNode == nil { return fmt.Errorf("artifact reference is not set") } - sig, err := model.NewSignature(digest, envelopeMediaType, sigEnvelope, formatter) - if err != nil { - return err - } - - h.cncfSigNode.Children = append(h.cncfSigNode.Children, toTreeNode(sig)) - return nil + return addSignatureToTree(h.cncfSigNode, digest, envelopeMediaType, sigEnvelope) } func (h *InspectHandler) Print() error { @@ -59,53 +58,67 @@ func (h *InspectHandler) Print() error { return h.root.Print(h.printer) } -func toTreeNode(s *model.Signature) *tree.Node { - sigNode := tree.New(s.Digest) - sigNode.AddPair("signature algorithm", s.SignatureAlgorithm) - sigNode.AddPair("signature envelope type", s.MediaType) +func formatTime(t time.Time) string { + return t.Format(time.ANSIC) +} + +func addSignatureToTree(node *tree.Node, digest string, envelopeMediaType string, sigEnvelope signature.Envelope) error { + envelopeContent, err := sigEnvelope.Content() + if err != nil { + return err + } + + signedArtifactDesc, err := envelope.DescriptorFromSignaturePayload(&envelopeContent.Payload) + if err != nil { + return err + } + + signatureAlgorithm, err := proto.EncodeSigningAlgorithm(envelopeContent.SignerInfo.SignatureAlgorithm) + if err != nil { + return err + } + sigNode := node.Add(digest) + sigNode.AddPair("signature algorithm", string(signatureAlgorithm)) + sigNode.AddPair("signature envelope type", envelopeMediaType) + + // Add signer attributes signedAttributesNode := sigNode.Add("signed attributes") - addMapToTree(signedAttributesNode, s.SignedAttributes) + signedAttributesNode.AddPair("signing scheme", string(envelopeContent.SignerInfo.SignedAttributes.SigningScheme)) + signedAttributesNode.AddPair("signing time", formatTime(envelopeContent.SignerInfo.SignedAttributes.SigningTime)) + if expiry := envelopeContent.SignerInfo.SignedAttributes.Expiry; !expiry.IsZero() { + signedAttributesNode.AddPair("expiry", formatTime(expiry)) + } + for _, attribute := range envelopeContent.SignerInfo.SignedAttributes.ExtendedAttributes { + signedAttributesNode.AddPair(fmt.Sprint(attribute.Key), fmt.Sprint(attribute.Value)) + } + // add user defined attributes userDefinedAttributesNode := sigNode.Add("user defined attributes") - addMapToTree(userDefinedAttributesNode, s.UserDefinedAttributes) + for _, k := range orderedKeys(signedArtifactDesc.Annotations) { + v := signedArtifactDesc.Annotations[k] + userDefinedAttributesNode.AddPair(k, v) + } + // add unsigned attributes unsignedAttributesNode := sigNode.Add("unsigned attributes") - for _, k := range orderedKeys(s.UnsignedAttributes) { - v := s.UnsignedAttributes[k] - switch value := v.(type) { - case string: - unsignedAttributesNode.AddPair(k, value) - case model.Timestamp: - timestampNode := unsignedAttributesNode.Add("timestamp signature") - if value.Error != "" { - timestampNode.AddPair("error", value.Error) - continue - } - timestampNode.AddPair("timestamp", value.Timestamp) - addCertificatesToTree(timestampNode, "certificates", value.Certificates) - } - } - - addCertificatesToTree(sigNode, "certificates", s.Certificates) + if timestamp := envelopeContent.SignerInfo.UnsignedAttributes.TimestampSignature; timestamp != nil { + addTimestampToTree(unsignedAttributesNode, envelopeContent.SignerInfo) + } + if signingAgent := envelopeContent.SignerInfo.UnsignedAttributes.SigningAgent; signingAgent != "" { + unsignedAttributesNode.AddPair("signing agent", signingAgent) + } - artifactNode := sigNode.Add("signed artifact") - artifactNode.AddPair("media type", s.SignedArtifact.MediaType) - artifactNode.AddPair("digest", s.SignedArtifact.Digest.String()) - artifactNode.AddPair("size", strconv.FormatInt(s.SignedArtifact.Size, 10)) - return sigNode -} + // add certificate chain + addCertificatesToTree(sigNode, envelopeContent.SignerInfo.CertificateChain) -func addMapToTree(node *tree.Node, m map[string]string) { - if len(m) == 0 { - node.Add("(empty)") - return - } + // add signed artifact + artifactNode := sigNode.Add("signed artifact") + artifactNode.AddPair("media type", signedArtifactDesc.MediaType) + artifactNode.AddPair("digest", signedArtifactDesc.Digest.String()) + artifactNode.AddPair("size", strconv.FormatInt(signedArtifactDesc.Size, 10)) - // Add each entry in sorted order - for _, k := range orderedKeys(m) { - node.AddPair(k, m[k]) - } + return nil } func orderedKeys[T any](m map[string]T) []string { @@ -117,22 +130,35 @@ func orderedKeys[T any](m map[string]T) []string { return keys } -func addCertificatesToTree(node *tree.Node, name string, certs []model.Certificate) { - certListNode := node.Add(name) - for _, cert := range certs { - certNode := certListNode.AddPair("SHA256 fingerprint", cert.SHA256Fingerprint) - certNode.AddPair("issued to", cert.IssuedTo) - certNode.AddPair("issued by", cert.IssuedBy) - certNode.AddPair("expiry", cert.Expiry) +func addCertificatesToTree(node *tree.Node, certChain []*x509.Certificate) { + certListNode := node.Add("certificates") + for _, cert := range certChain { + hash := sha256.Sum256(cert.Raw) + + certNode := certListNode.AddPair("SHA256 fingerprint", strings.ToLower(hex.EncodeToString(hash[:]))) + certNode.AddPair("issued to", cert.Subject.String()) + certNode.AddPair("issued by", cert.Issuer.String()) + certNode.AddPair("expiry", formatTime(cert.NotAfter)) } } -func formatter(v any) string { - switch v := v.(type) { - case time.Time: - return v.Format(time.ANSIC) - case tspclient.Timestamp: - return v.Format(time.ANSIC) +func addTimestampToTree(node *tree.Node, signerInfo signature.SignerInfo) { + timestampNode := node.Add("timestamp signature") + signedToken, err := tspclient.ParseSignedToken(signerInfo.UnsignedAttributes.TimestampSignature) + if err != nil { + timestampNode.AddPair("error", fmt.Sprintf("failed to parse timestamp countersignature: %s", err)) + return + } + info, err := signedToken.Info() + if err != nil { + timestampNode.AddPair("error", fmt.Sprintf("failed to parse timestamp countersignature: %s", err)) + return + } + timestamp, err := info.Validate(signerInfo.Signature) + if err != nil { + timestampNode.AddPair("error", fmt.Sprintf("failed to parse timestamp countersignature: %s", err)) + return } - return fmt.Sprint(v) + timestampNode.AddPair("timestamp", timestamp.Format(time.RFC3339Nano)) + addCertificatesToTree(timestampNode, signedToken.Certificates) } diff --git a/cmd/notation/internal/display/metadata/tree/inspect_test.go b/cmd/notation/internal/display/metadata/tree/inspect_test.go new file mode 100644 index 000000000..fb6e410f7 --- /dev/null +++ b/cmd/notation/internal/display/metadata/tree/inspect_test.go @@ -0,0 +1,46 @@ +package tree + +// func TestToTreeNode(t *testing.T) { +// t.Run("timestamp error", func(t *testing.T) { +// tsaToken, err := os.ReadFile("./testdata/TimeStampTokenWithInvalidSignature.p7s") +// if err != nil { +// t.Fatal(err) +// } + +// envelopeContent := signature.EnvelopeContent{ +// SignerInfo: signature.SignerInfo{ +// UnsignedAttributes: signature.UnsignedAttributes{ +// TimestampSignature: tsaToken, +// }, +// }, +// } +// sig := &model.Signature{ +// MediaType: "mediaType", +// SignatureAlgorithm: "sha256", +// UnsignedAttributes: getUnsignedAttributes(&envelopeContent, dummyFormatter), +// } + +// node := sig.ToNode("name") + +// if len(node.Children) != 7 { +// t.Fatalf("expected 7 children, but got %d", len(node.Children)) +// } + +// unsignedNode := node.Children[4] +// if len(unsignedNode.Children) != 1 { +// t.Fatalf("expected 1 child, but got %d", len(unsignedNode.Children)) +// } +// timestampNode := unsignedNode.Children[0] +// if len(timestampNode.Children) != 1 { +// t.Fatalf("expected 1 child, but got %d", len(timestampNode.Children)) +// } +// if timestampError, ok := timestampNode.Children[0].Value.(string); ok { +// expectedErrorMsg := "error: failed to parse timestamp countersignature: invalid TSTInfo: mismatched message" +// if timestampError != expectedErrorMsg { +// t.Fatalf("expected %s, but got %s", expectedErrorMsg, timestampError) +// } +// } else { +// t.Fatal("expected timestamp node") +// } +// }) +// } From 37b967a5a3aaa673b21e5065f78406a4aac4278d Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Mon, 20 Jan 2025 06:24:44 +0000 Subject: [PATCH 06/32] fix: update E2E Signed-off-by: Junjie Gao --- .../internal/display/metadata/json/inspect.go | 12 +- .../internal/display/metadata/tree/inspect.go | 109 ++++++++++-------- test/e2e/suite/command/inspect.go | 20 ++-- 3 files changed, 73 insertions(+), 68 deletions(-) diff --git a/cmd/notation/internal/display/metadata/json/inspect.go b/cmd/notation/internal/display/metadata/json/inspect.go index 9d2b4401d..839dedcc7 100644 --- a/cmd/notation/internal/display/metadata/json/inspect.go +++ b/cmd/notation/internal/display/metadata/json/inspect.go @@ -131,7 +131,6 @@ func getSignedAttributes(envelopeContent *signature.EnvelopeContent) map[string] if expiry := envelopeContent.SignerInfo.SignedAttributes.Expiry; !expiry.IsZero() { signedAttributes["expiry"] = expiry } - for _, attribute := range envelopeContent.SignerInfo.SignedAttributes.ExtendedAttributes { signedAttributes[fmt.Sprint(attribute.Key)] = fmt.Sprint(attribute.Value) } @@ -140,20 +139,17 @@ func getSignedAttributes(envelopeContent *signature.EnvelopeContent) map[string] func getUnsignedAttributes(envelopeContent *signature.EnvelopeContent) map[string]any { unsignedAttributes := make(map[string]any) - - if envelopeContent.SignerInfo.UnsignedAttributes.TimestampSignature != nil { - unsignedAttributes["timestampSignature"] = parseTimestamp(envelopeContent.SignerInfo) - } - if envelopeContent.SignerInfo.UnsignedAttributes.SigningAgent != "" { unsignedAttributes["signingAgent"] = envelopeContent.SignerInfo.UnsignedAttributes.SigningAgent } + if envelopeContent.SignerInfo.UnsignedAttributes.TimestampSignature != nil { + unsignedAttributes["timestampSignature"] = parseTimestamp(envelopeContent.SignerInfo) + } return unsignedAttributes } func getCertificates(certChain []*x509.Certificate) []Certificate { - certificates := []Certificate{} - + var certificates []Certificate for _, cert := range certChain { hash := sha256.Sum256(cert.Raw) certificates = append(certificates, Certificate{ diff --git a/cmd/notation/internal/display/metadata/tree/inspect.go b/cmd/notation/internal/display/metadata/tree/inspect.go index b443f16c6..90a26bd85 100644 --- a/cmd/notation/internal/display/metadata/tree/inspect.go +++ b/cmd/notation/internal/display/metadata/tree/inspect.go @@ -17,6 +17,7 @@ import ( "github.com/notaryproject/notation/internal/envelope" "github.com/notaryproject/notation/internal/tree" "github.com/notaryproject/tspclient-go" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) type InspectHandler struct { @@ -48,7 +49,7 @@ func (h *InspectHandler) InspectSignature(digest string, envelopeMediaType strin return fmt.Errorf("artifact reference is not set") } - return addSignatureToTree(h.cncfSigNode, digest, envelopeMediaType, sigEnvelope) + return addSignature(h.cncfSigNode, digest, envelopeMediaType, sigEnvelope) } func (h *InspectHandler) Print() error { @@ -58,32 +59,35 @@ func (h *InspectHandler) Print() error { return h.root.Print(h.printer) } -func formatTime(t time.Time) string { - return t.Format(time.ANSIC) -} - -func addSignatureToTree(node *tree.Node, digest string, envelopeMediaType string, sigEnvelope signature.Envelope) error { +func addSignature(node *tree.Node, digest string, envelopeMediaType string, sigEnvelope signature.Envelope) error { envelopeContent, err := sigEnvelope.Content() if err != nil { return err } - signedArtifactDesc, err := envelope.DescriptorFromSignaturePayload(&envelopeContent.Payload) if err != nil { return err } - signatureAlgorithm, err := proto.EncodeSigningAlgorithm(envelopeContent.SignerInfo.SignatureAlgorithm) if err != nil { return err } + // create signature node sigNode := node.Add(digest) sigNode.AddPair("signature algorithm", string(signatureAlgorithm)) sigNode.AddPair("signature envelope type", envelopeMediaType) - // Add signer attributes - signedAttributesNode := sigNode.Add("signed attributes") + addSignedAttributes(sigNode, envelopeContent) + addUserDefinedAttributes(sigNode, signedArtifactDesc.Annotations) + addUnsignedAttributes(sigNode, envelopeContent) + addCertificates(sigNode, envelopeContent.SignerInfo.CertificateChain) + addSignedArtifact(sigNode, signedArtifactDesc) + return nil +} + +func addSignedAttributes(node *tree.Node, envelopeContent *signature.EnvelopeContent) { + signedAttributesNode := node.Add("signed attributes") signedAttributesNode.AddPair("signing scheme", string(envelopeContent.SignerInfo.SignedAttributes.SigningScheme)) signedAttributesNode.AddPair("signing time", formatTime(envelopeContent.SignerInfo.SignedAttributes.SigningTime)) if expiry := envelopeContent.SignerInfo.SignedAttributes.Expiry; !expiry.IsZero() { @@ -92,57 +96,38 @@ func addSignatureToTree(node *tree.Node, digest string, envelopeMediaType string for _, attribute := range envelopeContent.SignerInfo.SignedAttributes.ExtendedAttributes { signedAttributesNode.AddPair(fmt.Sprint(attribute.Key), fmt.Sprint(attribute.Value)) } +} - // add user defined attributes - userDefinedAttributesNode := sigNode.Add("user defined attributes") - for _, k := range orderedKeys(signedArtifactDesc.Annotations) { - v := signedArtifactDesc.Annotations[k] +func addUserDefinedAttributes(node *tree.Node, annotations map[string]string) { + userDefinedAttributesNode := node.Add("user defined attributes") + if len(annotations) == 0 { + userDefinedAttributesNode.Add("(empty)") + return + } + for _, k := range orderedKeys(annotations) { + v := annotations[k] userDefinedAttributesNode.AddPair(k, v) } +} - // add unsigned attributes - unsignedAttributesNode := sigNode.Add("unsigned attributes") - if timestamp := envelopeContent.SignerInfo.UnsignedAttributes.TimestampSignature; timestamp != nil { - addTimestampToTree(unsignedAttributesNode, envelopeContent.SignerInfo) - } +func addUnsignedAttributes(node *tree.Node, envelopeContent *signature.EnvelopeContent) { + unsignedAttributesNode := node.Add("unsigned attributes") if signingAgent := envelopeContent.SignerInfo.UnsignedAttributes.SigningAgent; signingAgent != "" { unsignedAttributesNode.AddPair("signing agent", signingAgent) } + if timestamp := envelopeContent.SignerInfo.UnsignedAttributes.TimestampSignature; timestamp != nil { + addTimestamp(unsignedAttributesNode, envelopeContent.SignerInfo) + } +} - // add certificate chain - addCertificatesToTree(sigNode, envelopeContent.SignerInfo.CertificateChain) - - // add signed artifact - artifactNode := sigNode.Add("signed artifact") +func addSignedArtifact(node *tree.Node, signedArtifactDesc *ocispec.Descriptor) { + artifactNode := node.Add("signed artifact") artifactNode.AddPair("media type", signedArtifactDesc.MediaType) artifactNode.AddPair("digest", signedArtifactDesc.Digest.String()) artifactNode.AddPair("size", strconv.FormatInt(signedArtifactDesc.Size, 10)) - - return nil -} - -func orderedKeys[T any](m map[string]T) []string { - keys := make([]string, 0, len(m)) - for k := range m { - keys = append(keys, k) - } - slices.Sort(keys) - return keys -} - -func addCertificatesToTree(node *tree.Node, certChain []*x509.Certificate) { - certListNode := node.Add("certificates") - for _, cert := range certChain { - hash := sha256.Sum256(cert.Raw) - - certNode := certListNode.AddPair("SHA256 fingerprint", strings.ToLower(hex.EncodeToString(hash[:]))) - certNode.AddPair("issued to", cert.Subject.String()) - certNode.AddPair("issued by", cert.Issuer.String()) - certNode.AddPair("expiry", formatTime(cert.NotAfter)) - } } -func addTimestampToTree(node *tree.Node, signerInfo signature.SignerInfo) { +func addTimestamp(node *tree.Node, signerInfo signature.SignerInfo) { timestampNode := node.Add("timestamp signature") signedToken, err := tspclient.ParseSignedToken(signerInfo.UnsignedAttributes.TimestampSignature) if err != nil { @@ -159,6 +144,30 @@ func addTimestampToTree(node *tree.Node, signerInfo signature.SignerInfo) { timestampNode.AddPair("error", fmt.Sprintf("failed to parse timestamp countersignature: %s", err)) return } - timestampNode.AddPair("timestamp", timestamp.Format(time.RFC3339Nano)) - addCertificatesToTree(timestampNode, signedToken.Certificates) + timestampNode.AddPair("timestamp", timestamp.Format(time.ANSIC)) + addCertificates(timestampNode, signedToken.Certificates) +} + +func addCertificates(node *tree.Node, certChain []*x509.Certificate) { + certListNode := node.Add("certificates") + for _, cert := range certChain { + hash := sha256.Sum256(cert.Raw) + certNode := certListNode.AddPair("SHA256 fingerprint", strings.ToLower(hex.EncodeToString(hash[:]))) + certNode.AddPair("issued to", cert.Subject.String()) + certNode.AddPair("issued by", cert.Issuer.String()) + certNode.AddPair("expiry", formatTime(cert.NotAfter)) + } +} + +func orderedKeys(m map[string]string) []string { + keys := make([]string, 0, len(m)) + for k := range m { + keys = append(keys, k) + } + slices.Sort(keys) + return keys +} + +func formatTime(t time.Time) string { + return t.Format(time.ANSIC) } diff --git a/test/e2e/suite/command/inspect.go b/test/e2e/suite/command/inspect.go index 0bb477fec..3ca7e5297 100644 --- a/test/e2e/suite/command/inspect.go +++ b/test/e2e/suite/command/inspect.go @@ -29,12 +29,12 @@ var ( "├── media type:", "├── signature algorithm:", "├── signed attributes", - "signingTime:", - "signingScheme:", + "signing time:", + "signing scheme:", "├── user defined attributes", "│ └── (empty)", "├── unsigned attributes", - "│ └── signingAgent: notation-go/", + "│ └── signing agent: notation-go/", "├── certificates", "│ └── SHA256 fingerprint:", "issued to:", @@ -52,12 +52,12 @@ var ( "├── media type:", "├── signature algorithm:", "├── signed attributes", - "signingTime:", - "signingScheme:", + "signing time:", + "signing scheme:", "├── user defined attributes", "│ └── (empty)", "├── unsigned attributes", - "signingAgent: notation-go/", + "signing agent: notation-go/", "timestamp signature", "timestamp:", "certificates", @@ -182,12 +182,12 @@ var _ = Describe("notation inspect", func() { ├── signature algorithm: RSASSA-PSS-SHA-256 ├── signature envelope type: application/jose+json ├── signed attributes - │ ├── signingScheme: notary.x509 - │ └── signingTime: Fri Jan 17 06:36:19 2025 + │ ├── signing scheme: notary.x509 + │ └── signing time: Fri Jan 17 06:36:19 2025 ├── user defined attributes │ └── (empty) ├── unsigned attributes - │ ├── signingAgent: notation-go/1.3.0+unreleased + │ ├── signing agent: notation-go/1.3.0+unreleased │ └── timestamp signature │ ├── timestamp: [Fri Jan 17 06:36:19 2025, Fri Jan 17 06:36:20 2025] │ └── certificates @@ -233,7 +233,7 @@ var _ = Describe("notation inspect", func() { "unsignedAttributes": { "signingAgent": "notation-go/1.3.0+unreleased", "timestampSignature": { - "timestamp": "[2025-01-17T06:36:19Z, 2025-01-17T06:36:20Z]", + "timestamp": "[2025-01-17T06:36:19.734Z, 2025-01-17T06:36:20.734Z]", "certificates": [ { "SHA256Fingerprint": "36e731cfa9bfd69dafb643809f6dec500902f7197daeaad86ea0159a2268a2b8", From c2697db3a677bb5929554becb188f2f0c3bdae61 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Mon, 20 Jan 2025 06:52:55 +0000 Subject: [PATCH 07/32] fix: resolve comments Signed-off-by: Junjie Gao --- cmd/notation/inspect.go | 15 +++---- cmd/notation/internal/display/handler.go | 16 +++++++ .../internal/display/metadata/interface.go | 28 +++++++++--- .../internal/display/metadata/json/inspect.go | 24 +++++++--- .../internal/display/metadata/tree/inspect.go | 28 +++++++++--- .../display/metadata/tree/inspect_test.go | 13 ++++++ cmd/notation/internal/option/common.go | 45 +++++++++++++++++++ cmd/notation/internal/option/format.go | 15 +++++++ cmd/notation/internal/output/json.go | 15 +++++++ cmd/notation/internal/output/print.go | 25 ++++++----- cmd/notation/internal/output/print_test.go | 9 ---- 11 files changed, 186 insertions(+), 47 deletions(-) create mode 100644 cmd/notation/internal/option/common.go diff --git a/cmd/notation/inspect.go b/cmd/notation/inspect.go index 2c51f0f23..1f1beece6 100644 --- a/cmd/notation/inspect.go +++ b/cmd/notation/inspect.go @@ -23,7 +23,6 @@ import ( cmderr "github.com/notaryproject/notation/cmd/notation/internal/errors" "github.com/notaryproject/notation/cmd/notation/internal/experimental" "github.com/notaryproject/notation/cmd/notation/internal/option" - "github.com/notaryproject/notation/cmd/notation/internal/output" "github.com/notaryproject/notation/internal/cmd" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/spf13/cobra" @@ -32,6 +31,7 @@ import ( type inspectOpts struct { cmd.LoggingFlagOpts SecureFlagOpts + option.Common option.Format reference string allowReferrersAPI bool @@ -68,6 +68,9 @@ Example - Inspect signatures on an OCI artifact identified by a digest and outpu if err := opts.Format.Parse(cmd); err != nil { return err } + if err := opts.Common.Parse(cmd); err != nil { + return err + } return experimental.CheckFlagsAndWarn(cmd, "allow-referrers-api") }, RunE: func(cmd *cobra.Command, args []string) error { @@ -95,9 +98,8 @@ Example - Inspect signatures on an OCI artifact identified by a digest and outpu func runInspect(command *cobra.Command, opts *inspectOpts) error { // set log level ctx := opts.LoggingFlagOpts.InitializeLogger(command.Context()) - printer := output.NewPrinter(command.OutOrStdout(), command.OutOrStderr()) - displayHandler, err := display.NewInpsectHandler(printer, opts.Format) + displayHandler, err := display.NewInpsectHandler(opts.Printer, opts.Format) if err != nil { return err } @@ -114,10 +116,7 @@ func runInspect(command *cobra.Command, opts *inspectOpts) error { if err != nil { return err } - - // set output headers - displayHandler.SetMediaType(manifestDesc.MediaType) - displayHandler.SetReference(resolvedRef) + displayHandler.OnReferenceResolved(resolvedRef, manifestDesc.MediaType) skippedSignatures := false err = listSignatures(ctx, sigRepo, manifestDesc, opts.maxSignatures, func(sigManifestDesc ocispec.Descriptor) error { @@ -147,7 +146,7 @@ func runInspect(command *cobra.Command, opts *inspectOpts) error { return err } - if err := displayHandler.Print(); err != nil { + if err := displayHandler.Render(); err != nil { return err } diff --git a/cmd/notation/internal/display/handler.go b/cmd/notation/internal/display/handler.go index 31af48c31..60a296786 100644 --- a/cmd/notation/internal/display/handler.go +++ b/cmd/notation/internal/display/handler.go @@ -1,3 +1,18 @@ +// 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 display provides the display handlers to render information for +// commands. package display import ( @@ -10,6 +25,7 @@ import ( "github.com/notaryproject/notation/cmd/notation/internal/output" ) +// NewInpsectHandler creates a new InspectHandler based on the output format. func NewInpsectHandler(printer *output.Printer, format option.Format) (metadata.InspectHandler, error) { switch option.FormatType(format.FormatFlag) { case option.FormatTypeJSON: diff --git a/cmd/notation/internal/display/metadata/interface.go b/cmd/notation/internal/display/metadata/interface.go index 215c887a5..5bebff0ee 100644 --- a/cmd/notation/internal/display/metadata/interface.go +++ b/cmd/notation/internal/display/metadata/interface.go @@ -1,17 +1,31 @@ +// 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 metadata import "github.com/notaryproject/notation-core-go/signature" +// Renderer renders metadata information when an operation is complete. +type Renderer interface { + Render() error +} + type InspectHandler interface { - // SetReference sets the artifact reference for the handler. - SetReference(reference string) + Renderer - // SetMediaType sets the media type for the handler. - SetMediaType(mediaType string) + // OnReferenceResolved sets the artifact reference and media type for the handler. + OnReferenceResolved(reference, mediaType string) // InspectSignature inspects a signature to get it ready to be rendered. InspectSignature(digest string, envelopeMediaType string, sigEnvelope signature.Envelope) error - - // Print prints the metadata. - Print() error } diff --git a/cmd/notation/internal/display/metadata/json/inspect.go b/cmd/notation/internal/display/metadata/json/inspect.go index 839dedcc7..0c7437e58 100644 --- a/cmd/notation/internal/display/metadata/json/inspect.go +++ b/cmd/notation/internal/display/metadata/json/inspect.go @@ -1,3 +1,16 @@ +// 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 json import ( @@ -64,12 +77,11 @@ func NewInspectHandler(printer *output.Printer) *InspectHandler { } } -// SetReference sets the artifact reference for the handler. It is a no-op for this +// OnReferenceResolved sets the artifact reference and media type for the // handler. -func (h *InspectHandler) SetReference(_ string) {} - -// SetMediaType sets the media type for the handler. -func (h *InspectHandler) SetMediaType(mediaType string) { +// +// the reference is no-op for this handler. +func (h *InspectHandler) OnReferenceResolved(reference, mediaType string) { if h.output.MediaType == "" { h.output.MediaType = mediaType } @@ -85,7 +97,7 @@ func (h *InspectHandler) InspectSignature(digest string, envelopeMediaType strin return nil } -func (h *InspectHandler) Print() error { +func (h *InspectHandler) Render() error { return output.PrintPrettyJSON(h.printer, h.output) } diff --git a/cmd/notation/internal/display/metadata/tree/inspect.go b/cmd/notation/internal/display/metadata/tree/inspect.go index 90a26bd85..0b0012bac 100644 --- a/cmd/notation/internal/display/metadata/tree/inspect.go +++ b/cmd/notation/internal/display/metadata/tree/inspect.go @@ -1,3 +1,16 @@ +// 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 tree import ( @@ -32,18 +45,18 @@ func NewInspectHandler(printer *output.Printer) *InspectHandler { } } -// SetReference sets the artifact reference for the handler. -func (h *InspectHandler) SetReference(reference string) { +// OnReferenceResolved sets the artifact reference and media type for the +// handler. +// +// mediaType is a no-op for this handler. +func (h *InspectHandler) OnReferenceResolved(reference, mediaType string) { if h.root == nil { h.root = tree.New(reference) h.cncfSigNode = h.root.Add(registry.ArtifactTypeNotation) } } -// SetMediaType sets the media type for the handler. It is a no-op for this -// handler. -func (h *InspectHandler) SetMediaType(_ string) {} - +// InspectSignature inspects a signature to get it ready to be rendered. func (h *InspectHandler) InspectSignature(digest string, envelopeMediaType string, sigEnvelope signature.Envelope) error { if h.root == nil || h.cncfSigNode == nil { return fmt.Errorf("artifact reference is not set") @@ -52,7 +65,8 @@ func (h *InspectHandler) InspectSignature(digest string, envelopeMediaType strin return addSignature(h.cncfSigNode, digest, envelopeMediaType, sigEnvelope) } -func (h *InspectHandler) Print() error { +// Render renders the metadata information when an operation is complete. +func (h *InspectHandler) Render() error { if h.root == nil { return fmt.Errorf("artifact reference is not set") } diff --git a/cmd/notation/internal/display/metadata/tree/inspect_test.go b/cmd/notation/internal/display/metadata/tree/inspect_test.go index fb6e410f7..8379c467e 100644 --- a/cmd/notation/internal/display/metadata/tree/inspect_test.go +++ b/cmd/notation/internal/display/metadata/tree/inspect_test.go @@ -1,3 +1,16 @@ +// 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 tree // func TestToTreeNode(t *testing.T) { diff --git a/cmd/notation/internal/option/common.go b/cmd/notation/internal/option/common.go new file mode 100644 index 000000000..612a08513 --- /dev/null +++ b/cmd/notation/internal/option/common.go @@ -0,0 +1,45 @@ +// 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. + +// copied and adopted from https://github.com/oras-project/oras with +// modification +/* +Copyright The ORAS 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 option + +import ( + "github.com/notaryproject/notation/cmd/notation/internal/output" + "github.com/spf13/cobra" +) + +// Common option struct. +type Common struct { + Printer *output.Printer +} + +// Parse gets target options from user input. +func (opts *Common) Parse(cmd *cobra.Command) error { + opts.Printer = output.NewPrinter(cmd.OutOrStdout(), cmd.OutOrStderr()) + return nil +} diff --git a/cmd/notation/internal/option/format.go b/cmd/notation/internal/option/format.go index 5c2a8f280..1c836e1c9 100644 --- a/cmd/notation/internal/option/format.go +++ b/cmd/notation/internal/option/format.go @@ -13,6 +13,21 @@ See the License for the specific language governing permissions and limitations under the License. */ +// copied and adopted from https://github.com/oras-project/oras with +// modification +/* +Copyright The ORAS 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 option import ( diff --git a/cmd/notation/internal/output/json.go b/cmd/notation/internal/output/json.go index 184e1f7bd..812af3cb8 100644 --- a/cmd/notation/internal/output/json.go +++ b/cmd/notation/internal/output/json.go @@ -13,6 +13,21 @@ See the License for the specific language governing permissions and limitations under the License. */ +// copied and adopted from https://github.com/oras-project/oras with +// modification +/* +Copyright The ORAS 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 output import ( diff --git a/cmd/notation/internal/output/print.go b/cmd/notation/internal/output/print.go index 287f0831f..8c8f5a5eb 100644 --- a/cmd/notation/internal/output/print.go +++ b/cmd/notation/internal/output/print.go @@ -13,6 +13,21 @@ See the License for the specific language governing permissions and limitations under the License. */ +// copied and adopted from https://github.com/oras-project/oras with +// modification +/* +Copyright The ORAS 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 output import ( @@ -23,8 +38,6 @@ import ( // Printer prints for status handlers. type Printer struct { - Verbose bool - out io.Writer err io.Writer lock sync.Mutex @@ -67,11 +80,3 @@ func (p *Printer) Printf(format string, a ...any) error { // Errors are handled above, so return nil return nil } - -// PrintVerbose prints when verbose is true. -func (p *Printer) PrintVerbose(a ...any) error { - if !p.Verbose { - return nil - } - return p.Println(a...) -} diff --git a/cmd/notation/internal/output/print_test.go b/cmd/notation/internal/output/print_test.go index 38466f20e..7c8d2f1d1 100644 --- a/cmd/notation/internal/output/print_test.go +++ b/cmd/notation/internal/output/print_test.go @@ -73,10 +73,6 @@ func TestPrinter_PrintVerbose_noError(t *testing.T) { if err != nil { t.Error("Expected no error got <" + err.Error() + ">") } - err = printer.PrintVerbose("verbose") - if err != nil { - t.Error("Expected no error got <" + err.Error() + ">") - } actual := builder.String() if expected != actual { t.Error("Expected <" + expected + "> not equal to actual <" + actual + ">") @@ -86,17 +82,12 @@ func TestPrinter_PrintVerbose_noError(t *testing.T) { func TestPrinter_PrintVerbose(t *testing.T) { builder := &strings.Builder{} printer := NewPrinter(builder, os.Stderr) - printer.Verbose = true expected := "normal\nverbose\n" err := printer.Println("normal") if err != nil { t.Error("Expected no error got <" + err.Error() + ">") } - err = printer.PrintVerbose("verbose") - if err != nil { - t.Error("Expected no error got <" + err.Error() + ">") - } actual := builder.String() if expected != actual { t.Error("Expected <" + expected + "> not equal to actual <" + actual + ">") From edbfbad5e05fe582306cc4c03af2545f377a95d3 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Mon, 20 Jan 2025 07:00:14 +0000 Subject: [PATCH 08/32] fix: update license header Signed-off-by: Junjie Gao --- cmd/notation/internal/option/format.go | 26 ++++++++++------------ cmd/notation/internal/output/json.go | 26 ++++++++++------------ cmd/notation/internal/output/json_test.go | 17 ++++++++++++-- cmd/notation/internal/output/print.go | 26 ++++++++++------------ cmd/notation/internal/output/print_test.go | 17 ++++++++++++-- 5 files changed, 66 insertions(+), 46 deletions(-) diff --git a/cmd/notation/internal/option/format.go b/cmd/notation/internal/option/format.go index 1c836e1c9..a32447073 100644 --- a/cmd/notation/internal/option/format.go +++ b/cmd/notation/internal/option/format.go @@ -1,17 +1,15 @@ -/* -Copyright The ORAS 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. -*/ +// 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. // copied and adopted from https://github.com/oras-project/oras with // modification diff --git a/cmd/notation/internal/output/json.go b/cmd/notation/internal/output/json.go index 812af3cb8..667dff568 100644 --- a/cmd/notation/internal/output/json.go +++ b/cmd/notation/internal/output/json.go @@ -1,17 +1,15 @@ -/* -Copyright The ORAS 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. -*/ +// 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. // copied and adopted from https://github.com/oras-project/oras with // modification diff --git a/cmd/notation/internal/output/json_test.go b/cmd/notation/internal/output/json_test.go index 50b6d0c93..893f4e42c 100644 --- a/cmd/notation/internal/output/json_test.go +++ b/cmd/notation/internal/output/json_test.go @@ -1,11 +1,24 @@ +// 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. + +// copied and adopted from https://github.com/oras-project/oras with +// modification /* Copyright The ORAS 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. diff --git a/cmd/notation/internal/output/print.go b/cmd/notation/internal/output/print.go index 8c8f5a5eb..cf10931e3 100644 --- a/cmd/notation/internal/output/print.go +++ b/cmd/notation/internal/output/print.go @@ -1,17 +1,15 @@ -/* -Copyright The ORAS 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. -*/ +// 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. // copied and adopted from https://github.com/oras-project/oras with // modification diff --git a/cmd/notation/internal/output/print_test.go b/cmd/notation/internal/output/print_test.go index 7c8d2f1d1..4115e6a5b 100644 --- a/cmd/notation/internal/output/print_test.go +++ b/cmd/notation/internal/output/print_test.go @@ -1,11 +1,24 @@ +// 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. + +// copied and adopted from https://github.com/oras-project/oras with +// modification /* Copyright The ORAS 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. From eb078e3317db8ca8334b1e468de5a49fa9777e99 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Mon, 20 Jan 2025 07:03:08 +0000 Subject: [PATCH 09/32] fix: update test Signed-off-by: Junjie Gao --- cmd/notation/internal/output/print_test.go | 34 ---------------------- 1 file changed, 34 deletions(-) diff --git a/cmd/notation/internal/output/print_test.go b/cmd/notation/internal/output/print_test.go index 4115e6a5b..f5a8149bd 100644 --- a/cmd/notation/internal/output/print_test.go +++ b/cmd/notation/internal/output/print_test.go @@ -72,37 +72,3 @@ func TestPrinter_Println(t *testing.T) { t.Error("Expected error to be ignored") } } - -func TestPrinter_PrintVerbose_noError(t *testing.T) { - builder := &strings.Builder{} - printer := NewPrinter(builder, os.Stderr) - - expected := "normal\nthing one\n" - err := printer.Println("normal") - if err != nil { - t.Error("Expected no error got <" + err.Error() + ">") - } - err = printer.Printf("thing %s\n", "one") - if err != nil { - t.Error("Expected no error got <" + err.Error() + ">") - } - actual := builder.String() - if expected != actual { - t.Error("Expected <" + expected + "> not equal to actual <" + actual + ">") - } -} - -func TestPrinter_PrintVerbose(t *testing.T) { - builder := &strings.Builder{} - printer := NewPrinter(builder, os.Stderr) - - expected := "normal\nverbose\n" - err := printer.Println("normal") - if err != nil { - t.Error("Expected no error got <" + err.Error() + ">") - } - actual := builder.String() - if expected != actual { - t.Error("Expected <" + expected + "> not equal to actual <" + actual + ">") - } -} From 4d3aedd8ac195594ff551937983e3edb7d1af853 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Mon, 20 Jan 2025 07:37:01 +0000 Subject: [PATCH 10/32] test: add unit test Signed-off-by: Junjie Gao --- .../internal/display/metadata/json/inspect.go | 10 +- .../display/metadata/json/inspect_test.go | 27 ++++ .../internal/display/metadata/tree/inspect.go | 6 +- .../display/metadata/tree/inspect_test.go | 147 ++++++++++++------ 4 files changed, 142 insertions(+), 48 deletions(-) diff --git a/cmd/notation/internal/display/metadata/json/inspect.go b/cmd/notation/internal/display/metadata/json/inspect.go index 0c7437e58..a6f3fb35a 100644 --- a/cmd/notation/internal/display/metadata/json/inspect.go +++ b/cmd/notation/internal/display/metadata/json/inspect.go @@ -63,11 +63,12 @@ type Timestamp struct { Error string `json:"error,omitempty"` } +// InspectHandler is the handler for inspecting metadata information and +// rendering it in JSON format. type InspectHandler struct { - output inspectOutput - - // printer is the printer for output. printer *output.Printer + + output inspectOutput } // NewInspectHandler creates a new InspectHandler. @@ -98,6 +99,9 @@ func (h *InspectHandler) InspectSignature(digest string, envelopeMediaType strin } func (h *InspectHandler) Render() error { + if h.output.MediaType == "" { + return fmt.Errorf("media type is not set") + } return output.PrintPrettyJSON(h.printer, h.output) } diff --git a/cmd/notation/internal/display/metadata/json/inspect_test.go b/cmd/notation/internal/display/metadata/json/inspect_test.go index 1295e6b07..3251c169d 100644 --- a/cmd/notation/internal/display/metadata/json/inspect_test.go +++ b/cmd/notation/internal/display/metadata/json/inspect_test.go @@ -14,13 +14,29 @@ package json import ( + "errors" "os" + "strings" "testing" "time" "github.com/notaryproject/notation-core-go/signature" ) +type errorEnvelope struct{} + +func (e errorEnvelope) Sign(req *signature.SignRequest) ([]byte, error) { + return nil, errors.New("mock sign error") +} + +func (e errorEnvelope) Verify() (*signature.EnvelopeContent, error) { + return nil, errors.New("mock verify error") +} + +func (e errorEnvelope) Content() (*signature.EnvelopeContent, error) { + return nil, errors.New("mock content error") +} + func TestGetUnsignedAttributes(t *testing.T) { envContent := &signature.EnvelopeContent{ SignerInfo: signature.SignerInfo{ @@ -98,3 +114,14 @@ func TestParseTimestamp(t *testing.T) { } }) } + +func TestInspectSignature_NewSignatureError(t *testing.T) { + h := NewInspectHandler(nil) + // ...existing code to ensure h.output.MediaType is set... + h.OnReferenceResolved("test-ref", "test-media-type") + + err := h.InspectSignature("fake-digest", "fake-media-type", errorEnvelope{}) + if err == nil || !strings.Contains(err.Error(), "mock content error") { + t.Fatalf("expected error 'mock content error', got %v", err) + } +} diff --git a/cmd/notation/internal/display/metadata/tree/inspect.go b/cmd/notation/internal/display/metadata/tree/inspect.go index 0b0012bac..e9e6a1bcf 100644 --- a/cmd/notation/internal/display/metadata/tree/inspect.go +++ b/cmd/notation/internal/display/metadata/tree/inspect.go @@ -33,12 +33,16 @@ import ( ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) +// InspectHandler is a handler for inspecting metadata information and rendering +// it in a tree format. type InspectHandler struct { + printer *output.Printer + root *tree.Node cncfSigNode *tree.Node - printer *output.Printer } +// NewInspectHandler creates a new InspectHandler. func NewInspectHandler(printer *output.Printer) *InspectHandler { return &InspectHandler{ printer: printer, diff --git a/cmd/notation/internal/display/metadata/tree/inspect_test.go b/cmd/notation/internal/display/metadata/tree/inspect_test.go index 8379c467e..ac2e32fd1 100644 --- a/cmd/notation/internal/display/metadata/tree/inspect_test.go +++ b/cmd/notation/internal/display/metadata/tree/inspect_test.go @@ -13,47 +13,106 @@ package tree -// func TestToTreeNode(t *testing.T) { -// t.Run("timestamp error", func(t *testing.T) { -// tsaToken, err := os.ReadFile("./testdata/TimeStampTokenWithInvalidSignature.p7s") -// if err != nil { -// t.Fatal(err) -// } - -// envelopeContent := signature.EnvelopeContent{ -// SignerInfo: signature.SignerInfo{ -// UnsignedAttributes: signature.UnsignedAttributes{ -// TimestampSignature: tsaToken, -// }, -// }, -// } -// sig := &model.Signature{ -// MediaType: "mediaType", -// SignatureAlgorithm: "sha256", -// UnsignedAttributes: getUnsignedAttributes(&envelopeContent, dummyFormatter), -// } - -// node := sig.ToNode("name") - -// if len(node.Children) != 7 { -// t.Fatalf("expected 7 children, but got %d", len(node.Children)) -// } - -// unsignedNode := node.Children[4] -// if len(unsignedNode.Children) != 1 { -// t.Fatalf("expected 1 child, but got %d", len(unsignedNode.Children)) -// } -// timestampNode := unsignedNode.Children[0] -// if len(timestampNode.Children) != 1 { -// t.Fatalf("expected 1 child, but got %d", len(timestampNode.Children)) -// } -// if timestampError, ok := timestampNode.Children[0].Value.(string); ok { -// expectedErrorMsg := "error: failed to parse timestamp countersignature: invalid TSTInfo: mismatched message" -// if timestampError != expectedErrorMsg { -// t.Fatalf("expected %s, but got %s", expectedErrorMsg, timestampError) -// } -// } else { -// t.Fatal("expected timestamp node") -// } -// }) -// } +import ( + "fmt" + "testing" + "time" + + "github.com/notaryproject/notation-core-go/signature" + "github.com/notaryproject/notation/internal/tree" +) + +func TestInspectSignatureNoRoot(t *testing.T) { + h := NewInspectHandler(nil) + err := h.InspectSignature("test-digest", "application/notation", nil) + if err == nil || err.Error() != "artifact reference is not set" { + t.Fatalf("expected error 'artifact reference is not set', got: %v", err) + } +} + +func TestRenderNoRoot(t *testing.T) { + h := NewInspectHandler(nil) + err := h.Render() + if err == nil || err.Error() != "artifact reference is not set" { + t.Fatalf("expected error 'artifact reference is not set', got: %v", err) + } +} + +func TestAddSignedAttributes(t *testing.T) { + t.Run("empty envelopeContent", func(t *testing.T) { + node := tree.New("root") + ec := &signature.EnvelopeContent{} + addSignedAttributes(node, ec) + // No error or panic expected; minimal check or just ensure it doesn't crash. + }) + + t.Run("with expiry and extented node", func(t *testing.T) { + node := tree.New("root") + expiryTime := time.Now().Add(time.Hour) + ec := &signature.EnvelopeContent{ + SignerInfo: signature.SignerInfo{ + SignedAttributes: signature.SignedAttributes{ + Expiry: expiryTime, + ExtendedAttributes: []signature.Attribute{ + { + Key: "key", + Value: "value", + }, + }, + }, + }, + } + addSignedAttributes(node, ec) + // Verify node was added; for brevity, just check no panic + if len(node.Children) == 0 { + t.Fatal("expected children to be added") + } + signedAttrNode := node.Children[0] + if signedAttrNode.Value != "signed attributes" { + t.Fatalf("expected name 'signed attributes', got: %v", signedAttrNode.Value) + } + if len(signedAttrNode.Children) != 4 { + t.Fatalf("expected 3 children, got: %v", len(signedAttrNode.Children)) + } + // verify expiry node + expiryNode := signedAttrNode.Children[2] + if expiryNode.Value != fmt.Sprintf("expiry: %s", expiryTime.Format(time.ANSIC)) { + t.Fatalf("expected expiry node, got: %v", expiryNode.Value) + } + // verify extended attribute node + extendedAttrNode := signedAttrNode.Children[3] + if extendedAttrNode.Value != "key: value" { + t.Fatalf("expected extended attribute node, got: %v", extendedAttrNode.Value) + } + }) +} + +func TestAddUserDefinedAttributes(t *testing.T) { + t.Run("empty map", func(t *testing.T) { + node := tree.New("root") + addUserDefinedAttributes(node, nil) + if len(node.Children) == 0 { + t.Fatal("expected node to have children") + } + udaNode := node.Children[0] + if udaNode.Value != "user defined attributes" { + t.Fatalf("expected 'user defined attributes' node, got %s", udaNode.Value) + } + if len(udaNode.Children) == 0 || udaNode.Children[0].Value != "(empty)" { + t.Fatalf("expected '(empty)' node, got %v", udaNode.Children) + } + }) + + t.Run("non-empty map", func(t *testing.T) { + node := tree.New("root") + annotations := map[string]string{"key1": "val1", "key2": "val2"} + addUserDefinedAttributes(node, annotations) + udaNode := node.Children[0] + if udaNode.Value != "user defined attributes" { + t.Fatalf("expected 'user defined attributes' node, got %s", udaNode.Value) + } + if len(udaNode.Children) != len(annotations) { + t.Fatalf("expected %d children, got %d", len(annotations), len(udaNode.Children)) + } + }) +} From 8fdc98979f0d538bee5977c09f963b73e4cfd827 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Mon, 20 Jan 2025 08:04:50 +0000 Subject: [PATCH 11/32] test: add unit test for inspect Signed-off-by: Junjie Gao --- cmd/notation/inspect_test.go | 19 ++++++++ .../display/metadata/json/inspect_test.go | 8 ++++ cmd/notation/internal/output/print_test.go | 45 ++++++++++++------- 3 files changed, 56 insertions(+), 16 deletions(-) diff --git a/cmd/notation/inspect_test.go b/cmd/notation/inspect_test.go index 72584ed2e..bd437c783 100644 --- a/cmd/notation/inspect_test.go +++ b/cmd/notation/inspect_test.go @@ -93,3 +93,22 @@ func TestInspectCommand_MissingArgs(t *testing.T) { t.Fatal("Parse Args expected error, but ok") } } + +func TestInspectCommand_Invalid_Output(t *testing.T) { + opts := &inspectOpts{} + command := inspectCommand(opts) + if err := command.ParseFlags([]string{ + "ref", + "--output", "invalidFormat"}); 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 err := command.PreRunE(command, command.Flags().Args()); err == nil || err.Error() != "invalid format type: \"invalidFormat\"" { + t.Fatalf("PreRunE expected error 'invalid format type: \"invalidFormat\"', got: %v", err) + } + if err := command.RunE(command, command.Flags().Args()); err == nil || err.Error() != "unrecognized output format invalidFormat" { + t.Fatalf("RunE expected error 'unrecognized output format invalidFormat', got: %v", err) + } +} diff --git a/cmd/notation/internal/display/metadata/json/inspect_test.go b/cmd/notation/internal/display/metadata/json/inspect_test.go index 3251c169d..7a027643c 100644 --- a/cmd/notation/internal/display/metadata/json/inspect_test.go +++ b/cmd/notation/internal/display/metadata/json/inspect_test.go @@ -125,3 +125,11 @@ func TestInspectSignature_NewSignatureError(t *testing.T) { t.Fatalf("expected error 'mock content error', got %v", err) } } + +func TestRenderNoMediaType(t *testing.T) { + h := NewInspectHandler(nil) + err := h.Render() + if err == nil || !strings.Contains(err.Error(), "media type is not set") { + t.Fatalf("expected error 'media type is not set', got %v", err) + } +} diff --git a/cmd/notation/internal/output/print_test.go b/cmd/notation/internal/output/print_test.go index f5a8149bd..d8e6f58ee 100644 --- a/cmd/notation/internal/output/print_test.go +++ b/cmd/notation/internal/output/print_test.go @@ -31,7 +31,6 @@ package output import ( "fmt" "os" - "strconv" "strings" "testing" ) @@ -54,21 +53,35 @@ func (mw *mockWriter) String() string { return mw.written } -func TestPrinter_Println(t *testing.T) { +func TestPrinter_Print(t *testing.T) { mockWriter := &mockWriter{} printer := NewPrinter(mockWriter, os.Stderr) - err := printer.Println("boom") - if mockWriter.errorCount != 1 { - t.Error("Expected one error actual <" + strconv.Itoa(mockWriter.errorCount) + ">") - } - if err == nil { - t.Error("Expected error got ") - } - err = printer.Printf("boom") - if mockWriter.errorCount != 2 { - t.Error("Expected two errors actual <" + strconv.Itoa(mockWriter.errorCount) + ">") - } - if err != nil { - t.Error("Expected error to be ignored") - } + + t.Run("Println success", func(t *testing.T) { + err := printer.Println("hello") + if err != nil { + t.Errorf("Expected no error got <%v>", err) + } + if mockWriter.String() != "hello\n" { + t.Errorf("Expected hello got <%s>", mockWriter.String()) + } + }) + t.Run("Println failed", func(t *testing.T) { + err := printer.Println("boom") + if mockWriter.errorCount != 1 { + t.Errorf("Expected one error actual <%d>", mockWriter.errorCount) + } + if err == nil { + t.Error("Expected error got ") + } + }) + t.Run("Printf failed", func(t *testing.T) { + err := printer.Printf("boom") + if mockWriter.errorCount != 2 { + t.Errorf("Expected two errors actual <%d>", mockWriter.errorCount) + } + if err != nil { + t.Error("Expected error to be ignored") + } + }) } From 0640165c172a7e4e0b8b1771ea1114c37b8e350a Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Mon, 20 Jan 2025 08:33:55 +0000 Subject: [PATCH 12/32] test: update test Signed-off-by: Junjie Gao --- .../internal/display/metadata/interface.go | 2 + .../display/metadata/json/inspect_test.go | 2 +- .../TimeStampTokenWithInvalidSignature.p7s | Bin .../display/metadata/tree/inspect_test.go | 57 ++++++++++++++++++ 4 files changed, 60 insertions(+), 1 deletion(-) rename cmd/notation/internal/display/metadata/{json => }/testdata/TimeStampTokenWithInvalidSignature.p7s (100%) diff --git a/cmd/notation/internal/display/metadata/interface.go b/cmd/notation/internal/display/metadata/interface.go index 5bebff0ee..84d18a343 100644 --- a/cmd/notation/internal/display/metadata/interface.go +++ b/cmd/notation/internal/display/metadata/interface.go @@ -20,6 +20,8 @@ type Renderer interface { Render() error } +// InspectHandler is a handler for inspecting metadata information and rendering +// it in a specific format. type InspectHandler interface { Renderer diff --git a/cmd/notation/internal/display/metadata/json/inspect_test.go b/cmd/notation/internal/display/metadata/json/inspect_test.go index 7a027643c..16f7ca120 100644 --- a/cmd/notation/internal/display/metadata/json/inspect_test.go +++ b/cmd/notation/internal/display/metadata/json/inspect_test.go @@ -96,7 +96,7 @@ func TestParseTimestamp(t *testing.T) { }) t.Run("timestamp validation error", func(t *testing.T) { - tsaToken, err := os.ReadFile("./testdata/TimeStampTokenWithInvalidSignature.p7s") + tsaToken, err := os.ReadFile("../testdata/TimeStampTokenWithInvalidSignature.p7s") if err != nil { t.Fatal(err) } diff --git a/cmd/notation/internal/display/metadata/json/testdata/TimeStampTokenWithInvalidSignature.p7s b/cmd/notation/internal/display/metadata/testdata/TimeStampTokenWithInvalidSignature.p7s similarity index 100% rename from cmd/notation/internal/display/metadata/json/testdata/TimeStampTokenWithInvalidSignature.p7s rename to cmd/notation/internal/display/metadata/testdata/TimeStampTokenWithInvalidSignature.p7s diff --git a/cmd/notation/internal/display/metadata/tree/inspect_test.go b/cmd/notation/internal/display/metadata/tree/inspect_test.go index ac2e32fd1..c3b1c2d4b 100644 --- a/cmd/notation/internal/display/metadata/tree/inspect_test.go +++ b/cmd/notation/internal/display/metadata/tree/inspect_test.go @@ -15,6 +15,7 @@ package tree import ( "fmt" + "os" "testing" "time" @@ -116,3 +117,59 @@ func TestAddUserDefinedAttributes(t *testing.T) { } }) } + +func TestParseTimestamp(t *testing.T) { + t.Run("invalid timestamp signature", func(t *testing.T) { + node := tree.New("root") + signerInfo := signature.SignerInfo{ + UnsignedAttributes: signature.UnsignedAttributes{ + TimestampSignature: []byte("invalid"), + }, + } + addTimestamp(node, signerInfo) + if len(node.Children) == 0 { + t.Fatal("expected node to have children") + } + timestampNode := node.Children[0] + if timestampNode.Value != "timestamp signature" { + t.Fatalf("expected 'timestamp signature' node, got %s", timestampNode.Value) + } + if len(timestampNode.Children) == 0 { + t.Fatal("expected node to have children") + } + errNode := timestampNode.Children[0] + expectedErrMsg := "error: failed to parse timestamp countersignature: cms: syntax error: invalid signed data: failed to convert from BER to DER: asn1: syntax error: decoding BER length octets: short form length octets value should be less or equal to the subsequent octets length" + if errNode.Value != expectedErrMsg { + t.Fatalf("expected error node, got %s", errNode.Value) + } + }) + + t.Run("timestamp validation error", func(t *testing.T) { + tsaToken, err := os.ReadFile("../testdata/TimeStampTokenWithInvalidSignature.p7s") + if err != nil { + t.Fatal(err) + } + signerInfo := signature.SignerInfo{ + UnsignedAttributes: signature.UnsignedAttributes{ + TimestampSignature: tsaToken, + }, + } + node := tree.New("root") + addTimestamp(node, signerInfo) + if len(node.Children) == 0 { + t.Fatal("expected node to have children") + } + timestampNode := node.Children[0] + if timestampNode.Value != "timestamp signature" { + t.Fatalf("expected 'timestamp signature' node, got %s", timestampNode.Value) + } + if len(timestampNode.Children) == 0 { + t.Fatal("expected node to have children") + } + errNode := timestampNode.Children[0] + expectedErrMsg := "error: failed to parse timestamp countersignature: invalid TSTInfo: mismatched message" + if errNode.Value != expectedErrMsg { + t.Fatalf("expected error node, got %s", errNode.Value) + } + }) +} From a1e411f29fbb4078c1c1a8e25fc1704bd8dfaa9e Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Tue, 21 Jan 2025 01:44:18 +0000 Subject: [PATCH 13/32] fix: resolve comemnts Signed-off-by: Junjie Gao --- cmd/notation/inspect.go | 2 +- .../internal/display/metadata/interface.go | 7 +- .../internal/display/metadata/json/inspect.go | 72 +++++++++---------- .../display/metadata/json/inspect_test.go | 45 ++++++------ .../internal/display/metadata/tree/inspect.go | 33 +++++---- .../display/metadata/tree/inspect_test.go | 16 ----- cmd/notation/internal/option/format.go | 3 + internal/envelope/envelope.go | 10 +-- test/e2e/suite/command/inspect.go | 27 ++++++- 9 files changed, 111 insertions(+), 104 deletions(-) diff --git a/cmd/notation/inspect.go b/cmd/notation/inspect.go index 1f1beece6..5d9ca0558 100644 --- a/cmd/notation/inspect.go +++ b/cmd/notation/inspect.go @@ -134,7 +134,7 @@ func runInspect(command *cobra.Command, opts *inspectOpts) error { return nil } - if err := displayHandler.InspectSignature(sigManifestDesc.Digest.String(), sigDesc.MediaType, sigEnvelope); err != nil { + if err := displayHandler.InspectSignature(sigManifestDesc, sigDesc, sigEnvelope); err != nil { logSkippedSignature(sigManifestDesc, err) skippedSignatures = true return nil diff --git a/cmd/notation/internal/display/metadata/interface.go b/cmd/notation/internal/display/metadata/interface.go index 84d18a343..ce5f15865 100644 --- a/cmd/notation/internal/display/metadata/interface.go +++ b/cmd/notation/internal/display/metadata/interface.go @@ -13,7 +13,10 @@ package metadata -import "github.com/notaryproject/notation-core-go/signature" +import ( + "github.com/notaryproject/notation-core-go/signature" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" +) // Renderer renders metadata information when an operation is complete. type Renderer interface { @@ -29,5 +32,5 @@ type InspectHandler interface { OnReferenceResolved(reference, mediaType string) // InspectSignature inspects a signature to get it ready to be rendered. - InspectSignature(digest string, envelopeMediaType string, sigEnvelope signature.Envelope) error + InspectSignature(manifestDesc, blobDesc ocispec.Descriptor, envelope signature.Envelope) error } diff --git a/cmd/notation/internal/display/metadata/json/inspect.go b/cmd/notation/internal/display/metadata/json/inspect.go index a6f3fb35a..1b979b71c 100644 --- a/cmd/notation/internal/display/metadata/json/inspect.go +++ b/cmd/notation/internal/display/metadata/json/inspect.go @@ -21,7 +21,7 @@ import ( "strings" "time" - "github.com/notaryproject/notation-core-go/signature" + coresignature "github.com/notaryproject/notation-core-go/signature" "github.com/notaryproject/notation-go/plugin/proto" "github.com/notaryproject/notation/cmd/notation/internal/output" "github.com/notaryproject/notation/internal/envelope" @@ -32,35 +32,35 @@ import ( type inspectOutput struct { MediaType string `json:"mediaType"` - Signatures []Signature + Signatures []*signature } -// Signature is the signature envelope for printing in human readable format. -type Signature struct { +// signature is the signature envelope for printing in human readable format. +type signature struct { MediaType string `json:"mediaType"` Digest string `json:"digest,omitempty"` SignatureAlgorithm string `json:"signatureAlgorithm"` SignedAttributes map[string]any `json:"signedAttributes"` UserDefinedAttributes map[string]string `json:"userDefinedAttributes"` UnsignedAttributes map[string]any `json:"unsignedAttributes"` - Certificates []Certificate `json:"certificates"` + Certificates []*certificate `json:"certificates"` SignedArtifact ocispec.Descriptor `json:"signedArtifact"` } -// Certificate is the certificate information for printing in human readable +// certificate is the certificate information for printing in human readable // format. -type Certificate struct { +type certificate struct { SHA256Fingerprint string `json:"SHA256Fingerprint"` IssuedTo string `json:"issuedTo"` IssuedBy string `json:"issuedBy"` Expiry time.Time `json:"expiry"` } -// Timestamp is the timestamp information for printing in human readable. -type Timestamp struct { - Timestamp string `json:"timestamp,omitempty"` - Certificates []Certificate `json:"certificates,omitempty"` - Error string `json:"error,omitempty"` +// timestamp is the timestamp information for printing in human readable. +type timestamp struct { + Timestamp string `json:"timestamp,omitempty"` + Certificates []*certificate `json:"certificates,omitempty"` + Error string `json:"error,omitempty"` } // InspectHandler is the handler for inspecting metadata information and @@ -75,6 +75,9 @@ type InspectHandler struct { func NewInspectHandler(printer *output.Printer) *InspectHandler { return &InspectHandler{ printer: printer, + output: inspectOutput{ + Signatures: []*signature{}, + }, } } @@ -82,31 +85,26 @@ func NewInspectHandler(printer *output.Printer) *InspectHandler { // handler. // // the reference is no-op for this handler. -func (h *InspectHandler) OnReferenceResolved(reference, mediaType string) { - if h.output.MediaType == "" { - h.output.MediaType = mediaType - } +func (h *InspectHandler) OnReferenceResolved(_, mediaType string) { + h.output.MediaType = mediaType } // InspectSignature inspects a signature to get it ready to be rendered. -func (h *InspectHandler) InspectSignature(digest string, envelopeMediaType string, sigEnvelope signature.Envelope) error { - sig, err := newSignature(digest, envelopeMediaType, sigEnvelope) +func (h *InspectHandler) InspectSignature(manifestDesc, blobDesc ocispec.Descriptor, envelope coresignature.Envelope) error { + sig, err := newSignature(manifestDesc.Digest.String(), blobDesc.MediaType, envelope) if err != nil { return err } - h.output.Signatures = append(h.output.Signatures, *sig) + h.output.Signatures = append(h.output.Signatures, sig) return nil } func (h *InspectHandler) Render() error { - if h.output.MediaType == "" { - return fmt.Errorf("media type is not set") - } return output.PrintPrettyJSON(h.printer, h.output) } // newSignature parses the signature blob and returns a Signature object. -func newSignature(digest string, envelopeMediaType string, sigEnvelope signature.Envelope) (*Signature, error) { +func newSignature(digest string, envelopeMediaType string, sigEnvelope coresignature.Envelope) (*signature, error) { envelopeContent, err := sigEnvelope.Content() if err != nil { return nil, err @@ -121,7 +119,7 @@ func newSignature(digest string, envelopeMediaType string, sigEnvelope signature if err != nil { return nil, err } - sig := &Signature{ + sig := &signature{ MediaType: envelopeMediaType, Digest: digest, SignatureAlgorithm: string(signatureAlgorithm), @@ -129,7 +127,7 @@ func newSignature(digest string, envelopeMediaType string, sigEnvelope signature UserDefinedAttributes: signedArtifactDesc.Annotations, UnsignedAttributes: getUnsignedAttributes(envelopeContent), Certificates: getCertificates(envelopeContent.SignerInfo.CertificateChain), - SignedArtifact: *signedArtifactDesc, + SignedArtifact: signedArtifactDesc, } // clearing annotations from the SignedArtifact field since they're already @@ -139,7 +137,7 @@ func newSignature(digest string, envelopeMediaType string, sigEnvelope signature return sig, nil } -func getSignedAttributes(envelopeContent *signature.EnvelopeContent) map[string]any { +func getSignedAttributes(envelopeContent *coresignature.EnvelopeContent) map[string]any { signedAttributes := map[string]any{ "signingScheme": string(envelopeContent.SignerInfo.SignedAttributes.SigningScheme), "signingTime": envelopeContent.SignerInfo.SignedAttributes.SigningTime, @@ -153,7 +151,7 @@ func getSignedAttributes(envelopeContent *signature.EnvelopeContent) map[string] return signedAttributes } -func getUnsignedAttributes(envelopeContent *signature.EnvelopeContent) map[string]any { +func getUnsignedAttributes(envelopeContent *coresignature.EnvelopeContent) map[string]any { unsignedAttributes := make(map[string]any) if envelopeContent.SignerInfo.UnsignedAttributes.SigningAgent != "" { unsignedAttributes["signingAgent"] = envelopeContent.SignerInfo.UnsignedAttributes.SigningAgent @@ -164,11 +162,11 @@ func getUnsignedAttributes(envelopeContent *signature.EnvelopeContent) map[strin return unsignedAttributes } -func getCertificates(certChain []*x509.Certificate) []Certificate { - var certificates []Certificate +func getCertificates(certChain []*x509.Certificate) []*certificate { + var certificates []*certificate for _, cert := range certChain { hash := sha256.Sum256(cert.Raw) - certificates = append(certificates, Certificate{ + certificates = append(certificates, &certificate{ SHA256Fingerprint: strings.ToLower(hex.EncodeToString(hash[:])), IssuedTo: cert.Subject.String(), IssuedBy: cert.Issuer.String(), @@ -178,27 +176,27 @@ func getCertificates(certChain []*x509.Certificate) []Certificate { return certificates } -func parseTimestamp(signerInfo signature.SignerInfo) Timestamp { +func parseTimestamp(signerInfo coresignature.SignerInfo) *timestamp { signedToken, err := tspclient.ParseSignedToken(signerInfo.UnsignedAttributes.TimestampSignature) if err != nil { - return Timestamp{ + return ×tamp{ Error: fmt.Sprintf("failed to parse timestamp countersignature: %s", err), } } info, err := signedToken.Info() if err != nil { - return Timestamp{ + return ×tamp{ Error: fmt.Sprintf("failed to parse timestamp countersignature: %s", err), } } - timestamp, err := info.Validate(signerInfo.Signature) + ts, err := info.Validate(signerInfo.Signature) if err != nil { - return Timestamp{ + return ×tamp{ Error: fmt.Sprintf("failed to parse timestamp countersignature: %s", err), } } - return Timestamp{ - Timestamp: timestamp.Format(time.RFC3339Nano), + return ×tamp{ + Timestamp: ts.Format(time.RFC3339Nano), Certificates: getCertificates(signedToken.Certificates), } } diff --git a/cmd/notation/internal/display/metadata/json/inspect_test.go b/cmd/notation/internal/display/metadata/json/inspect_test.go index 16f7ca120..c0eba7625 100644 --- a/cmd/notation/internal/display/metadata/json/inspect_test.go +++ b/cmd/notation/internal/display/metadata/json/inspect_test.go @@ -20,34 +20,35 @@ import ( "testing" "time" - "github.com/notaryproject/notation-core-go/signature" + coresignature "github.com/notaryproject/notation-core-go/signature" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) type errorEnvelope struct{} -func (e errorEnvelope) Sign(req *signature.SignRequest) ([]byte, error) { +func (e errorEnvelope) Sign(req *coresignature.SignRequest) ([]byte, error) { return nil, errors.New("mock sign error") } -func (e errorEnvelope) Verify() (*signature.EnvelopeContent, error) { +func (e errorEnvelope) Verify() (*coresignature.EnvelopeContent, error) { return nil, errors.New("mock verify error") } -func (e errorEnvelope) Content() (*signature.EnvelopeContent, error) { +func (e errorEnvelope) Content() (*coresignature.EnvelopeContent, error) { return nil, errors.New("mock content error") } func TestGetUnsignedAttributes(t *testing.T) { - envContent := &signature.EnvelopeContent{ - SignerInfo: signature.SignerInfo{ - UnsignedAttributes: signature.UnsignedAttributes{ + envContent := &coresignature.EnvelopeContent{ + SignerInfo: coresignature.SignerInfo{ + UnsignedAttributes: coresignature.UnsignedAttributes{ TimestampSignature: []byte("invalid"), }, }, } expectedErrMsg := "failed to parse timestamp countersignature: cms: syntax error: invalid signed data: failed to convert from BER to DER: asn1: syntax error: decoding BER length octets: short form length octets value should be less or equal to the subsequent octets length" unsignedAttr := getUnsignedAttributes(envContent) - val, ok := unsignedAttr["timestampSignature"].(Timestamp) + val, ok := unsignedAttr["timestampSignature"].(timestamp) if !ok { t.Fatal("expected to have timestampSignature") } @@ -58,11 +59,11 @@ func TestGetUnsignedAttributes(t *testing.T) { func TestGetSignedAttributes(t *testing.T) { expiry := time.Now() - envContent := &signature.EnvelopeContent{ - SignerInfo: signature.SignerInfo{ - SignedAttributes: signature.SignedAttributes{ + envContent := &coresignature.EnvelopeContent{ + SignerInfo: coresignature.SignerInfo{ + SignedAttributes: coresignature.SignedAttributes{ Expiry: expiry, - ExtendedAttributes: []signature.Attribute{ + ExtendedAttributes: []coresignature.Attribute{ { Key: "keyName", Value: "value", @@ -83,8 +84,8 @@ func TestGetSignedAttributes(t *testing.T) { func TestParseTimestamp(t *testing.T) { t.Run("invalid timestamp signature", func(t *testing.T) { - signerInfo := signature.SignerInfo{ - UnsignedAttributes: signature.UnsignedAttributes{ + signerInfo := coresignature.SignerInfo{ + UnsignedAttributes: coresignature.UnsignedAttributes{ TimestampSignature: []byte("invalid"), }, } @@ -101,8 +102,8 @@ func TestParseTimestamp(t *testing.T) { t.Fatal(err) } - signerInfo := signature.SignerInfo{ - UnsignedAttributes: signature.UnsignedAttributes{ + signerInfo := coresignature.SignerInfo{ + UnsignedAttributes: coresignature.UnsignedAttributes{ TimestampSignature: tsaToken, }, } @@ -119,17 +120,11 @@ func TestInspectSignature_NewSignatureError(t *testing.T) { h := NewInspectHandler(nil) // ...existing code to ensure h.output.MediaType is set... h.OnReferenceResolved("test-ref", "test-media-type") + manifestDesc := ocispec.Descriptor{Digest: "fake-digest"} + blobDesc := ocispec.Descriptor{MediaType: "fake-media-type"} - err := h.InspectSignature("fake-digest", "fake-media-type", errorEnvelope{}) + err := h.InspectSignature(manifestDesc, blobDesc, errorEnvelope{}) if err == nil || !strings.Contains(err.Error(), "mock content error") { t.Fatalf("expected error 'mock content error', got %v", err) } } - -func TestRenderNoMediaType(t *testing.T) { - h := NewInspectHandler(nil) - err := h.Render() - if err == nil || !strings.Contains(err.Error(), "media type is not set") { - t.Fatalf("expected error 'media type is not set', got %v", err) - } -} diff --git a/cmd/notation/internal/display/metadata/tree/inspect.go b/cmd/notation/internal/display/metadata/tree/inspect.go index e9e6a1bcf..4e8e55f03 100644 --- a/cmd/notation/internal/display/metadata/tree/inspect.go +++ b/cmd/notation/internal/display/metadata/tree/inspect.go @@ -38,8 +38,12 @@ import ( type InspectHandler struct { printer *output.Printer - root *tree.Node - cncfSigNode *tree.Node + // rootReferenceNode is the root node with the artifact reference as the + // value. + rootReferenceNode *tree.Node + // notationSignaturesNode is the node for all signatures associated with the + // artifact. + notationSignaturesNode *tree.Node } // NewInspectHandler creates a new InspectHandler. @@ -53,28 +57,23 @@ func NewInspectHandler(printer *output.Printer) *InspectHandler { // handler. // // mediaType is a no-op for this handler. -func (h *InspectHandler) OnReferenceResolved(reference, mediaType string) { - if h.root == nil { - h.root = tree.New(reference) - h.cncfSigNode = h.root.Add(registry.ArtifactTypeNotation) - } +func (h *InspectHandler) OnReferenceResolved(reference, _ string) { + h.rootReferenceNode = tree.New(reference) + h.notationSignaturesNode = h.rootReferenceNode.Add(registry.ArtifactTypeNotation) } // InspectSignature inspects a signature to get it ready to be rendered. -func (h *InspectHandler) InspectSignature(digest string, envelopeMediaType string, sigEnvelope signature.Envelope) error { - if h.root == nil || h.cncfSigNode == nil { - return fmt.Errorf("artifact reference is not set") - } - - return addSignature(h.cncfSigNode, digest, envelopeMediaType, sigEnvelope) +func (h *InspectHandler) InspectSignature(manifestDesc, blobDesc ocispec.Descriptor, envelope signature.Envelope) error { + return addSignature(h.notationSignaturesNode, manifestDesc.Digest.String(), blobDesc.MediaType, envelope) } // Render renders the metadata information when an operation is complete. func (h *InspectHandler) Render() error { - if h.root == nil { - return fmt.Errorf("artifact reference is not set") + if len(h.notationSignaturesNode.Children) == 0 { + return h.printer.Printf("%s has no associated signature\n", h.rootReferenceNode.Value) } - return h.root.Print(h.printer) + h.printer.Println("Inspecting all signatures for signed artifact") + return h.rootReferenceNode.Print(h.printer) } func addSignature(node *tree.Node, digest string, envelopeMediaType string, sigEnvelope signature.Envelope) error { @@ -138,7 +137,7 @@ func addUnsignedAttributes(node *tree.Node, envelopeContent *signature.EnvelopeC } } -func addSignedArtifact(node *tree.Node, signedArtifactDesc *ocispec.Descriptor) { +func addSignedArtifact(node *tree.Node, signedArtifactDesc ocispec.Descriptor) { artifactNode := node.Add("signed artifact") artifactNode.AddPair("media type", signedArtifactDesc.MediaType) artifactNode.AddPair("digest", signedArtifactDesc.Digest.String()) diff --git a/cmd/notation/internal/display/metadata/tree/inspect_test.go b/cmd/notation/internal/display/metadata/tree/inspect_test.go index c3b1c2d4b..63e1da3da 100644 --- a/cmd/notation/internal/display/metadata/tree/inspect_test.go +++ b/cmd/notation/internal/display/metadata/tree/inspect_test.go @@ -23,22 +23,6 @@ import ( "github.com/notaryproject/notation/internal/tree" ) -func TestInspectSignatureNoRoot(t *testing.T) { - h := NewInspectHandler(nil) - err := h.InspectSignature("test-digest", "application/notation", nil) - if err == nil || err.Error() != "artifact reference is not set" { - t.Fatalf("expected error 'artifact reference is not set', got: %v", err) - } -} - -func TestRenderNoRoot(t *testing.T) { - h := NewInspectHandler(nil) - err := h.Render() - if err == nil || err.Error() != "artifact reference is not set" { - t.Fatalf("expected error 'artifact reference is not set', got: %v", err) - } -} - func TestAddSignedAttributes(t *testing.T) { t.Run("empty envelopeContent", func(t *testing.T) { node := tree.New("root") diff --git a/cmd/notation/internal/option/format.go b/cmd/notation/internal/option/format.go index a32447073..5585fa695 100644 --- a/cmd/notation/internal/option/format.go +++ b/cmd/notation/internal/option/format.go @@ -36,11 +36,14 @@ import ( "github.com/spf13/pflag" ) +// FormatType is the type of output format. type FormatType string // format types var ( + // FormatTypeJSON is the JSON format type. FormatTypeJSON FormatType = "json" + // FormatTypeText is the text format type for human-readable output. FormatTypeText FormatType = "text" ) diff --git a/internal/envelope/envelope.go b/internal/envelope/envelope.go index 4f49ca0f8..98d4f51d9 100644 --- a/internal/envelope/envelope.go +++ b/internal/envelope/envelope.go @@ -61,21 +61,21 @@ func ValidatePayloadContentType(payload *signature.Payload) error { // DescriptorFromPayload parses a signature payload and returns the descriptor // that was signed. Note: the descriptor was signed but may not be trusted -func DescriptorFromSignaturePayload(payload *signature.Payload) (*ocispec.Descriptor, error) { +func DescriptorFromSignaturePayload(payload *signature.Payload) (ocispec.Descriptor, error) { if payload == nil { - return nil, errors.New("empty payload") + return ocispec.Descriptor{}, errors.New("empty payload") } err := ValidatePayloadContentType(payload) if err != nil { - return nil, err + return ocispec.Descriptor{}, err } var parsedPayload Payload err = json.Unmarshal(payload.Content, &parsedPayload) if err != nil { - return nil, errors.New("failed to unmarshall the payload content to Payload") + return ocispec.Descriptor{}, errors.New("failed to unmarshall the payload content to Payload") } - return &parsedPayload.TargetArtifact, nil + return parsedPayload.TargetArtifact, nil } diff --git a/test/e2e/suite/command/inspect.go b/test/e2e/suite/command/inspect.go index 3ca7e5297..fb2dde4b2 100644 --- a/test/e2e/suite/command/inspect.go +++ b/test/e2e/suite/command/inspect.go @@ -24,6 +24,7 @@ import ( var ( inspectSuccessfully = []string{ + "Inspecting all signatures for signed artifact", "└── application/vnd.cncf.notary.signature", "└── sha256:", "├── media type:", @@ -47,6 +48,7 @@ var ( } inspectSuccessfullyWithTimestamp = []string{ + "Inspecting all signatures for signed artifact", "└── application/vnd.cncf.notary.signature", "└── sha256:", "├── media type:", @@ -176,7 +178,8 @@ var _ = Describe("notation inspect", func() { It("with timestamped oci layout", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { artifact := GenerateArtifact("e2e-with-timestamped-signature", "e2e") - expectedOutput := `localhost:5000/e2e@sha256:99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa77881ec3cef1c + expectedOutput := `Inspecting all signatures for signed artifact +localhost:5000/e2e@sha256:99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa77881ec3cef1c └── application/vnd.cncf.notary.signature └── sha256:54eab65f9262feac4ea9f31d15b62c870bf359d912aba86622cfc735337ae4fa ├── signature algorithm: RSASSA-PSS-SHA-256 @@ -272,4 +275,26 @@ var _ = Describe("notation inspect", func() { MatchContent(expectedOutput) }) }) + + It("with no signature in text format", func() { + Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { + artifact := GenerateArtifact("e2e", "e2e") + expectedOutput := "localhost:5000/e2e@sha256:b8479de3f88fb259a0a9ea82a5b2a052a1ef3c4ebbcfc61482d5ae4c831f8af9 has no associated signature\n" + notation.Exec("inspect", artifact.ReferenceWithDigest()). + MatchContent(expectedOutput) + }) + }) + + It("with no signature in JSON format", func() { + Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { + artifact := GenerateArtifact("e2e", "e2e") + expectedOutput := `{ + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "Signatures": [] +} +` + notation.Exec("inspect", "--output", "json", artifact.ReferenceWithDigest()). + MatchContent(expectedOutput) + }) + }) }) From 84a1991baba0cd50d6aeb5ffa3f5916c7069af3d Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Tue, 21 Jan 2025 01:47:48 +0000 Subject: [PATCH 14/32] fix: test Signed-off-by: Junjie Gao --- cmd/notation/internal/display/metadata/json/inspect_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/notation/internal/display/metadata/json/inspect_test.go b/cmd/notation/internal/display/metadata/json/inspect_test.go index c0eba7625..44239dc21 100644 --- a/cmd/notation/internal/display/metadata/json/inspect_test.go +++ b/cmd/notation/internal/display/metadata/json/inspect_test.go @@ -48,7 +48,7 @@ func TestGetUnsignedAttributes(t *testing.T) { } expectedErrMsg := "failed to parse timestamp countersignature: cms: syntax error: invalid signed data: failed to convert from BER to DER: asn1: syntax error: decoding BER length octets: short form length octets value should be less or equal to the subsequent octets length" unsignedAttr := getUnsignedAttributes(envContent) - val, ok := unsignedAttr["timestampSignature"].(timestamp) + val, ok := unsignedAttr["timestampSignature"].(*timestamp) if !ok { t.Fatal("expected to have timestampSignature") } From 8083c25e201b71563e03affde0774ab55a9e93a3 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Tue, 21 Jan 2025 03:06:38 +0000 Subject: [PATCH 15/32] fix: remove envelope media type Signed-off-by: Junjie Gao --- cmd/notation/inspect.go | 2 +- cmd/notation/internal/display/metadata/interface.go | 2 +- cmd/notation/internal/display/metadata/json/inspect.go | 8 +++----- .../internal/display/metadata/json/inspect_test.go | 3 +-- cmd/notation/internal/display/metadata/tree/inspect.go | 7 +++---- test/e2e/suite/command/inspect.go | 2 -- 6 files changed, 9 insertions(+), 15 deletions(-) diff --git a/cmd/notation/inspect.go b/cmd/notation/inspect.go index 5d9ca0558..e106475c0 100644 --- a/cmd/notation/inspect.go +++ b/cmd/notation/inspect.go @@ -134,7 +134,7 @@ func runInspect(command *cobra.Command, opts *inspectOpts) error { return nil } - if err := displayHandler.InspectSignature(sigManifestDesc, sigDesc, sigEnvelope); err != nil { + if err := displayHandler.InspectSignature(sigManifestDesc, sigEnvelope); err != nil { logSkippedSignature(sigManifestDesc, err) skippedSignatures = true return nil diff --git a/cmd/notation/internal/display/metadata/interface.go b/cmd/notation/internal/display/metadata/interface.go index ce5f15865..9424abc5c 100644 --- a/cmd/notation/internal/display/metadata/interface.go +++ b/cmd/notation/internal/display/metadata/interface.go @@ -32,5 +32,5 @@ type InspectHandler interface { OnReferenceResolved(reference, mediaType string) // InspectSignature inspects a signature to get it ready to be rendered. - InspectSignature(manifestDesc, blobDesc ocispec.Descriptor, envelope signature.Envelope) error + InspectSignature(manifestDesc ocispec.Descriptor, envelope signature.Envelope) error } diff --git a/cmd/notation/internal/display/metadata/json/inspect.go b/cmd/notation/internal/display/metadata/json/inspect.go index 1b979b71c..21bcaa17f 100644 --- a/cmd/notation/internal/display/metadata/json/inspect.go +++ b/cmd/notation/internal/display/metadata/json/inspect.go @@ -37,7 +37,6 @@ type inspectOutput struct { // signature is the signature envelope for printing in human readable format. type signature struct { - MediaType string `json:"mediaType"` Digest string `json:"digest,omitempty"` SignatureAlgorithm string `json:"signatureAlgorithm"` SignedAttributes map[string]any `json:"signedAttributes"` @@ -90,8 +89,8 @@ func (h *InspectHandler) OnReferenceResolved(_, mediaType string) { } // InspectSignature inspects a signature to get it ready to be rendered. -func (h *InspectHandler) InspectSignature(manifestDesc, blobDesc ocispec.Descriptor, envelope coresignature.Envelope) error { - sig, err := newSignature(manifestDesc.Digest.String(), blobDesc.MediaType, envelope) +func (h *InspectHandler) InspectSignature(manifestDesc ocispec.Descriptor, envelope coresignature.Envelope) error { + sig, err := newSignature(manifestDesc.Digest.String(), envelope) if err != nil { return err } @@ -104,7 +103,7 @@ func (h *InspectHandler) Render() error { } // newSignature parses the signature blob and returns a Signature object. -func newSignature(digest string, envelopeMediaType string, sigEnvelope coresignature.Envelope) (*signature, error) { +func newSignature(digest string, sigEnvelope coresignature.Envelope) (*signature, error) { envelopeContent, err := sigEnvelope.Content() if err != nil { return nil, err @@ -120,7 +119,6 @@ func newSignature(digest string, envelopeMediaType string, sigEnvelope coresigna return nil, err } sig := &signature{ - MediaType: envelopeMediaType, Digest: digest, SignatureAlgorithm: string(signatureAlgorithm), SignedAttributes: getSignedAttributes(envelopeContent), diff --git a/cmd/notation/internal/display/metadata/json/inspect_test.go b/cmd/notation/internal/display/metadata/json/inspect_test.go index 44239dc21..783c65fac 100644 --- a/cmd/notation/internal/display/metadata/json/inspect_test.go +++ b/cmd/notation/internal/display/metadata/json/inspect_test.go @@ -121,9 +121,8 @@ func TestInspectSignature_NewSignatureError(t *testing.T) { // ...existing code to ensure h.output.MediaType is set... h.OnReferenceResolved("test-ref", "test-media-type") manifestDesc := ocispec.Descriptor{Digest: "fake-digest"} - blobDesc := ocispec.Descriptor{MediaType: "fake-media-type"} - err := h.InspectSignature(manifestDesc, blobDesc, errorEnvelope{}) + err := h.InspectSignature(manifestDesc, errorEnvelope{}) if err == nil || !strings.Contains(err.Error(), "mock content error") { t.Fatalf("expected error 'mock content error', got %v", err) } diff --git a/cmd/notation/internal/display/metadata/tree/inspect.go b/cmd/notation/internal/display/metadata/tree/inspect.go index 4e8e55f03..b41480dd1 100644 --- a/cmd/notation/internal/display/metadata/tree/inspect.go +++ b/cmd/notation/internal/display/metadata/tree/inspect.go @@ -63,8 +63,8 @@ func (h *InspectHandler) OnReferenceResolved(reference, _ string) { } // InspectSignature inspects a signature to get it ready to be rendered. -func (h *InspectHandler) InspectSignature(manifestDesc, blobDesc ocispec.Descriptor, envelope signature.Envelope) error { - return addSignature(h.notationSignaturesNode, manifestDesc.Digest.String(), blobDesc.MediaType, envelope) +func (h *InspectHandler) InspectSignature(manifestDesc ocispec.Descriptor, envelope signature.Envelope) error { + return addSignature(h.notationSignaturesNode, manifestDesc.Digest.String(), envelope) } // Render renders the metadata information when an operation is complete. @@ -76,7 +76,7 @@ func (h *InspectHandler) Render() error { return h.rootReferenceNode.Print(h.printer) } -func addSignature(node *tree.Node, digest string, envelopeMediaType string, sigEnvelope signature.Envelope) error { +func addSignature(node *tree.Node, digest string, sigEnvelope signature.Envelope) error { envelopeContent, err := sigEnvelope.Content() if err != nil { return err @@ -93,7 +93,6 @@ func addSignature(node *tree.Node, digest string, envelopeMediaType string, sigE // create signature node sigNode := node.Add(digest) sigNode.AddPair("signature algorithm", string(signatureAlgorithm)) - sigNode.AddPair("signature envelope type", envelopeMediaType) addSignedAttributes(sigNode, envelopeContent) addUserDefinedAttributes(sigNode, signedArtifactDesc.Annotations) diff --git a/test/e2e/suite/command/inspect.go b/test/e2e/suite/command/inspect.go index fb2dde4b2..dbdac431b 100644 --- a/test/e2e/suite/command/inspect.go +++ b/test/e2e/suite/command/inspect.go @@ -183,7 +183,6 @@ localhost:5000/e2e@sha256:99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa778 └── application/vnd.cncf.notary.signature └── sha256:54eab65f9262feac4ea9f31d15b62c870bf359d912aba86622cfc735337ae4fa ├── signature algorithm: RSASSA-PSS-SHA-256 - ├── signature envelope type: application/jose+json ├── signed attributes │ ├── signing scheme: notary.x509 │ └── signing time: Fri Jan 17 06:36:19 2025 @@ -225,7 +224,6 @@ localhost:5000/e2e@sha256:99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa778 "mediaType": "application/vnd.oci.image.manifest.v1+json", "Signatures": [ { - "mediaType": "application/jose+json", "digest": "sha256:54eab65f9262feac4ea9f31d15b62c870bf359d912aba86622cfc735337ae4fa", "signatureAlgorithm": "RSASSA-PSS-SHA-256", "signedAttributes": { From 5dd55ca3730ab6c86b76b5575818171e5ab8e35a Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Tue, 21 Jan 2025 06:23:16 +0000 Subject: [PATCH 16/32] fix: e2e test Signed-off-by: Junjie Gao --- test/e2e/suite/command/inspect.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/e2e/suite/command/inspect.go b/test/e2e/suite/command/inspect.go index dbdac431b..16a1716d8 100644 --- a/test/e2e/suite/command/inspect.go +++ b/test/e2e/suite/command/inspect.go @@ -177,7 +177,7 @@ var _ = Describe("notation inspect", func() { It("with timestamped oci layout", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - artifact := GenerateArtifact("e2e-with-timestamped-signature", "e2e") + artifact := GenerateArtifact("e2e-with-timestamped-signature", "") expectedOutput := `Inspecting all signatures for signed artifact localhost:5000/e2e@sha256:99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa77881ec3cef1c └── application/vnd.cncf.notary.signature @@ -219,7 +219,7 @@ localhost:5000/e2e@sha256:99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa778 It("with timestamped oci layout and output in JSON", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - artifact := GenerateArtifact("e2e-with-timestamped-signature", "e2e") + artifact := GenerateArtifact("e2e-with-timestamped-signature", "") expectedOutput := `{ "mediaType": "application/vnd.oci.image.manifest.v1+json", "Signatures": [ @@ -276,7 +276,7 @@ localhost:5000/e2e@sha256:99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa778 It("with no signature in text format", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - artifact := GenerateArtifact("e2e", "e2e") + artifact := GenerateArtifact("e2e", "") expectedOutput := "localhost:5000/e2e@sha256:b8479de3f88fb259a0a9ea82a5b2a052a1ef3c4ebbcfc61482d5ae4c831f8af9 has no associated signature\n" notation.Exec("inspect", artifact.ReferenceWithDigest()). MatchContent(expectedOutput) @@ -285,7 +285,7 @@ localhost:5000/e2e@sha256:99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa778 It("with no signature in JSON format", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - artifact := GenerateArtifact("e2e", "e2e") + artifact := GenerateArtifact("e2e", "") expectedOutput := `{ "mediaType": "application/vnd.oci.image.manifest.v1+json", "Signatures": [] From 25bc6a0499a94efb5cc44193e2fddd9fb20c3c2b Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Tue, 21 Jan 2025 06:31:35 +0000 Subject: [PATCH 17/32] fix: e2e test Signed-off-by: Junjie Gao --- test/e2e/suite/command/inspect.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/e2e/suite/command/inspect.go b/test/e2e/suite/command/inspect.go index 16a1716d8..0ce446112 100644 --- a/test/e2e/suite/command/inspect.go +++ b/test/e2e/suite/command/inspect.go @@ -177,9 +177,9 @@ var _ = Describe("notation inspect", func() { It("with timestamped oci layout", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - artifact := GenerateArtifact("e2e-with-timestamped-signature", "") + artifact := GenerateArtifact("e2e-with-timestamped-signature", "e2e-insepct-timestamped") expectedOutput := `Inspecting all signatures for signed artifact -localhost:5000/e2e@sha256:99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa77881ec3cef1c +localhost:5000/e2e-insepct-timestamped@sha256:99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa77881ec3cef1c └── application/vnd.cncf.notary.signature └── sha256:54eab65f9262feac4ea9f31d15b62c870bf359d912aba86622cfc735337ae4fa ├── signature algorithm: RSASSA-PSS-SHA-256 @@ -219,7 +219,7 @@ localhost:5000/e2e@sha256:99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa778 It("with timestamped oci layout and output in JSON", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - artifact := GenerateArtifact("e2e-with-timestamped-signature", "") + artifact := GenerateArtifact("e2e-with-timestamped-signature", "e2e-inspect-timestamped-json") expectedOutput := `{ "mediaType": "application/vnd.oci.image.manifest.v1+json", "Signatures": [ @@ -276,8 +276,8 @@ localhost:5000/e2e@sha256:99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa778 It("with no signature in text format", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - artifact := GenerateArtifact("e2e", "") - expectedOutput := "localhost:5000/e2e@sha256:b8479de3f88fb259a0a9ea82a5b2a052a1ef3c4ebbcfc61482d5ae4c831f8af9 has no associated signature\n" + artifact := GenerateArtifact("e2e", "e2e-inspect-no-signature") + expectedOutput := "localhost:5000/e2e-inspect-no-signature@sha256:b8479de3f88fb259a0a9ea82a5b2a052a1ef3c4ebbcfc61482d5ae4c831f8af9 has no associated signature\n" notation.Exec("inspect", artifact.ReferenceWithDigest()). MatchContent(expectedOutput) }) @@ -285,7 +285,7 @@ localhost:5000/e2e@sha256:99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa778 It("with no signature in JSON format", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - artifact := GenerateArtifact("e2e", "") + artifact := GenerateArtifact("e2e", "e2e-inspect-no-signature-json") expectedOutput := `{ "mediaType": "application/vnd.oci.image.manifest.v1+json", "Signatures": [] From fd74cf704befd5dbf23bb9023c5c585c1585ec6e Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Tue, 21 Jan 2025 07:13:06 +0000 Subject: [PATCH 18/32] fix: update json output format Signed-off-by: Junjie Gao --- cmd/notation/internal/display/metadata/json/inspect.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/notation/internal/display/metadata/json/inspect.go b/cmd/notation/internal/display/metadata/json/inspect.go index 21bcaa17f..a1e83fcf4 100644 --- a/cmd/notation/internal/display/metadata/json/inspect.go +++ b/cmd/notation/internal/display/metadata/json/inspect.go @@ -31,8 +31,8 @@ import ( ) type inspectOutput struct { - MediaType string `json:"mediaType"` - Signatures []*signature + MediaType string `json:"mediaType"` + Signatures []*signature `json:"signatures"` } // signature is the signature envelope for printing in human readable format. From f1c2966e0e71a7f4f0e763c2c9d66ef5203ed19d Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Tue, 21 Jan 2025 07:40:29 +0000 Subject: [PATCH 19/32] fix: update e2e Signed-off-by: Junjie Gao --- test/e2e/suite/command/inspect.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/e2e/suite/command/inspect.go b/test/e2e/suite/command/inspect.go index 0ce446112..52fe09c7f 100644 --- a/test/e2e/suite/command/inspect.go +++ b/test/e2e/suite/command/inspect.go @@ -222,7 +222,7 @@ localhost:5000/e2e-insepct-timestamped@sha256:99950868628ed79ebc295e01f8397dcaca artifact := GenerateArtifact("e2e-with-timestamped-signature", "e2e-inspect-timestamped-json") expectedOutput := `{ "mediaType": "application/vnd.oci.image.manifest.v1+json", - "Signatures": [ + "signatures": [ { "digest": "sha256:54eab65f9262feac4ea9f31d15b62c870bf359d912aba86622cfc735337ae4fa", "signatureAlgorithm": "RSASSA-PSS-SHA-256", @@ -288,7 +288,7 @@ localhost:5000/e2e-insepct-timestamped@sha256:99950868628ed79ebc295e01f8397dcaca artifact := GenerateArtifact("e2e", "e2e-inspect-no-signature-json") expectedOutput := `{ "mediaType": "application/vnd.oci.image.manifest.v1+json", - "Signatures": [] + "signatures": [] } ` notation.Exec("inspect", "--output", "json", artifact.ReferenceWithDigest()). From 89b310f2d75a85aad24e0a514574d4836b442660 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Tue, 21 Jan 2025 08:25:37 +0000 Subject: [PATCH 20/32] fix: add content type in json output Signed-off-by: Junjie Gao --- cmd/notation/internal/display/metadata/json/inspect.go | 1 + test/e2e/suite/command/inspect.go | 1 + 2 files changed, 2 insertions(+) diff --git a/cmd/notation/internal/display/metadata/json/inspect.go b/cmd/notation/internal/display/metadata/json/inspect.go index a1e83fcf4..7f28fb3fe 100644 --- a/cmd/notation/internal/display/metadata/json/inspect.go +++ b/cmd/notation/internal/display/metadata/json/inspect.go @@ -137,6 +137,7 @@ func newSignature(digest string, sigEnvelope coresignature.Envelope) (*signature func getSignedAttributes(envelopeContent *coresignature.EnvelopeContent) map[string]any { signedAttributes := map[string]any{ + "contentType": envelopeContent.Payload.ContentType, "signingScheme": string(envelopeContent.SignerInfo.SignedAttributes.SigningScheme), "signingTime": envelopeContent.SignerInfo.SignedAttributes.SigningTime, } diff --git a/test/e2e/suite/command/inspect.go b/test/e2e/suite/command/inspect.go index 52fe09c7f..064f332a7 100644 --- a/test/e2e/suite/command/inspect.go +++ b/test/e2e/suite/command/inspect.go @@ -227,6 +227,7 @@ localhost:5000/e2e-insepct-timestamped@sha256:99950868628ed79ebc295e01f8397dcaca "digest": "sha256:54eab65f9262feac4ea9f31d15b62c870bf359d912aba86622cfc735337ae4fa", "signatureAlgorithm": "RSASSA-PSS-SHA-256", "signedAttributes": { + "contentType": "application/vnd.cncf.notary.payload.v1+json", "signingScheme": "notary.x509", "signingTime": "2025-01-17T06:36:19Z" }, From 58603cee5999918a5b7b9cfefa3fcba0dfd838e7 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Tue, 21 Jan 2025 09:14:50 +0000 Subject: [PATCH 21/32] test: add e2e test for invalid timestamp signature Signed-off-by: Junjie Gao --- test/e2e/suite/command/inspect.go | 129 ++++++++++++++---- ...16f7e8649e94fb4fc21fe77e8310c060f61caaff8a | 1 + ...30dfefcb7bcda1db5f9ac2566fc8c6c2248c8d4c5f | 12 ++ ...bbb863db250f328be9dc5c3041481d778a32f8130d | 1 + ...753bea67879ff62c08a73a49a41151ed18c4d1c000 | 1 + ...d6618f79508cf1e86877edf1c0bfe49a1b0a6467a} | 2 +- .../index.json | 1 + .../oci-layout | 1 + ...7e98cb328de9731f23783c546bea1aa7580a4a6a37 | 1 - ...1d15b62c870bf359d912aba86622cfc735337ae4fa | 1 - ...c2ee6a8d7759503a29f64624fa52970516d9ec45b2 | 1 - ...205780dfedb82a447593aba3383ec8461dda587954 | 1 + ...b3c02f0ab6aa2430c52c96b5e127513259309002d4 | 1 - ...bbb863db250f328be9dc5c3041481d778a32f8130d | 1 + ...87aea775b73e049cb2c51e636a3980658e55577d18 | 1 + ...d6618f79508cf1e86877edf1c0bfe49a1b0a6467a} | 2 +- ...cc672269e3228cdaaf38140f81e97f2ed29a714d70 | 1 - .../e2e-with-timestamped-signature/index.json | 2 +- 18 files changed, 127 insertions(+), 33 deletions(-) create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-with-invalid-timestamped-signature/blobs/sha256/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-with-invalid-timestamped-signature/blobs/sha256/8d2e70ffc2af68b7f2df6f30dfefcb7bcda1db5f9ac2566fc8c6c2248c8d4c5f create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-with-invalid-timestamped-signature/blobs/sha256/ce41e5246ead8bddd2a2b5bbb863db250f328be9dc5c3041481d778a32f8130d create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-with-invalid-timestamped-signature/blobs/sha256/eee3eec7d2947f77713484753bea67879ff62c08a73a49a41151ed18c4d1c000 rename test/e2e/testdata/registry/oci_layout/{e2e-with-timestamped-signature/blobs/sha256/d7b441acd8ea27f3fa22afeb4433c937a22c241f6f61ccc5ce84172509bf78ae => e2e-with-invalid-timestamped-signature/blobs/sha256/f1da8cd70d6d851fa2313c8d6618f79508cf1e86877edf1c0bfe49a1b0a6467a} (65%) create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-with-invalid-timestamped-signature/index.json create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-with-invalid-timestamped-signature/oci-layout delete mode 100644 test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/123ec9fa46aad212010c907e98cb328de9731f23783c546bea1aa7580a4a6a37 delete mode 100644 test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/54eab65f9262feac4ea9f31d15b62c870bf359d912aba86622cfc735337ae4fa delete mode 100644 test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/7c52011daa0bf2983c4687c2ee6a8d7759503a29f64624fa52970516d9ec45b2 create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/83a35ad9499039f2ba1fcd205780dfedb82a447593aba3383ec8461dda587954 delete mode 100644 test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/cacefba75b1771fa028faeb3c02f0ab6aa2430c52c96b5e127513259309002d4 create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/ce41e5246ead8bddd2a2b5bbb863db250f328be9dc5c3041481d778a32f8130d create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/e3222a9ea284789503cd2087aea775b73e049cb2c51e636a3980658e55577d18 rename test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/{99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa77881ec3cef1c => f1da8cd70d6d851fa2313c8d6618f79508cf1e86877edf1c0bfe49a1b0a6467a} (65%) delete mode 100644 test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/f27085b685cfed009862f7cc672269e3228cdaaf38140f81e97f2ed29a714d70 diff --git a/test/e2e/suite/command/inspect.go b/test/e2e/suite/command/inspect.go index 064f332a7..cf48fbae7 100644 --- a/test/e2e/suite/command/inspect.go +++ b/test/e2e/suite/command/inspect.go @@ -179,36 +179,36 @@ var _ = Describe("notation inspect", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { artifact := GenerateArtifact("e2e-with-timestamped-signature", "e2e-insepct-timestamped") expectedOutput := `Inspecting all signatures for signed artifact -localhost:5000/e2e-insepct-timestamped@sha256:99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa77881ec3cef1c +localhost:5000/e2e-insepct-timestamped@sha256:f1da8cd70d6d851fa2313c8d6618f79508cf1e86877edf1c0bfe49a1b0a6467a └── application/vnd.cncf.notary.signature - └── sha256:54eab65f9262feac4ea9f31d15b62c870bf359d912aba86622cfc735337ae4fa + └── sha256:e3222a9ea284789503cd2087aea775b73e049cb2c51e636a3980658e55577d18 ├── signature algorithm: RSASSA-PSS-SHA-256 ├── signed attributes │ ├── signing scheme: notary.x509 - │ └── signing time: Fri Jan 17 06:36:19 2025 + │ └── signing time: Tue Jan 21 08:41:17 2025 ├── user defined attributes - │ └── (empty) + │ └── purpose: e2e ├── unsigned attributes │ ├── signing agent: notation-go/1.3.0+unreleased │ └── timestamp signature - │ ├── timestamp: [Fri Jan 17 06:36:19 2025, Fri Jan 17 06:36:20 2025] + │ ├── timestamp: [Tue Jan 21 08:41:16 2025, Tue Jan 21 08:41:17 2025] │ └── certificates │ ├── SHA256 fingerprint: 36e731cfa9bfd69dafb643809f6dec500902f7197daeaad86ea0159a2268a2b8 │ │ ├── issued to: CN=Microsoft Public RSA Timestamping CA 2020,O=Microsoft Corporation,C=US │ │ ├── issued by: CN=Microsoft Identity Verification Root Certificate Authority 2020,O=Microsoft Corporation,C=US │ │ └── expiry: Mon Nov 19 20:42:31 2035 - │ └── SHA256 fingerprint: 2be4c1670d176be2b0e56081a7b6523487c528a7ea092febbb84ae9db03ceb9a - │ ├── issued to: CN=Microsoft Public RSA Time Stamping Authority,OU=Microsoft Ireland Operations Limited+OU=Thales TSS ESN:F5D6-96D6-909E,O=Microsoft Corporation,L=Redmond,ST=Washington,C=US + │ └── SHA256 fingerprint: b804553ac8c88a3f71e32fe6b84f1ccef488cf45d2ebca41150e7e21dfd26e71 + │ ├── issued to: CN=Microsoft Public RSA Time Stamping Authority,OU=Microsoft America Operations+OU=Thales TSS ESN:BB73-96FD-77EF,O=Microsoft Corporation,L=Redmond,ST=Washington,C=US │ ├── issued by: CN=Microsoft Public RSA Timestamping CA 2020,O=Microsoft Corporation,C=US - │ └── expiry: Thu Apr 17 17:59:12 2025 + │ └── expiry: Wed Nov 19 18:48:47 2025 ├── certificates - │ └── SHA256 fingerprint: 36b3e0e0fee117d33d5664a2f56b147ddbbe8b7ca3ad2ae56498703fd782a56e - │ ├── issued to: CN=testcert5,O=Notary,L=Seattle,ST=WA,C=US - │ ├── issued by: CN=testcert5,O=Notary,L=Seattle,ST=WA,C=US - │ └── expiry: Sat Jan 18 06:34:29 2025 + │ └── SHA256 fingerprint: 1717fa9d18f7e9c0f609499474adfe2b8e44172454f1d6e2183d5d04f79af475 + │ ├── issued to: CN=testcert7,O=Notary,L=Seattle,ST=WA,C=US + │ ├── issued by: CN=testcert7,O=Notary,L=Seattle,ST=WA,C=US + │ └── expiry: Wed Jan 22 08:36:26 2025 └── signed artifact ├── media type: application/vnd.oci.image.manifest.v1+json - ├── digest: sha256:99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa77881ec3cef1c + ├── digest: sha256:f1da8cd70d6d851fa2313c8d6618f79508cf1e86877edf1c0bfe49a1b0a6467a └── size: 582 ` @@ -224,18 +224,20 @@ localhost:5000/e2e-insepct-timestamped@sha256:99950868628ed79ebc295e01f8397dcaca "mediaType": "application/vnd.oci.image.manifest.v1+json", "signatures": [ { - "digest": "sha256:54eab65f9262feac4ea9f31d15b62c870bf359d912aba86622cfc735337ae4fa", + "digest": "sha256:e3222a9ea284789503cd2087aea775b73e049cb2c51e636a3980658e55577d18", "signatureAlgorithm": "RSASSA-PSS-SHA-256", "signedAttributes": { "contentType": "application/vnd.cncf.notary.payload.v1+json", "signingScheme": "notary.x509", - "signingTime": "2025-01-17T06:36:19Z" + "signingTime": "2025-01-21T08:41:17Z" + }, + "userDefinedAttributes": { + "purpose": "e2e" }, - "userDefinedAttributes": null, "unsignedAttributes": { "signingAgent": "notation-go/1.3.0+unreleased", "timestampSignature": { - "timestamp": "[2025-01-17T06:36:19.734Z, 2025-01-17T06:36:20.734Z]", + "timestamp": "[2025-01-21T08:41:16.915Z, 2025-01-21T08:41:17.915Z]", "certificates": [ { "SHA256Fingerprint": "36e731cfa9bfd69dafb643809f6dec500902f7197daeaad86ea0159a2268a2b8", @@ -244,25 +246,25 @@ localhost:5000/e2e-insepct-timestamped@sha256:99950868628ed79ebc295e01f8397dcaca "expiry": "2035-11-19T20:42:31Z" }, { - "SHA256Fingerprint": "2be4c1670d176be2b0e56081a7b6523487c528a7ea092febbb84ae9db03ceb9a", - "issuedTo": "CN=Microsoft Public RSA Time Stamping Authority,OU=Microsoft Ireland Operations Limited+OU=Thales TSS ESN:F5D6-96D6-909E,O=Microsoft Corporation,L=Redmond,ST=Washington,C=US", + "SHA256Fingerprint": "b804553ac8c88a3f71e32fe6b84f1ccef488cf45d2ebca41150e7e21dfd26e71", + "issuedTo": "CN=Microsoft Public RSA Time Stamping Authority,OU=Microsoft America Operations+OU=Thales TSS ESN:BB73-96FD-77EF,O=Microsoft Corporation,L=Redmond,ST=Washington,C=US", "issuedBy": "CN=Microsoft Public RSA Timestamping CA 2020,O=Microsoft Corporation,C=US", - "expiry": "2025-04-17T17:59:12Z" + "expiry": "2025-11-19T18:48:47Z" } ] } }, "certificates": [ { - "SHA256Fingerprint": "36b3e0e0fee117d33d5664a2f56b147ddbbe8b7ca3ad2ae56498703fd782a56e", - "issuedTo": "CN=testcert5,O=Notary,L=Seattle,ST=WA,C=US", - "issuedBy": "CN=testcert5,O=Notary,L=Seattle,ST=WA,C=US", - "expiry": "2025-01-18T06:34:29Z" + "SHA256Fingerprint": "1717fa9d18f7e9c0f609499474adfe2b8e44172454f1d6e2183d5d04f79af475", + "issuedTo": "CN=testcert7,O=Notary,L=Seattle,ST=WA,C=US", + "issuedBy": "CN=testcert7,O=Notary,L=Seattle,ST=WA,C=US", + "expiry": "2025-01-22T08:36:26Z" } ], "signedArtifact": { "mediaType": "application/vnd.oci.image.manifest.v1+json", - "digest": "sha256:99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa77881ec3cef1c", + "digest": "sha256:f1da8cd70d6d851fa2313c8d6618f79508cf1e86877edf1c0bfe49a1b0a6467a", "size": 582 } } @@ -291,6 +293,83 @@ localhost:5000/e2e-insepct-timestamped@sha256:99950868628ed79ebc295e01f8397dcaca "mediaType": "application/vnd.oci.image.manifest.v1+json", "signatures": [] } +` + notation.Exec("inspect", "--output", "json", artifact.ReferenceWithDigest()). + MatchContent(expectedOutput) + }) + }) + + It("with invalid timestamp signature in text format", func() { + Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { + artifact := GenerateArtifact("e2e-with-invalid-timestamped-signature", "e2e-inspect-invalid-timstamped") + expectedOutput := `Inspecting all signatures for signed artifact +localhost:5000/e2e-inspect-invalid-timstamped@sha256:f1da8cd70d6d851fa2313c8d6618f79508cf1e86877edf1c0bfe49a1b0a6467a +└── application/vnd.cncf.notary.signature + └── sha256:eee3eec7d2947f77713484753bea67879ff62c08a73a49a41151ed18c4d1c000 + ├── signature algorithm: RSASSA-PSS-SHA-256 + ├── signed attributes + │ ├── signing scheme: notary.x509 + │ └── signing time: Tue Jan 21 08:41:17 2025 + ├── user defined attributes + │ └── purpose: e2e + ├── unsigned attributes + │ ├── signing agent: notation-go/1.3.0+unreleased + │ └── timestamp signature + │ └── error: failed to parse timestamp countersignature: cms: syntax error: invalid signed data: failed to convert from BER to DER: asn1: syntax error: decoding BER length octets: short form length octets value should be less or equal to the subsequent octets length + ├── certificates + │ └── SHA256 fingerprint: 1717fa9d18f7e9c0f609499474adfe2b8e44172454f1d6e2183d5d04f79af475 + │ ├── issued to: CN=testcert7,O=Notary,L=Seattle,ST=WA,C=US + │ ├── issued by: CN=testcert7,O=Notary,L=Seattle,ST=WA,C=US + │ └── expiry: Wed Jan 22 08:36:26 2025 + └── signed artifact + ├── media type: application/vnd.oci.image.manifest.v1+json + ├── digest: sha256:f1da8cd70d6d851fa2313c8d6618f79508cf1e86877edf1c0bfe49a1b0a6467a + └── size: 582 +` + notation.Exec("inspect", artifact.ReferenceWithDigest()). + MatchContent(expectedOutput) + }) + }) + + It("with invalid timestamp signature in json format", func() { + Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { + artifact := GenerateArtifact("e2e-with-invalid-timestamped-signature", "e2e-inspect-invalid-timstamped") + expectedOutput := `{ + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "signatures": [ + { + "digest": "sha256:eee3eec7d2947f77713484753bea67879ff62c08a73a49a41151ed18c4d1c000", + "signatureAlgorithm": "RSASSA-PSS-SHA-256", + "signedAttributes": { + "contentType": "application/vnd.cncf.notary.payload.v1+json", + "signingScheme": "notary.x509", + "signingTime": "2025-01-21T08:41:17Z" + }, + "userDefinedAttributes": { + "purpose": "e2e" + }, + "unsignedAttributes": { + "signingAgent": "notation-go/1.3.0+unreleased", + "timestampSignature": { + "error": "failed to parse timestamp countersignature: cms: syntax error: invalid signed data: failed to convert from BER to DER: asn1: syntax error: decoding BER length octets: short form length octets value should be less or equal to the subsequent octets length" + } + }, + "certificates": [ + { + "SHA256Fingerprint": "1717fa9d18f7e9c0f609499474adfe2b8e44172454f1d6e2183d5d04f79af475", + "issuedTo": "CN=testcert7,O=Notary,L=Seattle,ST=WA,C=US", + "issuedBy": "CN=testcert7,O=Notary,L=Seattle,ST=WA,C=US", + "expiry": "2025-01-22T08:36:26Z" + } + ], + "signedArtifact": { + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "digest": "sha256:f1da8cd70d6d851fa2313c8d6618f79508cf1e86877edf1c0bfe49a1b0a6467a", + "size": 582 + } + } + ] +} ` notation.Exec("inspect", "--output", "json", artifact.ReferenceWithDigest()). MatchContent(expectedOutput) diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-invalid-timestamped-signature/blobs/sha256/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a b/test/e2e/testdata/registry/oci_layout/e2e-with-invalid-timestamped-signature/blobs/sha256/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-with-invalid-timestamped-signature/blobs/sha256/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-invalid-timestamped-signature/blobs/sha256/8d2e70ffc2af68b7f2df6f30dfefcb7bcda1db5f9ac2566fc8c6c2248c8d4c5f b/test/e2e/testdata/registry/oci_layout/e2e-with-invalid-timestamped-signature/blobs/sha256/8d2e70ffc2af68b7f2df6f30dfefcb7bcda1db5f9ac2566fc8c6c2248c8d4c5f new file mode 100644 index 000000000..d470f2d74 --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-with-invalid-timestamped-signature/blobs/sha256/8d2e70ffc2af68b7f2df6f30dfefcb7bcda1db5f9ac2566fc8c6c2248c8d4c5f @@ -0,0 +1,12 @@ +{ + "payload": "eyJ0YXJnZXRBcnRpZmFjdCI6eyJhbm5vdGF0aW9ucyI6eyJwdXJwb3NlIjoiZTJlIn0sImRpZ2VzdCI6InNoYTI1NjpmMWRhOGNkNzBkNmQ4NTFmYTIzMTNjOGQ2NjE4Zjc5NTA4Y2YxZTg2ODc3ZWRmMWMwYmZlNDlhMWIwYTY0NjdhIiwibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLm9jaS5pbWFnZS5tYW5pZmVzdC52MStqc29uIiwic2l6ZSI6NTgyfX0", + "protected": "eyJhbGciOiJQUzI1NiIsImNyaXQiOlsiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSJdLCJjdHkiOiJhcHBsaWNhdGlvbi92bmQuY25jZi5ub3RhcnkucGF5bG9hZC52MStqc29uIiwiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSI6Im5vdGFyeS54NTA5IiwiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1RpbWUiOiIyMDI1LTAxLTIxVDA4OjQxOjE3WiJ9", + "header": { + "io.cncf.notary.timestampSignature": "aW52YWxpZAo=", + "x5c": [ + "MIIDRTCCAi2gAwIBAgICALYwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZOb3RhcnkxEjAQBgNVBAMTCXRlc3RjZXJ0NzAeFw0yNTAxMjEwODM2MjZaFw0yNTAxMjIwODM2MjZaMFExCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJXQTEQMA4GA1UEBxMHU2VhdHRsZTEPMA0GA1UEChMGTm90YXJ5MRIwEAYDVQQDEwl0ZXN0Y2VydDcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDXjPHvnk0IiQjPPc1Z6Dfk09kq3+RtFK9f+wll6205QE3jPgvkebSwal79+xtRdXJlzmqa0pMskegrilvheehj12l4UGP9SbjPt1oO0Vf1k8qVaFV2h309Sg1h0JJvQYKtCIHBiwzSORdJfFuCDKFy1y1ErC4BuhR49JbWva0GWcvVJtPGCccxMKErf9U3xV9T+cqubjdrM2cMcAzgjttXwzSljmA3c+Kj0M+CaoZYb/V3r3iU1rmCDtIgWFFjKN2eb6EDLLFZrnKUVEDtTrTeUNH5jLjAMAlDQM0Mmjsv9Jb0ZeN/hY3BbuacHNoQeTQhIUEZ9Tol1vmbCC3qQSLHAgMBAAGjJzAlMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzANBgkqhkiG9w0BAQsFAAOCAQEAt9wF6UJ7eJBw9kcTLs88n0yAPqoD2YGw2ciRoMgdcFO8ncrojgqmrGNP+E9n+SZcgR5I0Q04aNnN6uiUX6sXUmJQLxptLD10nHF+953gQYfFNgpzONyunme7h/Xq5goZvJfBpM0551MUHmKVisKhZCMoF6puQ9GIDV6yDh7Vn+yBQcSq6maUF1/CNTSapgJA1ntEcbAo9Nd7w6+rnUmezdKwWbxPwzwL+EZ6EoQ8tywOJrjMLzZ4E5D3pvwK0e3j6VTcTlb8YCi/Xj8dAEdYn7+w4WjukdOGUVJtTOZ1WRPi7T3JoV96ry/LeNtnaQLrQU+mClRTa93OhnkuWNUw+Q==" + ], + "io.cncf.notary.signingAgent": "notation-go/1.3.0+unreleased" + }, + "signature": "uYBON0RNGgDI5U__12-0TkWk6ClgzcWHS9aCC5qtQ2yrCfl9a_NDkHYhkhEG_TvdA--8mWpw_kjiS1oQHbsjr0KZED29lux1Ow8cq_0h13VjKnVVGeL_4aATmypV6DA9zVuolOW_vzHpZg86un1qUFtLx6Gv5RYZ97SqwMv5lEIQuqk0BwO4XaBuidUrAw44g5SeNRix-TurQxUEUkXxPyp6NZ0MoAaJAMDVXZK8ExZ2UczYolOuOVxgqJdHxSRp9izKIZnJp9fgg3tOXWJ3y5sQ_sjmzgdlk2medRtEoum55H0cxGV5gQiYRMfiROcFacaK_y8IDhARFxjNethCOQ" +} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-invalid-timestamped-signature/blobs/sha256/ce41e5246ead8bddd2a2b5bbb863db250f328be9dc5c3041481d778a32f8130d b/test/e2e/testdata/registry/oci_layout/e2e-with-invalid-timestamped-signature/blobs/sha256/ce41e5246ead8bddd2a2b5bbb863db250f328be9dc5c3041481d778a32f8130d new file mode 100644 index 000000000..d383c56ff --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-with-invalid-timestamped-signature/blobs/sha256/ce41e5246ead8bddd2a2b5bbb863db250f328be9dc5c3041481d778a32f8130d @@ -0,0 +1 @@ +testdata diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-invalid-timestamped-signature/blobs/sha256/eee3eec7d2947f77713484753bea67879ff62c08a73a49a41151ed18c4d1c000 b/test/e2e/testdata/registry/oci_layout/e2e-with-invalid-timestamped-signature/blobs/sha256/eee3eec7d2947f77713484753bea67879ff62c08a73a49a41151ed18c4d1c000 new file mode 100644 index 000000000..96e80f65f --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-with-invalid-timestamped-signature/blobs/sha256/eee3eec7d2947f77713484753bea67879ff62c08a73a49a41151ed18c4d1c000 @@ -0,0 +1 @@ +{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.cncf.notary.signature","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2},"layers":[{"mediaType":"application/jose+json","digest":"sha256:8d2e70ffc2af68b7f2df6f30dfefcb7bcda1db5f9ac2566fc8c6c2248c8d4c5f","size":2269}],"subject":{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:f1da8cd70d6d851fa2313c8d6618f79508cf1e86877edf1c0bfe49a1b0a6467a","size":582},"annotations":{"io.cncf.notary.x509chain.thumbprint#S256":"[\"1717fa9d18f7e9c0f609499474adfe2b8e44172454f1d6e2183d5d04f79af475\"]","org.opencontainers.image.created":"2025-01-21T08:41:17Z"}} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/d7b441acd8ea27f3fa22afeb4433c937a22c241f6f61ccc5ce84172509bf78ae b/test/e2e/testdata/registry/oci_layout/e2e-with-invalid-timestamped-signature/blobs/sha256/f1da8cd70d6d851fa2313c8d6618f79508cf1e86877edf1c0bfe49a1b0a6467a similarity index 65% rename from test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/d7b441acd8ea27f3fa22afeb4433c937a22c241f6f61ccc5ce84172509bf78ae rename to test/e2e/testdata/registry/oci_layout/e2e-with-invalid-timestamped-signature/blobs/sha256/f1da8cd70d6d851fa2313c8d6618f79508cf1e86877edf1c0bfe49a1b0a6467a index 19f9776b8..b7ba87f01 100644 --- a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/d7b441acd8ea27f3fa22afeb4433c937a22c241f6f61ccc5ce84172509bf78ae +++ b/test/e2e/testdata/registry/oci_layout/e2e-with-invalid-timestamped-signature/blobs/sha256/f1da8cd70d6d851fa2313c8d6618f79508cf1e86877edf1c0bfe49a1b0a6467a @@ -1 +1 @@ -{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","artifactType":"application/vnd.unknown.artifact.v1","config":{"mediaType":"application/vnd.oci.empty.v1+json","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2,"data":"e30="},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar","digest":"sha256:7c52011daa0bf2983c4687c2ee6a8d7759503a29f64624fa52970516d9ec45b2","size":9,"annotations":{"org.opencontainers.image.title":"file"}}],"annotations":{"org.opencontainers.image.created":"2025-01-17T06:32:32Z"}} \ No newline at end of file +{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","artifactType":"application/vnd.unknown.artifact.v1","config":{"mediaType":"application/vnd.oci.empty.v1+json","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2,"data":"e30="},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar","digest":"sha256:ce41e5246ead8bddd2a2b5bbb863db250f328be9dc5c3041481d778a32f8130d","size":9,"annotations":{"org.opencontainers.image.title":"file"}}],"annotations":{"org.opencontainers.image.created":"2025-01-21T08:40:36Z"}} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-invalid-timestamped-signature/index.json b/test/e2e/testdata/registry/oci_layout/e2e-with-invalid-timestamped-signature/index.json new file mode 100644 index 000000000..2a461366f --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-with-invalid-timestamped-signature/index.json @@ -0,0 +1 @@ +{"schemaVersion":2,"manifests":[{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:f1da8cd70d6d851fa2313c8d6618f79508cf1e86877edf1c0bfe49a1b0a6467a","size":582,"annotations":{"org.opencontainers.image.created":"2025-01-21T08:40:36Z","org.opencontainers.image.ref.name":"v1"},"artifactType":"application/vnd.unknown.artifact.v1"},{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:eee3eec7d2947f77713484753bea67879ff62c08a73a49a41151ed18c4d1c000","size":728,"annotations":{"io.cncf.notary.x509chain.thumbprint#S256":"[\"1717fa9d18f7e9c0f609499474adfe2b8e44172454f1d6e2183d5d04f79af475\"]","org.opencontainers.image.created":"2025-01-21T08:41:17Z"}}]} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-invalid-timestamped-signature/oci-layout b/test/e2e/testdata/registry/oci_layout/e2e-with-invalid-timestamped-signature/oci-layout new file mode 100644 index 000000000..1343d370f --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-with-invalid-timestamped-signature/oci-layout @@ -0,0 +1 @@ +{"imageLayoutVersion":"1.0.0"} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/123ec9fa46aad212010c907e98cb328de9731f23783c546bea1aa7580a4a6a37 b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/123ec9fa46aad212010c907e98cb328de9731f23783c546bea1aa7580a4a6a37 deleted file mode 100644 index 5cb670ffd..000000000 --- a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/123ec9fa46aad212010c907e98cb328de9731f23783c546bea1aa7580a4a6a37 +++ /dev/null @@ -1 +0,0 @@ -{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.cncf.notary.signature","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2},"layers":[{"mediaType":"application/jose+json","digest":"sha256:cacefba75b1771fa028faeb3c02f0ab6aa2430c52c96b5e127513259309002d4","size":2089}],"subject":{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:d7b441acd8ea27f3fa22afeb4433c937a22c241f6f61ccc5ce84172509bf78ae","size":582},"annotations":{"io.cncf.notary.x509chain.thumbprint#S256":"[\"36b3e0e0fee117d33d5664a2f56b147ddbbe8b7ca3ad2ae56498703fd782a56e\"]","org.opencontainers.image.created":"2025-01-17T06:34:56Z"}} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/54eab65f9262feac4ea9f31d15b62c870bf359d912aba86622cfc735337ae4fa b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/54eab65f9262feac4ea9f31d15b62c870bf359d912aba86622cfc735337ae4fa deleted file mode 100644 index 394caad4c..000000000 --- a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/54eab65f9262feac4ea9f31d15b62c870bf359d912aba86622cfc735337ae4fa +++ /dev/null @@ -1 +0,0 @@ -{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.cncf.notary.signature","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2},"layers":[{"mediaType":"application/jose+json","digest":"sha256:f27085b685cfed009862f7cc672269e3228cdaaf38140f81e97f2ed29a714d70","size":10208}],"subject":{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa77881ec3cef1c","size":582},"annotations":{"io.cncf.notary.x509chain.thumbprint#S256":"[\"36b3e0e0fee117d33d5664a2f56b147ddbbe8b7ca3ad2ae56498703fd782a56e\"]","org.opencontainers.image.created":"2025-01-17T06:36:19Z"}} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/7c52011daa0bf2983c4687c2ee6a8d7759503a29f64624fa52970516d9ec45b2 b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/7c52011daa0bf2983c4687c2ee6a8d7759503a29f64624fa52970516d9ec45b2 deleted file mode 100644 index 022ea9754..000000000 --- a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/7c52011daa0bf2983c4687c2ee6a8d7759503a29f64624fa52970516d9ec45b2 +++ /dev/null @@ -1 +0,0 @@ -testFile diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/83a35ad9499039f2ba1fcd205780dfedb82a447593aba3383ec8461dda587954 b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/83a35ad9499039f2ba1fcd205780dfedb82a447593aba3383ec8461dda587954 new file mode 100644 index 000000000..2573c7d0e --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/83a35ad9499039f2ba1fcd205780dfedb82a447593aba3383ec8461dda587954 @@ -0,0 +1 @@ +{"payload":"eyJ0YXJnZXRBcnRpZmFjdCI6eyJhbm5vdGF0aW9ucyI6eyJwdXJwb3NlIjoiZTJlIn0sImRpZ2VzdCI6InNoYTI1NjpmMWRhOGNkNzBkNmQ4NTFmYTIzMTNjOGQ2NjE4Zjc5NTA4Y2YxZTg2ODc3ZWRmMWMwYmZlNDlhMWIwYTY0NjdhIiwibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLm9jaS5pbWFnZS5tYW5pZmVzdC52MStqc29uIiwic2l6ZSI6NTgyfX0","protected":"eyJhbGciOiJQUzI1NiIsImNyaXQiOlsiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSJdLCJjdHkiOiJhcHBsaWNhdGlvbi92bmQuY25jZi5ub3RhcnkucGF5bG9hZC52MStqc29uIiwiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSI6Im5vdGFyeS54NTA5IiwiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1RpbWUiOiIyMDI1LTAxLTIxVDA4OjQxOjE3WiJ9","header":{"io.cncf.notary.timestampSignature":"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","x5c":["MIIDRTCCAi2gAwIBAgICALYwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZOb3RhcnkxEjAQBgNVBAMTCXRlc3RjZXJ0NzAeFw0yNTAxMjEwODM2MjZaFw0yNTAxMjIwODM2MjZaMFExCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJXQTEQMA4GA1UEBxMHU2VhdHRsZTEPMA0GA1UEChMGTm90YXJ5MRIwEAYDVQQDEwl0ZXN0Y2VydDcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDXjPHvnk0IiQjPPc1Z6Dfk09kq3+RtFK9f+wll6205QE3jPgvkebSwal79+xtRdXJlzmqa0pMskegrilvheehj12l4UGP9SbjPt1oO0Vf1k8qVaFV2h309Sg1h0JJvQYKtCIHBiwzSORdJfFuCDKFy1y1ErC4BuhR49JbWva0GWcvVJtPGCccxMKErf9U3xV9T+cqubjdrM2cMcAzgjttXwzSljmA3c+Kj0M+CaoZYb/V3r3iU1rmCDtIgWFFjKN2eb6EDLLFZrnKUVEDtTrTeUNH5jLjAMAlDQM0Mmjsv9Jb0ZeN/hY3BbuacHNoQeTQhIUEZ9Tol1vmbCC3qQSLHAgMBAAGjJzAlMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzANBgkqhkiG9w0BAQsFAAOCAQEAt9wF6UJ7eJBw9kcTLs88n0yAPqoD2YGw2ciRoMgdcFO8ncrojgqmrGNP+E9n+SZcgR5I0Q04aNnN6uiUX6sXUmJQLxptLD10nHF+953gQYfFNgpzONyunme7h/Xq5goZvJfBpM0551MUHmKVisKhZCMoF6puQ9GIDV6yDh7Vn+yBQcSq6maUF1/CNTSapgJA1ntEcbAo9Nd7w6+rnUmezdKwWbxPwzwL+EZ6EoQ8tywOJrjMLzZ4E5D3pvwK0e3j6VTcTlb8YCi/Xj8dAEdYn7+w4WjukdOGUVJtTOZ1WRPi7T3JoV96ry/LeNtnaQLrQU+mClRTa93OhnkuWNUw+Q=="],"io.cncf.notary.signingAgent":"notation-go/1.3.0+unreleased"},"signature":"uYBON0RNGgDI5U__12-0TkWk6ClgzcWHS9aCC5qtQ2yrCfl9a_NDkHYhkhEG_TvdA--8mWpw_kjiS1oQHbsjr0KZED29lux1Ow8cq_0h13VjKnVVGeL_4aATmypV6DA9zVuolOW_vzHpZg86un1qUFtLx6Gv5RYZ97SqwMv5lEIQuqk0BwO4XaBuidUrAw44g5SeNRix-TurQxUEUkXxPyp6NZ0MoAaJAMDVXZK8ExZ2UczYolOuOVxgqJdHxSRp9izKIZnJp9fgg3tOXWJ3y5sQ_sjmzgdlk2medRtEoum55H0cxGV5gQiYRMfiROcFacaK_y8IDhARFxjNethCOQ"} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/cacefba75b1771fa028faeb3c02f0ab6aa2430c52c96b5e127513259309002d4 b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/cacefba75b1771fa028faeb3c02f0ab6aa2430c52c96b5e127513259309002d4 deleted file mode 100644 index 0e5a4c0c7..000000000 --- a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/cacefba75b1771fa028faeb3c02f0ab6aa2430c52c96b5e127513259309002d4 +++ /dev/null @@ -1 +0,0 @@ -{"payload":"eyJ0YXJnZXRBcnRpZmFjdCI6eyJkaWdlc3QiOiJzaGEyNTY6ZDdiNDQxYWNkOGVhMjdmM2ZhMjJhZmViNDQzM2M5MzdhMjJjMjQxZjZmNjFjY2M1Y2U4NDE3MjUwOWJmNzhhZSIsIm1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5vY2kuaW1hZ2UubWFuaWZlc3QudjEranNvbiIsInNpemUiOjU4Mn19","protected":"eyJhbGciOiJQUzI1NiIsImNyaXQiOlsiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSJdLCJjdHkiOiJhcHBsaWNhdGlvbi92bmQuY25jZi5ub3RhcnkucGF5bG9hZC52MStqc29uIiwiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSI6Im5vdGFyeS54NTA5IiwiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1RpbWUiOiIyMDI1LTAxLTE3VDA2OjM0OjU2WiJ9","header":{"x5c":["MIIDRDCCAiygAwIBAgIBDzANBgkqhkiG9w0BAQsFADBRMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTESMBAGA1UEAxMJdGVzdGNlcnQ1MB4XDTI1MDExNzA2MzQyOVoXDTI1MDExODA2MzQyOVowUTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZOb3RhcnkxEjAQBgNVBAMTCXRlc3RjZXJ0NTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPRXF8lqb67w7AjvuI3IB3VTwtJ9sZEP61PoAbvc/qt1WTgSuiI3VCTUbi8GMIIxLDRs+0xsfCRWZFKodbWrw6YXrYpWwwEW7ba2GN63XL0jiFgbbDWB/QylAzd5JSCX8OgBDFR+KiTSzTbfUgQ5+q8hLbR6TfiJOR0pLoSUQ96Y/oPty/aFv8hfcjp0+pbiHeNwZlNAU2uDfxnYZK+uhhOEznF/Jzd98rh9FMnZdM4AGrvg73rNjaY2JQSWWEwIgK6Fl1qCBH9zqrpmyQdC20NrtinOsRi1CNVz6b0GG558bVliETYTGH1KOMEXX4f6VAbvzrJAI24PSm+vZA5zFeECAwEAAaMnMCUwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA0GCSqGSIb3DQEBCwUAA4IBAQDNHCmlY2z93TLnO8ukW3d1q3/Yiddy+YDy31zVrofOd4WBUTemKlFlj8GpiVbTmWmHiX5F/9CgDeTBDgZFzxBOqUZbyIrsyVktpv7vuFXyAkMcC68Ztz8H5EepEFnfo4u1GpWlsmi9PE/TV0MP8KNCj3hVvh70xEiEuzHzk1xQqAt+0MxjKQMJdOaC+bao224j2pS+yZ4hMeMCF8qmZwjn3SEfCRghvzee02XmEAWDOVGcg1gCkFyq2oLp4RwdWBnc4/n7lbmg05+nrUDM0tq22FO2HPWrdGMf/hWlowaa2UZBJe3x4sJsMRQLMVgo29Zhie2KgbNSByu9C/5llapS"],"io.cncf.notary.signingAgent":"notation-go/1.3.0+unreleased"},"signature":"PvOhkuI6YHKU2hRzTeqIR23KpP3RBFSjwYJgh5e2nty2BoinvbHnZl6HQgkrS3brmhGDsjRvB98puX3QprJUcF5ElRmz290ICcBCMiPYTNLhrTomVOmahfeVfdykyamoi6hISoRw-sWA1IXSuAr0jXwDDPqbqmAkh1wmgWRnWwlOjT8yTQ7606CNEN05A9qOgTSiZJA30IoORrQFSWbfC8c6_mtCc2yL4U9RLnl21YCIDEFCEI0kv-UmeFL5vtaaiSl5uLt-4I2myMrn0AiNj9DlGSJnN7rHIKUqM31rgKYdKK5HqUtTNONI2607I_Yfv8WjxskrT5n1K--uAlWNPw"} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/ce41e5246ead8bddd2a2b5bbb863db250f328be9dc5c3041481d778a32f8130d b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/ce41e5246ead8bddd2a2b5bbb863db250f328be9dc5c3041481d778a32f8130d new file mode 100644 index 000000000..d383c56ff --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/ce41e5246ead8bddd2a2b5bbb863db250f328be9dc5c3041481d778a32f8130d @@ -0,0 +1 @@ +testdata diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/e3222a9ea284789503cd2087aea775b73e049cb2c51e636a3980658e55577d18 b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/e3222a9ea284789503cd2087aea775b73e049cb2c51e636a3980658e55577d18 new file mode 100644 index 000000000..054f99491 --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/e3222a9ea284789503cd2087aea775b73e049cb2c51e636a3980658e55577d18 @@ -0,0 +1 @@ +{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.cncf.notary.signature","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2},"layers":[{"mediaType":"application/jose+json","digest":"sha256:83a35ad9499039f2ba1fcd205780dfedb82a447593aba3383ec8461dda587954","size":9219}],"subject":{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:f1da8cd70d6d851fa2313c8d6618f79508cf1e86877edf1c0bfe49a1b0a6467a","size":582},"annotations":{"io.cncf.notary.x509chain.thumbprint#S256":"[\"1717fa9d18f7e9c0f609499474adfe2b8e44172454f1d6e2183d5d04f79af475\"]","org.opencontainers.image.created":"2025-01-21T08:41:17Z"}} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa77881ec3cef1c b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/f1da8cd70d6d851fa2313c8d6618f79508cf1e86877edf1c0bfe49a1b0a6467a similarity index 65% rename from test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa77881ec3cef1c rename to test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/f1da8cd70d6d851fa2313c8d6618f79508cf1e86877edf1c0bfe49a1b0a6467a index f20214f45..b7ba87f01 100644 --- a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa77881ec3cef1c +++ b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/f1da8cd70d6d851fa2313c8d6618f79508cf1e86877edf1c0bfe49a1b0a6467a @@ -1 +1 @@ -{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","artifactType":"application/vnd.unknown.artifact.v1","config":{"mediaType":"application/vnd.oci.empty.v1+json","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2,"data":"e30="},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar","digest":"sha256:7c52011daa0bf2983c4687c2ee6a8d7759503a29f64624fa52970516d9ec45b2","size":9,"annotations":{"org.opencontainers.image.title":"file"}}],"annotations":{"org.opencontainers.image.created":"2025-01-17T06:35:54Z"}} \ No newline at end of file +{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","artifactType":"application/vnd.unknown.artifact.v1","config":{"mediaType":"application/vnd.oci.empty.v1+json","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2,"data":"e30="},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar","digest":"sha256:ce41e5246ead8bddd2a2b5bbb863db250f328be9dc5c3041481d778a32f8130d","size":9,"annotations":{"org.opencontainers.image.title":"file"}}],"annotations":{"org.opencontainers.image.created":"2025-01-21T08:40:36Z"}} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/f27085b685cfed009862f7cc672269e3228cdaaf38140f81e97f2ed29a714d70 b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/f27085b685cfed009862f7cc672269e3228cdaaf38140f81e97f2ed29a714d70 deleted file mode 100644 index ee20e5006..000000000 --- a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/f27085b685cfed009862f7cc672269e3228cdaaf38140f81e97f2ed29a714d70 +++ /dev/null @@ -1 +0,0 @@ -{"payload":"eyJ0YXJnZXRBcnRpZmFjdCI6eyJkaWdlc3QiOiJzaGEyNTY6OTk5NTA4Njg2MjhlZDc5ZWJjMjk1ZTAxZjgzOTdkY2FjYWQzNWUxN2ZiM2I3YTlmMGZhNzc4ODFlYzNjZWYxYyIsIm1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5vY2kuaW1hZ2UubWFuaWZlc3QudjEranNvbiIsInNpemUiOjU4Mn19","protected":"eyJhbGciOiJQUzI1NiIsImNyaXQiOlsiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSJdLCJjdHkiOiJhcHBsaWNhdGlvbi92bmQuY25jZi5ub3RhcnkucGF5bG9hZC52MStqc29uIiwiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSI6Im5vdGFyeS54NTA5IiwiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1RpbWUiOiIyMDI1LTAxLTE3VDA2OjM2OjE5WiJ9","header":{"io.cncf.notary.timestampSignature":"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","x5c":["MIIDRDCCAiygAwIBAgIBDzANBgkqhkiG9w0BAQsFADBRMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTESMBAGA1UEAxMJdGVzdGNlcnQ1MB4XDTI1MDExNzA2MzQyOVoXDTI1MDExODA2MzQyOVowUTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZOb3RhcnkxEjAQBgNVBAMTCXRlc3RjZXJ0NTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPRXF8lqb67w7AjvuI3IB3VTwtJ9sZEP61PoAbvc/qt1WTgSuiI3VCTUbi8GMIIxLDRs+0xsfCRWZFKodbWrw6YXrYpWwwEW7ba2GN63XL0jiFgbbDWB/QylAzd5JSCX8OgBDFR+KiTSzTbfUgQ5+q8hLbR6TfiJOR0pLoSUQ96Y/oPty/aFv8hfcjp0+pbiHeNwZlNAU2uDfxnYZK+uhhOEznF/Jzd98rh9FMnZdM4AGrvg73rNjaY2JQSWWEwIgK6Fl1qCBH9zqrpmyQdC20NrtinOsRi1CNVz6b0GG558bVliETYTGH1KOMEXX4f6VAbvzrJAI24PSm+vZA5zFeECAwEAAaMnMCUwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA0GCSqGSIb3DQEBCwUAA4IBAQDNHCmlY2z93TLnO8ukW3d1q3/Yiddy+YDy31zVrofOd4WBUTemKlFlj8GpiVbTmWmHiX5F/9CgDeTBDgZFzxBOqUZbyIrsyVktpv7vuFXyAkMcC68Ztz8H5EepEFnfo4u1GpWlsmi9PE/TV0MP8KNCj3hVvh70xEiEuzHzk1xQqAt+0MxjKQMJdOaC+bao224j2pS+yZ4hMeMCF8qmZwjn3SEfCRghvzee02XmEAWDOVGcg1gCkFyq2oLp4RwdWBnc4/n7lbmg05+nrUDM0tq22FO2HPWrdGMf/hWlowaa2UZBJe3x4sJsMRQLMVgo29Zhie2KgbNSByu9C/5llapS"],"io.cncf.notary.signingAgent":"notation-go/1.3.0+unreleased"},"signature":"HPR6idroqimVvWszvmX4v3dF6YpktvoYyOVRPnjzfkQJDAw3AJ9x7G__2Jb1hhEL53jnwJMe8Ojnm_5z0x2ebqtl9Ndr_kAYymT4gf29Qxmr_e9U0nWpkjOi_MoYIXWo5udPsTy3oNbP9K-CX_4FQ2bRDCI8476nJ6vc6-uREJJg6HhexeQJ1nYeltbvc7hBmkYe9TX1IkbSVkOLuSYMVFU2T9S-P92Q5l22Qwpw4MG4CXxp7GlLjR1_wq0Hc71ARZZFz8olWv03AQZtR_QJqhdZHBch4MSUJ1cTJMs9NNaQQnU63I7C_k4Ca0-bvlF3JD7SN7Wiz3cP-IqiJKFHcw"} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/index.json b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/index.json index 947cbf68f..80a632601 100644 --- a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/index.json +++ b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/index.json @@ -1 +1 @@ -{"schemaVersion":2,"manifests":[{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:99950868628ed79ebc295e01f8397dcacad35e17fb3b7a9f0fa77881ec3cef1c","size":582,"annotations":{"org.opencontainers.image.created":"2025-01-17T06:35:54Z","org.opencontainers.image.ref.name":"v1"},"artifactType":"application/vnd.unknown.artifact.v1"},{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:d7b441acd8ea27f3fa22afeb4433c937a22c241f6f61ccc5ce84172509bf78ae","size":582,"annotations":{"org.opencontainers.image.created":"2025-01-17T06:32:32Z"},"artifactType":"application/vnd.unknown.artifact.v1"},{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:123ec9fa46aad212010c907e98cb328de9731f23783c546bea1aa7580a4a6a37","size":728,"annotations":{"io.cncf.notary.x509chain.thumbprint#S256":"[\"36b3e0e0fee117d33d5664a2f56b147ddbbe8b7ca3ad2ae56498703fd782a56e\"]","org.opencontainers.image.created":"2025-01-17T06:34:56Z"}},{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:54eab65f9262feac4ea9f31d15b62c870bf359d912aba86622cfc735337ae4fa","size":729,"annotations":{"io.cncf.notary.x509chain.thumbprint#S256":"[\"36b3e0e0fee117d33d5664a2f56b147ddbbe8b7ca3ad2ae56498703fd782a56e\"]","org.opencontainers.image.created":"2025-01-17T06:36:19Z"}}]} \ No newline at end of file +{"schemaVersion":2,"manifests":[{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:f1da8cd70d6d851fa2313c8d6618f79508cf1e86877edf1c0bfe49a1b0a6467a","size":582,"annotations":{"org.opencontainers.image.created":"2025-01-21T08:40:36Z","org.opencontainers.image.ref.name":"v1"},"artifactType":"application/vnd.unknown.artifact.v1"},{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:e3222a9ea284789503cd2087aea775b73e049cb2c51e636a3980658e55577d18","size":728,"annotations":{"io.cncf.notary.x509chain.thumbprint#S256":"[\"1717fa9d18f7e9c0f609499474adfe2b8e44172454f1d6e2183d5d04f79af475\"]","org.opencontainers.image.created":"2025-01-21T08:41:17Z"}}]} \ No newline at end of file From b3a97e5d1e9237649077ad7fe3bc995ad5a05e92 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Tue, 21 Jan 2025 09:24:55 +0000 Subject: [PATCH 22/32] fix: update e2e test Signed-off-by: Junjie Gao --- .../internal/display/metadata/tree/inspect.go | 1 + test/e2e/suite/command/inspect.go | 34 +++++++++++-------- ...1736ac40cfc8eb928642348fd843ba3f0483c0c20} | 2 +- ...0b76137c2c4a10451daab11327a7c6f86dac250fee | 1 + ...af13954fc80ef9aeb665262b6ae4fe469ad7ea3aea | 1 + ...205780dfedb82a447593aba3383ec8461dda587954 | 1 - ...87aea775b73e049cb2c51e636a3980658e55577d18 | 1 - .../e2e-with-timestamped-signature/index.json | 2 +- 8 files changed, 24 insertions(+), 19 deletions(-) rename test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/{f1da8cd70d6d851fa2313c8d6618f79508cf1e86877edf1c0bfe49a1b0a6467a => 53b0191218aed9a3c1f7c661736ac40cfc8eb928642348fd843ba3f0483c0c20} (87%) create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/603ff134e97a79cd7a15560b76137c2c4a10451daab11327a7c6f86dac250fee create mode 100644 test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/6a5cd3a886707a317935dcaf13954fc80ef9aeb665262b6ae4fe469ad7ea3aea delete mode 100644 test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/83a35ad9499039f2ba1fcd205780dfedb82a447593aba3383ec8461dda587954 delete mode 100644 test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/e3222a9ea284789503cd2087aea775b73e049cb2c51e636a3980658e55577d18 diff --git a/cmd/notation/internal/display/metadata/tree/inspect.go b/cmd/notation/internal/display/metadata/tree/inspect.go index b41480dd1..3391a9e6c 100644 --- a/cmd/notation/internal/display/metadata/tree/inspect.go +++ b/cmd/notation/internal/display/metadata/tree/inspect.go @@ -104,6 +104,7 @@ func addSignature(node *tree.Node, digest string, sigEnvelope signature.Envelope func addSignedAttributes(node *tree.Node, envelopeContent *signature.EnvelopeContent) { signedAttributesNode := node.Add("signed attributes") + signedAttributesNode.AddPair("content type", string(envelopeContent.Payload.ContentType)) signedAttributesNode.AddPair("signing scheme", string(envelopeContent.SignerInfo.SignedAttributes.SigningScheme)) signedAttributesNode.AddPair("signing time", formatTime(envelopeContent.SignerInfo.SignedAttributes.SigningTime)) if expiry := envelopeContent.SignerInfo.SignedAttributes.Expiry; !expiry.IsZero() { diff --git a/test/e2e/suite/command/inspect.go b/test/e2e/suite/command/inspect.go index cf48fbae7..3414a0dc9 100644 --- a/test/e2e/suite/command/inspect.go +++ b/test/e2e/suite/command/inspect.go @@ -179,28 +179,30 @@ var _ = Describe("notation inspect", func() { Host(BaseOptions(), func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { artifact := GenerateArtifact("e2e-with-timestamped-signature", "e2e-insepct-timestamped") expectedOutput := `Inspecting all signatures for signed artifact -localhost:5000/e2e-insepct-timestamped@sha256:f1da8cd70d6d851fa2313c8d6618f79508cf1e86877edf1c0bfe49a1b0a6467a +localhost:5000/e2e-insepct-timestamped@sha256:53b0191218aed9a3c1f7c661736ac40cfc8eb928642348fd843ba3f0483c0c20 └── application/vnd.cncf.notary.signature - └── sha256:e3222a9ea284789503cd2087aea775b73e049cb2c51e636a3980658e55577d18 + └── sha256:6a5cd3a886707a317935dcaf13954fc80ef9aeb665262b6ae4fe469ad7ea3aea ├── signature algorithm: RSASSA-PSS-SHA-256 ├── signed attributes + │ ├── content type: application/vnd.cncf.notary.payload.v1+json │ ├── signing scheme: notary.x509 - │ └── signing time: Tue Jan 21 08:41:17 2025 + │ ├── signing time: Tue Jan 21 09:17:46 2025 + │ └── expiry: Tue Jan 21 12:39:46 2025 ├── user defined attributes │ └── purpose: e2e ├── unsigned attributes │ ├── signing agent: notation-go/1.3.0+unreleased │ └── timestamp signature - │ ├── timestamp: [Tue Jan 21 08:41:16 2025, Tue Jan 21 08:41:17 2025] + │ ├── timestamp: [Tue Jan 21 09:17:46 2025, Tue Jan 21 09:17:47 2025] │ └── certificates │ ├── SHA256 fingerprint: 36e731cfa9bfd69dafb643809f6dec500902f7197daeaad86ea0159a2268a2b8 │ │ ├── issued to: CN=Microsoft Public RSA Timestamping CA 2020,O=Microsoft Corporation,C=US │ │ ├── issued by: CN=Microsoft Identity Verification Root Certificate Authority 2020,O=Microsoft Corporation,C=US │ │ └── expiry: Mon Nov 19 20:42:31 2035 - │ └── SHA256 fingerprint: b804553ac8c88a3f71e32fe6b84f1ccef488cf45d2ebca41150e7e21dfd26e71 - │ ├── issued to: CN=Microsoft Public RSA Time Stamping Authority,OU=Microsoft America Operations+OU=Thales TSS ESN:BB73-96FD-77EF,O=Microsoft Corporation,L=Redmond,ST=Washington,C=US + │ └── SHA256 fingerprint: 59283806bbc4fec702339b6026633c2034333fd3cae5368323186209e406d522 + │ ├── issued to: CN=Microsoft Public RSA Time Stamping Authority,OU=Microsoft America Operations+OU=Thales TSS ESN:45D6-96C5-5E63,O=Microsoft Corporation,L=Redmond,ST=Washington,C=US │ ├── issued by: CN=Microsoft Public RSA Timestamping CA 2020,O=Microsoft Corporation,C=US - │ └── expiry: Wed Nov 19 18:48:47 2025 + │ └── expiry: Wed Nov 19 18:48:54 2025 ├── certificates │ └── SHA256 fingerprint: 1717fa9d18f7e9c0f609499474adfe2b8e44172454f1d6e2183d5d04f79af475 │ ├── issued to: CN=testcert7,O=Notary,L=Seattle,ST=WA,C=US @@ -208,7 +210,7 @@ localhost:5000/e2e-insepct-timestamped@sha256:f1da8cd70d6d851fa2313c8d6618f79508 │ └── expiry: Wed Jan 22 08:36:26 2025 └── signed artifact ├── media type: application/vnd.oci.image.manifest.v1+json - ├── digest: sha256:f1da8cd70d6d851fa2313c8d6618f79508cf1e86877edf1c0bfe49a1b0a6467a + ├── digest: sha256:53b0191218aed9a3c1f7c661736ac40cfc8eb928642348fd843ba3f0483c0c20 └── size: 582 ` @@ -224,12 +226,13 @@ localhost:5000/e2e-insepct-timestamped@sha256:f1da8cd70d6d851fa2313c8d6618f79508 "mediaType": "application/vnd.oci.image.manifest.v1+json", "signatures": [ { - "digest": "sha256:e3222a9ea284789503cd2087aea775b73e049cb2c51e636a3980658e55577d18", + "digest": "sha256:6a5cd3a886707a317935dcaf13954fc80ef9aeb665262b6ae4fe469ad7ea3aea", "signatureAlgorithm": "RSASSA-PSS-SHA-256", "signedAttributes": { "contentType": "application/vnd.cncf.notary.payload.v1+json", + "expiry": "2025-01-21T12:39:46Z", "signingScheme": "notary.x509", - "signingTime": "2025-01-21T08:41:17Z" + "signingTime": "2025-01-21T09:17:46Z" }, "userDefinedAttributes": { "purpose": "e2e" @@ -237,7 +240,7 @@ localhost:5000/e2e-insepct-timestamped@sha256:f1da8cd70d6d851fa2313c8d6618f79508 "unsignedAttributes": { "signingAgent": "notation-go/1.3.0+unreleased", "timestampSignature": { - "timestamp": "[2025-01-21T08:41:16.915Z, 2025-01-21T08:41:17.915Z]", + "timestamp": "[2025-01-21T09:17:46.141Z, 2025-01-21T09:17:47.141Z]", "certificates": [ { "SHA256Fingerprint": "36e731cfa9bfd69dafb643809f6dec500902f7197daeaad86ea0159a2268a2b8", @@ -246,10 +249,10 @@ localhost:5000/e2e-insepct-timestamped@sha256:f1da8cd70d6d851fa2313c8d6618f79508 "expiry": "2035-11-19T20:42:31Z" }, { - "SHA256Fingerprint": "b804553ac8c88a3f71e32fe6b84f1ccef488cf45d2ebca41150e7e21dfd26e71", - "issuedTo": "CN=Microsoft Public RSA Time Stamping Authority,OU=Microsoft America Operations+OU=Thales TSS ESN:BB73-96FD-77EF,O=Microsoft Corporation,L=Redmond,ST=Washington,C=US", + "SHA256Fingerprint": "59283806bbc4fec702339b6026633c2034333fd3cae5368323186209e406d522", + "issuedTo": "CN=Microsoft Public RSA Time Stamping Authority,OU=Microsoft America Operations+OU=Thales TSS ESN:45D6-96C5-5E63,O=Microsoft Corporation,L=Redmond,ST=Washington,C=US", "issuedBy": "CN=Microsoft Public RSA Timestamping CA 2020,O=Microsoft Corporation,C=US", - "expiry": "2025-11-19T18:48:47Z" + "expiry": "2025-11-19T18:48:54Z" } ] } @@ -264,7 +267,7 @@ localhost:5000/e2e-insepct-timestamped@sha256:f1da8cd70d6d851fa2313c8d6618f79508 ], "signedArtifact": { "mediaType": "application/vnd.oci.image.manifest.v1+json", - "digest": "sha256:f1da8cd70d6d851fa2313c8d6618f79508cf1e86877edf1c0bfe49a1b0a6467a", + "digest": "sha256:53b0191218aed9a3c1f7c661736ac40cfc8eb928642348fd843ba3f0483c0c20", "size": 582 } } @@ -308,6 +311,7 @@ localhost:5000/e2e-inspect-invalid-timstamped@sha256:f1da8cd70d6d851fa2313c8d661 └── sha256:eee3eec7d2947f77713484753bea67879ff62c08a73a49a41151ed18c4d1c000 ├── signature algorithm: RSASSA-PSS-SHA-256 ├── signed attributes + │ ├── content type: application/vnd.cncf.notary.payload.v1+json │ ├── signing scheme: notary.x509 │ └── signing time: Tue Jan 21 08:41:17 2025 ├── user defined attributes diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/f1da8cd70d6d851fa2313c8d6618f79508cf1e86877edf1c0bfe49a1b0a6467a b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/53b0191218aed9a3c1f7c661736ac40cfc8eb928642348fd843ba3f0483c0c20 similarity index 87% rename from test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/f1da8cd70d6d851fa2313c8d6618f79508cf1e86877edf1c0bfe49a1b0a6467a rename to test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/53b0191218aed9a3c1f7c661736ac40cfc8eb928642348fd843ba3f0483c0c20 index b7ba87f01..552763b11 100644 --- a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/f1da8cd70d6d851fa2313c8d6618f79508cf1e86877edf1c0bfe49a1b0a6467a +++ b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/53b0191218aed9a3c1f7c661736ac40cfc8eb928642348fd843ba3f0483c0c20 @@ -1 +1 @@ -{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","artifactType":"application/vnd.unknown.artifact.v1","config":{"mediaType":"application/vnd.oci.empty.v1+json","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2,"data":"e30="},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar","digest":"sha256:ce41e5246ead8bddd2a2b5bbb863db250f328be9dc5c3041481d778a32f8130d","size":9,"annotations":{"org.opencontainers.image.title":"file"}}],"annotations":{"org.opencontainers.image.created":"2025-01-21T08:40:36Z"}} \ No newline at end of file +{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","artifactType":"application/vnd.unknown.artifact.v1","config":{"mediaType":"application/vnd.oci.empty.v1+json","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2,"data":"e30="},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar","digest":"sha256:ce41e5246ead8bddd2a2b5bbb863db250f328be9dc5c3041481d778a32f8130d","size":9,"annotations":{"org.opencontainers.image.title":"file"}}],"annotations":{"org.opencontainers.image.created":"2025-01-21T09:17:17Z"}} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/603ff134e97a79cd7a15560b76137c2c4a10451daab11327a7c6f86dac250fee b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/603ff134e97a79cd7a15560b76137c2c4a10451daab11327a7c6f86dac250fee new file mode 100644 index 000000000..ebafdb8d1 --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/603ff134e97a79cd7a15560b76137c2c4a10451daab11327a7c6f86dac250fee @@ -0,0 +1 @@ +{"payload":"eyJ0YXJnZXRBcnRpZmFjdCI6eyJhbm5vdGF0aW9ucyI6eyJwdXJwb3NlIjoiZTJlIn0sImRpZ2VzdCI6InNoYTI1Njo1M2IwMTkxMjE4YWVkOWEzYzFmN2M2NjE3MzZhYzQwY2ZjOGViOTI4NjQyMzQ4ZmQ4NDNiYTNmMDQ4M2MwYzIwIiwibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLm9jaS5pbWFnZS5tYW5pZmVzdC52MStqc29uIiwic2l6ZSI6NTgyfX0","protected":"eyJhbGciOiJQUzI1NiIsImNyaXQiOlsiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSIsImlvLmNuY2Yubm90YXJ5LmV4cGlyeSJdLCJjdHkiOiJhcHBsaWNhdGlvbi92bmQuY25jZi5ub3RhcnkucGF5bG9hZC52MStqc29uIiwiaW8uY25jZi5ub3RhcnkuZXhwaXJ5IjoiMjAyNS0wMS0yMVQxMjozOTo0NloiLCJpby5jbmNmLm5vdGFyeS5zaWduaW5nU2NoZW1lIjoibm90YXJ5Lng1MDkiLCJpby5jbmNmLm5vdGFyeS5zaWduaW5nVGltZSI6IjIwMjUtMDEtMjFUMDk6MTc6NDZaIn0","header":{"io.cncf.notary.timestampSignature":"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","x5c":["MIIDRTCCAi2gAwIBAgICALYwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZOb3RhcnkxEjAQBgNVBAMTCXRlc3RjZXJ0NzAeFw0yNTAxMjEwODM2MjZaFw0yNTAxMjIwODM2MjZaMFExCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJXQTEQMA4GA1UEBxMHU2VhdHRsZTEPMA0GA1UEChMGTm90YXJ5MRIwEAYDVQQDEwl0ZXN0Y2VydDcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDXjPHvnk0IiQjPPc1Z6Dfk09kq3+RtFK9f+wll6205QE3jPgvkebSwal79+xtRdXJlzmqa0pMskegrilvheehj12l4UGP9SbjPt1oO0Vf1k8qVaFV2h309Sg1h0JJvQYKtCIHBiwzSORdJfFuCDKFy1y1ErC4BuhR49JbWva0GWcvVJtPGCccxMKErf9U3xV9T+cqubjdrM2cMcAzgjttXwzSljmA3c+Kj0M+CaoZYb/V3r3iU1rmCDtIgWFFjKN2eb6EDLLFZrnKUVEDtTrTeUNH5jLjAMAlDQM0Mmjsv9Jb0ZeN/hY3BbuacHNoQeTQhIUEZ9Tol1vmbCC3qQSLHAgMBAAGjJzAlMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzANBgkqhkiG9w0BAQsFAAOCAQEAt9wF6UJ7eJBw9kcTLs88n0yAPqoD2YGw2ciRoMgdcFO8ncrojgqmrGNP+E9n+SZcgR5I0Q04aNnN6uiUX6sXUmJQLxptLD10nHF+953gQYfFNgpzONyunme7h/Xq5goZvJfBpM0551MUHmKVisKhZCMoF6puQ9GIDV6yDh7Vn+yBQcSq6maUF1/CNTSapgJA1ntEcbAo9Nd7w6+rnUmezdKwWbxPwzwL+EZ6EoQ8tywOJrjMLzZ4E5D3pvwK0e3j6VTcTlb8YCi/Xj8dAEdYn7+w4WjukdOGUVJtTOZ1WRPi7T3JoV96ry/LeNtnaQLrQU+mClRTa93OhnkuWNUw+Q=="],"io.cncf.notary.signingAgent":"notation-go/1.3.0+unreleased"},"signature":"V56oTNY5JLB7KFy4tytRS7R7xv5YOoACEFo9TC4_HIyTfWsyFxy00TbdrAdSupds7XSDlX3daouGpfUHF5XU6WEeGr1iqztNILj-MA_P69FBR3qZIZTo-fincym0UvhTgwY8LMCCCvq1mTZcdnyIUl67GoJZx_N7t6mpzISjQ-VXmX-oPSngjDi3FqJpKzRPFBOAbu-oQhic3D3CSnx1Ot1ckvQTw3WCL5WYEJ7WEz_fAzbqY35guATIgGrSZKR4H9NjX-_A79zewpmGVUu_tGSY1WpqVhshm3taBtaVwxYzs4HT19ql7ch_xdd656trWpEmm5R_P_MIeKl0heHYGw"} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/6a5cd3a886707a317935dcaf13954fc80ef9aeb665262b6ae4fe469ad7ea3aea b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/6a5cd3a886707a317935dcaf13954fc80ef9aeb665262b6ae4fe469ad7ea3aea new file mode 100644 index 000000000..fb9240479 --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/6a5cd3a886707a317935dcaf13954fc80ef9aeb665262b6ae4fe469ad7ea3aea @@ -0,0 +1 @@ +{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.cncf.notary.signature","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2},"layers":[{"mediaType":"application/jose+json","digest":"sha256:603ff134e97a79cd7a15560b76137c2c4a10451daab11327a7c6f86dac250fee","size":10314}],"subject":{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:53b0191218aed9a3c1f7c661736ac40cfc8eb928642348fd843ba3f0483c0c20","size":582},"annotations":{"io.cncf.notary.x509chain.thumbprint#S256":"[\"1717fa9d18f7e9c0f609499474adfe2b8e44172454f1d6e2183d5d04f79af475\"]","org.opencontainers.image.created":"2025-01-21T09:17:46Z"}} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/83a35ad9499039f2ba1fcd205780dfedb82a447593aba3383ec8461dda587954 b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/83a35ad9499039f2ba1fcd205780dfedb82a447593aba3383ec8461dda587954 deleted file mode 100644 index 2573c7d0e..000000000 --- a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/83a35ad9499039f2ba1fcd205780dfedb82a447593aba3383ec8461dda587954 +++ /dev/null @@ -1 +0,0 @@ -{"payload":"eyJ0YXJnZXRBcnRpZmFjdCI6eyJhbm5vdGF0aW9ucyI6eyJwdXJwb3NlIjoiZTJlIn0sImRpZ2VzdCI6InNoYTI1NjpmMWRhOGNkNzBkNmQ4NTFmYTIzMTNjOGQ2NjE4Zjc5NTA4Y2YxZTg2ODc3ZWRmMWMwYmZlNDlhMWIwYTY0NjdhIiwibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLm9jaS5pbWFnZS5tYW5pZmVzdC52MStqc29uIiwic2l6ZSI6NTgyfX0","protected":"eyJhbGciOiJQUzI1NiIsImNyaXQiOlsiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSJdLCJjdHkiOiJhcHBsaWNhdGlvbi92bmQuY25jZi5ub3RhcnkucGF5bG9hZC52MStqc29uIiwiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSI6Im5vdGFyeS54NTA5IiwiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1RpbWUiOiIyMDI1LTAxLTIxVDA4OjQxOjE3WiJ9","header":{"io.cncf.notary.timestampSignature":"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","x5c":["MIIDRTCCAi2gAwIBAgICALYwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZOb3RhcnkxEjAQBgNVBAMTCXRlc3RjZXJ0NzAeFw0yNTAxMjEwODM2MjZaFw0yNTAxMjIwODM2MjZaMFExCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJXQTEQMA4GA1UEBxMHU2VhdHRsZTEPMA0GA1UEChMGTm90YXJ5MRIwEAYDVQQDEwl0ZXN0Y2VydDcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDXjPHvnk0IiQjPPc1Z6Dfk09kq3+RtFK9f+wll6205QE3jPgvkebSwal79+xtRdXJlzmqa0pMskegrilvheehj12l4UGP9SbjPt1oO0Vf1k8qVaFV2h309Sg1h0JJvQYKtCIHBiwzSORdJfFuCDKFy1y1ErC4BuhR49JbWva0GWcvVJtPGCccxMKErf9U3xV9T+cqubjdrM2cMcAzgjttXwzSljmA3c+Kj0M+CaoZYb/V3r3iU1rmCDtIgWFFjKN2eb6EDLLFZrnKUVEDtTrTeUNH5jLjAMAlDQM0Mmjsv9Jb0ZeN/hY3BbuacHNoQeTQhIUEZ9Tol1vmbCC3qQSLHAgMBAAGjJzAlMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzANBgkqhkiG9w0BAQsFAAOCAQEAt9wF6UJ7eJBw9kcTLs88n0yAPqoD2YGw2ciRoMgdcFO8ncrojgqmrGNP+E9n+SZcgR5I0Q04aNnN6uiUX6sXUmJQLxptLD10nHF+953gQYfFNgpzONyunme7h/Xq5goZvJfBpM0551MUHmKVisKhZCMoF6puQ9GIDV6yDh7Vn+yBQcSq6maUF1/CNTSapgJA1ntEcbAo9Nd7w6+rnUmezdKwWbxPwzwL+EZ6EoQ8tywOJrjMLzZ4E5D3pvwK0e3j6VTcTlb8YCi/Xj8dAEdYn7+w4WjukdOGUVJtTOZ1WRPi7T3JoV96ry/LeNtnaQLrQU+mClRTa93OhnkuWNUw+Q=="],"io.cncf.notary.signingAgent":"notation-go/1.3.0+unreleased"},"signature":"uYBON0RNGgDI5U__12-0TkWk6ClgzcWHS9aCC5qtQ2yrCfl9a_NDkHYhkhEG_TvdA--8mWpw_kjiS1oQHbsjr0KZED29lux1Ow8cq_0h13VjKnVVGeL_4aATmypV6DA9zVuolOW_vzHpZg86un1qUFtLx6Gv5RYZ97SqwMv5lEIQuqk0BwO4XaBuidUrAw44g5SeNRix-TurQxUEUkXxPyp6NZ0MoAaJAMDVXZK8ExZ2UczYolOuOVxgqJdHxSRp9izKIZnJp9fgg3tOXWJ3y5sQ_sjmzgdlk2medRtEoum55H0cxGV5gQiYRMfiROcFacaK_y8IDhARFxjNethCOQ"} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/e3222a9ea284789503cd2087aea775b73e049cb2c51e636a3980658e55577d18 b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/e3222a9ea284789503cd2087aea775b73e049cb2c51e636a3980658e55577d18 deleted file mode 100644 index 054f99491..000000000 --- a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/blobs/sha256/e3222a9ea284789503cd2087aea775b73e049cb2c51e636a3980658e55577d18 +++ /dev/null @@ -1 +0,0 @@ -{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.cncf.notary.signature","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2},"layers":[{"mediaType":"application/jose+json","digest":"sha256:83a35ad9499039f2ba1fcd205780dfedb82a447593aba3383ec8461dda587954","size":9219}],"subject":{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:f1da8cd70d6d851fa2313c8d6618f79508cf1e86877edf1c0bfe49a1b0a6467a","size":582},"annotations":{"io.cncf.notary.x509chain.thumbprint#S256":"[\"1717fa9d18f7e9c0f609499474adfe2b8e44172454f1d6e2183d5d04f79af475\"]","org.opencontainers.image.created":"2025-01-21T08:41:17Z"}} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/index.json b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/index.json index 80a632601..09234f337 100644 --- a/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/index.json +++ b/test/e2e/testdata/registry/oci_layout/e2e-with-timestamped-signature/index.json @@ -1 +1 @@ -{"schemaVersion":2,"manifests":[{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:f1da8cd70d6d851fa2313c8d6618f79508cf1e86877edf1c0bfe49a1b0a6467a","size":582,"annotations":{"org.opencontainers.image.created":"2025-01-21T08:40:36Z","org.opencontainers.image.ref.name":"v1"},"artifactType":"application/vnd.unknown.artifact.v1"},{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:e3222a9ea284789503cd2087aea775b73e049cb2c51e636a3980658e55577d18","size":728,"annotations":{"io.cncf.notary.x509chain.thumbprint#S256":"[\"1717fa9d18f7e9c0f609499474adfe2b8e44172454f1d6e2183d5d04f79af475\"]","org.opencontainers.image.created":"2025-01-21T08:41:17Z"}}]} \ No newline at end of file +{"schemaVersion":2,"manifests":[{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:53b0191218aed9a3c1f7c661736ac40cfc8eb928642348fd843ba3f0483c0c20","size":582,"annotations":{"org.opencontainers.image.created":"2025-01-21T09:17:17Z","org.opencontainers.image.ref.name":"v1"},"artifactType":"application/vnd.unknown.artifact.v1"},{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:6a5cd3a886707a317935dcaf13954fc80ef9aeb665262b6ae4fe469ad7ea3aea","size":729,"annotations":{"io.cncf.notary.x509chain.thumbprint#S256":"[\"1717fa9d18f7e9c0f609499474adfe2b8e44172454f1d6e2183d5d04f79af475\"]","org.opencontainers.image.created":"2025-01-21T09:17:46Z"}}]} \ No newline at end of file From b108fcd90ce22704db9e96079b51573ac391915a Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Tue, 21 Jan 2025 09:31:01 +0000 Subject: [PATCH 23/32] fix: unit test Signed-off-by: Junjie Gao --- .../internal/display/metadata/tree/inspect_test.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/cmd/notation/internal/display/metadata/tree/inspect_test.go b/cmd/notation/internal/display/metadata/tree/inspect_test.go index 63e1da3da..cfbb08702 100644 --- a/cmd/notation/internal/display/metadata/tree/inspect_test.go +++ b/cmd/notation/internal/display/metadata/tree/inspect_test.go @@ -35,6 +35,9 @@ func TestAddSignedAttributes(t *testing.T) { node := tree.New("root") expiryTime := time.Now().Add(time.Hour) ec := &signature.EnvelopeContent{ + Payload: signature.Payload{ + ContentType: "application/vnd.cncf.notary.payload.v1+json", + }, SignerInfo: signature.SignerInfo{ SignedAttributes: signature.SignedAttributes{ Expiry: expiryTime, @@ -56,16 +59,16 @@ func TestAddSignedAttributes(t *testing.T) { if signedAttrNode.Value != "signed attributes" { t.Fatalf("expected name 'signed attributes', got: %v", signedAttrNode.Value) } - if len(signedAttrNode.Children) != 4 { - t.Fatalf("expected 3 children, got: %v", len(signedAttrNode.Children)) + if len(signedAttrNode.Children) != 5 { + t.Fatalf("expected 5 children, got: %v", len(signedAttrNode.Children)) } // verify expiry node - expiryNode := signedAttrNode.Children[2] + expiryNode := signedAttrNode.Children[3] if expiryNode.Value != fmt.Sprintf("expiry: %s", expiryTime.Format(time.ANSIC)) { t.Fatalf("expected expiry node, got: %v", expiryNode.Value) } // verify extended attribute node - extendedAttrNode := signedAttrNode.Children[3] + extendedAttrNode := signedAttrNode.Children[4] if extendedAttrNode.Value != "key: value" { t.Fatalf("expected extended attribute node, got: %v", extendedAttrNode.Value) } From 721518aad213b335908d35cedd99cb88db99ee0d Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Wed, 22 Jan 2025 06:47:56 +0000 Subject: [PATCH 24/32] fix: resolve comments Signed-off-by: Junjie Gao --- cmd/notation/inspect_test.go | 4 +- cmd/notation/internal/display/handler.go | 4 +- .../internal/display/metadata/interface.go | 2 + .../internal/display/metadata/json/inspect.go | 9 ++-- .../internal/display/metadata/tree/inspect.go | 12 +---- cmd/notation/internal/option/format.go | 19 ++++---- cmd/notation/internal/output/json.go | 41 ---------------- cmd/notation/internal/output/json_test.go | 48 ------------------- cmd/notation/internal/output/print.go | 13 ++++- cmd/notation/internal/output/print_test.go | 18 ++++++- internal/cmd/flags.go | 9 ---- internal/ioutil/print.go | 13 ----- test/e2e/suite/command/inspect.go | 2 +- 13 files changed, 50 insertions(+), 144 deletions(-) delete mode 100644 cmd/notation/internal/output/json.go delete mode 100644 cmd/notation/internal/output/json_test.go diff --git a/cmd/notation/inspect_test.go b/cmd/notation/inspect_test.go index bd437c783..a9c8bcba3 100644 --- a/cmd/notation/inspect_test.go +++ b/cmd/notation/inspect_test.go @@ -24,7 +24,7 @@ func TestInspectCommand_SecretsFromArgs(t *testing.T) { opts := &inspectOpts{} command := inspectCommand(opts) format := option.Format{ - FormatFlag: "text", + CurrentFormat: "text", } format.SetTypes(option.FormatTypeText, option.FormatTypeJSON) expected := &inspectOpts{ @@ -60,7 +60,7 @@ func TestInspectCommand_SecretsFromEnv(t *testing.T) { format := option.Format{} format.SetTypes(option.FormatTypeText, option.FormatTypeJSON) - format.FormatFlag = "json" + format.CurrentFormat = "json" expected := &inspectOpts{ reference: "ref", SecureFlagOpts: SecureFlagOpts{ diff --git a/cmd/notation/internal/display/handler.go b/cmd/notation/internal/display/handler.go index 60a296786..99bdbd311 100644 --- a/cmd/notation/internal/display/handler.go +++ b/cmd/notation/internal/display/handler.go @@ -27,11 +27,11 @@ import ( // NewInpsectHandler creates a new InspectHandler based on the output format. func NewInpsectHandler(printer *output.Printer, format option.Format) (metadata.InspectHandler, error) { - switch option.FormatType(format.FormatFlag) { + switch option.FormatType(format.CurrentFormat) { case option.FormatTypeJSON: return json.NewInspectHandler(printer), nil case option.FormatTypeText: return tree.NewInspectHandler(printer), nil } - return nil, fmt.Errorf("unrecognized output format %s", format.FormatFlag) + return nil, fmt.Errorf("unrecognized output format %s", format.CurrentFormat) } diff --git a/cmd/notation/internal/display/metadata/interface.go b/cmd/notation/internal/display/metadata/interface.go index 9424abc5c..9dbae52cd 100644 --- a/cmd/notation/internal/display/metadata/interface.go +++ b/cmd/notation/internal/display/metadata/interface.go @@ -11,6 +11,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +// Package metadata provides interfaces for handlers that render metadata +// information for each command. package metadata import ( diff --git a/cmd/notation/internal/display/metadata/json/inspect.go b/cmd/notation/internal/display/metadata/json/inspect.go index 7f28fb3fe..8903d4c67 100644 --- a/cmd/notation/internal/display/metadata/json/inspect.go +++ b/cmd/notation/internal/display/metadata/json/inspect.go @@ -35,7 +35,7 @@ type inspectOutput struct { Signatures []*signature `json:"signatures"` } -// signature is the signature envelope for printing in human readable format. +// signature is the signature envelope for printing in JSON format. type signature struct { Digest string `json:"digest,omitempty"` SignatureAlgorithm string `json:"signatureAlgorithm"` @@ -46,8 +46,7 @@ type signature struct { SignedArtifact ocispec.Descriptor `json:"signedArtifact"` } -// certificate is the certificate information for printing in human readable -// format. +// certificate is the certificate information for printing in JSON format. type certificate struct { SHA256Fingerprint string `json:"SHA256Fingerprint"` IssuedTo string `json:"issuedTo"` @@ -55,7 +54,7 @@ type certificate struct { Expiry time.Time `json:"expiry"` } -// timestamp is the timestamp information for printing in human readable. +// timestamp is the timestamp information for printing in JSON format. type timestamp struct { Timestamp string `json:"timestamp,omitempty"` Certificates []*certificate `json:"certificates,omitempty"` @@ -83,7 +82,7 @@ func NewInspectHandler(printer *output.Printer) *InspectHandler { // OnReferenceResolved sets the artifact reference and media type for the // handler. // -// the reference is no-op for this handler. +// The reference is no-op for this handler. func (h *InspectHandler) OnReferenceResolved(_, mediaType string) { h.output.MediaType = mediaType } diff --git a/cmd/notation/internal/display/metadata/tree/inspect.go b/cmd/notation/internal/display/metadata/tree/inspect.go index 3391a9e6c..2fdcc1012 100644 --- a/cmd/notation/internal/display/metadata/tree/inspect.go +++ b/cmd/notation/internal/display/metadata/tree/inspect.go @@ -18,6 +18,7 @@ import ( "crypto/x509" "encoding/hex" "fmt" + "maps" "slices" "strconv" "strings" @@ -121,7 +122,7 @@ func addUserDefinedAttributes(node *tree.Node, annotations map[string]string) { userDefinedAttributesNode.Add("(empty)") return } - for _, k := range orderedKeys(annotations) { + for _, k := range slices.Sorted(maps.Keys(annotations)) { v := annotations[k] userDefinedAttributesNode.AddPair(k, v) } @@ -176,15 +177,6 @@ func addCertificates(node *tree.Node, certChain []*x509.Certificate) { } } -func orderedKeys(m map[string]string) []string { - keys := make([]string, 0, len(m)) - for k := range m { - keys = append(keys, k) - } - slices.Sort(keys) - return keys -} - func formatTime(t time.Time) string { return t.Format(time.ANSIC) } diff --git a/cmd/notation/internal/option/format.go b/cmd/notation/internal/option/format.go index 5585fa695..c0f4bdd40 100644 --- a/cmd/notation/internal/option/format.go +++ b/cmd/notation/internal/option/format.go @@ -30,6 +30,7 @@ package option import ( "fmt" + "slices" "strings" "github.com/spf13/cobra" @@ -49,13 +50,13 @@ var ( // Format contains input and parsed options for formatted output flags. type Format struct { - FormatFlag string - allowedTypes []FormatType + CurrentFormat string + allowedTypes []FormatType } // SetTypes sets the default format type and allowed format types. func (f *Format) SetTypes(defaultType FormatType, otherTypes ...FormatType) { - f.FormatFlag = string(defaultType) + f.CurrentFormat = string(defaultType) f.allowedTypes = append(otherTypes, defaultType) } @@ -67,16 +68,14 @@ func (f *Format) ApplyFlags(fs *pflag.FlagSet) { } usage := fmt.Sprintf("output format, options: %s", strings.Join(quotedAllowedTypes, ", ")) // apply flags - fs.StringVar(&f.FormatFlag, "output", f.FormatFlag, usage) + fs.StringVarP(&f.CurrentFormat, "output", "o", f.CurrentFormat, usage) } // Parse parses the input format flag. func (opts *Format) Parse(_ *cobra.Command) error { - for _, t := range opts.allowedTypes { - if opts.FormatFlag == string(t) { - // type validation passed - return nil - } + if ok := slices.Contains(opts.allowedTypes, FormatType(opts.CurrentFormat)); ok { + // type validation passed + return nil } - return fmt.Errorf("invalid format type: %q", opts.FormatFlag) + return fmt.Errorf("invalid format type: %q", opts.CurrentFormat) } diff --git a/cmd/notation/internal/output/json.go b/cmd/notation/internal/output/json.go deleted file mode 100644 index 667dff568..000000000 --- a/cmd/notation/internal/output/json.go +++ /dev/null @@ -1,41 +0,0 @@ -// 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. - -// copied and adopted from https://github.com/oras-project/oras with -// modification -/* -Copyright The ORAS 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 output - -import ( - "encoding/json" - "io" -) - -// PrintPrettyJSON prints the object to the writer in JSON format. -func PrintPrettyJSON(out io.Writer, object any) error { - encoder := json.NewEncoder(out) - encoder.SetIndent("", " ") - return encoder.Encode(object) -} diff --git a/cmd/notation/internal/output/json_test.go b/cmd/notation/internal/output/json_test.go deleted file mode 100644 index 893f4e42c..000000000 --- a/cmd/notation/internal/output/json_test.go +++ /dev/null @@ -1,48 +0,0 @@ -// 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. - -// copied and adopted from https://github.com/oras-project/oras with -// modification -/* -Copyright The ORAS 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 output - -import ( - "strings" - "testing" -) - -func Test_PrintPrettyJSON(t *testing.T) { - builder := &strings.Builder{} - given := map[string]int{"bob": 5} - expected := "{\n \"bob\": 5\n}\n" - err := PrintPrettyJSON(builder, given) - if err != nil { - t.Error("Expected no error got <" + err.Error() + ">") - } - actual := builder.String() - if expected != actual { - t.Error("Expected <" + expected + "> not equal to actual <" + actual + ">") - } -} diff --git a/cmd/notation/internal/output/print.go b/cmd/notation/internal/output/print.go index cf10931e3..3c95145da 100644 --- a/cmd/notation/internal/output/print.go +++ b/cmd/notation/internal/output/print.go @@ -29,6 +29,7 @@ limitations under the License. package output import ( + "encoding/json" "fmt" "io" "sync" @@ -50,6 +51,7 @@ func NewPrinter(out io.Writer, err io.Writer) *Printer { func (p *Printer) Write(b []byte) (int, error) { p.lock.Lock() defer p.lock.Unlock() + return p.out.Write(b) } @@ -57,6 +59,7 @@ func (p *Printer) Write(b []byte) (int, error) { func (p *Printer) Println(a ...any) error { p.lock.Lock() defer p.lock.Unlock() + _, err := fmt.Fprintln(p.out, a...) if err != nil { err = fmt.Errorf("display output error: %w", err) @@ -70,11 +73,19 @@ func (p *Printer) Println(a ...any) error { func (p *Printer) Printf(format string, a ...any) error { p.lock.Lock() defer p.lock.Unlock() + _, err := fmt.Fprintf(p.out, format, a...) if err != nil { err = fmt.Errorf("display output error: %w", err) _, _ = fmt.Fprint(p.err, err) + return err } - // Errors are handled above, so return nil return nil } + +// PrintPrettyJSON prints object to out in JSON format. +func PrintPrettyJSON(out io.Writer, object any) error { + encoder := json.NewEncoder(out) + encoder.SetIndent("", " ") + return encoder.Encode(object) +} diff --git a/cmd/notation/internal/output/print_test.go b/cmd/notation/internal/output/print_test.go index d8e6f58ee..0d07ff1f1 100644 --- a/cmd/notation/internal/output/print_test.go +++ b/cmd/notation/internal/output/print_test.go @@ -80,8 +80,22 @@ func TestPrinter_Print(t *testing.T) { if mockWriter.errorCount != 2 { t.Errorf("Expected two errors actual <%d>", mockWriter.errorCount) } - if err != nil { - t.Error("Expected error to be ignored") + if err == nil { + t.Error("Expected error got ") } }) } + +func Test_PrintPrettyJSON(t *testing.T) { + builder := &strings.Builder{} + given := map[string]int{"bob": 5} + expected := "{\n \"bob\": 5\n}\n" + err := PrintPrettyJSON(builder, given) + if err != nil { + t.Error("Expected no error got <" + err.Error() + ">") + } + actual := builder.String() + if expected != actual { + t.Error("Expected <" + expected + "> not equal to actual <" + actual + ">") + } +} diff --git a/internal/cmd/flags.go b/internal/cmd/flags.go index a2bed995b..3c7c04640 100644 --- a/internal/cmd/flags.go +++ b/internal/cmd/flags.go @@ -121,15 +121,6 @@ var ( SetPflagReferrersTag = func(fs *pflag.FlagSet, p *bool, usage string) { fs.BoolVar(p, PflagReferrersTag.Name, true, usage) } - - PflagOutput = &pflag.Flag{ - Name: "output", - Shorthand: "o", - } - PflagOutputUsage = fmt.Sprintf("output format, options: '%s', '%s'", OutputJSON, OutputPlaintext) - SetPflagOutput = func(fs *pflag.FlagSet, p *string, usage string) { - fs.StringVarP(p, PflagOutput.Name, PflagOutput.Shorthand, OutputPlaintext, usage) - } ) // KeyValueSlice is a flag with type int diff --git a/internal/ioutil/print.go b/internal/ioutil/print.go index a7a3bac6c..2eedb794e 100644 --- a/internal/ioutil/print.go +++ b/internal/ioutil/print.go @@ -14,7 +14,6 @@ package ioutil import ( - "encoding/json" "fmt" "io" "path/filepath" @@ -79,15 +78,3 @@ func PrintCertMap(w io.Writer, certPaths []string) error { } return tw.Flush() } - -// PrintObjectAsJSON takes an interface and prints it as an indented JSON string -func PrintObjectAsJSON(i interface{}) error { - jsonBytes, err := json.MarshalIndent(i, "", " ") - if err != nil { - return err - } - - fmt.Println(string(jsonBytes)) - - return nil -} diff --git a/test/e2e/suite/command/inspect.go b/test/e2e/suite/command/inspect.go index 3414a0dc9..e896e1bff 100644 --- a/test/e2e/suite/command/inspect.go +++ b/test/e2e/suite/command/inspect.go @@ -375,7 +375,7 @@ localhost:5000/e2e-inspect-invalid-timstamped@sha256:f1da8cd70d6d851fa2313c8d661 ] } ` - notation.Exec("inspect", "--output", "json", artifact.ReferenceWithDigest()). + notation.Exec("inspect", "-o", "json", artifact.ReferenceWithDigest()). MatchContent(expectedOutput) }) }) From 099787579d5fec02d66840bfd35094df8fe77c2a Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Thu, 23 Jan 2025 02:33:02 +0000 Subject: [PATCH 25/32] fix: resolve comments Signed-off-by: Junjie Gao --- cmd/notation/inspect.go | 7 ++----- cmd/notation/inspect_test.go | 15 ++++++++------- .../display/metadata/{interface.go => handler.go} | 0 .../internal/display/metadata/json/inspect.go | 2 +- .../internal/display/metadata/tree/inspect.go | 2 +- cmd/notation/internal/option/common.go | 3 +-- cmd/notation/internal/option/format.go | 10 +++++----- internal/envelope/envelope.go | 10 +++++----- 8 files changed, 23 insertions(+), 26 deletions(-) rename cmd/notation/internal/display/metadata/{interface.go => handler.go} (100%) diff --git a/cmd/notation/inspect.go b/cmd/notation/inspect.go index e106475c0..3ad485684 100644 --- a/cmd/notation/inspect.go +++ b/cmd/notation/inspect.go @@ -68,9 +68,7 @@ Example - Inspect signatures on an OCI artifact identified by a digest and outpu if err := opts.Format.Parse(cmd); err != nil { return err } - if err := opts.Common.Parse(cmd); err != nil { - return err - } + opts.Common.Parse(cmd) return experimental.CheckFlagsAndWarn(cmd, "allow-referrers-api") }, RunE: func(cmd *cobra.Command, args []string) error { @@ -90,8 +88,7 @@ Example - Inspect signatures on an OCI artifact identified by a digest and outpu cmd.SetPflagReferrersAPI(command.Flags(), &opts.allowReferrersAPI, fmt.Sprintf(cmd.PflagReferrersUsageFormat, "inspect")) // set output format - opts.SetTypes(option.FormatTypeText, option.FormatTypeJSON) - opts.Format.ApplyFlags(command.Flags()) + opts.Format.ApplyFlags(command.Flags(), option.FormatTypeText, option.FormatTypeJSON) return command } diff --git a/cmd/notation/inspect_test.go b/cmd/notation/inspect_test.go index a9c8bcba3..79393c730 100644 --- a/cmd/notation/inspect_test.go +++ b/cmd/notation/inspect_test.go @@ -18,15 +18,15 @@ import ( "testing" "github.com/notaryproject/notation/cmd/notation/internal/option" + "github.com/spf13/pflag" ) func TestInspectCommand_SecretsFromArgs(t *testing.T) { opts := &inspectOpts{} command := inspectCommand(opts) - format := option.Format{ - CurrentFormat: "text", - } - format.SetTypes(option.FormatTypeText, option.FormatTypeJSON) + format := option.Format{} + format.ApplyFlags(&pflag.FlagSet{}, option.FormatTypeText, option.FormatTypeJSON) + format.CurrentFormat = string(option.FormatTypeText) expected := &inspectOpts{ reference: "ref", SecureFlagOpts: SecureFlagOpts{ @@ -56,11 +56,10 @@ func TestInspectCommand_SecretsFromArgs(t *testing.T) { func TestInspectCommand_SecretsFromEnv(t *testing.T) { t.Setenv(defaultUsernameEnv, "user") t.Setenv(defaultPasswordEnv, "password") - opts := &inspectOpts{} format := option.Format{} - format.SetTypes(option.FormatTypeText, option.FormatTypeJSON) - format.CurrentFormat = "json" + format.ApplyFlags(&pflag.FlagSet{}, option.FormatTypeText, option.FormatTypeJSON) + format.CurrentFormat = string(option.FormatTypeJSON) expected := &inspectOpts{ reference: "ref", SecureFlagOpts: SecureFlagOpts{ @@ -70,6 +69,8 @@ func TestInspectCommand_SecretsFromEnv(t *testing.T) { Format: format, maxSignatures: 100, } + + opts := &inspectOpts{} command := inspectCommand(opts) if err := command.ParseFlags([]string{ expected.reference, diff --git a/cmd/notation/internal/display/metadata/interface.go b/cmd/notation/internal/display/metadata/handler.go similarity index 100% rename from cmd/notation/internal/display/metadata/interface.go rename to cmd/notation/internal/display/metadata/handler.go diff --git a/cmd/notation/internal/display/metadata/json/inspect.go b/cmd/notation/internal/display/metadata/json/inspect.go index 8903d4c67..98275db4d 100644 --- a/cmd/notation/internal/display/metadata/json/inspect.go +++ b/cmd/notation/internal/display/metadata/json/inspect.go @@ -124,7 +124,7 @@ func newSignature(digest string, sigEnvelope coresignature.Envelope) (*signature UserDefinedAttributes: signedArtifactDesc.Annotations, UnsignedAttributes: getUnsignedAttributes(envelopeContent), Certificates: getCertificates(envelopeContent.SignerInfo.CertificateChain), - SignedArtifact: signedArtifactDesc, + SignedArtifact: *signedArtifactDesc, } // clearing annotations from the SignedArtifact field since they're already diff --git a/cmd/notation/internal/display/metadata/tree/inspect.go b/cmd/notation/internal/display/metadata/tree/inspect.go index 2fdcc1012..ca60237b0 100644 --- a/cmd/notation/internal/display/metadata/tree/inspect.go +++ b/cmd/notation/internal/display/metadata/tree/inspect.go @@ -99,7 +99,7 @@ func addSignature(node *tree.Node, digest string, sigEnvelope signature.Envelope addUserDefinedAttributes(sigNode, signedArtifactDesc.Annotations) addUnsignedAttributes(sigNode, envelopeContent) addCertificates(sigNode, envelopeContent.SignerInfo.CertificateChain) - addSignedArtifact(sigNode, signedArtifactDesc) + addSignedArtifact(sigNode, *signedArtifactDesc) return nil } diff --git a/cmd/notation/internal/option/common.go b/cmd/notation/internal/option/common.go index 612a08513..064509eab 100644 --- a/cmd/notation/internal/option/common.go +++ b/cmd/notation/internal/option/common.go @@ -39,7 +39,6 @@ type Common struct { } // Parse gets target options from user input. -func (opts *Common) Parse(cmd *cobra.Command) error { +func (opts *Common) Parse(cmd *cobra.Command) { opts.Printer = output.NewPrinter(cmd.OutOrStdout(), cmd.OutOrStderr()) - return nil } diff --git a/cmd/notation/internal/option/format.go b/cmd/notation/internal/option/format.go index c0f4bdd40..411ef9ec4 100644 --- a/cmd/notation/internal/option/format.go +++ b/cmd/notation/internal/option/format.go @@ -54,14 +54,14 @@ type Format struct { allowedTypes []FormatType } -// SetTypes sets the default format type and allowed format types. -func (f *Format) SetTypes(defaultType FormatType, otherTypes ...FormatType) { +// ApplyFlags sets up the flags for the format option. +// +// The defaultType is the default format type. +// The otherTypes are additional format types that are allowed. +func (f *Format) ApplyFlags(fs *pflag.FlagSet, defaultType FormatType, otherTypes ...FormatType) { f.CurrentFormat = string(defaultType) f.allowedTypes = append(otherTypes, defaultType) -} -// ApplyFlags implements FlagProvider.ApplyFlag. -func (f *Format) ApplyFlags(fs *pflag.FlagSet) { var quotedAllowedTypes []string for _, t := range f.allowedTypes { quotedAllowedTypes = append(quotedAllowedTypes, fmt.Sprintf("'%s'", t)) diff --git a/internal/envelope/envelope.go b/internal/envelope/envelope.go index 98d4f51d9..4f49ca0f8 100644 --- a/internal/envelope/envelope.go +++ b/internal/envelope/envelope.go @@ -61,21 +61,21 @@ func ValidatePayloadContentType(payload *signature.Payload) error { // DescriptorFromPayload parses a signature payload and returns the descriptor // that was signed. Note: the descriptor was signed but may not be trusted -func DescriptorFromSignaturePayload(payload *signature.Payload) (ocispec.Descriptor, error) { +func DescriptorFromSignaturePayload(payload *signature.Payload) (*ocispec.Descriptor, error) { if payload == nil { - return ocispec.Descriptor{}, errors.New("empty payload") + return nil, errors.New("empty payload") } err := ValidatePayloadContentType(payload) if err != nil { - return ocispec.Descriptor{}, err + return nil, err } var parsedPayload Payload err = json.Unmarshal(payload.Content, &parsedPayload) if err != nil { - return ocispec.Descriptor{}, errors.New("failed to unmarshall the payload content to Payload") + return nil, errors.New("failed to unmarshall the payload content to Payload") } - return parsedPayload.TargetArtifact, nil + return &parsedPayload.TargetArtifact, nil } From aef5735974f76a06ab0854ab6e0e1abc1c649563 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Thu, 23 Jan 2025 05:16:08 +0000 Subject: [PATCH 26/32] refactor: display package structure Signed-off-by: Junjie Gao --- cmd/notation/inspect.go | 2 +- cmd/notation/internal/display/handler.go | 18 ++++++---- .../{json/inspect.go => inspect/json.go} | 24 +++++++------ .../inspect_test.go => inspect/json_test.go} | 6 ++-- .../TimeStampTokenWithInvalidSignature.p7s | Bin .../{tree/inspect.go => inspect/tree.go} | 33 +++++++++--------- .../inspect_test.go => inspect/tree_test.go} | 28 +++++++-------- .../metadata/{handler.go => interface.go} | 5 +-- .../internal/{ => display}/output/print.go | 2 ++ .../{ => display}/output/print_test.go | 0 cmd/notation/internal/option/common.go | 2 +- 11 files changed, 65 insertions(+), 55 deletions(-) rename cmd/notation/internal/display/metadata/{json/inspect.go => inspect/json.go} (90%) rename cmd/notation/internal/display/metadata/{json/inspect_test.go => inspect/json_test.go} (97%) rename cmd/notation/internal/display/metadata/{ => inspect}/testdata/TimeStampTokenWithInvalidSignature.p7s (100%) rename cmd/notation/internal/display/metadata/{tree/inspect.go => inspect/tree.go} (84%) rename cmd/notation/internal/display/metadata/{tree/inspect_test.go => inspect/tree_test.go} (88%) rename cmd/notation/internal/display/metadata/{handler.go => interface.go} (85%) rename cmd/notation/internal/{ => display}/output/print.go (96%) rename cmd/notation/internal/{ => display}/output/print_test.go (100%) diff --git a/cmd/notation/inspect.go b/cmd/notation/inspect.go index 3ad485684..34e50421b 100644 --- a/cmd/notation/inspect.go +++ b/cmd/notation/inspect.go @@ -96,7 +96,7 @@ func runInspect(command *cobra.Command, opts *inspectOpts) error { // set log level ctx := opts.LoggingFlagOpts.InitializeLogger(command.Context()) - displayHandler, err := display.NewInpsectHandler(opts.Printer, opts.Format) + displayHandler, err := display.NewMetadataInpsectHandler(opts.Printer, opts.Format) if err != nil { return err } diff --git a/cmd/notation/internal/display/handler.go b/cmd/notation/internal/display/handler.go index 99bdbd311..a064346ef 100644 --- a/cmd/notation/internal/display/handler.go +++ b/cmd/notation/internal/display/handler.go @@ -13,25 +13,29 @@ // Package display provides the display handlers to render information for // commands. +// +// - It includes the metadata, content and status packages for handling +// different types of information. +// - It includes the output package for writing information to the output. package display import ( "fmt" "github.com/notaryproject/notation/cmd/notation/internal/display/metadata" - "github.com/notaryproject/notation/cmd/notation/internal/display/metadata/json" - "github.com/notaryproject/notation/cmd/notation/internal/display/metadata/tree" + "github.com/notaryproject/notation/cmd/notation/internal/display/metadata/inspect" + "github.com/notaryproject/notation/cmd/notation/internal/display/output" "github.com/notaryproject/notation/cmd/notation/internal/option" - "github.com/notaryproject/notation/cmd/notation/internal/output" ) -// NewInpsectHandler creates a new InspectHandler based on the output format. -func NewInpsectHandler(printer *output.Printer, format option.Format) (metadata.InspectHandler, error) { +// NewMetadataInpsectHandler creates a new InspectHandler based on the output +// format. +func NewMetadataInpsectHandler(printer *output.Printer, format option.Format) (metadata.InspectHandler, error) { switch option.FormatType(format.CurrentFormat) { case option.FormatTypeJSON: - return json.NewInspectHandler(printer), nil + return inspect.NewJSONHandler(printer), nil case option.FormatTypeText: - return tree.NewInspectHandler(printer), nil + return inspect.NewTreeHandler(printer), nil } return nil, fmt.Errorf("unrecognized output format %s", format.CurrentFormat) } diff --git a/cmd/notation/internal/display/metadata/json/inspect.go b/cmd/notation/internal/display/metadata/inspect/json.go similarity index 90% rename from cmd/notation/internal/display/metadata/json/inspect.go rename to cmd/notation/internal/display/metadata/inspect/json.go index 98275db4d..9e3a83b12 100644 --- a/cmd/notation/internal/display/metadata/json/inspect.go +++ b/cmd/notation/internal/display/metadata/inspect/json.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package json +package inspect import ( "crypto/sha256" @@ -23,7 +23,7 @@ import ( coresignature "github.com/notaryproject/notation-core-go/signature" "github.com/notaryproject/notation-go/plugin/proto" - "github.com/notaryproject/notation/cmd/notation/internal/output" + "github.com/notaryproject/notation/cmd/notation/internal/display/output" "github.com/notaryproject/notation/internal/envelope" "github.com/notaryproject/tspclient-go" @@ -61,17 +61,19 @@ type timestamp struct { Error string `json:"error,omitempty"` } -// InspectHandler is the handler for inspecting metadata information and -// rendering it in JSON format. -type InspectHandler struct { +// JSONHandler is the handler for inspecting metadata information and +// rendering it in JSON format. It implements the metadata.InspectHandler +// interface. +type JSONHandler struct { printer *output.Printer output inspectOutput } -// NewInspectHandler creates a new InspectHandler. -func NewInspectHandler(printer *output.Printer) *InspectHandler { - return &InspectHandler{ +// NewJSONHandler creates a new JsonHandler to inspect signatures and print in +// JSON format. +func NewJSONHandler(printer *output.Printer) *JSONHandler { + return &JSONHandler{ printer: printer, output: inspectOutput{ Signatures: []*signature{}, @@ -83,12 +85,12 @@ func NewInspectHandler(printer *output.Printer) *InspectHandler { // handler. // // The reference is no-op for this handler. -func (h *InspectHandler) OnReferenceResolved(_, mediaType string) { +func (h *JSONHandler) OnReferenceResolved(_, mediaType string) { h.output.MediaType = mediaType } // InspectSignature inspects a signature to get it ready to be rendered. -func (h *InspectHandler) InspectSignature(manifestDesc ocispec.Descriptor, envelope coresignature.Envelope) error { +func (h *JSONHandler) InspectSignature(manifestDesc ocispec.Descriptor, envelope coresignature.Envelope) error { sig, err := newSignature(manifestDesc.Digest.String(), envelope) if err != nil { return err @@ -97,7 +99,7 @@ func (h *InspectHandler) InspectSignature(manifestDesc ocispec.Descriptor, envel return nil } -func (h *InspectHandler) Render() error { +func (h *JSONHandler) Render() error { return output.PrintPrettyJSON(h.printer, h.output) } diff --git a/cmd/notation/internal/display/metadata/json/inspect_test.go b/cmd/notation/internal/display/metadata/inspect/json_test.go similarity index 97% rename from cmd/notation/internal/display/metadata/json/inspect_test.go rename to cmd/notation/internal/display/metadata/inspect/json_test.go index 783c65fac..b89adc907 100644 --- a/cmd/notation/internal/display/metadata/json/inspect_test.go +++ b/cmd/notation/internal/display/metadata/inspect/json_test.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package json +package inspect import ( "errors" @@ -97,7 +97,7 @@ func TestParseTimestamp(t *testing.T) { }) t.Run("timestamp validation error", func(t *testing.T) { - tsaToken, err := os.ReadFile("../testdata/TimeStampTokenWithInvalidSignature.p7s") + tsaToken, err := os.ReadFile("./testdata/TimeStampTokenWithInvalidSignature.p7s") if err != nil { t.Fatal(err) } @@ -117,7 +117,7 @@ func TestParseTimestamp(t *testing.T) { } func TestInspectSignature_NewSignatureError(t *testing.T) { - h := NewInspectHandler(nil) + h := NewJSONHandler(nil) // ...existing code to ensure h.output.MediaType is set... h.OnReferenceResolved("test-ref", "test-media-type") manifestDesc := ocispec.Descriptor{Digest: "fake-digest"} diff --git a/cmd/notation/internal/display/metadata/testdata/TimeStampTokenWithInvalidSignature.p7s b/cmd/notation/internal/display/metadata/inspect/testdata/TimeStampTokenWithInvalidSignature.p7s similarity index 100% rename from cmd/notation/internal/display/metadata/testdata/TimeStampTokenWithInvalidSignature.p7s rename to cmd/notation/internal/display/metadata/inspect/testdata/TimeStampTokenWithInvalidSignature.p7s diff --git a/cmd/notation/internal/display/metadata/tree/inspect.go b/cmd/notation/internal/display/metadata/inspect/tree.go similarity index 84% rename from cmd/notation/internal/display/metadata/tree/inspect.go rename to cmd/notation/internal/display/metadata/inspect/tree.go index ca60237b0..053a28710 100644 --- a/cmd/notation/internal/display/metadata/tree/inspect.go +++ b/cmd/notation/internal/display/metadata/inspect/tree.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package tree +package inspect import ( "crypto/sha256" @@ -24,19 +24,19 @@ import ( "strings" "time" - "github.com/notaryproject/notation-core-go/signature" + coresignature "github.com/notaryproject/notation-core-go/signature" "github.com/notaryproject/notation-go/plugin/proto" "github.com/notaryproject/notation-go/registry" - "github.com/notaryproject/notation/cmd/notation/internal/output" + "github.com/notaryproject/notation/cmd/notation/internal/display/output" "github.com/notaryproject/notation/internal/envelope" "github.com/notaryproject/notation/internal/tree" "github.com/notaryproject/tspclient-go" ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) -// InspectHandler is a handler for inspecting metadata information and rendering -// it in a tree format. -type InspectHandler struct { +// TreeHandler is a handler for inspecting metadata information and rendering +// it in a tree format. It implements the metadata.InspectHandler interface. +type TreeHandler struct { printer *output.Printer // rootReferenceNode is the root node with the artifact reference as the @@ -47,9 +47,10 @@ type InspectHandler struct { notationSignaturesNode *tree.Node } -// NewInspectHandler creates a new InspectHandler. -func NewInspectHandler(printer *output.Printer) *InspectHandler { - return &InspectHandler{ +// NewTreeHandler creates a TreeHandler to inspect signatures and print in tree +// format. +func NewTreeHandler(printer *output.Printer) *TreeHandler { + return &TreeHandler{ printer: printer, } } @@ -58,18 +59,18 @@ func NewInspectHandler(printer *output.Printer) *InspectHandler { // handler. // // mediaType is a no-op for this handler. -func (h *InspectHandler) OnReferenceResolved(reference, _ string) { +func (h *TreeHandler) OnReferenceResolved(reference, _ string) { h.rootReferenceNode = tree.New(reference) h.notationSignaturesNode = h.rootReferenceNode.Add(registry.ArtifactTypeNotation) } // InspectSignature inspects a signature to get it ready to be rendered. -func (h *InspectHandler) InspectSignature(manifestDesc ocispec.Descriptor, envelope signature.Envelope) error { +func (h *TreeHandler) InspectSignature(manifestDesc ocispec.Descriptor, envelope coresignature.Envelope) error { return addSignature(h.notationSignaturesNode, manifestDesc.Digest.String(), envelope) } // Render renders the metadata information when an operation is complete. -func (h *InspectHandler) Render() error { +func (h *TreeHandler) Render() error { if len(h.notationSignaturesNode.Children) == 0 { return h.printer.Printf("%s has no associated signature\n", h.rootReferenceNode.Value) } @@ -77,7 +78,7 @@ func (h *InspectHandler) Render() error { return h.rootReferenceNode.Print(h.printer) } -func addSignature(node *tree.Node, digest string, sigEnvelope signature.Envelope) error { +func addSignature(node *tree.Node, digest string, sigEnvelope coresignature.Envelope) error { envelopeContent, err := sigEnvelope.Content() if err != nil { return err @@ -103,7 +104,7 @@ func addSignature(node *tree.Node, digest string, sigEnvelope signature.Envelope return nil } -func addSignedAttributes(node *tree.Node, envelopeContent *signature.EnvelopeContent) { +func addSignedAttributes(node *tree.Node, envelopeContent *coresignature.EnvelopeContent) { signedAttributesNode := node.Add("signed attributes") signedAttributesNode.AddPair("content type", string(envelopeContent.Payload.ContentType)) signedAttributesNode.AddPair("signing scheme", string(envelopeContent.SignerInfo.SignedAttributes.SigningScheme)) @@ -128,7 +129,7 @@ func addUserDefinedAttributes(node *tree.Node, annotations map[string]string) { } } -func addUnsignedAttributes(node *tree.Node, envelopeContent *signature.EnvelopeContent) { +func addUnsignedAttributes(node *tree.Node, envelopeContent *coresignature.EnvelopeContent) { unsignedAttributesNode := node.Add("unsigned attributes") if signingAgent := envelopeContent.SignerInfo.UnsignedAttributes.SigningAgent; signingAgent != "" { unsignedAttributesNode.AddPair("signing agent", signingAgent) @@ -145,7 +146,7 @@ func addSignedArtifact(node *tree.Node, signedArtifactDesc ocispec.Descriptor) { artifactNode.AddPair("size", strconv.FormatInt(signedArtifactDesc.Size, 10)) } -func addTimestamp(node *tree.Node, signerInfo signature.SignerInfo) { +func addTimestamp(node *tree.Node, signerInfo coresignature.SignerInfo) { timestampNode := node.Add("timestamp signature") signedToken, err := tspclient.ParseSignedToken(signerInfo.UnsignedAttributes.TimestampSignature) if err != nil { diff --git a/cmd/notation/internal/display/metadata/tree/inspect_test.go b/cmd/notation/internal/display/metadata/inspect/tree_test.go similarity index 88% rename from cmd/notation/internal/display/metadata/tree/inspect_test.go rename to cmd/notation/internal/display/metadata/inspect/tree_test.go index cfbb08702..b1038b0a1 100644 --- a/cmd/notation/internal/display/metadata/tree/inspect_test.go +++ b/cmd/notation/internal/display/metadata/inspect/tree_test.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package tree +package inspect import ( "fmt" @@ -19,14 +19,14 @@ import ( "testing" "time" - "github.com/notaryproject/notation-core-go/signature" + coresignature "github.com/notaryproject/notation-core-go/signature" "github.com/notaryproject/notation/internal/tree" ) func TestAddSignedAttributes(t *testing.T) { t.Run("empty envelopeContent", func(t *testing.T) { node := tree.New("root") - ec := &signature.EnvelopeContent{} + ec := &coresignature.EnvelopeContent{} addSignedAttributes(node, ec) // No error or panic expected; minimal check or just ensure it doesn't crash. }) @@ -34,14 +34,14 @@ func TestAddSignedAttributes(t *testing.T) { t.Run("with expiry and extented node", func(t *testing.T) { node := tree.New("root") expiryTime := time.Now().Add(time.Hour) - ec := &signature.EnvelopeContent{ - Payload: signature.Payload{ + ec := &coresignature.EnvelopeContent{ + Payload: coresignature.Payload{ ContentType: "application/vnd.cncf.notary.payload.v1+json", }, - SignerInfo: signature.SignerInfo{ - SignedAttributes: signature.SignedAttributes{ + SignerInfo: coresignature.SignerInfo{ + SignedAttributes: coresignature.SignedAttributes{ Expiry: expiryTime, - ExtendedAttributes: []signature.Attribute{ + ExtendedAttributes: []coresignature.Attribute{ { Key: "key", Value: "value", @@ -105,11 +105,11 @@ func TestAddUserDefinedAttributes(t *testing.T) { }) } -func TestParseTimestamp(t *testing.T) { +func TestAddTimestamp(t *testing.T) { t.Run("invalid timestamp signature", func(t *testing.T) { node := tree.New("root") - signerInfo := signature.SignerInfo{ - UnsignedAttributes: signature.UnsignedAttributes{ + signerInfo := coresignature.SignerInfo{ + UnsignedAttributes: coresignature.UnsignedAttributes{ TimestampSignature: []byte("invalid"), }, } @@ -132,12 +132,12 @@ func TestParseTimestamp(t *testing.T) { }) t.Run("timestamp validation error", func(t *testing.T) { - tsaToken, err := os.ReadFile("../testdata/TimeStampTokenWithInvalidSignature.p7s") + tsaToken, err := os.ReadFile("./testdata/TimeStampTokenWithInvalidSignature.p7s") if err != nil { t.Fatal(err) } - signerInfo := signature.SignerInfo{ - UnsignedAttributes: signature.UnsignedAttributes{ + signerInfo := coresignature.SignerInfo{ + UnsignedAttributes: coresignature.UnsignedAttributes{ TimestampSignature: tsaToken, }, } diff --git a/cmd/notation/internal/display/metadata/handler.go b/cmd/notation/internal/display/metadata/interface.go similarity index 85% rename from cmd/notation/internal/display/metadata/handler.go rename to cmd/notation/internal/display/metadata/interface.go index 9dbae52cd..01c717703 100644 --- a/cmd/notation/internal/display/metadata/handler.go +++ b/cmd/notation/internal/display/metadata/interface.go @@ -11,8 +11,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package metadata provides interfaces for handlers that render metadata -// information for each command. +// Package metadata defines interfaces for handlers that render metadata +// information for each command. The metadata provides information about the +// original data with formatted output in JSON, tree, or text. package metadata import ( diff --git a/cmd/notation/internal/output/print.go b/cmd/notation/internal/display/output/print.go similarity index 96% rename from cmd/notation/internal/output/print.go rename to cmd/notation/internal/display/output/print.go index 3c95145da..afdcf56b7 100644 --- a/cmd/notation/internal/output/print.go +++ b/cmd/notation/internal/display/output/print.go @@ -26,6 +26,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +// Package output provides the output tools for writing information to the +// output. package output import ( diff --git a/cmd/notation/internal/output/print_test.go b/cmd/notation/internal/display/output/print_test.go similarity index 100% rename from cmd/notation/internal/output/print_test.go rename to cmd/notation/internal/display/output/print_test.go diff --git a/cmd/notation/internal/option/common.go b/cmd/notation/internal/option/common.go index 064509eab..4713d79a1 100644 --- a/cmd/notation/internal/option/common.go +++ b/cmd/notation/internal/option/common.go @@ -29,7 +29,7 @@ limitations under the License. package option import ( - "github.com/notaryproject/notation/cmd/notation/internal/output" + "github.com/notaryproject/notation/cmd/notation/internal/display/output" "github.com/spf13/cobra" ) From 5b28eda4f922975c09eece8819eea5ca95077451 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Thu, 23 Jan 2025 07:59:44 +0000 Subject: [PATCH 27/32] fix: update to use *ocispec.Descriptor Signed-off-by: Junjie Gao --- cmd/notation/internal/display/metadata/inspect/json.go | 2 +- cmd/notation/internal/display/metadata/inspect/tree.go | 2 +- internal/envelope/envelope.go | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cmd/notation/internal/display/metadata/inspect/json.go b/cmd/notation/internal/display/metadata/inspect/json.go index 9e3a83b12..28e80dbb7 100644 --- a/cmd/notation/internal/display/metadata/inspect/json.go +++ b/cmd/notation/internal/display/metadata/inspect/json.go @@ -126,7 +126,7 @@ func newSignature(digest string, sigEnvelope coresignature.Envelope) (*signature UserDefinedAttributes: signedArtifactDesc.Annotations, UnsignedAttributes: getUnsignedAttributes(envelopeContent), Certificates: getCertificates(envelopeContent.SignerInfo.CertificateChain), - SignedArtifact: *signedArtifactDesc, + SignedArtifact: signedArtifactDesc, } // clearing annotations from the SignedArtifact field since they're already diff --git a/cmd/notation/internal/display/metadata/inspect/tree.go b/cmd/notation/internal/display/metadata/inspect/tree.go index 053a28710..33e00deee 100644 --- a/cmd/notation/internal/display/metadata/inspect/tree.go +++ b/cmd/notation/internal/display/metadata/inspect/tree.go @@ -100,7 +100,7 @@ func addSignature(node *tree.Node, digest string, sigEnvelope coresignature.Enve addUserDefinedAttributes(sigNode, signedArtifactDesc.Annotations) addUnsignedAttributes(sigNode, envelopeContent) addCertificates(sigNode, envelopeContent.SignerInfo.CertificateChain) - addSignedArtifact(sigNode, *signedArtifactDesc) + addSignedArtifact(sigNode, signedArtifactDesc) return nil } diff --git a/internal/envelope/envelope.go b/internal/envelope/envelope.go index 4f49ca0f8..98d4f51d9 100644 --- a/internal/envelope/envelope.go +++ b/internal/envelope/envelope.go @@ -61,21 +61,21 @@ func ValidatePayloadContentType(payload *signature.Payload) error { // DescriptorFromPayload parses a signature payload and returns the descriptor // that was signed. Note: the descriptor was signed but may not be trusted -func DescriptorFromSignaturePayload(payload *signature.Payload) (*ocispec.Descriptor, error) { +func DescriptorFromSignaturePayload(payload *signature.Payload) (ocispec.Descriptor, error) { if payload == nil { - return nil, errors.New("empty payload") + return ocispec.Descriptor{}, errors.New("empty payload") } err := ValidatePayloadContentType(payload) if err != nil { - return nil, err + return ocispec.Descriptor{}, err } var parsedPayload Payload err = json.Unmarshal(payload.Content, &parsedPayload) if err != nil { - return nil, errors.New("failed to unmarshall the payload content to Payload") + return ocispec.Descriptor{}, errors.New("failed to unmarshall the payload content to Payload") } - return &parsedPayload.TargetArtifact, nil + return parsedPayload.TargetArtifact, nil } From 8f5d88f2b944677cbfda7007f8b3240d8a852ac0 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Thu, 23 Jan 2025 08:21:05 +0000 Subject: [PATCH 28/32] fix: restore package structure Signed-off-by: Junjie Gao --- cmd/notation/internal/display/handler.go | 7 ++++--- .../{inspect/json.go => json/inspect.go} | 18 +++++++++--------- .../json_test.go => json/inspect_test.go} | 6 +++--- .../TimeStampTokenWithInvalidSignature.p7s | Bin .../{inspect/tree.go => tree/inspect.go} | 18 +++++++++--------- .../tree_test.go => tree/inspect_test.go} | 4 ++-- 6 files changed, 27 insertions(+), 26 deletions(-) rename cmd/notation/internal/display/metadata/{inspect/json.go => json/inspect.go} (92%) rename cmd/notation/internal/display/metadata/{inspect/json_test.go => json/inspect_test.go} (97%) rename cmd/notation/internal/display/metadata/{inspect => }/testdata/TimeStampTokenWithInvalidSignature.p7s (100%) rename cmd/notation/internal/display/metadata/{inspect/tree.go => tree/inspect.go} (92%) rename cmd/notation/internal/display/metadata/{inspect/tree_test.go => tree/inspect_test.go} (98%) diff --git a/cmd/notation/internal/display/handler.go b/cmd/notation/internal/display/handler.go index a064346ef..9381be9cb 100644 --- a/cmd/notation/internal/display/handler.go +++ b/cmd/notation/internal/display/handler.go @@ -23,7 +23,8 @@ import ( "fmt" "github.com/notaryproject/notation/cmd/notation/internal/display/metadata" - "github.com/notaryproject/notation/cmd/notation/internal/display/metadata/inspect" + "github.com/notaryproject/notation/cmd/notation/internal/display/metadata/json" + "github.com/notaryproject/notation/cmd/notation/internal/display/metadata/tree" "github.com/notaryproject/notation/cmd/notation/internal/display/output" "github.com/notaryproject/notation/cmd/notation/internal/option" ) @@ -33,9 +34,9 @@ import ( func NewMetadataInpsectHandler(printer *output.Printer, format option.Format) (metadata.InspectHandler, error) { switch option.FormatType(format.CurrentFormat) { case option.FormatTypeJSON: - return inspect.NewJSONHandler(printer), nil + return json.NewInspectHandler(printer), nil case option.FormatTypeText: - return inspect.NewTreeHandler(printer), nil + return tree.NewInspectHandler(printer), nil } return nil, fmt.Errorf("unrecognized output format %s", format.CurrentFormat) } diff --git a/cmd/notation/internal/display/metadata/inspect/json.go b/cmd/notation/internal/display/metadata/json/inspect.go similarity index 92% rename from cmd/notation/internal/display/metadata/inspect/json.go rename to cmd/notation/internal/display/metadata/json/inspect.go index 28e80dbb7..67c8c4957 100644 --- a/cmd/notation/internal/display/metadata/inspect/json.go +++ b/cmd/notation/internal/display/metadata/json/inspect.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package inspect +package json import ( "crypto/sha256" @@ -61,19 +61,19 @@ type timestamp struct { Error string `json:"error,omitempty"` } -// JSONHandler is the handler for inspecting metadata information and +// InspectHandler is the handler for inspecting metadata information and // rendering it in JSON format. It implements the metadata.InspectHandler // interface. -type JSONHandler struct { +type InspectHandler struct { printer *output.Printer output inspectOutput } -// NewJSONHandler creates a new JsonHandler to inspect signatures and print in +// NewInspectHandler creates a new JsonHandler to inspect signatures and print in // JSON format. -func NewJSONHandler(printer *output.Printer) *JSONHandler { - return &JSONHandler{ +func NewInspectHandler(printer *output.Printer) *InspectHandler { + return &InspectHandler{ printer: printer, output: inspectOutput{ Signatures: []*signature{}, @@ -85,12 +85,12 @@ func NewJSONHandler(printer *output.Printer) *JSONHandler { // handler. // // The reference is no-op for this handler. -func (h *JSONHandler) OnReferenceResolved(_, mediaType string) { +func (h *InspectHandler) OnReferenceResolved(_, mediaType string) { h.output.MediaType = mediaType } // InspectSignature inspects a signature to get it ready to be rendered. -func (h *JSONHandler) InspectSignature(manifestDesc ocispec.Descriptor, envelope coresignature.Envelope) error { +func (h *InspectHandler) InspectSignature(manifestDesc ocispec.Descriptor, envelope coresignature.Envelope) error { sig, err := newSignature(manifestDesc.Digest.String(), envelope) if err != nil { return err @@ -99,7 +99,7 @@ func (h *JSONHandler) InspectSignature(manifestDesc ocispec.Descriptor, envelope return nil } -func (h *JSONHandler) Render() error { +func (h *InspectHandler) Render() error { return output.PrintPrettyJSON(h.printer, h.output) } diff --git a/cmd/notation/internal/display/metadata/inspect/json_test.go b/cmd/notation/internal/display/metadata/json/inspect_test.go similarity index 97% rename from cmd/notation/internal/display/metadata/inspect/json_test.go rename to cmd/notation/internal/display/metadata/json/inspect_test.go index b89adc907..783c65fac 100644 --- a/cmd/notation/internal/display/metadata/inspect/json_test.go +++ b/cmd/notation/internal/display/metadata/json/inspect_test.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package inspect +package json import ( "errors" @@ -97,7 +97,7 @@ func TestParseTimestamp(t *testing.T) { }) t.Run("timestamp validation error", func(t *testing.T) { - tsaToken, err := os.ReadFile("./testdata/TimeStampTokenWithInvalidSignature.p7s") + tsaToken, err := os.ReadFile("../testdata/TimeStampTokenWithInvalidSignature.p7s") if err != nil { t.Fatal(err) } @@ -117,7 +117,7 @@ func TestParseTimestamp(t *testing.T) { } func TestInspectSignature_NewSignatureError(t *testing.T) { - h := NewJSONHandler(nil) + h := NewInspectHandler(nil) // ...existing code to ensure h.output.MediaType is set... h.OnReferenceResolved("test-ref", "test-media-type") manifestDesc := ocispec.Descriptor{Digest: "fake-digest"} diff --git a/cmd/notation/internal/display/metadata/inspect/testdata/TimeStampTokenWithInvalidSignature.p7s b/cmd/notation/internal/display/metadata/testdata/TimeStampTokenWithInvalidSignature.p7s similarity index 100% rename from cmd/notation/internal/display/metadata/inspect/testdata/TimeStampTokenWithInvalidSignature.p7s rename to cmd/notation/internal/display/metadata/testdata/TimeStampTokenWithInvalidSignature.p7s diff --git a/cmd/notation/internal/display/metadata/inspect/tree.go b/cmd/notation/internal/display/metadata/tree/inspect.go similarity index 92% rename from cmd/notation/internal/display/metadata/inspect/tree.go rename to cmd/notation/internal/display/metadata/tree/inspect.go index 33e00deee..c9f58ed23 100644 --- a/cmd/notation/internal/display/metadata/inspect/tree.go +++ b/cmd/notation/internal/display/metadata/tree/inspect.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package inspect +package tree import ( "crypto/sha256" @@ -34,9 +34,9 @@ import ( ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) -// TreeHandler is a handler for inspecting metadata information and rendering +// InspectHandler is a handler for inspecting metadata information and rendering // it in a tree format. It implements the metadata.InspectHandler interface. -type TreeHandler struct { +type InspectHandler struct { printer *output.Printer // rootReferenceNode is the root node with the artifact reference as the @@ -47,10 +47,10 @@ type TreeHandler struct { notationSignaturesNode *tree.Node } -// NewTreeHandler creates a TreeHandler to inspect signatures and print in tree +// NewInspectHandler creates a TreeHandler to inspect signatures and print in tree // format. -func NewTreeHandler(printer *output.Printer) *TreeHandler { - return &TreeHandler{ +func NewInspectHandler(printer *output.Printer) *InspectHandler { + return &InspectHandler{ printer: printer, } } @@ -59,18 +59,18 @@ func NewTreeHandler(printer *output.Printer) *TreeHandler { // handler. // // mediaType is a no-op for this handler. -func (h *TreeHandler) OnReferenceResolved(reference, _ string) { +func (h *InspectHandler) OnReferenceResolved(reference, _ string) { h.rootReferenceNode = tree.New(reference) h.notationSignaturesNode = h.rootReferenceNode.Add(registry.ArtifactTypeNotation) } // InspectSignature inspects a signature to get it ready to be rendered. -func (h *TreeHandler) InspectSignature(manifestDesc ocispec.Descriptor, envelope coresignature.Envelope) error { +func (h *InspectHandler) InspectSignature(manifestDesc ocispec.Descriptor, envelope coresignature.Envelope) error { return addSignature(h.notationSignaturesNode, manifestDesc.Digest.String(), envelope) } // Render renders the metadata information when an operation is complete. -func (h *TreeHandler) Render() error { +func (h *InspectHandler) Render() error { if len(h.notationSignaturesNode.Children) == 0 { return h.printer.Printf("%s has no associated signature\n", h.rootReferenceNode.Value) } diff --git a/cmd/notation/internal/display/metadata/inspect/tree_test.go b/cmd/notation/internal/display/metadata/tree/inspect_test.go similarity index 98% rename from cmd/notation/internal/display/metadata/inspect/tree_test.go rename to cmd/notation/internal/display/metadata/tree/inspect_test.go index b1038b0a1..6a5eddeca 100644 --- a/cmd/notation/internal/display/metadata/inspect/tree_test.go +++ b/cmd/notation/internal/display/metadata/tree/inspect_test.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package inspect +package tree import ( "fmt" @@ -132,7 +132,7 @@ func TestAddTimestamp(t *testing.T) { }) t.Run("timestamp validation error", func(t *testing.T) { - tsaToken, err := os.ReadFile("./testdata/TimeStampTokenWithInvalidSignature.p7s") + tsaToken, err := os.ReadFile("../testdata/TimeStampTokenWithInvalidSignature.p7s") if err != nil { t.Fatal(err) } From 1adc392dc1becac2a1bd39dc07c76fc1b7e44349 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Thu, 23 Jan 2025 08:28:35 +0000 Subject: [PATCH 29/32] fix: restore "NewInpsectHandler" function name Signed-off-by: Junjie Gao --- cmd/notation/inspect.go | 2 +- cmd/notation/internal/display/handler.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/notation/inspect.go b/cmd/notation/inspect.go index 34e50421b..3ad485684 100644 --- a/cmd/notation/inspect.go +++ b/cmd/notation/inspect.go @@ -96,7 +96,7 @@ func runInspect(command *cobra.Command, opts *inspectOpts) error { // set log level ctx := opts.LoggingFlagOpts.InitializeLogger(command.Context()) - displayHandler, err := display.NewMetadataInpsectHandler(opts.Printer, opts.Format) + displayHandler, err := display.NewInpsectHandler(opts.Printer, opts.Format) if err != nil { return err } diff --git a/cmd/notation/internal/display/handler.go b/cmd/notation/internal/display/handler.go index 9381be9cb..e254c266b 100644 --- a/cmd/notation/internal/display/handler.go +++ b/cmd/notation/internal/display/handler.go @@ -29,9 +29,9 @@ import ( "github.com/notaryproject/notation/cmd/notation/internal/option" ) -// NewMetadataInpsectHandler creates a new InspectHandler based on the output +// NewInpsectHandler creates a new InspectHandler based on the output // format. -func NewMetadataInpsectHandler(printer *output.Printer, format option.Format) (metadata.InspectHandler, error) { +func NewInpsectHandler(printer *output.Printer, format option.Format) (metadata.InspectHandler, error) { switch option.FormatType(format.CurrentFormat) { case option.FormatTypeJSON: return json.NewInspectHandler(printer), nil From da16190ff03fe66bd01ed0f49dd7c2475ccf2f3d Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Thu, 23 Jan 2025 08:40:49 +0000 Subject: [PATCH 30/32] fix: move internal/tree to tree handlers package Signed-off-by: Junjie Gao --- .../internal/display/metadata/tree/inspect.go | 21 +++++++++---------- .../display/metadata/tree/inspect_test.go | 13 ++++++------ .../internal/display/metadata}/tree/tree.go | 0 .../display/metadata}/tree/tree_test.go | 0 4 files changed, 16 insertions(+), 18 deletions(-) rename {internal => cmd/notation/internal/display/metadata}/tree/tree.go (100%) rename {internal => cmd/notation/internal/display/metadata}/tree/tree_test.go (100%) diff --git a/cmd/notation/internal/display/metadata/tree/inspect.go b/cmd/notation/internal/display/metadata/tree/inspect.go index c9f58ed23..51aa161e5 100644 --- a/cmd/notation/internal/display/metadata/tree/inspect.go +++ b/cmd/notation/internal/display/metadata/tree/inspect.go @@ -29,7 +29,6 @@ import ( "github.com/notaryproject/notation-go/registry" "github.com/notaryproject/notation/cmd/notation/internal/display/output" "github.com/notaryproject/notation/internal/envelope" - "github.com/notaryproject/notation/internal/tree" "github.com/notaryproject/tspclient-go" ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) @@ -41,10 +40,10 @@ type InspectHandler struct { // rootReferenceNode is the root node with the artifact reference as the // value. - rootReferenceNode *tree.Node + rootReferenceNode *Node // notationSignaturesNode is the node for all signatures associated with the // artifact. - notationSignaturesNode *tree.Node + notationSignaturesNode *Node } // NewInspectHandler creates a TreeHandler to inspect signatures and print in tree @@ -60,7 +59,7 @@ func NewInspectHandler(printer *output.Printer) *InspectHandler { // // mediaType is a no-op for this handler. func (h *InspectHandler) OnReferenceResolved(reference, _ string) { - h.rootReferenceNode = tree.New(reference) + h.rootReferenceNode = New(reference) h.notationSignaturesNode = h.rootReferenceNode.Add(registry.ArtifactTypeNotation) } @@ -78,7 +77,7 @@ func (h *InspectHandler) Render() error { return h.rootReferenceNode.Print(h.printer) } -func addSignature(node *tree.Node, digest string, sigEnvelope coresignature.Envelope) error { +func addSignature(node *Node, digest string, sigEnvelope coresignature.Envelope) error { envelopeContent, err := sigEnvelope.Content() if err != nil { return err @@ -104,7 +103,7 @@ func addSignature(node *tree.Node, digest string, sigEnvelope coresignature.Enve return nil } -func addSignedAttributes(node *tree.Node, envelopeContent *coresignature.EnvelopeContent) { +func addSignedAttributes(node *Node, envelopeContent *coresignature.EnvelopeContent) { signedAttributesNode := node.Add("signed attributes") signedAttributesNode.AddPair("content type", string(envelopeContent.Payload.ContentType)) signedAttributesNode.AddPair("signing scheme", string(envelopeContent.SignerInfo.SignedAttributes.SigningScheme)) @@ -117,7 +116,7 @@ func addSignedAttributes(node *tree.Node, envelopeContent *coresignature.Envelop } } -func addUserDefinedAttributes(node *tree.Node, annotations map[string]string) { +func addUserDefinedAttributes(node *Node, annotations map[string]string) { userDefinedAttributesNode := node.Add("user defined attributes") if len(annotations) == 0 { userDefinedAttributesNode.Add("(empty)") @@ -129,7 +128,7 @@ func addUserDefinedAttributes(node *tree.Node, annotations map[string]string) { } } -func addUnsignedAttributes(node *tree.Node, envelopeContent *coresignature.EnvelopeContent) { +func addUnsignedAttributes(node *Node, envelopeContent *coresignature.EnvelopeContent) { unsignedAttributesNode := node.Add("unsigned attributes") if signingAgent := envelopeContent.SignerInfo.UnsignedAttributes.SigningAgent; signingAgent != "" { unsignedAttributesNode.AddPair("signing agent", signingAgent) @@ -139,14 +138,14 @@ func addUnsignedAttributes(node *tree.Node, envelopeContent *coresignature.Envel } } -func addSignedArtifact(node *tree.Node, signedArtifactDesc ocispec.Descriptor) { +func addSignedArtifact(node *Node, signedArtifactDesc ocispec.Descriptor) { artifactNode := node.Add("signed artifact") artifactNode.AddPair("media type", signedArtifactDesc.MediaType) artifactNode.AddPair("digest", signedArtifactDesc.Digest.String()) artifactNode.AddPair("size", strconv.FormatInt(signedArtifactDesc.Size, 10)) } -func addTimestamp(node *tree.Node, signerInfo coresignature.SignerInfo) { +func addTimestamp(node *Node, signerInfo coresignature.SignerInfo) { timestampNode := node.Add("timestamp signature") signedToken, err := tspclient.ParseSignedToken(signerInfo.UnsignedAttributes.TimestampSignature) if err != nil { @@ -167,7 +166,7 @@ func addTimestamp(node *tree.Node, signerInfo coresignature.SignerInfo) { addCertificates(timestampNode, signedToken.Certificates) } -func addCertificates(node *tree.Node, certChain []*x509.Certificate) { +func addCertificates(node *Node, certChain []*x509.Certificate) { certListNode := node.Add("certificates") for _, cert := range certChain { hash := sha256.Sum256(cert.Raw) diff --git a/cmd/notation/internal/display/metadata/tree/inspect_test.go b/cmd/notation/internal/display/metadata/tree/inspect_test.go index 6a5eddeca..3900c9595 100644 --- a/cmd/notation/internal/display/metadata/tree/inspect_test.go +++ b/cmd/notation/internal/display/metadata/tree/inspect_test.go @@ -20,19 +20,18 @@ import ( "time" coresignature "github.com/notaryproject/notation-core-go/signature" - "github.com/notaryproject/notation/internal/tree" ) func TestAddSignedAttributes(t *testing.T) { t.Run("empty envelopeContent", func(t *testing.T) { - node := tree.New("root") + node := New("root") ec := &coresignature.EnvelopeContent{} addSignedAttributes(node, ec) // No error or panic expected; minimal check or just ensure it doesn't crash. }) t.Run("with expiry and extented node", func(t *testing.T) { - node := tree.New("root") + node := New("root") expiryTime := time.Now().Add(time.Hour) ec := &coresignature.EnvelopeContent{ Payload: coresignature.Payload{ @@ -77,7 +76,7 @@ func TestAddSignedAttributes(t *testing.T) { func TestAddUserDefinedAttributes(t *testing.T) { t.Run("empty map", func(t *testing.T) { - node := tree.New("root") + node := New("root") addUserDefinedAttributes(node, nil) if len(node.Children) == 0 { t.Fatal("expected node to have children") @@ -92,7 +91,7 @@ func TestAddUserDefinedAttributes(t *testing.T) { }) t.Run("non-empty map", func(t *testing.T) { - node := tree.New("root") + node := New("root") annotations := map[string]string{"key1": "val1", "key2": "val2"} addUserDefinedAttributes(node, annotations) udaNode := node.Children[0] @@ -107,7 +106,7 @@ func TestAddUserDefinedAttributes(t *testing.T) { func TestAddTimestamp(t *testing.T) { t.Run("invalid timestamp signature", func(t *testing.T) { - node := tree.New("root") + node := New("root") signerInfo := coresignature.SignerInfo{ UnsignedAttributes: coresignature.UnsignedAttributes{ TimestampSignature: []byte("invalid"), @@ -141,7 +140,7 @@ func TestAddTimestamp(t *testing.T) { TimestampSignature: tsaToken, }, } - node := tree.New("root") + node := New("root") addTimestamp(node, signerInfo) if len(node.Children) == 0 { t.Fatal("expected node to have children") diff --git a/internal/tree/tree.go b/cmd/notation/internal/display/metadata/tree/tree.go similarity index 100% rename from internal/tree/tree.go rename to cmd/notation/internal/display/metadata/tree/tree.go diff --git a/internal/tree/tree_test.go b/cmd/notation/internal/display/metadata/tree/tree_test.go similarity index 100% rename from internal/tree/tree_test.go rename to cmd/notation/internal/display/metadata/tree/tree_test.go From 7cf59d44d24b9e5b066b6928c4703abb221edb11 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Thu, 6 Feb 2025 03:45:54 +0000 Subject: [PATCH 31/32] fix: resolve comments Signed-off-by: Junjie Gao --- cmd/notation/inspect_test.go | 4 ++-- cmd/notation/internal/display/handler.go | 6 ++--- .../internal/display/metadata/tree/inspect.go | 20 ++++++++--------- .../display/metadata/tree/inspect_test.go | 12 +++++----- .../internal/display/metadata/tree/tree.go | 22 +++++++++---------- .../display/metadata/tree/tree_test.go | 20 ++++++++--------- cmd/notation/internal/option/common.go | 2 ++ cmd/notation/internal/option/format.go | 15 ++++++------- 8 files changed, 51 insertions(+), 50 deletions(-) diff --git a/cmd/notation/inspect_test.go b/cmd/notation/inspect_test.go index 79393c730..f5401d17f 100644 --- a/cmd/notation/inspect_test.go +++ b/cmd/notation/inspect_test.go @@ -26,7 +26,7 @@ func TestInspectCommand_SecretsFromArgs(t *testing.T) { command := inspectCommand(opts) format := option.Format{} format.ApplyFlags(&pflag.FlagSet{}, option.FormatTypeText, option.FormatTypeJSON) - format.CurrentFormat = string(option.FormatTypeText) + format.CurrentType = string(option.FormatTypeText) expected := &inspectOpts{ reference: "ref", SecureFlagOpts: SecureFlagOpts{ @@ -59,7 +59,7 @@ func TestInspectCommand_SecretsFromEnv(t *testing.T) { format := option.Format{} format.ApplyFlags(&pflag.FlagSet{}, option.FormatTypeText, option.FormatTypeJSON) - format.CurrentFormat = string(option.FormatTypeJSON) + format.CurrentType = string(option.FormatTypeJSON) expected := &inspectOpts{ reference: "ref", SecureFlagOpts: SecureFlagOpts{ diff --git a/cmd/notation/internal/display/handler.go b/cmd/notation/internal/display/handler.go index e254c266b..1f127d896 100644 --- a/cmd/notation/internal/display/handler.go +++ b/cmd/notation/internal/display/handler.go @@ -29,14 +29,14 @@ import ( "github.com/notaryproject/notation/cmd/notation/internal/option" ) -// NewInpsectHandler creates a new InspectHandler based on the output +// NewInpsectHandler creates a new metadata InspectHandler based on the output // format. func NewInpsectHandler(printer *output.Printer, format option.Format) (metadata.InspectHandler, error) { - switch option.FormatType(format.CurrentFormat) { + switch option.FormatType(format.CurrentType) { case option.FormatTypeJSON: return json.NewInspectHandler(printer), nil case option.FormatTypeText: return tree.NewInspectHandler(printer), nil } - return nil, fmt.Errorf("unrecognized output format %s", format.CurrentFormat) + return nil, fmt.Errorf("unrecognized output format %s", format.CurrentType) } diff --git a/cmd/notation/internal/display/metadata/tree/inspect.go b/cmd/notation/internal/display/metadata/tree/inspect.go index 51aa161e5..2f22d8b65 100644 --- a/cmd/notation/internal/display/metadata/tree/inspect.go +++ b/cmd/notation/internal/display/metadata/tree/inspect.go @@ -40,10 +40,10 @@ type InspectHandler struct { // rootReferenceNode is the root node with the artifact reference as the // value. - rootReferenceNode *Node + rootReferenceNode *node // notationSignaturesNode is the node for all signatures associated with the // artifact. - notationSignaturesNode *Node + notationSignaturesNode *node } // NewInspectHandler creates a TreeHandler to inspect signatures and print in tree @@ -59,7 +59,7 @@ func NewInspectHandler(printer *output.Printer) *InspectHandler { // // mediaType is a no-op for this handler. func (h *InspectHandler) OnReferenceResolved(reference, _ string) { - h.rootReferenceNode = New(reference) + h.rootReferenceNode = new(reference) h.notationSignaturesNode = h.rootReferenceNode.Add(registry.ArtifactTypeNotation) } @@ -77,7 +77,7 @@ func (h *InspectHandler) Render() error { return h.rootReferenceNode.Print(h.printer) } -func addSignature(node *Node, digest string, sigEnvelope coresignature.Envelope) error { +func addSignature(node *node, digest string, sigEnvelope coresignature.Envelope) error { envelopeContent, err := sigEnvelope.Content() if err != nil { return err @@ -103,7 +103,7 @@ func addSignature(node *Node, digest string, sigEnvelope coresignature.Envelope) return nil } -func addSignedAttributes(node *Node, envelopeContent *coresignature.EnvelopeContent) { +func addSignedAttributes(node *node, envelopeContent *coresignature.EnvelopeContent) { signedAttributesNode := node.Add("signed attributes") signedAttributesNode.AddPair("content type", string(envelopeContent.Payload.ContentType)) signedAttributesNode.AddPair("signing scheme", string(envelopeContent.SignerInfo.SignedAttributes.SigningScheme)) @@ -116,7 +116,7 @@ func addSignedAttributes(node *Node, envelopeContent *coresignature.EnvelopeCont } } -func addUserDefinedAttributes(node *Node, annotations map[string]string) { +func addUserDefinedAttributes(node *node, annotations map[string]string) { userDefinedAttributesNode := node.Add("user defined attributes") if len(annotations) == 0 { userDefinedAttributesNode.Add("(empty)") @@ -128,7 +128,7 @@ func addUserDefinedAttributes(node *Node, annotations map[string]string) { } } -func addUnsignedAttributes(node *Node, envelopeContent *coresignature.EnvelopeContent) { +func addUnsignedAttributes(node *node, envelopeContent *coresignature.EnvelopeContent) { unsignedAttributesNode := node.Add("unsigned attributes") if signingAgent := envelopeContent.SignerInfo.UnsignedAttributes.SigningAgent; signingAgent != "" { unsignedAttributesNode.AddPair("signing agent", signingAgent) @@ -138,14 +138,14 @@ func addUnsignedAttributes(node *Node, envelopeContent *coresignature.EnvelopeCo } } -func addSignedArtifact(node *Node, signedArtifactDesc ocispec.Descriptor) { +func addSignedArtifact(node *node, signedArtifactDesc ocispec.Descriptor) { artifactNode := node.Add("signed artifact") artifactNode.AddPair("media type", signedArtifactDesc.MediaType) artifactNode.AddPair("digest", signedArtifactDesc.Digest.String()) artifactNode.AddPair("size", strconv.FormatInt(signedArtifactDesc.Size, 10)) } -func addTimestamp(node *Node, signerInfo coresignature.SignerInfo) { +func addTimestamp(node *node, signerInfo coresignature.SignerInfo) { timestampNode := node.Add("timestamp signature") signedToken, err := tspclient.ParseSignedToken(signerInfo.UnsignedAttributes.TimestampSignature) if err != nil { @@ -166,7 +166,7 @@ func addTimestamp(node *Node, signerInfo coresignature.SignerInfo) { addCertificates(timestampNode, signedToken.Certificates) } -func addCertificates(node *Node, certChain []*x509.Certificate) { +func addCertificates(node *node, certChain []*x509.Certificate) { certListNode := node.Add("certificates") for _, cert := range certChain { hash := sha256.Sum256(cert.Raw) diff --git a/cmd/notation/internal/display/metadata/tree/inspect_test.go b/cmd/notation/internal/display/metadata/tree/inspect_test.go index 3900c9595..abbb2d1b8 100644 --- a/cmd/notation/internal/display/metadata/tree/inspect_test.go +++ b/cmd/notation/internal/display/metadata/tree/inspect_test.go @@ -24,14 +24,14 @@ import ( func TestAddSignedAttributes(t *testing.T) { t.Run("empty envelopeContent", func(t *testing.T) { - node := New("root") + node := new("root") ec := &coresignature.EnvelopeContent{} addSignedAttributes(node, ec) // No error or panic expected; minimal check or just ensure it doesn't crash. }) t.Run("with expiry and extented node", func(t *testing.T) { - node := New("root") + node := new("root") expiryTime := time.Now().Add(time.Hour) ec := &coresignature.EnvelopeContent{ Payload: coresignature.Payload{ @@ -76,7 +76,7 @@ func TestAddSignedAttributes(t *testing.T) { func TestAddUserDefinedAttributes(t *testing.T) { t.Run("empty map", func(t *testing.T) { - node := New("root") + node := new("root") addUserDefinedAttributes(node, nil) if len(node.Children) == 0 { t.Fatal("expected node to have children") @@ -91,7 +91,7 @@ func TestAddUserDefinedAttributes(t *testing.T) { }) t.Run("non-empty map", func(t *testing.T) { - node := New("root") + node := new("root") annotations := map[string]string{"key1": "val1", "key2": "val2"} addUserDefinedAttributes(node, annotations) udaNode := node.Children[0] @@ -106,7 +106,7 @@ func TestAddUserDefinedAttributes(t *testing.T) { func TestAddTimestamp(t *testing.T) { t.Run("invalid timestamp signature", func(t *testing.T) { - node := New("root") + node := new("root") signerInfo := coresignature.SignerInfo{ UnsignedAttributes: coresignature.UnsignedAttributes{ TimestampSignature: []byte("invalid"), @@ -140,7 +140,7 @@ func TestAddTimestamp(t *testing.T) { TimestampSignature: tsaToken, }, } - node := New("root") + node := new("root") addTimestamp(node, signerInfo) if len(node.Children) == 0 { t.Fatal("expected node to have children") diff --git a/cmd/notation/internal/display/metadata/tree/tree.go b/cmd/notation/internal/display/metadata/tree/tree.go index 6ee3080ba..24f136730 100644 --- a/cmd/notation/internal/display/metadata/tree/tree.go +++ b/cmd/notation/internal/display/metadata/tree/tree.go @@ -25,35 +25,35 @@ const ( subTreePrefixLast = " " ) -// represents a Node in a tree -type Node struct { +// represents a node in a tree +type node struct { Value string - Children []*Node + Children []*node } -// creates a new Node with the given value -func New(value string) *Node { - return &Node{Value: value} +// creates a new node with the given value +func new(value string) *node { + return &node{Value: value} } // adds a new child node with the given value -func (parent *Node) Add(value string) *Node { - node := New(value) +func (parent *node) Add(value string) *node { + node := new(value) parent.Children = append(parent.Children, node) return node } // adds a new child node with the formatted pair as the value -func (parent *Node) AddPair(key string, value string) *Node { +func (parent *node) AddPair(key string, value string) *node { return parent.Add(key + ": " + value) } // prints the tree represented by the root node -func (root *Node) Print(w io.Writer) error { +func (root *node) Print(w io.Writer) error { return print(w, "", "", "", root) } -func print(w io.Writer, prefix string, itemMarker string, nextPrefix string, n *Node) error { +func print(w io.Writer, prefix string, itemMarker string, nextPrefix string, n *node) error { if _, err := fmt.Fprintln(w, prefix+itemMarker+n.Value); err != nil { return err } diff --git a/cmd/notation/internal/display/metadata/tree/tree_test.go b/cmd/notation/internal/display/metadata/tree/tree_test.go index 99067acf3..53b2b0237 100644 --- a/cmd/notation/internal/display/metadata/tree/tree_test.go +++ b/cmd/notation/internal/display/metadata/tree/tree_test.go @@ -20,16 +20,16 @@ import ( ) func TestNodeCreation(t *testing.T) { - node := New("root") - expected := Node{Value: "root"} + treeNode := new("root") + expected := node{Value: "root"} - if !reflect.DeepEqual(*node, expected) { - t.Fatalf("expected %+v, got %+v", expected, *node) + if !reflect.DeepEqual(*treeNode, expected) { + t.Fatalf("expected %+v, got %+v", expected, *treeNode) } } func TestNodeAdd(t *testing.T) { - root := New("root") + root := new("root") root.Add("child") if !root.ContainsChild("child") { @@ -39,7 +39,7 @@ func TestNodeAdd(t *testing.T) { } func TestNodeAddPair(t *testing.T) { - root := New("root") + root := new("root") root.AddPair("key", "value") if !root.ContainsChild("key: value") { @@ -49,7 +49,7 @@ func TestNodeAddPair(t *testing.T) { } func ExampleRootPrint() { - root := New("root") + root := new("root") root.Print(os.Stdout) // Output: @@ -57,7 +57,7 @@ func ExampleRootPrint() { } func ExampleSingleLayerPrint() { - root := New("root") + root := new("root") root.Add("child1") root.Add("child2") root.Print(os.Stdout) @@ -69,7 +69,7 @@ func ExampleSingleLayerPrint() { } func ExampleMultiLayerPrint() { - root := New("root") + root := new("root") child1 := root.Add("child1") child1.AddPair("key", "value") child2 := root.Add("child2") @@ -86,7 +86,7 @@ func ExampleMultiLayerPrint() { // └── child2.2 } -func (n *Node) ContainsChild(value string) bool { +func (n *node) ContainsChild(value string) bool { for _, child := range n.Children { if child.Value == value { return true diff --git a/cmd/notation/internal/option/common.go b/cmd/notation/internal/option/common.go index 4713d79a1..28289ce1d 100644 --- a/cmd/notation/internal/option/common.go +++ b/cmd/notation/internal/option/common.go @@ -26,6 +26,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +// Package option abstracts the options and flags for commands to be used in +// methods, such as ApplyFlags and Parse. package option import ( diff --git a/cmd/notation/internal/option/format.go b/cmd/notation/internal/option/format.go index 411ef9ec4..7f7bd867f 100644 --- a/cmd/notation/internal/option/format.go +++ b/cmd/notation/internal/option/format.go @@ -50,8 +50,8 @@ var ( // Format contains input and parsed options for formatted output flags. type Format struct { - CurrentFormat string - allowedTypes []FormatType + CurrentType string + allowedTypes []FormatType } // ApplyFlags sets up the flags for the format option. @@ -59,7 +59,7 @@ type Format struct { // The defaultType is the default format type. // The otherTypes are additional format types that are allowed. func (f *Format) ApplyFlags(fs *pflag.FlagSet, defaultType FormatType, otherTypes ...FormatType) { - f.CurrentFormat = string(defaultType) + f.CurrentType = string(defaultType) f.allowedTypes = append(otherTypes, defaultType) var quotedAllowedTypes []string @@ -68,14 +68,13 @@ func (f *Format) ApplyFlags(fs *pflag.FlagSet, defaultType FormatType, otherType } usage := fmt.Sprintf("output format, options: %s", strings.Join(quotedAllowedTypes, ", ")) // apply flags - fs.StringVarP(&f.CurrentFormat, "output", "o", f.CurrentFormat, usage) + fs.StringVarP(&f.CurrentType, "output", "o", f.CurrentType, usage) } // Parse parses the input format flag. func (opts *Format) Parse(_ *cobra.Command) error { - if ok := slices.Contains(opts.allowedTypes, FormatType(opts.CurrentFormat)); ok { - // type validation passed - return nil + if ok := slices.Contains(opts.allowedTypes, FormatType(opts.CurrentType)); !ok { + return fmt.Errorf("invalid format type: %q", opts.CurrentType) } - return fmt.Errorf("invalid format type: %q", opts.CurrentFormat) + return nil } From 60035a71bb68d18f3caa5d0ef5b3dac55b4c82ab Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Fri, 7 Feb 2025 01:32:23 +0000 Subject: [PATCH 32/32] fix: udpate new to newNode Signed-off-by: Junjie Gao --- .../internal/display/metadata/tree/inspect.go | 2 +- .../internal/display/metadata/tree/inspect_test.go | 12 ++++++------ cmd/notation/internal/display/metadata/tree/tree.go | 6 +++--- .../internal/display/metadata/tree/tree_test.go | 12 ++++++------ 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/cmd/notation/internal/display/metadata/tree/inspect.go b/cmd/notation/internal/display/metadata/tree/inspect.go index 2f22d8b65..6804e0a5d 100644 --- a/cmd/notation/internal/display/metadata/tree/inspect.go +++ b/cmd/notation/internal/display/metadata/tree/inspect.go @@ -59,7 +59,7 @@ func NewInspectHandler(printer *output.Printer) *InspectHandler { // // mediaType is a no-op for this handler. func (h *InspectHandler) OnReferenceResolved(reference, _ string) { - h.rootReferenceNode = new(reference) + h.rootReferenceNode = newNode(reference) h.notationSignaturesNode = h.rootReferenceNode.Add(registry.ArtifactTypeNotation) } diff --git a/cmd/notation/internal/display/metadata/tree/inspect_test.go b/cmd/notation/internal/display/metadata/tree/inspect_test.go index abbb2d1b8..9f8b86033 100644 --- a/cmd/notation/internal/display/metadata/tree/inspect_test.go +++ b/cmd/notation/internal/display/metadata/tree/inspect_test.go @@ -24,14 +24,14 @@ import ( func TestAddSignedAttributes(t *testing.T) { t.Run("empty envelopeContent", func(t *testing.T) { - node := new("root") + node := newNode("root") ec := &coresignature.EnvelopeContent{} addSignedAttributes(node, ec) // No error or panic expected; minimal check or just ensure it doesn't crash. }) t.Run("with expiry and extented node", func(t *testing.T) { - node := new("root") + node := newNode("root") expiryTime := time.Now().Add(time.Hour) ec := &coresignature.EnvelopeContent{ Payload: coresignature.Payload{ @@ -76,7 +76,7 @@ func TestAddSignedAttributes(t *testing.T) { func TestAddUserDefinedAttributes(t *testing.T) { t.Run("empty map", func(t *testing.T) { - node := new("root") + node := newNode("root") addUserDefinedAttributes(node, nil) if len(node.Children) == 0 { t.Fatal("expected node to have children") @@ -91,7 +91,7 @@ func TestAddUserDefinedAttributes(t *testing.T) { }) t.Run("non-empty map", func(t *testing.T) { - node := new("root") + node := newNode("root") annotations := map[string]string{"key1": "val1", "key2": "val2"} addUserDefinedAttributes(node, annotations) udaNode := node.Children[0] @@ -106,7 +106,7 @@ func TestAddUserDefinedAttributes(t *testing.T) { func TestAddTimestamp(t *testing.T) { t.Run("invalid timestamp signature", func(t *testing.T) { - node := new("root") + node := newNode("root") signerInfo := coresignature.SignerInfo{ UnsignedAttributes: coresignature.UnsignedAttributes{ TimestampSignature: []byte("invalid"), @@ -140,7 +140,7 @@ func TestAddTimestamp(t *testing.T) { TimestampSignature: tsaToken, }, } - node := new("root") + node := newNode("root") addTimestamp(node, signerInfo) if len(node.Children) == 0 { t.Fatal("expected node to have children") diff --git a/cmd/notation/internal/display/metadata/tree/tree.go b/cmd/notation/internal/display/metadata/tree/tree.go index 24f136730..b12766140 100644 --- a/cmd/notation/internal/display/metadata/tree/tree.go +++ b/cmd/notation/internal/display/metadata/tree/tree.go @@ -31,14 +31,14 @@ type node struct { Children []*node } -// creates a new node with the given value -func new(value string) *node { +// creates a newNode node with the given value +func newNode(value string) *node { return &node{Value: value} } // adds a new child node with the given value func (parent *node) Add(value string) *node { - node := new(value) + node := newNode(value) parent.Children = append(parent.Children, node) return node } diff --git a/cmd/notation/internal/display/metadata/tree/tree_test.go b/cmd/notation/internal/display/metadata/tree/tree_test.go index 53b2b0237..aaa156535 100644 --- a/cmd/notation/internal/display/metadata/tree/tree_test.go +++ b/cmd/notation/internal/display/metadata/tree/tree_test.go @@ -20,7 +20,7 @@ import ( ) func TestNodeCreation(t *testing.T) { - treeNode := new("root") + treeNode := newNode("root") expected := node{Value: "root"} if !reflect.DeepEqual(*treeNode, expected) { @@ -29,7 +29,7 @@ func TestNodeCreation(t *testing.T) { } func TestNodeAdd(t *testing.T) { - root := new("root") + root := newNode("root") root.Add("child") if !root.ContainsChild("child") { @@ -39,7 +39,7 @@ func TestNodeAdd(t *testing.T) { } func TestNodeAddPair(t *testing.T) { - root := new("root") + root := newNode("root") root.AddPair("key", "value") if !root.ContainsChild("key: value") { @@ -49,7 +49,7 @@ func TestNodeAddPair(t *testing.T) { } func ExampleRootPrint() { - root := new("root") + root := newNode("root") root.Print(os.Stdout) // Output: @@ -57,7 +57,7 @@ func ExampleRootPrint() { } func ExampleSingleLayerPrint() { - root := new("root") + root := newNode("root") root.Add("child1") root.Add("child2") root.Print(os.Stdout) @@ -69,7 +69,7 @@ func ExampleSingleLayerPrint() { } func ExampleMultiLayerPrint() { - root := new("root") + root := newNode("root") child1 := root.Add("child1") child1.AddPair("key", "value") child2 := root.Add("child2")