github.com/anchore/syft@v1.38.2/syft/format/cpes/decoder.go (about) 1 package cpes 2 3 import ( 4 "bufio" 5 "errors" 6 "fmt" 7 "io" 8 "strings" 9 10 "github.com/anchore/syft/internal/log" 11 "github.com/anchore/syft/syft/cpe" 12 "github.com/anchore/syft/syft/format/internal" 13 "github.com/anchore/syft/syft/pkg" 14 "github.com/anchore/syft/syft/sbom" 15 ) 16 17 const ID sbom.FormatID = "cpes" 18 const version = "1" 19 20 var _ sbom.FormatDecoder = (*decoder)(nil) 21 22 type decoder struct{} 23 24 func NewFormatDecoder() sbom.FormatDecoder { 25 return decoder{} 26 } 27 28 func (d decoder) Decode(r io.Reader) (*sbom.SBOM, sbom.FormatID, string, error) { 29 if r == nil { 30 return nil, "", "", fmt.Errorf("no reader provided") 31 } 32 s, err := toSyftModel(r) 33 return s, ID, version, err 34 } 35 36 func (d decoder) Identify(r io.Reader) (sbom.FormatID, string) { 37 if r == nil { 38 return "", "" 39 } 40 41 scanner := bufio.NewScanner(r) 42 for scanner.Scan() { 43 line := strings.TrimSpace(scanner.Text()) 44 if line == "" { 45 // skip whitespace only lines 46 continue 47 } 48 49 err := cpe.ValidateString(line) 50 if err != nil { 51 return "", "" 52 } 53 54 return ID, version 55 } 56 57 return "", "" 58 } 59 60 func toSyftModel(r io.Reader) (*sbom.SBOM, error) { 61 var errs []error 62 pkgs := pkg.NewCollection() 63 64 scanner := bufio.NewScanner(r) 65 for scanner.Scan() { 66 line := strings.TrimSpace(scanner.Text()) 67 68 if line == "" { 69 continue 70 } 71 72 // skip invalid CPEs 73 c, err := cpe.New(line, "") 74 if err != nil { 75 log.WithFields("error", err, "line", line).Debug("unable to parse cpe") 76 continue 77 } 78 79 p := pkg.Package{ 80 Name: c.Attributes.Product, 81 Version: c.Attributes.Version, 82 CPEs: []cpe.CPE{c}, 83 } 84 85 internal.Backfill(&p) 86 p.SetID() 87 pkgs.Add(p) 88 } 89 90 return &sbom.SBOM{ 91 Artifacts: sbom.Artifacts{ 92 Packages: pkgs, 93 }, 94 }, errors.Join(errs...) 95 }