github.com/anchore/syft@v1.38.2/syft/pkg/cataloger/rust/parse_audit_binary.go (about) 1 package rust 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 8 "github.com/rust-secure-code/go-rustaudit" 9 10 "github.com/anchore/syft/internal/log" 11 "github.com/anchore/syft/internal/relationship" 12 "github.com/anchore/syft/syft/artifact" 13 "github.com/anchore/syft/syft/file" 14 "github.com/anchore/syft/syft/internal/unionreader" 15 "github.com/anchore/syft/syft/pkg" 16 "github.com/anchore/syft/syft/pkg/cataloger/generic" 17 ) 18 19 // Catalog identifies executables then attempts to read Rust dependency information from them 20 func parseAuditBinary(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { 21 var pkgs []pkg.Package 22 var relationships []artifact.Relationship 23 24 unionReader, err := unionreader.GetUnionReader(reader.ReadCloser) 25 if err != nil { 26 return nil, nil, err 27 } 28 29 infos, err := parseAuditBinaryEntry(unionReader, reader.RealPath) 30 for _, versionInfo := range infos { 31 auditPkgs, auditRelationships := processAuditVersionInfo(reader.Location, versionInfo) 32 pkgs = append(pkgs, auditPkgs...) 33 relationships = append(relationships, auditRelationships...) 34 } 35 36 return pkgs, relationships, err 37 } 38 39 // scanFile scans file to try to report the Rust crate dependencies 40 func parseAuditBinaryEntry(reader unionreader.UnionReader, filename string) ([]rustaudit.VersionInfo, error) { 41 // NOTE: multiple readers are returned to cover universal binaries, which are files 42 // with more than one binary 43 readers, err := unionreader.GetReaders(reader) 44 if err != nil { 45 log.Debugf("rust cataloger: failed to open a binary: %v", err) 46 return nil, fmt.Errorf("rust cataloger: failed to open a binary: %w", err) 47 } 48 49 var versionInfos []rustaudit.VersionInfo 50 for _, r := range readers { 51 versionInfo, err := rustaudit.GetDependencyInfo(r) 52 53 if err != nil { 54 if errors.Is(err, rustaudit.ErrNoRustDepInfo) { 55 // since the cataloger can only select executables and not distinguish if they are a Rust-compiled 56 // binary, we should not show warnings/logs in this case. 57 return nil, nil 58 } 59 log.Tracef("rust cataloger: unable to read dependency information (file=%q): %v", filename, err) 60 return nil, fmt.Errorf("rust cataloger: unable to read dependency information: %w", err) 61 } 62 63 versionInfos = append(versionInfos, versionInfo) 64 } 65 66 return versionInfos, nil 67 } 68 69 // auditPkgPair is a helper struct to track the original index of the package in the original audit report + the syft package created for it 70 type auditPkgPair struct { 71 pkg *pkg.Package 72 rustPkg rustaudit.Package 73 index int 74 } 75 76 func processAuditVersionInfo(location file.Location, versionInfo rustaudit.VersionInfo) ([]pkg.Package, []artifact.Relationship) { 77 var pkgs []pkg.Package 78 79 // first pass: create packages for all runtime dependencies (skip dev and invalid dependencies) 80 pairsByOgIndex := make(map[int]auditPkgPair) 81 for idx, dep := range versionInfo.Packages { 82 p := newPackageFromAudit(&dep, location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)) 83 pair := auditPkgPair{ 84 rustPkg: dep, 85 index: idx, 86 } 87 if pkg.IsValid(&p) && dep.Kind == rustaudit.Runtime { 88 pkgs = append(pkgs, p) 89 pair.pkg = &pkgs[len(pkgs)-1] 90 } 91 pairsByOgIndex[idx] = pair 92 } 93 94 // second pass: create relationships between any packages created 95 // we have all the original audit package indices + info, but not all audit packages will have syft packages. 96 // we need to be careful to not create relationships for packages that were not created. 97 var rels []artifact.Relationship 98 for _, parentPair := range pairsByOgIndex { 99 // the rust-audit report lists dependencies by index from the original version info object. We need to find 100 // the syft packages created for each listed dependency from that original object. 101 for _, ogPkgIndex := range parentPair.rustPkg.Dependencies { 102 if ogPkgIndex >= uint(len(versionInfo.Packages)) { 103 log.WithFields("pkg", parentPair.pkg).Trace("cargo audit dependency index out of range: %d", ogPkgIndex) 104 continue 105 } 106 depPair, ok := pairsByOgIndex[int(ogPkgIndex)] 107 if !ok { 108 log.WithFields("pkg", parentPair.pkg).Trace("cargo audit dependency not found: %d", ogPkgIndex) 109 continue 110 } 111 112 if depPair.pkg == nil || parentPair.pkg == nil { 113 // skip relationships for syft packages that were not created from the original report (no matter the reason) 114 continue 115 } 116 117 rels = append(rels, artifact.Relationship{ 118 From: *depPair.pkg, 119 To: *parentPair.pkg, 120 Type: artifact.DependencyOfRelationship, 121 }) 122 } 123 } 124 125 relationship.Sort(rels) 126 127 return pkgs, rels 128 }