@@ -701,6 +701,8 @@ func processVSAResult(vsaResult *vsa.ValidationResult, imgStatus *ImageStatus, d
701701 }
702702
703703 // Aggregate signature status
704+ // When SignatureVerified is false, mark as NOT VERIFIED
705+ // This covers both cases: signature verification failure and retrieval failure (no VSA to verify)
704706 if ! vsaResult .SignatureVerified {
705707 data .SignatureStatus = "NOT VERIFIED"
706708 }
@@ -745,9 +747,17 @@ func aggregateAllSectionsData(allResults []vsa.ComponentResult) AllSectionsData
745747 Digest : shortDigest ,
746748 }
747749
748- // Process VSA result
750+ // Process VSA result (always process if available, even if there's an error)
749751 if result .Result != nil {
750752 processVSAResult (result .Result , & imgStatus , & data , shortDigest )
753+ } else if result .Error != nil {
754+ // If there's an error but no Result, create a status from the error
755+ reason := extractReasonFromMessage (result .Error .Error ())
756+ imgStatus .VSAStatus = fmt .Sprintf ("FAILED(reason=%s)" , reason )
757+ data .VSAFailed ++
758+ data .FallbackReasons [reason ] = true
759+ // When VSA retrieval fails, signature cannot be verified
760+ data .SignatureStatus = "NOT VERIFIED"
751761 }
752762
753763 // Handle fallback
@@ -1137,17 +1147,34 @@ func processSnapshotComponentWithWorkerContext(ctx context.Context, component ap
11371147 // Extract digest from ContainerImage
11381148 digest , err := vsa .ExtractDigestFromImageRef (component .ContainerImage )
11391149 if err != nil {
1150+ // Extract digest errors don't trigger fallback (invalid input)
11401151 return createErrorResult (component , fmt .Errorf ("failed to extract digest: %w" , err ))
11411152 }
11421153
11431154 // Perform VSA validation
11441155 result , err := performVSAValidation (ctx , digest , data )
1156+
1157+ // Create ValidationResult from error if needed (for proper display)
11451158 if err != nil {
1146- return createErrorResult (component , fmt .Errorf ("VSA validation failed: %w" , err ))
1159+ // Create a ValidationResult from the error for proper display
1160+ errorResult := createValidationResultFromError (err )
1161+
1162+ // Check if fallback should be triggered BEFORE returning error
1163+ if data .fallbackToImageValidation && shouldTriggerFallbackForComponent (err , errorResult ) {
1164+ return handleComponentFallback (ctx , component , data , errorResult , workerFallbackContext )
1165+ }
1166+
1167+ // Return error result with ValidationResult for proper display
1168+ return vsa.ComponentResult {
1169+ ComponentName : component .Name ,
1170+ ImageRef : component .ContainerImage ,
1171+ Result : errorResult ,
1172+ Error : fmt .Errorf ("VSA validation failed: %w" , err ),
1173+ }
11471174 }
11481175
11491176 // Handle fallback if enabled and needed
1150- if data .fallbackToImageValidation && shouldTriggerFallbackForComponent (err , result ) {
1177+ if data .fallbackToImageValidation && shouldTriggerFallbackForComponent (nil , result ) {
11511178 return handleComponentFallback (ctx , component , data , result , workerFallbackContext )
11521179 }
11531180
@@ -1232,13 +1259,45 @@ func handleComponentFallback(ctx context.Context, component app.SnapshotComponen
12321259
12331260// createErrorResult creates a ComponentResult with an error
12341261func createErrorResult (component app.SnapshotComponent , err error ) vsa.ComponentResult {
1262+ // Create ValidationResult from error for proper display
1263+ errorResult := createValidationResultFromError (err )
12351264 return vsa.ComponentResult {
12361265 ComponentName : component .Name ,
12371266 ImageRef : component .ContainerImage ,
1267+ Result : errorResult ,
12381268 Error : err ,
12391269 }
12401270}
12411271
1272+ // createValidationResultFromError creates a ValidationResult from an error
1273+ // This ensures proper display of VSA status even when validation fails
1274+ func createValidationResultFromError (err error ) * vsa.ValidationResult {
1275+ if err == nil {
1276+ return nil
1277+ }
1278+
1279+ // Determine reason code from error message
1280+ reasonCode := "retrieval_failed"
1281+ errMsg := err .Error ()
1282+
1283+ // Check for specific error types
1284+ if strings .Contains (errMsg , "timeout" ) || strings .Contains (errMsg , "exceeded allowed execution time" ) {
1285+ reasonCode = "retrieval_failed"
1286+ } else if strings .Contains (errMsg , "no entries found" ) || strings .Contains (errMsg , "not found" ) {
1287+ reasonCode = "no_vsa"
1288+ } else if strings .Contains (errMsg , "signature" ) {
1289+ reasonCode = "retrieval_failed"
1290+ }
1291+
1292+ return & vsa.ValidationResult {
1293+ Passed : false ,
1294+ Message : errMsg ,
1295+ SignatureVerified : false , // When retrieval fails, signature cannot be verified
1296+ PredicateOutcome : "" ,
1297+ ReasonCode : reasonCode ,
1298+ }
1299+ }
1300+
12421301// OutputFormatter defines the interface for objects that can be formatted for output
12431302type OutputFormatter interface {
12441303 PrintJSON (writer io.Writer ) error
0 commit comments