Is Trivy go library can generate SBOMs with custom path? #8167
-
QuestionI would like to use trivy to generate the SPDX or CycloneDX SBOM with customize paths. like the following in syft // cfg := options.DefaultCatalog().ToSBOMConfig(clio.Identification{})
sbomCfg = sbomCfg.WithFilesConfig(filecataloging.Config{
Selection: file.AllFilesSelection,
Hashers: []crypto.Hash{
crypto.SHA256,
},
Content: filecontent.Config{
// Include go.mod and go.sum to detect dependencies
Globs: []string{"*.go", "go.mod", "go.sum", "./sbom/*"},
},
})
src, err := syft.GetSource(context.Background(),
dirPath,
syft.DefaultGetSourceConfig())
if err != nil {
fmt.Printf("Error getting source: %v\n", err)
return
}
defer src.Close()
// Generate SBOM using CreateSBOM
sbomResult, err := syft.CreateSBOM(context.Background(),
src,
sbomCfg)
if err != nil {
fmt.Printf("Error creating SBOM: %v\n", err)
return
}
config := spdxjson.DefaultEncoderConfig()
encoder, err := spdxjson.NewFormatEncoderWithConfig(config)
if err != nil {
fmt.Printf("Error creating encoder: %v\n", err)
return
}
// Encode SBOM
var sbomBuffer bytes.Buffer
if err := encoder.Encode(&sbomBuffer, *sbomResult); err != nil {
fmt.Printf("Error encoding SBOM: %v\n", err)
return
} Is it possible to do in Trivy? TargetSBOM ScannerNone Output FormatNone ModeNone Operating SystemLinux Version0.58 |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 8 replies
-
Hello @pohanhuangtw It's not entirely clear what you want. |
Beta Was this translation helpful? Give feedback.
-
Thank @DmitriyLewen for the quick response. Title:Trivy scan fails on Syft-generated SBOM: “unsupported type” — does Trivy have a similar SBOM generation approach? IssuesThank you @DmitriyLewen for the quick response. I’m currently generating an SBOM with Syft and then attempting to scan it using Trivy. However, Trivy fails with the following error: 2024-12-24T14:21:11+08:00 FATAL Fatal error sbom scan error: scan error: scan failed: failed analysis:
SBOM decode error: failed to decode: failed to parse sbom: failed to parse root component:
failed to parse metadata component: failed to unmarshal component type: unsupported type Based on that error, it seems Trivy may not fully support the CycloneDx-JSON output generated by Syft. Below is an example code snippet (using Syft’s spdxjson encoder) that shows how I’m creating the SBOM: func main() {
// Call the function from sbomTrivy package
generateContainerSBOM()
}
func writeSBOMAndScan(sbomFile, trivyOutput string) {
cmd := exec.Command("trivy", "sbom", "-f", "json", "-o", trivyOutput, sbomFile)
if err := cmd.Run(); err != nil {
fmt.Printf("Error running Trivy scan: %v\n", err)
return
}
}
func generateContainerSBOM() {
dirPath := "."
sbomCfg := syft.DefaultCreateSBOMConfig()
sbomCfg = sbomCfg.WithFilesConfig(filecataloging.Config{
Selection: file.AllFilesSelection,
Hashers: []crypto.Hash{
crypto.SHA256,
},
Content: filecontent.Config{
// Include go.mod and go.sum to detect dependencies
Globs: []string{"*.go", "go.mod", "go.sum", "./sbom/*"},
},
})
src, err := syft.GetSource(context.Background(),
dirPath,
syft.DefaultGetSourceConfig())
if err != nil {
fmt.Printf("Error getting source: %v\n", err)
return
}
defer src.Close()
// Generate SBOM using CreateSBOM
sbomResult, err := syft.CreateSBOM(context.Background(),
src,
sbomCfg)
if err != nil {
fmt.Printf("Error creating SBOM: %v\n", err)
return
}
config := spdxjson.DefaultEncoderConfig()
encoder, err := spdxjson.NewFormatEncoderWithConfig(config)
if err != nil {
fmt.Printf("Error creating encoder: %v\n", err)
return
}
// Encode SBOM
var sbomBuffer bytes.Buffer
if err := encoder.Encode(&sbomBuffer, *sbomResult); err != nil {
fmt.Printf("Error encoding SBOM: %v\n", err)
return
}
sbomFile := "sbom-container-onefilee.json"
if err := os.WriteFile(sbomFile, sbomBuffer.Bytes(), 0644); err != nil {
fmt.Printf("Error writing SBOM to file: %v\n", err)
return
}
defer os.RemoveAll(sbomFile)
trivyOutput := filepath.Join("trivy-sbom.json")
writeSBOMAndScan(sbomFile, trivyOutput)
} My question:
Any guidance on how to create Trivy-compatible SBOMs (or pointers to relevant docs) would be greatly appreciated. Thank you! |
Beta Was this translation helpful? Give feedback.
-
Thanks for the help, However I try with the following snip func generateTrivySBOM(filePath string) error {
ctx := context.Background()
flagOpts := flag.Options{
ScanOptions: flag.ScanOptions{
Target: filePath,
},
ReportOptions: flag.ReportOptions{
Format: types.FormatCycloneDX,
Output: "sbom.json",
},
}
runner, err := cmd.NewRunner(ctx, flagOpts)
if err != nil {
return fmt.Errorf("failed to create runner: %w", err)
}
report, err := runner.ScanFilesystem(ctx, flagOpts)
if err != nil {
return fmt.Errorf("failed to scan filesystem: %w", err)
}
err = runner.Report(ctx, flagOpts, report)
if err != nil {
return fmt.Errorf("failed to report: %w", err)
}
return nil
}
func main() {
// these just to introduce net lib
A := "https://github.com/aquasecurity/trivy"
B := "go.mod"
url.JoinPath(A, B)
err := generateTrivySBOM(".")
if err != nil {
fmt.Println(err)
}
} the result is quite different from
Do you know where did I set up wrong? sbom.json generate by go lib
{
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"serialNumber": "urn:uuid:6b471e42-59ed-47af-bbbc-5d7d93b1ba3d",
"version": 1,
"metadata": {
"timestamp": "2024-12-24T08:11:25+00:00",
"tools": {
"components": [
{
"type": "application",
"group": "aquasecurity",
"name": "trivy"
}
]
},
"component": {
"bom-ref": "195a47d4-b4cf-43e0-802b-00766983d58c",
"type": "application",
"name": ".",
"properties": [
{
"name": "aquasecurity:trivy:SchemaVersion",
"value": "2"
}
]
}
},
"components": [],
"dependencies": [
{
"ref": "195a47d4-b4cf-43e0-802b-00766983d58c",
"dependsOn": []
}
],
"vulnerabilities": []
}
result.json generate by binary
{
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"serialNumber": "urn:uuid:377aab5b-401c-4ab5-9594-65de04e2b68b",
"version": 1,
"metadata": {
"timestamp": "2024-12-24T08:13:12+00:00",
"tools": {
"components": [
{
"type": "application",
"group": "aquasecurity",
"name": "trivy",
"version": "0.54.1"
}
]
},
"component": {
"bom-ref": "7e1dfb7b-0c78-4fdf-ae40-e81320bbd4ec",
"type": "application",
"name": ".",
"properties": [
{
"name": "aquasecurity:trivy:SchemaVersion",
"value": "2"
}
]
}
},
"components": [
{
"bom-ref": "57d81fab-6b77-4d9a-b14c-2a4616cd4840",
"type": "application",
"name": "go.mod",
"properties": [
{
"name": "aquasecurity:trivy:Class",
"value": "lang-pkgs"
},
{
"name": "aquasecurity:trivy:Type",
"value": "gomod"
}
]
},
{
"bom-ref": "pkg:golang/cloud.google.com/go/compute/metadata@0.5.0",
"type": "library",
"name": "cloud.google.com/go/compute/metadata",
"version": "0.5.0",
"licenses": [
{
"license": {
"name": "Apache-2.0"
}
}
],
"purl": "pkg:golang/cloud.google.com/go/compute/metadata@0.5.0",
"properties": [
{
"name": "aquasecurity:trivy:PkgID",
"value": "cloud.google.com/go/compute/metadata@v0.5.0"
},
{
"name": "aquasecurity:trivy:PkgType",
"value": "gomod"
}
]
},
....
} |
Beta Was this translation helpful? Give feedback.
-
Thanks @DmitriyLewen , just found out the root cause. For those don't want to run the binary, Here is my example code package main
import (
"context"
"fmt"
"os"
"github.com/aquasecurity/trivy/pkg/commands"
cmd "github.com/aquasecurity/trivy/pkg/commands/artifact"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/flag"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/spf13/viper"
)
// able to run trivy with args without binary
func runTrivy(osArgs []string) {
defer viper.Reset()
ctx := context.Background()
app := commands.NewApp()
app.SetArgs(osArgs)
// app.SetOut(io.Discard)
// Run Trivy
app.ExecuteContext(ctx)
}
// With FormatCycloneDX
func generateTrivySBOM(filePath string) error {
ctx := context.Background()
flagOpts := flag.Options{
ScanOptions: flag.ScanOptions{
Target: filePath,
Scanners: types.Scanners{
types.SBOMScanner,
},
},
ReportOptions: flag.ReportOptions{
Format: types.FormatCycloneDX,
Output: "sbom.json",
},
PackageOptions: flag.PackageOptions{
PkgRelationships: ftypes.Relationships,
PkgTypes: []string{
types.PkgTypeOS,
types.PkgTypeLibrary,
},
},
}
runner, err := cmd.NewRunner(ctx, flagOpts)
if err != nil {
return fmt.Errorf("failed to create runner: %w", err)
}
report, err := runner.ScanFilesystem(ctx, flagOpts)
if err != nil {
return fmt.Errorf("failed to scan filesystem: %w", err)
}
err = runner.Report(ctx, flagOpts, report)
if err != nil {
return fmt.Errorf("failed to report: %w", err)
}
return nil
}
func main() {
err := generateTrivySBOM(".")
if err != nil {
fmt.Println(err)
}
runTrivy([]string{"sbom", "sbom.json"})
} |
Beta Was this translation helpful? Give feedback.
Thanks @DmitriyLewen , just found out the root cause.
For those don't want to run the binary, Here is my example code