@@ -30,6 +30,7 @@ import (
30
30
"github.com/in-toto/go-witness/log"
31
31
"github.com/in-toto/go-witness/registry"
32
32
"github.com/in-toto/go-witness/timestamp"
33
+ "github.com/in-toto/witness/internal/errors"
33
34
"github.com/in-toto/witness/options"
34
35
"github.com/spf13/cobra"
35
36
)
@@ -63,15 +64,91 @@ func RunCmd() *cobra.Command {
63
64
return cmd
64
65
}
65
66
67
+ // isAttestorError determines if an error is attestor-related
68
+ func isAttestorError (err error ) bool {
69
+ return errors .IsAttestorError (err )
70
+ }
71
+
72
+ // handleInfraError handles infrastructure operation errors based on continue flags
73
+ // Returns true if execution should continue, and the error if it should be tracked
74
+ func handleInfraError (ro options.RunOptions , err error , operationDesc string , commandSucceeded bool ) (bool , error ) {
75
+ if ! commandSucceeded {
76
+ return false , nil
77
+ }
78
+
79
+ // Wrap the error with infrastructure error type
80
+ infraErr := errors .NewInfrastructureError (operationDesc , err )
81
+
82
+ if ro .ContinueOnAllErrors {
83
+ log .Warnf ("Failed to %s: %v" , operationDesc , err )
84
+ log .Warnf ("Continuing due to --continue-on-errors flag" )
85
+ return true , infraErr
86
+ } else if ro .ContinueOnInfraError {
87
+ log .Warnf ("Failed to %s: %v" , operationDesc , err )
88
+ log .Warnf ("Continuing due to --continue-on-infra-error flag" )
89
+ return true , infraErr
90
+ }
91
+
92
+ return false , nil
93
+ }
94
+
95
+ // handleErrorWithContinueFlags applies the appropriate error handling logic based on flags
96
+ // Returns true if execution should continue, false if the error should be returned
97
+ func handleErrorWithContinueFlags (ro options.RunOptions , err error , commandSucceeded bool ) (bool , error , error ) {
98
+ var infraError , attestorError error
99
+
100
+ // If command didn't succeed or no continue flags are set, don't continue
101
+ if ! commandSucceeded {
102
+ return false , nil , nil
103
+ }
104
+
105
+ // Check if the all-errors flag is set, which takes precedence
106
+ if ro .ContinueOnAllErrors {
107
+ log .Warnf ("Encountered error: %v" , err )
108
+ log .Warnf ("Continuing due to --continue-on-errors flag" )
109
+
110
+ // Still classify the error for summary purposes
111
+ if isAttestorError (err ) {
112
+ attestorError = err
113
+ } else {
114
+ // Default to infrastructure error if not an attestor error
115
+ infraError = errors .NewInfrastructureError ("run command" , err )
116
+ }
117
+ return true , infraError , attestorError
118
+ }
119
+
120
+ // Check specific error type flags
121
+ isAttestor := isAttestorError (err )
122
+ if isAttestor && ro .ContinueOnAttestorError {
123
+ log .Warnf ("Encountered attestor error: %v" , err )
124
+ log .Warnf ("Continuing due to --continue-on-attestor-error flag" )
125
+ attestorError = err
126
+ return true , infraError , attestorError
127
+ } else if ! isAttestor && ro .ContinueOnInfraError {
128
+ log .Warnf ("Encountered infrastructure error: %v" , err )
129
+ log .Warnf ("Continuing due to --continue-on-infra-error flag" )
130
+ infraError = errors .NewInfrastructureError ("run command" , err )
131
+ return true , infraError , attestorError
132
+ }
133
+
134
+ // No applicable flag was set, don't continue
135
+ return false , nil , nil
136
+ }
137
+
66
138
func runRun (ctx context.Context , ro options.RunOptions , args []string , signers ... cryptoutil.Signer ) error {
67
139
if len (signers ) > 1 {
68
- return fmt .Errorf ("only one signer is supported" )
140
+ return errors . NewInfrastructureError ( "signer validation" , fmt .Errorf ("only one signer is supported" ) )
69
141
}
70
142
71
143
if len (signers ) == 0 {
72
- return fmt .Errorf ("no signers found" )
144
+ return errors . NewInfrastructureError ( "signer validation" , fmt .Errorf ("no signers found" ) )
73
145
}
74
146
147
+ // Track if wrapped command succeeded but we had errors
148
+ var commandSucceeded bool
149
+ var infraError error
150
+ var attestorError error
151
+
75
152
timestampers := []timestamp.Timestamper {}
76
153
for _ , url := range ro .TimestampServers {
77
154
timestampers = append (timestampers , timestamp .NewTimestamper (timestamp .TimestampWithUrl (url )))
@@ -101,7 +178,7 @@ func runRun(ctx context.Context, ro options.RunOptions, args []string, signers .
101
178
if ! duplicate {
102
179
attestor , err := attestation .GetAttestor (a )
103
180
if err != nil {
104
- return fmt .Errorf ("failed to create attestor: %w" , err )
181
+ return errors . NewAttestorError ( a , fmt .Errorf ("failed to create attestor: %w" , err ) )
105
182
}
106
183
attestors = append (attestors , attestor )
107
184
}
@@ -115,23 +192,23 @@ func runRun(ctx context.Context, ro options.RunOptions, args []string, signers .
115
192
116
193
attestor , err := registry .SetOptions (attestor , setters ... )
117
194
if err != nil {
118
- return fmt .Errorf ("failed to set attestor option for %v: %w" , attestor .Type (), err )
195
+ return errors . NewAttestorError ( attestor . Name (), fmt .Errorf ("failed to set attestor option for %v: %w" , attestor .Type (), err ) )
119
196
}
120
197
}
121
198
122
199
var roHashes []cryptoutil.DigestValue
123
200
for _ , hashStr := range ro .Hashes {
124
201
hash , err := cryptoutil .HashFromString (hashStr )
125
202
if err != nil {
126
- return fmt .Errorf ("failed to parse hash: %w" , err )
203
+ return errors . NewInfrastructureError ( "parse hash" , fmt .Errorf ("failed to parse hash: %w" , err ) )
127
204
}
128
205
roHashes = append (roHashes , cryptoutil.DigestValue {Hash : hash , GitOID : false })
129
206
}
130
207
131
208
for _ , dirHashGlobItem := range ro .DirHashGlobs {
132
209
_ , err := glob .Compile (dirHashGlobItem )
133
210
if err != nil {
134
- return fmt .Errorf ("failed to compile glob: %v" , err )
211
+ return errors . NewInfrastructureError ( "compile glob" , fmt .Errorf ("failed to compile glob: %v" , err ) )
135
212
}
136
213
}
137
214
@@ -149,14 +226,49 @@ func runRun(ctx context.Context, ro options.RunOptions, args []string, signers .
149
226
),
150
227
witness .RunWithTimestampers (timestampers ... ),
151
228
)
229
+
230
+ // Check if command ran successfully
231
+ if len (args ) > 0 { // Only check for command success if a command was run
232
+ for _ , result := range results {
233
+ if result .AttestorName == "command-run" {
234
+ // Command completed and we have the attestation, so it succeeded
235
+ commandSucceeded = true
236
+ break
237
+ }
238
+ }
239
+ } else {
240
+ // If no command was specified, we're just collecting attestations
241
+ // In this case, treat as if command succeeded for flag purposes
242
+ commandSucceeded = true
243
+ }
244
+
152
245
if err != nil {
153
- return err
246
+ // Apply error handling logic based on flags
247
+ shouldContinue , newInfraErr , newAttestorErr := handleErrorWithContinueFlags (ro , err , commandSucceeded )
248
+ if shouldContinue {
249
+ // Update the error tracking variables
250
+ if newInfraErr != nil {
251
+ infraError = newInfraErr
252
+ }
253
+ if newAttestorErr != nil {
254
+ attestorError = newAttestorErr
255
+ }
256
+ } else {
257
+ // If we shouldn't continue, return the error
258
+ return err
259
+ }
154
260
}
155
261
156
262
for _ , result := range results {
157
263
signedBytes , err := json .Marshal (& result .SignedEnvelope )
158
264
if err != nil {
159
- return fmt .Errorf ("failed to marshal envelope: %w" , err )
265
+ shouldContinue , newInfraErr := handleInfraError (ro , err , "marshal envelope" , commandSucceeded )
266
+ if shouldContinue {
267
+ infraError = newInfraErr
268
+ continue // Skip to next result
269
+ } else {
270
+ return fmt .Errorf ("failed to marshal envelope: %w" , err )
271
+ }
160
272
}
161
273
162
274
// TODO: Find out explicit way to describe "prefix" in CLI options
@@ -167,22 +279,68 @@ func runRun(ctx context.Context, ro options.RunOptions, args []string, signers .
167
279
168
280
out , err := loadOutfile (outfile )
169
281
if err != nil {
170
- return fmt .Errorf ("failed to open out file: %w" , err )
282
+ shouldContinue , newInfraErr := handleInfraError (ro , err , fmt .Sprintf ("open out file %s" , outfile ), commandSucceeded )
283
+ if shouldContinue {
284
+ infraError = newInfraErr
285
+ continue // Skip to next result
286
+ } else {
287
+ return fmt .Errorf ("failed to open out file: %w" , err )
288
+ }
171
289
}
172
290
defer out .Close ()
173
291
174
292
if _ , err := out .Write (signedBytes ); err != nil {
175
- return fmt .Errorf ("failed to write envelope to out file: %w" , err )
293
+ shouldContinue , newInfraErr := handleInfraError (ro , err , fmt .Sprintf ("write envelope to file %s" , outfile ), commandSucceeded )
294
+ if shouldContinue {
295
+ infraError = newInfraErr
296
+ continue // Skip to next result
297
+ } else {
298
+ return fmt .Errorf ("failed to write envelope to out file: %w" , err )
299
+ }
176
300
}
177
301
178
302
if ro .ArchivistaOptions .Enable {
179
303
archivistaClient := archivista .New (ro .ArchivistaOptions .Url )
180
- if gitoid , err := archivistaClient .Store (ctx , result .SignedEnvelope ); err != nil {
181
- return fmt .Errorf ("failed to store artifact in archivista: %w" , err )
304
+ gitoid , err := archivistaClient .Store (ctx , result .SignedEnvelope )
305
+ if err != nil {
306
+ shouldContinue , newInfraErr := handleInfraError (ro , err , "store artifact in archivista" , commandSucceeded )
307
+ if shouldContinue {
308
+ infraError = newInfraErr
309
+ } else {
310
+ return fmt .Errorf ("failed to store artifact in archivista: %w" , err )
311
+ }
182
312
} else {
183
313
log .Infof ("Stored in archivista as %v\n " , gitoid )
184
314
}
185
315
}
186
316
}
317
+
318
+ // Display summary warnings if we had errors but continued
319
+ if commandSucceeded && (attestorError != nil || infraError != nil ) {
320
+ // Show a combined message if we used the combined flag
321
+ if ro .ContinueOnAllErrors {
322
+ log .Warnf ("Command completed successfully, but encountered errors" )
323
+ if attestorError != nil {
324
+ log .Warnf ("Some attestations may be missing" )
325
+ }
326
+ if infraError != nil {
327
+ log .Warnf ("Some attestation functionality may have been compromised" )
328
+ }
329
+ } else {
330
+ // Show specific messages for specific flags
331
+ if attestorError != nil && ro .ContinueOnAttestorError {
332
+ log .Warnf ("Command completed successfully, but encountered attestor errors" )
333
+ log .Warnf ("Some attestations may be missing" )
334
+ }
335
+
336
+ if infraError != nil && ro .ContinueOnInfraError {
337
+ log .Warnf ("Command completed successfully, but encountered infrastructure errors" )
338
+ log .Warnf ("Some attestation functionality may have been compromised" )
339
+ }
340
+ }
341
+
342
+ // We had errors but continued, so return success
343
+ return nil
344
+ }
187
345
return nil
188
346
}
0 commit comments