14
14
package main
15
15
16
16
import (
17
- "crypto/sha256"
18
- "crypto/x509"
19
- "encoding/hex"
20
17
"errors"
21
18
"fmt"
22
19
"os"
23
- "strconv"
24
- "strings"
25
- "time"
26
20
27
21
"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"
30
23
cmderr "github.com/notaryproject/notation/cmd/notation/internal/errors"
31
24
"github.com/notaryproject/notation/cmd/notation/internal/experimental"
25
+ "github.com/notaryproject/notation/cmd/notation/internal/option"
32
26
"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"
37
27
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
38
28
"github.com/spf13/cobra"
39
29
)
40
30
41
31
type inspectOpts struct {
42
32
cmd.LoggingFlagOpts
43
33
SecureFlagOpts
34
+ option.Common
35
+ option.Format
44
36
reference string
45
- outputFormat string
46
37
allowReferrersAPI bool
47
38
maxSignatures int
48
39
}
49
40
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
-
79
41
func inspectCommand (opts * inspectOpts ) * cobra.Command {
80
42
if opts == nil {
81
43
opts = & inspectOpts {}
@@ -103,6 +65,10 @@ Example - Inspect signatures on an OCI artifact identified by a digest and outpu
103
65
return nil
104
66
},
105
67
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 )
106
72
return experimental .CheckFlagsAndWarn (cmd , "allow-referrers-api" )
107
73
},
108
74
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
118
84
119
85
opts .LoggingFlagOpts .ApplyFlags (command .Flags ())
120
86
opts .SecureFlagOpts .ApplyFlags (command .Flags ())
121
- cmd .SetPflagOutput (command .Flags (), & opts .outputFormat , cmd .PflagOutputUsage )
122
87
command .Flags ().IntVar (& opts .maxSignatures , "max-signatures" , 100 , "maximum number of signatures to evaluate or examine" )
123
88
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 )
124
92
return command
125
93
}
126
94
127
95
func runInspect (command * cobra.Command , opts * inspectOpts ) error {
128
96
// set log level
129
97
ctx := opts .LoggingFlagOpts .InitializeLogger (command .Context ())
130
98
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
133
102
}
134
103
135
104
// initialize
@@ -144,7 +113,8 @@ func runInspect(command *cobra.Command, opts *inspectOpts) error {
144
113
if err != nil {
145
114
return err
146
115
}
147
- output := inspectOutput {MediaType : manifestDesc .MediaType , Signatures : []signatureOutput {}}
116
+ displayHandler .OnReferenceResolved (resolvedRef , manifestDesc .MediaType )
117
+
148
118
skippedSignatures := false
149
119
err = listSignatures (ctx , sigRepo , manifestDesc , opts .maxSignatures , func (sigManifestDesc ocispec.Descriptor ) error {
150
120
sigBlob , sigDesc , err := sigRepo .FetchSignatureBlob (ctx , sigManifestDesc )
@@ -161,52 +131,19 @@ func runInspect(command *cobra.Command, opts *inspectOpts) error {
161
131
return nil
162
132
}
163
133
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 {
173
135
logSkippedSignature (sigManifestDesc , err )
174
136
skippedSignatures = true
175
137
return nil
176
138
}
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
-
202
139
return nil
203
140
})
204
141
var errorExceedMaxSignatures cmderr.ErrorExceedMaxSignatures
205
142
if err != nil && ! errors .As (err , & errorExceedMaxSignatures ) {
206
143
return err
207
144
}
208
145
209
- if err := printOutput ( opts . outputFormat , resolvedRef , output ); err != nil {
146
+ if err := displayHandler . Render ( ); err != nil {
210
147
return err
211
148
}
212
149
@@ -224,169 +161,3 @@ func runInspect(command *cobra.Command, opts *inspectOpts) error {
224
161
func logSkippedSignature (sigDesc ocispec.Descriptor , err error ) {
225
162
fmt .Fprintf (os .Stderr , "Warning: Skipping signature %s because of error: %v\n " , sigDesc .Digest .String (), err )
226
163
}
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