github.com/anchore/syft@v1.38.2/syft/pkg/cataloger/rust/parse_cargo_lock.go (about) 1 package rust 2 3 import ( 4 "context" 5 "fmt" 6 "strings" 7 8 "github.com/pelletier/go-toml" 9 10 "github.com/anchore/syft/internal/log" 11 "github.com/anchore/syft/internal/unknown" 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 "github.com/anchore/syft/syft/pkg/cataloger/internal/dependency" 17 ) 18 19 var _ generic.Parser = parseCargoLock 20 21 type cargoLockFile struct { 22 Packages []pkg.RustCargoLockEntry `toml:"package"` 23 } 24 25 // parseCargoLock is a parser function for Cargo.lock contents, returning all rust cargo crates discovered. 26 func parseCargoLock(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { 27 tree, err := toml.LoadReader(reader) 28 if err != nil { 29 return nil, nil, fmt.Errorf("unable to load Cargo.lock for parsing: %w", err) 30 } 31 32 m := cargoLockFile{} 33 err = tree.Unmarshal(&m) 34 if err != nil { 35 return nil, nil, fmt.Errorf("unable to parse Cargo.lock: %w", err) 36 } 37 38 var pkgs []pkg.Package 39 40 for _, p := range m.Packages { 41 if p.Dependencies == nil { 42 p.Dependencies = make([]string, 0) 43 } 44 newPkg := newPackageFromCargoMetadata( 45 p, 46 reader.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), 47 ) 48 pkgs = append( 49 pkgs, 50 newPkg, 51 ) 52 } 53 54 return pkgs, dependency.Resolve(dependencySpecification, pkgs), unknown.IfEmptyf(pkgs, "unable to determine packages") 55 } 56 57 func dependencySpecification(p pkg.Package) dependency.Specification { 58 rustMeta, ok := p.Metadata.(pkg.RustCargoLockEntry) 59 if !ok { 60 log.Tracef("cataloger failed to extract rust Cargo.lock metadata for package %+v", p.Name) 61 return dependency.Specification{} 62 } 63 64 // Cargo.lock dependencies are strings that are the name of a package, if that 65 // is unambiguous, or a string like "name version" if the name alone is not 66 // ambiguous, or strings like "name version (source)" if "name version" is ambiguous. 67 // Provide all the strings, since we don't know which string will be used. 68 // In other words, each package "provides" 3 entries, one for each name format, 69 // and each package "requires" whatever it actually requires based on the Cargo.lock. 70 provides := []string{ 71 p.Name, 72 fmt.Sprintf("%s %s", p.Name, p.Version), 73 } 74 75 if rustMeta.Source != "" { 76 src := rustMeta.Source 77 if strings.HasPrefix(src, "git") && strings.Contains(src, "#") { 78 src = strings.Split(src, "#")[0] 79 } 80 81 provides = append(provides, fmt.Sprintf("%s %s (%s)", p.Name, p.Version, src)) 82 } 83 84 return dependency.Specification{ 85 ProvidesRequires: dependency.ProvidesRequires{ 86 Provides: provides, 87 Requires: rustMeta.Dependencies, 88 }, 89 } 90 }