Skip to content

Commit a9c7e40

Browse files
authored
Merge pull request #14 from shiftleftcyber/feature/AddValidationResult
break: return ValidationResult object when verifying SBOMs
2 parents 7578fac + 9f61265 commit a9c7e40

File tree

4 files changed

+82
-30
lines changed

4 files changed

+82
-30
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ bin/
33
coverage/
44
coverage.out
55
sbom-validator.*.cdx.json
6+
vendor/

README.md

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -60,24 +60,25 @@ func main() {
6060
log.Fatalf("Failed to read SBOM file: %v", err)
6161
}
6262

63-
isValid, validationErrors, err := sbomvalidator.ValidateSBOMData(jsonData)
64-
if err != nil {
65-
log.Fatalf("Error during validation - %v", err)
66-
}
67-
68-
if isValid {
69-
fmt.Println("SBOM is valid")
70-
} else {
71-
fmt.Printf("Validation failed! Showing up to %d errors:\n", 10)
72-
73-
for i, errMsg := range validationErrors {
74-
if i >= 10 {
75-
fmt.Printf("...and %d more errors.\n", len(validationErrors)-10)
76-
break
77-
}
78-
fmt.Printf("- %s\n", errMsg)
79-
}
80-
}
63+
result, err := sbomvalidator.ValidateSBOMData(jsonData)
64+
if err != nil {
65+
log.Fatalf("Error during validation - %v", err)
66+
}
67+
68+
if result.IsValid {
69+
output, _ := json.MarshalIndent(result, "", " ")
70+
fmt.Println(string(output))
71+
} else {
72+
fmt.Printf("Validation failed! Showing up to %d errors:\n", 10)
73+
74+
for i, errMsg := range result.ValidationErrors {
75+
if i >= 10 {
76+
fmt.Printf("...and %d more errors.\n", len(result.ValidationErrors)-10)
77+
break
78+
}
79+
fmt.Printf("- %s\n", errMsg)
80+
}
81+
}
8182
}
8283
```
8384

@@ -103,7 +104,12 @@ make build
103104
./bin/sbom-validator-example -file sample-sboms/sample-1.6.cdx.json
104105
CycloneDX SBOM type detected
105106
CycloneDX version is set to: 1.6
106-
SBOM is valid
107+
{
108+
"isValid": true,
109+
"sbomType": "CycloneDX",
110+
"sbomVersion": "1.6",
111+
"detectedFormat": "JSON"
112+
}
107113
```
108114

109115
## License

example/main.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"encoding/json"
45
"flag"
56
"fmt"
67
"log"
@@ -45,19 +46,20 @@ func main() {
4546
log.Fatalf("Failed to read SBOM file: %v", err)
4647
}
4748

48-
isValid, validationErrors, err := sbomvalidator.ValidateSBOMData(jsonData)
49+
result, err := sbomvalidator.ValidateSBOMData(jsonData)
4950
if err != nil {
5051
log.Fatalf("Error during validation - %v", err)
5152
}
5253

53-
if isValid {
54-
fmt.Println("SBOM is valid")
54+
if result.IsValid {
55+
output, _ := json.MarshalIndent(result, "", " ")
56+
fmt.Println(string(output))
5557
} else {
5658
fmt.Printf("Validation failed! Showing up to %d errors:\n", 10)
5759

58-
for i, errMsg := range validationErrors {
60+
for i, errMsg := range result.ValidationErrors {
5961
if i >= 10 {
60-
fmt.Printf("...and %d more errors.\n", len(validationErrors)-10)
62+
fmt.Printf("...and %d more errors.\n", len(result.ValidationErrors)-10)
6163
break
6264
}
6365
fmt.Printf("- %s\n", errMsg)

validator.go

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,27 @@ const (
1515
SBOM_SPDX = "SPDX"
1616
)
1717

18+
// ValidationResult represents the outcome of validating a Software Bill of Materials (SBOM).
19+
//
20+
// It provides detailed information about the validation process, including:
21+
// - Whether the SBOM is valid (`IsValid`).
22+
// - The detected SBOM type (e.g., CycloneDX, SPDX).
23+
// - The SBOM schema or specification version.
24+
// - A list of any validation errors encountered.
25+
// - The schema file or source used during validation.
26+
// - The detected input format (e.g., JSON, XML, etc.).
27+
//
28+
// This struct is returned by `ValidateSBOMData` and can be serialized to JSON
29+
// for use in CLI tools, APIs, or automated pipelines.
30+
type ValidationResult struct {
31+
IsValid bool `json:"isValid"`
32+
SBOMType string `json:"sbomType,omitempty"`
33+
SBOMVersion string `json:"sbomVersion,omitempty"`
34+
ValidationErrors []string `json:"validationErrors,omitempty"`
35+
SchemaUsed string `json:"schemaUsed,omitempty"`
36+
DetectedFormat string `json:"detectedFormat,omitempty"`
37+
}
38+
1839
// Embed all JSON schema files from the schemas/cyclonedx directory
1940
//
2041
//go:embed schemas/cyclonedx/*.json schemas/spdx/*.json
@@ -61,27 +82,49 @@ var schemaFS embed.FS
6182
// } else {
6283
// fmt.Println("SBOM validation errors:", errors)
6384
// }
64-
func ValidateSBOMData(sbomContent []byte) (bool, []string, error) {
85+
func ValidateSBOMData(sbomContent []byte) (*ValidationResult, error) {
86+
result := &ValidationResult{}
87+
6588
if isJSON(sbomContent) {
89+
result.DetectedFormat = "JSON"
90+
6691
sbomType, err := detectSBOMType(string(sbomContent))
6792
if err != nil {
68-
return false, nil, fmt.Errorf("error detecting SBOM Type %s", err.Error())
93+
return result, fmt.Errorf("error detecting SBOM Type %v", err)
6994
}
95+
result.SBOMType = sbomType
7096

7197
sbomSchemaVersion, err := extractSBOMVersion(string(sbomContent), sbomType)
7298
if err != nil {
73-
return false, nil, fmt.Errorf("failed to extract version: %v", err)
99+
return result, fmt.Errorf("failed to extract SBOM version: %v", err)
74100
}
101+
result.SBOMVersion = sbomSchemaVersion
75102

76103
schema, err := loadSBOMSchema(sbomSchemaVersion, sbomType)
77104
if err != nil {
78-
return false, nil, fmt.Errorf("failed to load schema: %v", err)
105+
return result, fmt.Errorf("failed to load schema: %v", err)
106+
}
107+
108+
isValid, validationErrors, err := validateSBOM(schema, string(sbomContent))
109+
if err != nil {
110+
return result, fmt.Errorf("validation error: %v", err)
111+
}
112+
113+
result.IsValid = isValid
114+
result.ValidationErrors = validationErrors
115+
116+
// for SPDX SBOMs split the type and version (ie: SPDX-2.3)
117+
if strings.HasPrefix(sbomType, SBOM_SPDX) {
118+
result.SBOMType = SBOM_SPDX
119+
result.SBOMVersion, _ = getSPDXVersion(sbomType)
79120
}
80121

81-
return validateSBOM(schema, string(sbomContent))
122+
return result, nil
82123

83124
} else {
84-
return false, nil, fmt.Errorf("unsupported file format")
125+
result.IsValid = false
126+
result.DetectedFormat = "non-JSON"
127+
return result, fmt.Errorf("unsupported file format")
85128
}
86129
}
87130

0 commit comments

Comments
 (0)