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