github.com/nextlinux/gosbom@v0.81.1-0.20230627115839-1ff50c281391/gosbom/pkg/cataloger/haskell/parse_stack_lock.go (about) 1 package haskell 2 3 import ( 4 "fmt" 5 "io" 6 "strings" 7 8 "github.com/nextlinux/gosbom/gosbom/artifact" 9 "github.com/nextlinux/gosbom/gosbom/file" 10 "github.com/nextlinux/gosbom/gosbom/pkg" 11 "github.com/nextlinux/gosbom/gosbom/pkg/cataloger/generic" 12 "gopkg.in/yaml.v3" 13 ) 14 15 var _ generic.Parser = parseStackLock 16 17 type stackLock struct { 18 Packages []stackPackage `yaml:"packages"` 19 Snapshots []stackSnapshot `yaml:"snapshots"` 20 } 21 22 type stackPackage struct { 23 Completed completedPackage `yaml:"completed"` 24 } 25 26 type completedPackage struct { 27 Hackage string `yaml:"hackage"` 28 } 29 30 type stackSnapshot struct { 31 Completed completedSnapshot `yaml:"completed"` 32 } 33 34 type completedSnapshot struct { 35 URL string `yaml:"url"` 36 Sha string `yaml:"sha256"` 37 } 38 39 // parseStackLock is a parser function for stack.yaml.lock contents, returning all packages discovered. 40 func parseStackLock(_ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { 41 bytes, err := io.ReadAll(reader) 42 if err != nil { 43 return nil, nil, fmt.Errorf("failed to load stack.yaml.lock file: %w", err) 44 } 45 46 var lockFile stackLock 47 48 if err := yaml.Unmarshal(bytes, &lockFile); err != nil { 49 return nil, nil, fmt.Errorf("failed to parse stack.yaml.lock file: %w", err) 50 } 51 52 var ( 53 pkgs []pkg.Package 54 snapshotURL string 55 ) 56 57 for _, snap := range lockFile.Snapshots { 58 // TODO: handle multiple snapshots (split the metadata struct into more distinct structs and types) 59 snapshotURL = snap.Completed.URL 60 } 61 62 for _, pack := range lockFile.Packages { 63 pkgName, pkgVersion, pkgHash := parseStackPackageEncoding(pack.Completed.Hackage) 64 pkgs = append( 65 pkgs, 66 newPackage( 67 pkgName, 68 pkgVersion, 69 &pkg.HackageMetadata{ 70 PkgHash: pkgHash, 71 SnapshotURL: snapshotURL, 72 }, 73 reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), 74 ), 75 ) 76 } 77 78 return pkgs, nil, nil 79 } 80 func parseStackPackageEncoding(pkgEncoding string) (name, version, hash string) { 81 lastDashIdx := strings.LastIndex(pkgEncoding, "-") 82 name = pkgEncoding[:lastDashIdx] 83 remainingEncoding := pkgEncoding[lastDashIdx+1:] 84 encodingSplits := strings.Split(remainingEncoding, "@") 85 version = encodingSplits[0] 86 startHash, endHash := strings.Index(encodingSplits[1], ":")+1, strings.Index(encodingSplits[1], ",") 87 hash = encodingSplits[1][startHash:endHash] 88 return 89 }