github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/syft/pkg/cataloger/python/parse_pipfile_lock.go (about) 1 package python 2 3 import ( 4 "context" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "io" 9 "strings" 10 11 "github.com/anchore/syft/syft/artifact" 12 "github.com/anchore/syft/syft/file" 13 "github.com/anchore/syft/syft/pkg" 14 "github.com/anchore/syft/syft/pkg/cataloger/generic" 15 ) 16 17 type pipfileLock struct { 18 Meta struct { 19 Hash struct { 20 Sha256 string `json:"sha256"` 21 } `json:"hash"` 22 PipfileSpec int `json:"pipfile-spec"` 23 Requires struct { 24 PythonVersion string `json:"python_version"` 25 } `json:"requires"` 26 Sources []struct { 27 Name string `json:"name"` 28 URL string `json:"url"` 29 VerifySsl bool `json:"verify_ssl"` 30 } `json:"sources"` 31 } `json:"_meta"` 32 Default map[string]pipfileLockDependency `json:"default"` 33 Develop map[string]pipfileLockDependency `json:"develop"` 34 } 35 36 type pipfileLockDependency struct { 37 Hashes []string `json:"hashes"` 38 Version string `json:"version"` 39 Index string `json:"index"` 40 } 41 42 var _ generic.Parser = parsePipfileLock 43 44 // parsePipfileLock is a parser function for Pipfile.lock contents, returning "Default" python packages discovered. 45 func parsePipfileLock(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { 46 pkgs := make([]pkg.Package, 0) 47 dec := json.NewDecoder(reader) 48 49 for { 50 var lock pipfileLock 51 if err := dec.Decode(&lock); errors.Is(err, io.EOF) { 52 break 53 } else if err != nil { 54 return nil, nil, fmt.Errorf("failed to parse Pipfile.lock file: %w", err) 55 } 56 sourcesMap := map[string]string{} 57 for _, source := range lock.Meta.Sources { 58 sourcesMap[source.Name] = source.URL 59 } 60 for name, pkgMeta := range lock.Default { 61 var index string 62 if pkgMeta.Index != "" { 63 index = sourcesMap[pkgMeta.Index] 64 } else { 65 // https://pipenv.pypa.io/en/latest/indexes.html 66 index = "https://pypi.org/simple" 67 } 68 version := strings.TrimPrefix(pkgMeta.Version, "==") 69 pkgs = append(pkgs, newPackageForIndexWithMetadata(name, version, pkg.PythonPipfileLockEntry{Index: index, Hashes: pkgMeta.Hashes}, reader.Location)) 70 } 71 } 72 73 pkg.Sort(pkgs) 74 75 return pkgs, nil, nil 76 }