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