Skip to content

Commit f4a98c7

Browse files
feat: add basic spec version validation and related tests to enhance BOM validation process
1 parent 5056ae7 commit f4a98c7

File tree

2 files changed

+128
-4
lines changed

2 files changed

+128
-4
lines changed

internal/validator/validator.go

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,16 @@ func Validate(bom *cdx.BOM, opts ValidationOptions) ValidationResult {
4646
result.Errors = append(result.Errors, "BOM missing metadata.component")
4747
}
4848

49-
// 3. Run completeness check (leverages existing package)
49+
// 3. Validate spec version
50+
validateSpecVersion(bom, &result)
51+
52+
// 4. Run completeness check (leverages existing package)
5053
report := completeness.Check(bom)
5154
result.CompletenessScore = report.Score
5255
result.MissingRequired = report.MissingRequired
5356
result.MissingOptional = report.MissingOptional
5457

55-
// 4. Strict mode enforcement
58+
// 5. Strict mode enforcement
5659
if opts.StrictMode {
5760
if len(report.MissingRequired) > 0 {
5861
result.Valid = false
@@ -69,20 +72,48 @@ func Validate(bom *cdx.BOM, opts ValidationOptions) ValidationResult {
6972
}
7073
}
7174

72-
// 5. Add warnings for optional fields
75+
// 6. Add warnings for optional fields
7376
for _, key := range report.MissingOptional {
7477
msg := fmt.Sprintf("optional field missing: %s", key)
7578
result.Warnings = append(result.Warnings, msg)
7679
}
7780

78-
// 6. Model card validation
81+
// 7. Model card validation
7982
if opts.CheckModelCard {
8083
validateModelCard(bom, &result)
8184
}
8285

8386
return result
8487
}
8588

89+
func validateSpecVersion(bom *cdx.BOM, result *ValidationResult) {
90+
if bom.SpecVersion == 0 {
91+
result.Valid = false
92+
result.Errors = append(result.Errors, "BOM missing spec version")
93+
return
94+
}
95+
96+
// Check if spec version is valid
97+
switch bom.SpecVersion {
98+
case cdx.SpecVersion1_0, cdx.SpecVersion1_1, cdx.SpecVersion1_2,
99+
cdx.SpecVersion1_3, cdx.SpecVersion1_4, cdx.SpecVersion1_5,
100+
cdx.SpecVersion1_6:
101+
// Valid spec version
102+
default:
103+
result.Valid = false
104+
result.Errors = append(result.Errors,
105+
fmt.Sprintf("invalid or unsupported spec version: %d", bom.SpecVersion))
106+
return
107+
}
108+
109+
// Warn about older spec versions (< 1.5 doesn't have full ML-BOM support)
110+
if bom.SpecVersion < cdx.SpecVersion1_5 {
111+
result.Warnings = append(result.Warnings,
112+
fmt.Sprintf("spec version 1.%d predates ML-BOM support (consider upgrading to 1.5+)",
113+
bom.SpecVersion-1))
114+
}
115+
}
116+
86117
func validateModelCard(bom *cdx.BOM, result *ValidationResult) {
87118
comp := bom.Metadata.Component
88119
if comp == nil {

internal/validator/validator_test.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ func TestValidate_MissingMetadata(t *testing.T) {
3535

3636
func TestValidate_ValidBOM(t *testing.T) {
3737
bom := &cdx.BOM{
38+
SpecVersion: cdx.SpecVersion1_6,
3839
Metadata: &cdx.Metadata{
3940
Component: &cdx.Component{
4041
Name: "test-model",
@@ -55,6 +56,7 @@ func TestValidate_ValidBOM(t *testing.T) {
5556

5657
func TestValidate_StrictMode(t *testing.T) {
5758
bom := &cdx.BOM{
59+
SpecVersion: cdx.SpecVersion1_6,
5860
Metadata: &cdx.Metadata{
5961
Component: &cdx.Component{
6062
Name: "test-model",
@@ -76,6 +78,7 @@ func TestValidate_StrictMode(t *testing.T) {
7678

7779
func TestValidateModelCard_Missing(t *testing.T) {
7880
bom := &cdx.BOM{
81+
SpecVersion: cdx.SpecVersion1_6,
7982
Metadata: &cdx.Metadata{
8083
Component: &cdx.Component{
8184
Name: "test-model",
@@ -93,3 +96,93 @@ func TestValidateModelCard_Missing(t *testing.T) {
9396
t.Error("expected warnings about missing model card")
9497
}
9598
}
99+
100+
func TestValidateSpecVersion_Missing(t *testing.T) {
101+
bom := &cdx.BOM{
102+
Metadata: &cdx.Metadata{
103+
Component: &cdx.Component{
104+
Name: "test-model",
105+
},
106+
},
107+
}
108+
opts := ValidationOptions{}
109+
result := Validate(bom, opts)
110+
111+
if result.Valid {
112+
t.Error("expected validation to fail for BOM without spec version")
113+
}
114+
115+
foundError := false
116+
for _, err := range result.Errors {
117+
if err == "BOM missing spec version" {
118+
foundError = true
119+
break
120+
}
121+
}
122+
if !foundError {
123+
t.Error("expected error about missing spec version")
124+
}
125+
}
126+
127+
func TestValidateSpecVersion_Invalid(t *testing.T) {
128+
bom := &cdx.BOM{
129+
SpecVersion: 999,
130+
Metadata: &cdx.Metadata{
131+
Component: &cdx.Component{
132+
Name: "test-model",
133+
},
134+
},
135+
}
136+
opts := ValidationOptions{}
137+
result := Validate(bom, opts)
138+
139+
if result.Valid {
140+
t.Error("expected validation to fail for invalid spec version")
141+
}
142+
}
143+
144+
func TestValidateSpecVersion_Valid(t *testing.T) {
145+
bom := &cdx.BOM{
146+
SpecVersion: cdx.SpecVersion1_6,
147+
Metadata: &cdx.Metadata{
148+
Component: &cdx.Component{
149+
Name: "test-model",
150+
},
151+
},
152+
}
153+
opts := ValidationOptions{
154+
CheckModelCard: false,
155+
}
156+
result := Validate(bom, opts)
157+
158+
if !result.Valid {
159+
t.Errorf("expected validation to pass for valid spec version, got errors: %v", result.Errors)
160+
}
161+
}
162+
163+
func TestValidateSpecVersion_OldVersionWarning(t *testing.T) {
164+
bom := &cdx.BOM{
165+
SpecVersion: cdx.SpecVersion1_3,
166+
Metadata: &cdx.Metadata{
167+
Component: &cdx.Component{
168+
Name: "test-model",
169+
},
170+
},
171+
}
172+
opts := ValidationOptions{
173+
CheckModelCard: false,
174+
}
175+
result := Validate(bom, opts)
176+
177+
// Should have warning about old spec version
178+
foundWarning := false
179+
for _, warn := range result.Warnings {
180+
if len(warn) > 0 && (warn[0:4] == "spec" || warn[0:4] == "Spec") {
181+
foundWarning = true
182+
break
183+
}
184+
}
185+
if !foundWarning {
186+
t.Error("expected warning about old spec version")
187+
}
188+
}

0 commit comments

Comments
 (0)