github.com/kastenhq/syft@v0.0.0-20230821225854-0710af25cdbe/syft/pkg/cataloger/golang/scan_binary.go (about) 1 package golang 2 3 import ( 4 "debug/buildinfo" 5 "fmt" 6 "io" 7 "runtime/debug" 8 9 version "github.com/kastenhq/goversion/version" 10 "github.com/kastenhq/syft/internal/log" 11 "github.com/kastenhq/syft/syft/pkg/cataloger/internal/unionreader" 12 ) 13 14 type ExtendedBuildInfo struct { 15 *debug.BuildInfo 16 cryptoSettings string 17 } 18 19 // scanFile scans file to try to report the Go and module versions. 20 func scanFile(reader unionreader.UnionReader, filename string) ([]*ExtendedBuildInfo, []string) { 21 // NOTE: multiple readers are returned to cover universal binaries, which are files 22 // with more than one binary 23 readers, err := unionreader.GetReaders(reader) 24 if err != nil { 25 log.WithFields("error", err).Warnf("failed to open a golang binary") 26 return nil, nil 27 } 28 29 var builds []*ExtendedBuildInfo 30 for _, r := range readers { 31 bi, err := getBuildInfo(r) 32 if err != nil { 33 log.WithFields("file", filename, "error", err).Trace("unable to read golang buildinfo") 34 continue 35 } 36 if bi == nil { 37 continue 38 } 39 40 v, err := getCryptoInformation(r) 41 if err != nil { 42 log.WithFields("file", filename, "error", err).Trace("unable to read golang version info") 43 continue 44 } 45 46 builds = append(builds, &ExtendedBuildInfo{bi, v}) 47 } 48 49 archs := getArchs(readers, builds) 50 51 return builds, archs 52 } 53 54 func getCryptoInformation(reader io.ReaderAt) (string, error) { 55 v, err := version.ReadExeFromReader(reader) 56 if err != nil { 57 return "", err 58 } 59 60 cryptoInfo := "" 61 switch { 62 case v.BoringCrypto && v.StandardCrypto: 63 cryptoInfo += "boring AND standard crypto!!!" 64 case v.BoringCrypto: 65 cryptoInfo += "boring crypto" 66 case v.StandardCrypto: 67 cryptoInfo += "standard crypto" 68 } 69 if v.FIPSOnly { 70 cryptoInfo += " +crypto/tls/fipsonly" 71 } 72 return cryptoInfo, nil 73 } 74 75 func getBuildInfo(r io.ReaderAt) (bi *debug.BuildInfo, err error) { 76 defer func() { 77 if r := recover(); r != nil { 78 // this can happen in cases where a malformed binary is passed in can be initially parsed, but not 79 // used without error later down the line. This is the case with : 80 // https://github.com/llvm/llvm-project/blob/llvmorg-15.0.6/llvm/test/Object/Inputs/macho-invalid-dysymtab-bad-size 81 err = fmt.Errorf("recovered from panic: %v", r) 82 } 83 }() 84 bi, err = buildinfo.Read(r) 85 86 // note: the stdlib does not export the error we need to check for 87 if err != nil { 88 if err.Error() == "not a Go executable" { 89 // since the cataloger can only select executables and not distinguish if they are a go-compiled 90 // binary, we should not show warnings/logs in this case. For this reason we nil-out err here. 91 err = nil 92 return 93 } 94 // in this case we could not read the or parse the file, but not explicitly because it is not a 95 // go-compiled binary (though it still might be). 96 return 97 } 98 return 99 }