Skip to content

Commit f2d11ed

Browse files
committed
Results difference
The results differed between validate image and the fallback validate image used in validate vsa. The root cause was when validating the identifier passed was not a file, the "latest" tag was appended to the image ref. This was fixed and the validation code has been simplified. Assisted by: Claude Sonnet
1 parent 648b700 commit f2d11ed

File tree

6 files changed

+15
-437
lines changed

6 files changed

+15
-437
lines changed

cmd/validate/vsa.go

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -452,32 +452,18 @@ func performVSAValidationForSingle(ctx context.Context, identifier string, data
452452
}
453453

454454
// handleFallbackValidation handles the fallback validation logic
455-
func handleFallbackValidation(ctx context.Context, identifier string, result *vsa.ValidationResult, data *validateVSAData, fs afero.Fs) error {
455+
func handleFallbackValidation(ctx context.Context, imageRef string, result *vsa.ValidationResult, data *validateVSAData, fs afero.Fs) error {
456456
printVSAInfo(os.Stdout, "Falling back to image validation...")
457-
458-
// Extract image reference from VSA identifier for fallback
459-
imageRef, extractErr := vsa.ExtractImageFromVSAIdentifier(identifier)
460-
if extractErr != nil {
461-
return fmt.Errorf("fallback validation not supported for file paths: %s", identifier)
462-
}
457+
printVSAInfo(os.Stdout, fmt.Sprintf("Image reference: %s", imageRef))
463458

464459
// Create worker context for fallback validation
465460
workerFallbackContext, workerErr := vsa.CreateWorkerFallbackContext(ctx, data.fallbackContext.FallbackPolicy)
466461
if workerErr != nil {
467462
return fmt.Errorf("failed to create fallback context: %w", workerErr)
468463
}
469464

470-
// Create fallback config
471-
fallbackConfig := &vsa.FallbackConfig{
472-
FallbackToImageValidation: data.fallbackToImageValidation,
473-
FallbackPublicKey: data.fallbackPublicKey,
474-
PolicyConfig: data.policyConfig,
475-
EffectiveTime: data.effectiveTime,
476-
Info: data.info,
477-
}
478-
479465
// Use the common fallback validation logic
480-
fallbackResult := vsa.PerformFallbackValidation(ctx, fallbackConfig, data.fallbackContext, imageRef, "single-vsa-component", result, "", workerFallbackContext)
466+
fallbackResult := vsa.PerformFallbackValidation(result, "")
481467
if fallbackResult.Error != nil {
482468
return fallbackResult.Error
483469
}
@@ -831,6 +817,7 @@ func validateImageFallbackWithWorkerContext(ctx context.Context, data *validateV
831817

832818
// Perform image validation using precomputed context and worker-specific evaluators
833819
log.Debugf("🔄 Fallback: Starting image validation...")
820+
log.Debugf("🔄 Fallback: Using fallback policy: %s", data.fallbackContext.FallbackPolicy)
834821
result, err := image.ValidateImage(ctx, comp, spec, data.fallbackContext.FallbackPolicy, workerFallbackContext.Evaluators, data.info)
835822
if err != nil {
836823
log.Debugf("🔄 Fallback: Image validation failed with error: %v", err)
@@ -910,17 +897,8 @@ func handleComponentFallback(ctx context.Context, component app.SnapshotComponen
910897
predicateStatus = result.PredicateOutcome
911898
}
912899

913-
// Create fallback config
914-
fallbackConfig := &vsa.FallbackConfig{
915-
FallbackToImageValidation: data.fallbackToImageValidation,
916-
FallbackPublicKey: data.fallbackPublicKey,
917-
PolicyConfig: data.policyConfig,
918-
EffectiveTime: data.effectiveTime,
919-
Info: data.info,
920-
}
921-
922900
// Use the common fallback validation logic
923-
fallbackResult := vsa.PerformFallbackValidation(ctx, fallbackConfig, data.fallbackContext, imageRef, component.Name, result, predicateStatus, workerFallbackContext)
901+
fallbackResult := vsa.PerformFallbackValidation(result, predicateStatus)
924902
if fallbackResult.Error != nil {
925903
return createErrorResult(component, fallbackResult.Error)
926904
}

cmd/validate/vsa_test.go

Lines changed: 3 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ import (
3535
"github.com/spf13/cobra"
3636
"github.com/stretchr/testify/assert"
3737

38-
"github.com/conforma/cli/internal/evaluator"
3938
"github.com/conforma/cli/internal/output"
4039
"github.com/conforma/cli/internal/validate/vsa"
4140
)
@@ -1779,25 +1778,15 @@ func (m *mockOutputFormatter) PrintText(writer io.Writer) error {
17791778

17801779
// TestPerformFallbackValidation tests the extracted fallback validation function
17811780
func TestPerformFallbackValidation(t *testing.T) {
1782-
ctx := context.Background()
1783-
1784-
// Create a mock worker fallback context
1785-
workerContext := &vsa.WorkerFallbackContext{
1786-
Evaluators: []evaluator.Evaluator{}, // Empty for testing
1787-
}
17881781

17891782
tests := []struct {
17901783
name string
1791-
imageRef string
1792-
componentName string
17931784
result *vsa.ValidationResult
17941785
predicateStatus string
17951786
expectError bool
17961787
}{
17971788
{
1798-
name: "successful fallback with VSA result",
1799-
imageRef: "test-image:latest",
1800-
componentName: "test-component",
1789+
name: "successful fallback with VSA result",
18011790
result: &vsa.ValidationResult{
18021791
Passed: false,
18031792
Message: "VSA validation failed",
@@ -1807,16 +1796,12 @@ func TestPerformFallbackValidation(t *testing.T) {
18071796
},
18081797
{
18091798
name: "successful fallback without VSA result",
1810-
imageRef: "test-image:latest",
1811-
componentName: "test-component",
18121799
result: nil,
18131800
predicateStatus: "failed",
18141801
expectError: false,
18151802
},
18161803
{
18171804
name: "fallback with empty predicate status",
1818-
imageRef: "test-image:latest",
1819-
componentName: "test-component",
18201805
result: nil,
18211806
predicateStatus: "",
18221807
expectError: false,
@@ -1827,16 +1812,7 @@ func TestPerformFallbackValidation(t *testing.T) {
18271812
t.Run(tt.name, func(t *testing.T) {
18281813
// Note: This test will fail in practice because it requires actual
18291814
// fallback context and evaluators, but it tests the function structure
1830-
// Create fallback config
1831-
fallbackConfig := &vsa.FallbackConfig{
1832-
FallbackToImageValidation: true,
1833-
FallbackPublicKey: "test-key",
1834-
PolicyConfig: "test-policy",
1835-
EffectiveTime: "2023-01-01T00:00:00Z",
1836-
Info: false,
1837-
}
1838-
1839-
fallbackResult := vsa.PerformFallbackValidation(ctx, fallbackConfig, nil, tt.imageRef, tt.componentName, tt.result, tt.predicateStatus, workerContext)
1815+
fallbackResult := vsa.PerformFallbackValidation(tt.result, tt.predicateStatus)
18401816

18411817
if tt.expectError {
18421818
assert.Error(t, fallbackResult.Error)
@@ -1852,19 +1828,8 @@ func TestPerformFallbackValidation(t *testing.T) {
18521828

18531829
// TestPerformFallbackValidation_ErrorHandling tests error handling in fallback validation
18541830
func TestPerformFallbackValidation_ErrorHandling(t *testing.T) {
1855-
ctx := context.Background()
1856-
18571831
// Test with nil worker context - should not cause error as function only handles VSA result logic
1858-
// Create fallback config
1859-
fallbackConfig := &vsa.FallbackConfig{
1860-
FallbackToImageValidation: true,
1861-
FallbackPublicKey: "test-key",
1862-
PolicyConfig: "test-policy",
1863-
EffectiveTime: "2023-01-01T00:00:00Z",
1864-
Info: false,
1865-
}
1866-
1867-
fallbackResult := vsa.PerformFallbackValidation(ctx, fallbackConfig, nil, "test-image:latest", "test-component", nil, "failed", nil)
1832+
fallbackResult := vsa.PerformFallbackValidation(nil, "failed")
18681833
// The function now only handles VSA result logic, so it should not return an error
18691834
assert.NoError(t, fallbackResult.Error)
18701835
assert.NotNil(t, fallbackResult.VSAResult)

internal/validate/vsa/fallback.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ func ShouldTriggerFallback(err error, result *ValidationResult) bool {
8383

8484
// performFallbackValidation performs the common fallback validation logic
8585
// Note: This function now only handles the VSA result logic, image validation is handled in CLI layer
86-
func PerformFallbackValidation(ctx context.Context, config *FallbackConfig, fallbackContext *FallbackValidationContext, imageRef string, componentName string, result *ValidationResult, predicateStatus string, workerFallbackContext *WorkerFallbackContext) *FallbackResult {
86+
func PerformFallbackValidation(result *ValidationResult, predicateStatus string) *FallbackResult {
8787
// Use the actual VSA result for fallback case
8888
// If we have a result, use it; otherwise create a minimal result
8989
var vsaResult *ValidationResult

internal/validate/vsa/fallback_test.go

Lines changed: 6 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -113,44 +113,22 @@ func TestShouldTriggerFallback(t *testing.T) {
113113
}
114114

115115
func TestPerformFallbackValidation(t *testing.T) {
116-
ctx := context.Background()
117-
118116
tests := []struct {
119-
name string
120-
config *FallbackConfig
121-
fallbackContext *FallbackValidationContext
122-
imageRef string
123-
componentName string
124-
result *ValidationResult
125-
predicateStatus string
126-
workerFallbackContext *WorkerFallbackContext
127-
expectedVSAResult *ValidationResult
128-
expectedError error
117+
name string
118+
result *ValidationResult
119+
predicateStatus string
120+
expectedVSAResult *ValidationResult
121+
expectedError error
129122
}{
130123
{
131124
name: "With VSA result - should use provided result",
132-
config: &FallbackConfig{
133-
FallbackToImageValidation: true,
134-
FallbackPublicKey: "test-key",
135-
PolicyConfig: "test-policy",
136-
EffectiveTime: "2023-01-01T00:00:00Z",
137-
Info: false,
138-
},
139-
fallbackContext: &FallbackValidationContext{
140-
PolicyConfiguration: "test-policy",
141-
},
142-
imageRef: "test-image:latest",
143-
componentName: "test-component",
144125
result: &ValidationResult{
145126
Passed: false,
146127
Message: "VSA validation failed",
147128
SignatureVerified: false,
148129
PredicateOutcome: "failed",
149130
},
150131
predicateStatus: "failed",
151-
workerFallbackContext: &WorkerFallbackContext{
152-
Evaluators: []evaluator.Evaluator{},
153-
},
154132
expectedVSAResult: &ValidationResult{
155133
Passed: false,
156134
Message: "VSA validation failed",
@@ -160,24 +138,9 @@ func TestPerformFallbackValidation(t *testing.T) {
160138
expectedError: nil,
161139
},
162140
{
163-
name: "Without VSA result - should create minimal result",
164-
config: &FallbackConfig{
165-
FallbackToImageValidation: true,
166-
FallbackPublicKey: "test-key",
167-
PolicyConfig: "test-policy",
168-
EffectiveTime: "2023-01-01T00:00:00Z",
169-
Info: false,
170-
},
171-
fallbackContext: &FallbackValidationContext{
172-
PolicyConfiguration: "test-policy",
173-
},
174-
imageRef: "test-image:latest",
175-
componentName: "test-component",
141+
name: "Without VSA result - should create minimal result",
176142
result: nil, // No VSA result
177143
predicateStatus: "failed",
178-
workerFallbackContext: &WorkerFallbackContext{
179-
Evaluators: []evaluator.Evaluator{},
180-
},
181144
expectedVSAResult: &ValidationResult{
182145
Passed: false,
183146
Message: "VSA validation failed",
@@ -188,28 +151,13 @@ func TestPerformFallbackValidation(t *testing.T) {
188151
},
189152
{
190153
name: "With successful VSA result - should use provided result",
191-
config: &FallbackConfig{
192-
FallbackToImageValidation: true,
193-
FallbackPublicKey: "test-key",
194-
PolicyConfig: "test-policy",
195-
EffectiveTime: "2023-01-01T00:00:00Z",
196-
Info: false,
197-
},
198-
fallbackContext: &FallbackValidationContext{
199-
PolicyConfiguration: "test-policy",
200-
},
201-
imageRef: "test-image:latest",
202-
componentName: "test-component",
203154
result: &ValidationResult{
204155
Passed: true,
205156
Message: "VSA validation passed",
206157
SignatureVerified: true,
207158
PredicateOutcome: "passed",
208159
},
209160
predicateStatus: "passed",
210-
workerFallbackContext: &WorkerFallbackContext{
211-
Evaluators: []evaluator.Evaluator{},
212-
},
213161
expectedVSAResult: &ValidationResult{
214162
Passed: true,
215163
Message: "VSA validation passed",
@@ -223,14 +171,8 @@ func TestPerformFallbackValidation(t *testing.T) {
223171
for _, tt := range tests {
224172
t.Run(tt.name, func(t *testing.T) {
225173
fallbackResult := PerformFallbackValidation(
226-
ctx,
227-
tt.config,
228-
tt.fallbackContext,
229-
tt.imageRef,
230-
tt.componentName,
231174
tt.result,
232175
tt.predicateStatus,
233-
tt.workerFallbackContext,
234176
)
235177

236178
// Verify the result structure
@@ -426,7 +368,6 @@ func TestShouldTriggerFallback_EdgeCases(t *testing.T) {
426368

427369
// Test PerformFallbackValidation with different predicate statuses
428370
func TestPerformFallbackValidation_PredicateStatuses(t *testing.T) {
429-
ctx := context.Background()
430371

431372
tests := []struct {
432373
name string
@@ -461,23 +402,9 @@ func TestPerformFallbackValidation_PredicateStatuses(t *testing.T) {
461402

462403
for _, tt := range tests {
463404
t.Run(tt.name, func(t *testing.T) {
464-
config := &FallbackConfig{
465-
FallbackToImageValidation: true,
466-
FallbackPublicKey: "test-key",
467-
PolicyConfig: "test-policy",
468-
EffectiveTime: "2023-01-01T00:00:00Z",
469-
Info: false,
470-
}
471-
472405
fallbackResult := PerformFallbackValidation(
473-
ctx,
474-
config,
475-
nil, // fallbackContext
476-
"test-image:latest",
477-
"test-component",
478406
tt.result,
479407
tt.predicateStatus,
480-
nil, // workerFallbackContext
481408
)
482409

483410
require.NotNil(t, fallbackResult)

internal/validate/vsa/vsa.go

Lines changed: 0 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -931,36 +931,6 @@ func ExtractDigestFromImageRef(imageRef string) (string, error) {
931931
return imageRef, nil
932932
}
933933

934-
// ExtractImageFromVSAIdentifier extracts the image reference from VSA identifier
935-
// This function is used for fallback validation when VSA validation fails
936-
func ExtractImageFromVSAIdentifier(identifier string) (string, error) {
937-
// Handle empty identifier specially
938-
if identifier == "" {
939-
return "", nil
940-
}
941-
942-
identifierType := DetectIdentifierType(identifier)
943-
944-
switch identifierType {
945-
case IdentifierImageDigest:
946-
// Convert digest to image reference
947-
// e.g., registry/image@sha256:abc123 -> registry/image:tag
948-
return ConvertDigestToImageRef(identifier)
949-
case IdentifierImageReference:
950-
// Check if it's actually a file path that was incorrectly detected as image reference
951-
if IsFilePathLike(identifier) {
952-
return "", fmt.Errorf("fallback validation not supported for file paths: %s", identifier)
953-
}
954-
// Already an image reference
955-
return identifier, nil
956-
case IdentifierFile:
957-
// Cannot extract image from file path - fallback not supported
958-
return "", fmt.Errorf("fallback validation not supported for file paths: %s", identifier)
959-
default:
960-
return "", fmt.Errorf("fallback validation not supported for identifier type: %s", identifier)
961-
}
962-
}
963-
964934
// isFilePathLike checks if an identifier looks like a file path
965935
// This handles the case where name.ParseReference incorrectly accepts file paths as valid image references
966936
func IsFilePathLike(identifier string) bool {
@@ -987,28 +957,3 @@ func IsFilePathLike(identifier string) bool {
987957

988958
return false
989959
}
990-
991-
// ConvertDigestToImageRef converts a digest to an image reference
992-
// This is a simplified implementation that attempts to construct a reasonable image reference
993-
func ConvertDigestToImageRef(digest string) (string, error) {
994-
// If the digest is already in the format registry/image@sha256:abc123,
995-
// we can extract the repository part and construct a tag reference
996-
if strings.Contains(digest, "@") {
997-
parts := strings.Split(digest, "@")
998-
if len(parts) == 2 {
999-
repository := parts[0]
1000-
// Try to construct a reasonable tag reference
1001-
// This is a best-effort approach since we don't have the original tag
1002-
imageRef := repository + ":latest"
1003-
1004-
// Validate that the constructed reference is valid
1005-
if _, err := name.ParseReference(imageRef); err == nil {
1006-
return imageRef, nil
1007-
}
1008-
}
1009-
}
1010-
1011-
// If we can't convert the digest to an image reference,
1012-
// return an error as fallback is not supported for pure digests
1013-
return "", fmt.Errorf("fallback validation: cannot convert digest to image reference: %s", digest)
1014-
}

0 commit comments

Comments
 (0)