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