@@ -23,6 +23,7 @@ import (
2323 "encoding/json"
2424 "fmt"
2525 "strings"
26+ "sync"
2627
2728 "github.com/google/go-containerregistry/pkg/name"
2829 ssldsse "github.com/secure-systems-lab/go-securesystemslib/dsse"
@@ -279,27 +280,39 @@ func compareRules(vsaRuleResults map[string][]RuleResult, requiredRules map[stri
279280 return result
280281}
281282
283+ // ValidationResultWithContent contains both validation result and VSA content
284+ type ValidationResultWithContent struct {
285+ * ValidationResult
286+ VSAContent string
287+ }
288+
282289// ValidateVSA is the main validation function called by the command
283290func ValidateVSA (ctx context.Context , imageRef string , policy policy.Policy , retriever VSADataRetriever , publicKey string ) (* ValidationResult , error ) {
291+ result , _ , err := ValidateVSAWithContent (ctx , imageRef , policy , retriever , publicKey )
292+ return result , err
293+ }
294+
295+ // ValidateVSAWithContent returns both validation result and VSA content to avoid redundant retrieval
296+ func ValidateVSAWithContent (ctx context.Context , imageRef string , policy policy.Policy , retriever VSADataRetriever , publicKey string ) (* ValidationResult , string , error ) {
284297 // Extract digest from image reference
285298 ref , err := name .ParseReference (imageRef )
286299 if err != nil {
287- return nil , fmt .Errorf ("invalid image reference: %w" , err )
300+ return nil , "" , fmt .Errorf ("invalid image reference: %w" , err )
288301 }
289302
290303 digest := ref .Identifier ()
291304
292305 // Retrieve VSA data using the provided retriever
293306 vsaContent , err := retriever .RetrieveVSAData (ctx )
294307 if err != nil {
295- return nil , fmt .Errorf ("failed to retrieve VSA data: %w" , err )
308+ return nil , "" , fmt .Errorf ("failed to retrieve VSA data: %w" , err )
296309 }
297310
298311 // Verify signature if public key is provided
299312 signatureVerified := false
300313 if publicKey != "" {
301314 if vsaContent == "" {
302- return nil , fmt .Errorf ("signature verification not supported for this VSA retriever" )
315+ return nil , "" , fmt .Errorf ("signature verification not supported for this VSA retriever" )
303316 }
304317 if err := verifyVSASignature (vsaContent , publicKey ); err != nil {
305318 // For now, log the error but don't fail the validation
@@ -314,7 +327,7 @@ func ValidateVSA(ctx context.Context, imageRef string, policy policy.Policy, ret
314327 // Parse the VSA content to extract violations and successes
315328 predicate , err := ParseVSAContent (vsaContent )
316329 if err != nil {
317- return nil , fmt .Errorf ("failed to parse VSA content: %w" , err )
330+ return nil , "" , fmt .Errorf ("failed to parse VSA content: %w" , err )
318331 }
319332
320333 // Create policy resolver and discover available rules
@@ -335,7 +348,7 @@ func ValidateVSA(ctx context.Context, imageRef string, policy policy.Policy, ret
335348 ruleDiscovery := evaluator .NewRuleDiscoveryService ()
336349 rules , nonAnnotatedRules , err := ruleDiscovery .DiscoverRulesWithNonAnnotated (ctx , policySources )
337350 if err != nil {
338- return nil , fmt .Errorf ("failed to discover rules from policy sources: %w" , err )
351+ return nil , "" , fmt .Errorf ("failed to discover rules from policy sources: %w" , err )
339352 }
340353
341354 // Combine rules for filtering
@@ -356,7 +369,7 @@ func ValidateVSA(ctx context.Context, imageRef string, policy policy.Policy, ret
356369 if vsaPolicyResolver != nil {
357370 requiredRules , err = vsaPolicyResolver .GetRequiredRules (ctx , digest )
358371 if err != nil {
359- return nil , fmt .Errorf ("failed to get required rules from policy: %w" , err )
372+ return nil , "" , fmt .Errorf ("failed to get required rules from policy: %w" , err )
360373 }
361374 } else {
362375 // If no policy resolver is available, consider all rules in VSA as required
@@ -370,15 +383,37 @@ func ValidateVSA(ctx context.Context, imageRef string, policy policy.Policy, ret
370383 result := compareRules (vsaRuleResults , requiredRules , digest )
371384 result .SignatureVerified = signatureVerified
372385
373- return result , nil
386+ return result , vsaContent , nil
374387}
375388
376- // extractPackageFromCode extracts the package name from a rule code
389+ // packageCache caches package name extractions to avoid repeated string operations
390+ var packageCache = make (map [string ]string )
391+ var packageCacheMutex sync.RWMutex
392+
393+ // extractPackageFromCode extracts the package name from a rule code with caching
377394func extractPackageFromCode (code string ) string {
395+ // Check cache first
396+ packageCacheMutex .RLock ()
397+ if cached , exists := packageCache [code ]; exists {
398+ packageCacheMutex .RUnlock ()
399+ return cached
400+ }
401+ packageCacheMutex .RUnlock ()
402+
403+ // Extract package name
404+ var packageName string
378405 if idx := strings .Index (code , "." ); idx != - 1 {
379- return code [:idx ]
406+ packageName = code [:idx ]
407+ } else {
408+ packageName = code
380409 }
381- return code
410+
411+ // Cache the result
412+ packageCacheMutex .Lock ()
413+ packageCache [code ] = packageName
414+ packageCacheMutex .Unlock ()
415+
416+ return packageName
382417}
383418
384419// verifyVSASignature verifies the signature of a VSA file using cosign's DSSE verification
0 commit comments