1414package main
1515
1616import (
17- "crypto/sha256"
18- "crypto/x509"
19- "encoding/hex"
2017 "errors"
2118 "fmt"
2219 "os"
23- "strconv"
24- "strings"
25- "time"
2620
2721 "github.com/notaryproject/notation-core-go/signature"
28- "github.com/notaryproject/notation-go/plugin/proto"
29- "github.com/notaryproject/notation-go/registry"
22+ "github.com/notaryproject/notation/cmd/notation/internal/display"
3023 cmderr "github.com/notaryproject/notation/cmd/notation/internal/errors"
3124 "github.com/notaryproject/notation/cmd/notation/internal/experimental"
25+ "github.com/notaryproject/notation/cmd/notation/internal/option"
3226 "github.com/notaryproject/notation/internal/cmd"
33- "github.com/notaryproject/notation/internal/envelope"
34- "github.com/notaryproject/notation/internal/ioutil"
35- "github.com/notaryproject/notation/internal/tree"
36- "github.com/notaryproject/tspclient-go"
3727 ocispec "github.com/opencontainers/image-spec/specs-go/v1"
3828 "github.com/spf13/cobra"
3929)
4030
4131type inspectOpts struct {
4232 cmd.LoggingFlagOpts
4333 SecureFlagOpts
34+ option.Common
35+ option.Format
4436 reference string
45- outputFormat string
4637 allowReferrersAPI bool
4738 maxSignatures int
4839}
4940
50- type inspectOutput struct {
51- MediaType string `json:"mediaType"`
52- Signatures []signatureOutput
53- }
54-
55- type signatureOutput struct {
56- MediaType string `json:"mediaType"`
57- Digest string `json:"digest"`
58- SignatureAlgorithm string `json:"signatureAlgorithm"`
59- SignedAttributes map [string ]string `json:"signedAttributes"`
60- UserDefinedAttributes map [string ]string `json:"userDefinedAttributes"`
61- UnsignedAttributes map [string ]any `json:"unsignedAttributes"`
62- Certificates []certificateOutput `json:"certificates"`
63- SignedArtifact ocispec.Descriptor `json:"signedArtifact"`
64- }
65-
66- type certificateOutput struct {
67- SHA256Fingerprint string `json:"SHA256Fingerprint"`
68- IssuedTo string `json:"issuedTo"`
69- IssuedBy string `json:"issuedBy"`
70- Expiry string `json:"expiry"`
71- }
72-
73- type timestampOutput struct {
74- Timestamp string `json:"timestamp,omitempty"`
75- Certificates []certificateOutput `json:"certificates,omitempty"`
76- Error string `json:"error,omitempty"`
77- }
78-
7941func inspectCommand (opts * inspectOpts ) * cobra.Command {
8042 if opts == nil {
8143 opts = & inspectOpts {}
@@ -103,6 +65,10 @@ Example - Inspect signatures on an OCI artifact identified by a digest and outpu
10365 return nil
10466 },
10567 PreRunE : func (cmd * cobra.Command , args []string ) error {
68+ if err := opts .Format .Parse (cmd ); err != nil {
69+ return err
70+ }
71+ opts .Common .Parse (cmd )
10672 return experimental .CheckFlagsAndWarn (cmd , "allow-referrers-api" )
10773 },
10874 RunE : func (cmd * cobra.Command , args []string ) error {
@@ -118,18 +84,21 @@ Example - Inspect signatures on an OCI artifact identified by a digest and outpu
11884
11985 opts .LoggingFlagOpts .ApplyFlags (command .Flags ())
12086 opts .SecureFlagOpts .ApplyFlags (command .Flags ())
121- cmd .SetPflagOutput (command .Flags (), & opts .outputFormat , cmd .PflagOutputUsage )
12287 command .Flags ().IntVar (& opts .maxSignatures , "max-signatures" , 100 , "maximum number of signatures to evaluate or examine" )
12388 cmd .SetPflagReferrersAPI (command .Flags (), & opts .allowReferrersAPI , fmt .Sprintf (cmd .PflagReferrersUsageFormat , "inspect" ))
89+
90+ // set output format
91+ opts .Format .ApplyFlags (command .Flags (), option .FormatTypeText , option .FormatTypeJSON )
12492 return command
12593}
12694
12795func runInspect (command * cobra.Command , opts * inspectOpts ) error {
12896 // set log level
12997 ctx := opts .LoggingFlagOpts .InitializeLogger (command .Context ())
13098
131- if opts .outputFormat != cmd .OutputJSON && opts .outputFormat != cmd .OutputPlaintext {
132- return fmt .Errorf ("unrecognized output format %s" , opts .outputFormat )
99+ displayHandler , err := display .NewInpsectHandler (opts .Printer , opts .Format )
100+ if err != nil {
101+ return err
133102 }
134103
135104 // initialize
@@ -144,7 +113,8 @@ func runInspect(command *cobra.Command, opts *inspectOpts) error {
144113 if err != nil {
145114 return err
146115 }
147- output := inspectOutput {MediaType : manifestDesc .MediaType , Signatures : []signatureOutput {}}
116+ displayHandler .OnReferenceResolved (resolvedRef , manifestDesc .MediaType )
117+
148118 skippedSignatures := false
149119 err = listSignatures (ctx , sigRepo , manifestDesc , opts .maxSignatures , func (sigManifestDesc ocispec.Descriptor ) error {
150120 sigBlob , sigDesc , err := sigRepo .FetchSignatureBlob (ctx , sigManifestDesc )
@@ -161,52 +131,19 @@ func runInspect(command *cobra.Command, opts *inspectOpts) error {
161131 return nil
162132 }
163133
164- envelopeContent , err := sigEnvelope .Content ()
165- if err != nil {
166- logSkippedSignature (sigManifestDesc , err )
167- skippedSignatures = true
168- return nil
169- }
170-
171- signedArtifactDesc , err := envelope .DescriptorFromSignaturePayload (& envelopeContent .Payload )
172- if err != nil {
134+ if err := displayHandler .InspectSignature (sigManifestDesc , sigEnvelope ); err != nil {
173135 logSkippedSignature (sigManifestDesc , err )
174136 skippedSignatures = true
175137 return nil
176138 }
177-
178- signatureAlgorithm , err := proto .EncodeSigningAlgorithm (envelopeContent .SignerInfo .SignatureAlgorithm )
179- if err != nil {
180- logSkippedSignature (sigManifestDesc , err )
181- skippedSignatures = true
182- return nil
183- }
184-
185- sig := signatureOutput {
186- MediaType : sigDesc .MediaType ,
187- Digest : sigManifestDesc .Digest .String (),
188- SignatureAlgorithm : string (signatureAlgorithm ),
189- SignedAttributes : getSignedAttributes (opts .outputFormat , envelopeContent ),
190- UserDefinedAttributes : signedArtifactDesc .Annotations ,
191- UnsignedAttributes : getUnsignedAttributes (opts .outputFormat , envelopeContent ),
192- Certificates : getCertificates (opts .outputFormat , envelopeContent .SignerInfo .CertificateChain ),
193- SignedArtifact : * signedArtifactDesc ,
194- }
195-
196- // clearing annotations from the SignedArtifact field since they're already
197- // displayed as UserDefinedAttributes
198- sig .SignedArtifact .Annotations = nil
199-
200- output .Signatures = append (output .Signatures , sig )
201-
202139 return nil
203140 })
204141 var errorExceedMaxSignatures cmderr.ErrorExceedMaxSignatures
205142 if err != nil && ! errors .As (err , & errorExceedMaxSignatures ) {
206143 return err
207144 }
208145
209- if err := printOutput ( opts . outputFormat , resolvedRef , output ); err != nil {
146+ if err := displayHandler . Render ( ); err != nil {
210147 return err
211148 }
212149
@@ -224,169 +161,3 @@ func runInspect(command *cobra.Command, opts *inspectOpts) error {
224161func logSkippedSignature (sigDesc ocispec.Descriptor , err error ) {
225162 fmt .Fprintf (os .Stderr , "Warning: Skipping signature %s because of error: %v\n " , sigDesc .Digest .String (), err )
226163}
227-
228- func getSignedAttributes (outputFormat string , envContent * signature.EnvelopeContent ) map [string ]string {
229- signedAttributes := map [string ]string {
230- "signingScheme" : string (envContent .SignerInfo .SignedAttributes .SigningScheme ),
231- "signingTime" : formatTimestamp (outputFormat , envContent .SignerInfo .SignedAttributes .SigningTime ),
232- }
233- expiry := envContent .SignerInfo .SignedAttributes .Expiry
234- if ! expiry .IsZero () {
235- signedAttributes ["expiry" ] = formatTimestamp (outputFormat , expiry )
236- }
237-
238- for _ , attribute := range envContent .SignerInfo .SignedAttributes .ExtendedAttributes {
239- signedAttributes [fmt .Sprint (attribute .Key )] = fmt .Sprint (attribute .Value )
240- }
241-
242- return signedAttributes
243- }
244-
245- func getUnsignedAttributes (outputFormat string , envContent * signature.EnvelopeContent ) map [string ]any {
246- unsignedAttributes := make (map [string ]any )
247-
248- if envContent .SignerInfo .UnsignedAttributes .TimestampSignature != nil {
249- unsignedAttributes ["timestampSignature" ] = parseTimestamp (outputFormat , envContent .SignerInfo )
250- }
251-
252- if envContent .SignerInfo .UnsignedAttributes .SigningAgent != "" {
253- unsignedAttributes ["signingAgent" ] = envContent .SignerInfo .UnsignedAttributes .SigningAgent
254- }
255-
256- return unsignedAttributes
257- }
258-
259- func formatTimestamp (outputFormat string , t time.Time ) string {
260- switch outputFormat {
261- case cmd .OutputJSON :
262- return t .Format (time .RFC3339 )
263- default :
264- return t .Format (time .ANSIC )
265- }
266- }
267-
268- func getCertificates (outputFormat string , certChain []* x509.Certificate ) []certificateOutput {
269- certificates := []certificateOutput {}
270-
271- for _ , cert := range certChain {
272- h := sha256 .Sum256 (cert .Raw )
273- fingerprint := strings .ToLower (hex .EncodeToString (h [:]))
274-
275- certificate := certificateOutput {
276- SHA256Fingerprint : fingerprint ,
277- IssuedTo : cert .Subject .String (),
278- IssuedBy : cert .Issuer .String (),
279- Expiry : formatTimestamp (outputFormat , cert .NotAfter ),
280- }
281-
282- certificates = append (certificates , certificate )
283- }
284-
285- return certificates
286- }
287-
288- func printOutput (outputFormat string , ref string , output inspectOutput ) error {
289- if outputFormat == cmd .OutputJSON {
290- return ioutil .PrintObjectAsJSON (output )
291- }
292-
293- if len (output .Signatures ) == 0 {
294- fmt .Printf ("%s has no associated signature\n " , ref )
295- return nil
296- }
297-
298- fmt .Println ("Inspecting all signatures for signed artifact" )
299- root := tree .New (ref )
300- cncfSigNode := root .Add (registry .ArtifactTypeNotation )
301-
302- for _ , signature := range output .Signatures {
303- sigNode := cncfSigNode .Add (signature .Digest )
304- sigNode .AddPair ("media type" , signature .MediaType )
305- sigNode .AddPair ("signature algorithm" , signature .SignatureAlgorithm )
306-
307- signedAttributesNode := sigNode .Add ("signed attributes" )
308- addMapToTree (signedAttributesNode , signature .SignedAttributes )
309-
310- userDefinedAttributesNode := sigNode .Add ("user defined attributes" )
311- addMapToTree (userDefinedAttributesNode , signature .UserDefinedAttributes )
312-
313- unsignedAttributesNode := sigNode .Add ("unsigned attributes" )
314- for k , v := range signature .UnsignedAttributes {
315- switch value := v .(type ) {
316- case string :
317- unsignedAttributesNode .AddPair (k , value )
318- case timestampOutput :
319- timestampNode := unsignedAttributesNode .Add ("timestamp signature" )
320- if value .Error != "" {
321- timestampNode .AddPair ("error" , value .Error )
322- break
323- }
324- timestampNode .AddPair ("timestamp" , value .Timestamp )
325- addCertificatesToTree (timestampNode , "certificates" , value .Certificates )
326- }
327- }
328-
329- addCertificatesToTree (sigNode , "certificates" , signature .Certificates )
330-
331- artifactNode := sigNode .Add ("signed artifact" )
332- artifactNode .AddPair ("media type" , signature .SignedArtifact .MediaType )
333- artifactNode .AddPair ("digest" , signature .SignedArtifact .Digest .String ())
334- artifactNode .AddPair ("size" , strconv .FormatInt (signature .SignedArtifact .Size , 10 ))
335- }
336-
337- root .Print ()
338- return nil
339- }
340-
341- func addMapToTree (node * tree.Node , m map [string ]string ) {
342- if len (m ) > 0 {
343- for k , v := range m {
344- node .AddPair (k , v )
345- }
346- } else {
347- node .Add ("(empty)" )
348- }
349- }
350-
351- func addCertificatesToTree (node * tree.Node , name string , certs []certificateOutput ) {
352- certListNode := node .Add (name )
353- for _ , cert := range certs {
354- certNode := certListNode .AddPair ("SHA256 fingerprint" , cert .SHA256Fingerprint )
355- certNode .AddPair ("issued to" , cert .IssuedTo )
356- certNode .AddPair ("issued by" , cert .IssuedBy )
357- certNode .AddPair ("expiry" , cert .Expiry )
358- }
359- }
360-
361- func parseTimestamp (outputFormat string , signerInfo signature.SignerInfo ) timestampOutput {
362- signedToken , err := tspclient .ParseSignedToken (signerInfo .UnsignedAttributes .TimestampSignature )
363- if err != nil {
364- return timestampOutput {
365- Error : fmt .Sprintf ("failed to parse timestamp countersignature: %s" , err .Error ()),
366- }
367- }
368- info , err := signedToken .Info ()
369- if err != nil {
370- return timestampOutput {
371- Error : fmt .Sprintf ("failed to parse timestamp countersignature: %s" , err .Error ()),
372- }
373- }
374- timestamp , err := info .Validate (signerInfo .Signature )
375- if err != nil {
376- return timestampOutput {
377- Error : fmt .Sprintf ("failed to parse timestamp countersignature: %s" , err .Error ()),
378- }
379- }
380- certificates := getCertificates (outputFormat , signedToken .Certificates )
381- var formatTimestamp string
382- switch outputFormat {
383- case cmd .OutputJSON :
384- formatTimestamp = timestamp .Format (time .RFC3339 )
385- default :
386- formatTimestamp = timestamp .Format (time .ANSIC )
387- }
388- return timestampOutput {
389- Timestamp : formatTimestamp ,
390- Certificates : certificates ,
391- }
392- }
0 commit comments