diff --git a/go.mod b/go.mod index 101e64916..8a1b11f1a 100644 --- a/go.mod +++ b/go.mod @@ -18,18 +18,18 @@ require ( github.com/dustin/go-humanize v1.0.0 github.com/facebookincubator/nvdtools v0.1.4 github.com/go-test/deep v1.0.7 - github.com/google/uuid v1.1.1 + github.com/google/uuid v1.2.0 github.com/gookit/color v1.2.7 github.com/hashicorp/go-multierror v1.1.0 github.com/hashicorp/go-version v1.2.0 github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/mapstructure v1.3.1 github.com/olekukonko/tablewriter v0.0.4 - github.com/pelletier/go-toml v1.8.0 + github.com/pelletier/go-toml v1.8.1 github.com/pkg/profile v1.5.0 github.com/scylladb/go-set v1.0.2 github.com/sergi/go-diff v1.1.0 - github.com/sirupsen/logrus v1.6.0 + github.com/sirupsen/logrus v1.8.1 github.com/spdx/tools-golang v0.1.0 github.com/spf13/afero v1.2.2 github.com/spf13/cobra v1.0.1-0.20200909172742-8a63648dd905 @@ -42,8 +42,8 @@ require ( github.com/wagoodman/jotframe v0.0.0-20200730190914-3517092dd163 github.com/x-cray/logrus-prefixed-formatter v0.5.2 github.com/xeipuuv/gojsonschema v1.2.0 - golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 golang.org/x/mod v0.3.0 golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d - gopkg.in/yaml.v2 v2.3.0 + golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 + gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 6a192fdda..88aeb92a0 100644 --- a/go.sum +++ b/go.sum @@ -73,7 +73,6 @@ github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQ github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI= github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Djarvur/go-err113 v0.0.0-20200410182137-af658d038157/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= @@ -374,8 +373,9 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 github.com/google/rpmpack v0.0.0-20191226140753-aa36bfddb3a0/go.mod h1:RaTPr0KUf2K7fnZYLNDrr8rxAamWs3iNywJLtQ2AzBg= github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/wire v0.3.0/go.mod h1:i1DMg/Lu8Sz5yYl25iOdmc5CT5qusaa+zmRWs16741s= github.com/google/wire v0.4.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU= github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= @@ -479,7 +479,6 @@ github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM52 github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -595,8 +594,9 @@ github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.8.0 h1:Keo9qb7iRJs2voHvunFtuuYFsbWeOBh8/P9v/kVMFtw= github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs= +github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM= +github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -660,8 +660,9 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.0.0 h1:UVQPSSmc3qtTi+zPPkCXvZX9VvW/xT/NsRvKfwY81a8= github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= @@ -800,9 +801,8 @@ golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -944,6 +944,7 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1138,8 +1139,9 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= diff --git a/internal/ui/select.go b/internal/ui/select.go index 863f1d30d..39e036c3b 100644 --- a/internal/ui/select.go +++ b/internal/ui/select.go @@ -5,7 +5,7 @@ import ( "os" "runtime" - "golang.org/x/crypto/ssh/terminal" + "golang.org/x/term" ) // TODO: build tags to exclude options from windows @@ -16,8 +16,8 @@ import ( // are environmental problems (e.g. cannot write to the terminal). A writer is provided to capture the output of // the final SBOM report. func Select(verbose, quiet bool, reportWriter io.Writer) (uis []UI) { - isStdoutATty := terminal.IsTerminal(int(os.Stdout.Fd())) - isStderrATty := terminal.IsTerminal(int(os.Stderr.Fd())) + isStdoutATty := term.IsTerminal(int(os.Stdout.Fd())) + isStderrATty := term.IsTerminal(int(os.Stderr.Fd())) notATerminal := !isStderrATty && !isStdoutATty switch { diff --git a/syft/pkg/cataloger/cataloger.go b/syft/pkg/cataloger/cataloger.go index 29eb9b933..de2e1a2ce 100644 --- a/syft/pkg/cataloger/cataloger.go +++ b/syft/pkg/cataloger/cataloger.go @@ -39,7 +39,8 @@ func ImageCatalogers() []Cataloger { rpmdb.NewRpmdbCataloger(), java.NewJavaCataloger(), apkdb.NewApkdbCataloger(), - golang.NewGoModCataloger(), + golang.NewGoModuleBinaryCataloger(), + golang.NewGoModFileCataloger(), rust.NewCargoLockCataloger(), } } @@ -55,7 +56,8 @@ func DirectoryCatalogers() []Cataloger { rpmdb.NewRpmdbCataloger(), java.NewJavaCataloger(), apkdb.NewApkdbCataloger(), - golang.NewGoModCataloger(), + golang.NewGoModFileCataloger(), + golang.NewGoModuleBinaryCataloger(), rust.NewCargoLockCataloger(), } } diff --git a/syft/pkg/cataloger/golang/bin_cataloger.go b/syft/pkg/cataloger/golang/bin_cataloger.go new file mode 100644 index 000000000..b92f2360d --- /dev/null +++ b/syft/pkg/cataloger/golang/bin_cataloger.go @@ -0,0 +1,61 @@ +/* +Package golang provides a concrete Cataloger implementation for go.mod files. +*/ +package golang + +import ( + "fmt" + + "github.com/anchore/syft/internal/log" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" +) + +const catalogerName = "go-module-binary-cataloger" + +// current mime types to search by to discover go binaries +var mimeTypes = []string{ + "application/x-executable", + "application/x-mach-binary", + "application/x-elf", + "application/x-sharedlib", + "application/vnd.microsoft.portable-executable", +} + +type Cataloger struct{} + +// NewGoModuleBinaryCataloger returns a new Golang cataloger object. +func NewGoModuleBinaryCataloger() *Cataloger { + return &Cataloger{} +} + +// Name returns a string that uniquely describes a cataloger +func (c *Cataloger) Name() string { + return catalogerName +} + +// Catalog is given an object to resolve file references and content, this function returns any discovered Packages after analyzing rpm db installation. +func (c *Cataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, error) { + pkgs := make([]pkg.Package, 0) + fileMatches, err := resolver.FilesByMIMEType(mimeTypes...) + if err != nil { + return pkgs, fmt.Errorf("failed to find bin by mime types: %w", err) + } + + for _, location := range fileMatches { + r, err := resolver.FileContentsByLocation(location) + if err != nil { + return pkgs, fmt.Errorf("failed to resolve file contents by location: %w", err) + } + + goPkgs, err := parseGoBin(location.RealPath, r) + if err != nil { + log.Infof("could not parse go bin for: %w", err) + } + + r.Close() + pkgs = append(pkgs, goPkgs...) + } + + return pkgs, nil +} diff --git a/syft/pkg/cataloger/golang/exe.go b/syft/pkg/cataloger/golang/exe.go new file mode 100644 index 000000000..b4b8bba5a --- /dev/null +++ b/syft/pkg/cataloger/golang/exe.go @@ -0,0 +1,271 @@ +// This code was copied from the Go std library. +// https://github.com/golang/go/blob/master/src/cmd/go/internal/version/exe.go +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//nolint +package golang + +import ( + "bytes" + "debug/elf" + "debug/macho" + "debug/pe" + "fmt" + "io" +) + +// An exe is a generic interface to an OS executable (ELF, Mach-O, PE, XCOFF). +type exe interface { + // Close closes the underlying file. + Close() error + + // ReadData reads and returns up to size byte starting at virtual address addr. + ReadData(addr, size uint64) ([]byte, error) + + // DataStart returns the writable data segment start address. + DataStart() uint64 +} + +// openExe opens file and returns it as an exe. +// we changed this signature from accpeting a string +// to a ReadCloser so we could adapt the code to the +// stereoscope api. We removed the file open methods. +func openExe(file io.ReadCloser) (exe, error) { + /* + f, err := os.Open(file) + if err != nil { + return nil, err + } + data := make([]byte, 16) + if _, err := io.ReadFull(f, data); err != nil { + return nil, err + } + f.Seek(0, 0) + */ + data, err := io.ReadAll(file) + if err != nil { + return nil, err + } + + r := bytes.NewReader(data) + f := io.NewSectionReader(r, 0, int64(len(data))) + if bytes.HasPrefix(data, []byte("\x7FELF")) { + e, err := elf.NewFile(f) + if err != nil { + return nil, err + } + + return &elfExe{file, e}, nil + } + + if bytes.HasPrefix(data, []byte("MZ")) { + e, err := pe.NewFile(f) + if err != nil { + return nil, err + } + return &peExe{file, e}, nil + } + + if bytes.HasPrefix(data, []byte("\xFE\xED\xFA")) || bytes.HasPrefix(data[1:], []byte("\xFA\xED\xFE")) { + e, err := macho.NewFile(f) + if err != nil { + return nil, err + } + return &machoExe{file, e}, nil + } + + return nil, fmt.Errorf("unrecognized executable format") +} + +// elfExe is the ELF implementation of the exe interface. +// updated os to be io.ReadCloser to interopt with stereoscope +type elfExe struct { + os io.ReadCloser + f *elf.File +} + +func (x *elfExe) Close() error { + return x.os.Close() +} + +func (x *elfExe) ReadData(addr, size uint64) ([]byte, error) { + for _, prog := range x.f.Progs { + if prog.Vaddr <= addr && addr <= prog.Vaddr+prog.Filesz-1 { + n := prog.Vaddr + prog.Filesz - addr + if n > size { + n = size + } + data := make([]byte, n) + _, err := prog.ReadAt(data, int64(addr-prog.Vaddr)) + if err != nil { + return nil, err + } + return data, nil + } + } + return nil, fmt.Errorf("address not mapped") +} + +func (x *elfExe) DataStart() uint64 { + for _, s := range x.f.Sections { + if s.Name == ".go.buildinfo" { + return s.Addr + } + } + for _, p := range x.f.Progs { + if p.Type == elf.PT_LOAD && p.Flags&(elf.PF_X|elf.PF_W) == elf.PF_W { + return p.Vaddr + } + } + return 0 +} + +// peExe is the PE (Windows Portable Executable) implementation of the exe interface. +type peExe struct { + os io.ReadCloser + f *pe.File +} + +func (x *peExe) Close() error { + return x.os.Close() +} + +func (x *peExe) imageBase() uint64 { + switch oh := x.f.OptionalHeader.(type) { + case *pe.OptionalHeader32: + return uint64(oh.ImageBase) + case *pe.OptionalHeader64: + return oh.ImageBase + } + return 0 +} + +func (x *peExe) ReadData(addr, size uint64) ([]byte, error) { + addr -= x.imageBase() + for _, sect := range x.f.Sections { + if uint64(sect.VirtualAddress) <= addr && addr <= uint64(sect.VirtualAddress+sect.Size-1) { + n := uint64(sect.VirtualAddress+sect.Size) - addr + if n > size { + n = size + } + data := make([]byte, n) + _, err := sect.ReadAt(data, int64(addr-uint64(sect.VirtualAddress))) + if err != nil { + return nil, err + } + return data, nil + } + } + return nil, fmt.Errorf("address not mapped") +} + +func (x *peExe) DataStart() uint64 { + // Assume data is first writable section. + const ( + IMAGE_SCN_CNT_CODE = 0x00000020 + IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 + IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080 + IMAGE_SCN_MEM_EXECUTE = 0x20000000 + IMAGE_SCN_MEM_READ = 0x40000000 + IMAGE_SCN_MEM_WRITE = 0x80000000 + IMAGE_SCN_MEM_DISCARDABLE = 0x2000000 + IMAGE_SCN_LNK_NRELOC_OVFL = 0x1000000 + IMAGE_SCN_ALIGN_32BYTES = 0x600000 + ) + for _, sect := range x.f.Sections { + if sect.VirtualAddress != 0 && sect.Size != 0 && + sect.Characteristics&^IMAGE_SCN_ALIGN_32BYTES == IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE { + return uint64(sect.VirtualAddress) + x.imageBase() + } + } + return 0 +} + +// machoExe is the Mach-O (Apple macOS/iOS) implementation of the exe interface. +type machoExe struct { + os io.ReadCloser + f *macho.File +} + +func (x *machoExe) Close() error { + return x.os.Close() +} + +func (x *machoExe) ReadData(addr, size uint64) ([]byte, error) { + for _, load := range x.f.Loads { + seg, ok := load.(*macho.Segment) + if !ok { + continue + } + if seg.Addr <= addr && addr <= seg.Addr+seg.Filesz-1 { + if seg.Name == "__PAGEZERO" { + continue + } + n := seg.Addr + seg.Filesz - addr + if n > size { + n = size + } + data := make([]byte, n) + _, err := seg.ReadAt(data, int64(addr-seg.Addr)) + if err != nil { + return nil, err + } + return data, nil + } + } + return nil, fmt.Errorf("address not mapped") +} + +func (x *machoExe) DataStart() uint64 { + // Look for section named "__go_buildinfo". + for _, sec := range x.f.Sections { + if sec.Name == "__go_buildinfo" { + return sec.Addr + } + } + // Try the first non-empty writable segment. + const RW = 3 + for _, load := range x.f.Loads { + seg, ok := load.(*macho.Segment) + if ok && seg.Addr != 0 && seg.Filesz != 0 && seg.Prot == RW && seg.Maxprot == RW { + return seg.Addr + } + } + return 0 +} + +/* +// xcoffExe is the XCOFF (AIX eXtended COFF) implementation of the exe interface. +type xcoffExe struct { + os *os.File + f *xcoff.File +} + +func (x *xcoffExe) Close() error { + return x.os.Close() +} + +func (x *xcoffExe) ReadData(addr, size uint64) ([]byte, error) { + for _, sect := range x.f.Sections { + if uint64(sect.VirtualAddress) <= addr && addr <= uint64(sect.VirtualAddress+sect.Size-1) { + n := uint64(sect.VirtualAddress+sect.Size) - addr + if n > size { + n = size + } + data := make([]byte, n) + _, err := sect.ReadAt(data, int64(addr-uint64(sect.VirtualAddress))) + if err != nil { + return nil, err + } + return data, nil + } + } + return nil, fmt.Errorf("address not mapped") +} + +func (x *xcoffExe) DataStart() uint64 { + return x.f.SectionByType(xcoff.STYP_DATA).VirtualAddress +} +*/ diff --git a/syft/pkg/cataloger/golang/cataloger.go b/syft/pkg/cataloger/golang/mod_cataloger.go similarity index 54% rename from syft/pkg/cataloger/golang/cataloger.go rename to syft/pkg/cataloger/golang/mod_cataloger.go index 34f33a595..50ce25923 100644 --- a/syft/pkg/cataloger/golang/cataloger.go +++ b/syft/pkg/cataloger/golang/mod_cataloger.go @@ -7,11 +7,11 @@ import ( "github.com/anchore/syft/syft/pkg/cataloger/common" ) -// NewGoModCataloger returns a new Go module cataloger object. -func NewGoModCataloger() *common.GenericCataloger { +// NewGoModFileCataloger returns a new Go module cataloger object. +func NewGoModFileCataloger() *common.GenericCataloger { globParsers := map[string]common.ParserFn{ "**/go.mod": parseGoMod, } - return common.NewGenericCataloger(nil, globParsers, "go-cataloger") + return common.NewGenericCataloger(nil, globParsers, "go-mod-file-cataloger") } diff --git a/syft/pkg/cataloger/golang/parse_go_bin.go b/syft/pkg/cataloger/golang/parse_go_bin.go new file mode 100644 index 000000000..e8176e2a4 --- /dev/null +++ b/syft/pkg/cataloger/golang/parse_go_bin.go @@ -0,0 +1,63 @@ +package golang + +import ( + "bufio" + "io" + "strings" + + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" +) + +const ( + packageIdentifier = "dep" + replaceIdentifier = "=>" +) + +func parseGoBin(path string, reader io.ReadCloser) ([]pkg.Package, error) { + // Identify if bin was compiled by go + x, err := openExe(reader) + if err != nil { + return nil, err + } + + _, mod := findVers(x) + + pkgs := buildGoPkgInfo(path, mod) + + return pkgs, nil +} + +func buildGoPkgInfo(path, mod string) []pkg.Package { + pkgsSlice := make([]pkg.Package, 0) + scanner := bufio.NewScanner(strings.NewReader(mod)) + + // filter mod dependencies: [dep, name, version, sha] + for scanner.Scan() { + fields := strings.Fields(scanner.Text()) + // must have dep, name, version + if len(fields) < 3 { + continue + } + switch fields[0] { + case packageIdentifier: + pkgsSlice = append(pkgsSlice, pkg.Package{ + Name: fields[1], + Version: fields[2], + Language: pkg.Go, + Type: pkg.GoModulePkg, + Locations: []source.Location{ + { + RealPath: path, + }, + }, + }) + case replaceIdentifier: + pkg := &pkgsSlice[len(pkgsSlice)-1] + pkg.Name = fields[1] + pkg.Version = fields[2] + } + } + + return pkgsSlice +} diff --git a/syft/pkg/cataloger/golang/parse_go_bin_test.go b/syft/pkg/cataloger/golang/parse_go_bin_test.go new file mode 100644 index 000000000..45665a404 --- /dev/null +++ b/syft/pkg/cataloger/golang/parse_go_bin_test.go @@ -0,0 +1,95 @@ +package golang + +import ( + "testing" + + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" + "github.com/stretchr/testify/assert" +) + +func TestBuildGoPkgInfo(t *testing.T) { + tests := []struct { + name string + mod string + expected []pkg.Package + }{ + { + name: "buildGoPkgInfo parses a blank mod string and returns no packages", + mod: "", + expected: make([]pkg.Package, 0), + }, + { + name: "buildGoPkgInfo parses a populated mod string and returns packages but no source info", + mod: `path github.com/anchore/syft mod github.com/anchore/syft (devel) + dep github.com/adrg/xdg v0.2.1 h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic= + dep github.com/anchore/client-go v0.0.0-20210222170800-9c70f9b80bcf h1:DYssiUV1pBmKqzKsm4mqXx8artqC0Q8HgZsVI3lMsAg=`, + expected: []pkg.Package{ + { + Name: "github.com/adrg/xdg", + Version: "v0.2.1", + Language: pkg.Go, + Type: pkg.GoModulePkg, + Locations: []source.Location{ + {}, + }, + }, + { + Name: "github.com/anchore/client-go", + Version: "v0.0.0-20210222170800-9c70f9b80bcf", + Language: pkg.Go, + Type: pkg.GoModulePkg, + Locations: []source.Location{ + {}, + }, + }, + }, + }, + { + name: "buildGoPkgInfo parses a populated mod string and returns packages when a replace directive exists", + mod: `path github.com/anchore/test + mod github.com/anchore/test (devel) + dep golang.org/x/net v0.0.0-20211006190231-62292e806868 h1:KlOXYy8wQWTUJYFgkUI40Lzr06ofg5IRXUK5C7qZt1k= + dep golang.org/x/sys v0.0.0-20211006194710-c8a6f5223071 h1:PjhxBct4MZii8FFR8+oeS7QOvxKOTZXgk63EU2XpfJE= + dep golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 + => golang.org/x/term v0.0.0-20210916214954-140adaaadfaf h1:Ihq/mm/suC88gF8WFcVwk+OV6Tq+wyA1O0E5UEvDglI=`, + expected: []pkg.Package{ + { + Name: "golang.org/x/net", + Version: "v0.0.0-20211006190231-62292e806868", + Language: pkg.Go, + Type: pkg.GoModulePkg, + Locations: []source.Location{ + {}, + }, + }, + { + Name: "golang.org/x/sys", + Version: "v0.0.0-20211006194710-c8a6f5223071", + Language: pkg.Go, + Type: pkg.GoModulePkg, + Locations: []source.Location{ + {}, + }, + }, + { + Name: "golang.org/x/term", + Version: "v0.0.0-20210916214954-140adaaadfaf", + Language: pkg.Go, + Type: pkg.GoModulePkg, + Locations: []source.Location{ + {}, + }, + }, + }, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + pkgs := buildGoPkgInfo("", tt.mod) + assert.Equal(t, tt.expected, pkgs) + }) + } +} diff --git a/syft/pkg/cataloger/golang/version.go b/syft/pkg/cataloger/golang/version.go new file mode 100644 index 000000000..6306a2da9 --- /dev/null +++ b/syft/pkg/cataloger/golang/version.go @@ -0,0 +1,226 @@ +// This code was copied from the Go std library. +// https://github.com/golang/go/blob/master/src/cmd/go/internal/version/version.go +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package version implements the ``go version'' command. +package golang + +import ( + "bytes" + "encoding/binary" + // "cmd/go/internal/base" +) + +/* +var CmdVersion = &base.Command{ + UsageLine: "go version [-m] [-v] [file ...]", + Short: "print Go version", + Long: `Version prints the build information for Go executables. + +Go version reports the Go version used to build each of the named +executable files. + +If no files are named on the command line, go version prints its own +version information. + +If a directory is named, go version walks that directory, recursively, +looking for recognized Go binaries and reporting their versions. +By default, go version does not report unrecognized files found +during a directory scan. The -v flag causes it to report unrecognized files. + +The -m flag causes go version to print each executable's embedded +module version information, when available. In the output, the module +information consists of multiple lines following the version line, each +indented by a leading tab character. + +See also: go doc runtime/debug.BuildInfo. +`, +} + +func init() { + CmdVersion.Run = runVersion // break init cycle +} + +var ( + versionM = CmdVersion.Flag.Bool("m", false, "") + versionV = CmdVersion.Flag.Bool("v", false, "") +) + +func runVersion(ctx context.Context, cmd *base.Command, args []string) { + if len(args) == 0 { + // If any of this command's flags were passed explicitly, error + // out, because they only make sense with arguments. + // + // Don't error if the flags came from GOFLAGS, since that can be + // a reasonable use case. For example, imagine GOFLAGS=-v to + // turn "verbose mode" on for all Go commands, which should not + // break "go version". + var argOnlyFlag string + if !base.InGOFLAGS("-m") && *versionM { + argOnlyFlag = "-m" + } else if !base.InGOFLAGS("-v") && *versionV { + argOnlyFlag = "-v" + } + if argOnlyFlag != "" { + fmt.Fprintf(os.Stderr, "go: 'go version' only accepts %s flag with arguments\n", argOnlyFlag) + base.SetExitStatus(2) + return + } + fmt.Printf("go version %s %s/%s\n", runtime.Version(), runtime.GOOS, runtime.GOARCH) + return + } + + for _, arg := range args { + info, err := os.Stat(arg) + if err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + base.SetExitStatus(1) + continue + } + if info.IsDir() { + scanDir(arg) + } else { + scanFile(arg, info, true) + } + } +} + +// scanDir scans a directory for executables to run scanFile on. +func scanDir(dir string) { + filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { + if d.Type().IsRegular() || d.Type()&fs.ModeSymlink != 0 { + info, err := d.Info() + if err != nil { + if *versionV { + fmt.Fprintf(os.Stderr, "%s: %v\n", path, err) + } + return nil + } + scanFile(path, info, *versionV) + } + return nil + }) +} + +// isExe reports whether the file should be considered executable. +func isExe(file string, info fs.FileInfo) bool { + if runtime.GOOS == "windows" { + return strings.HasSuffix(strings.ToLower(file), ".exe") + } + return info.Mode().IsRegular() && info.Mode()&0111 != 0 +} + +// scanFile scans file to try to report the Go and module versions. +// If mustPrint is true, scanFile will report any error reading file. +// Otherwise (mustPrint is false, because scanFile is being called +// by scanDir) scanFile prints nothing for non-Go executables. +func scanFile(file string, info fs.FileInfo, mustPrint bool) { + if info.Mode()&fs.ModeSymlink != 0 { + // Accept file symlinks only. + i, err := os.Stat(file) + if err != nil || !i.Mode().IsRegular() { + if mustPrint { + fmt.Fprintf(os.Stderr, "%s: symlink\n", file) + } + return + } + info = i + } + + if !isExe(file, info) { + if mustPrint { + fmt.Fprintf(os.Stderr, "%s: not executable file\n", file) + } + return + } + + x, err := openExe(file) + if err != nil { + if mustPrint { + fmt.Fprintf(os.Stderr, "%s: %v\n", file, err) + } + return + } + defer x.Close() + + vers, mod := findVers(x) + if vers == "" { + if mustPrint { + fmt.Fprintf(os.Stderr, "%s: go version not found\n", file) + } + return + } + + fmt.Printf("%s: %s\n", file, vers) + if *versionM && mod != "" { + fmt.Printf("\t%s\n", strings.ReplaceAll(mod[:len(mod)-1], "\n", "\n\t")) + } +} +*/ + +// The build info blob left by the linker is identified by +// a 16-byte header, consisting of buildInfoMagic (14 bytes), +// the binary's pointer size (1 byte), +// and whether the binary is big endian (1 byte). +var buildInfoMagic = []byte("\xff Go buildinf:") + +// findVers finds and returns the Go version and module version information +// in the executable x. +func findVers(x exe) (vers, mod string) { + // Read the first 64kB of text to find the build info blob. + text := x.DataStart() + data, err := x.ReadData(text, 64*1024) + if err != nil { + return + } + for ; !bytes.HasPrefix(data, buildInfoMagic); data = data[32:] { + if len(data) < 32 { + return + } + } + + // Decode the blob. + ptrSize := int(data[14]) + bigEndian := data[15] != 0 + var bo binary.ByteOrder + if bigEndian { + bo = binary.BigEndian + } else { + bo = binary.LittleEndian + } + var readPtr func([]byte) uint64 + if ptrSize == 4 { + readPtr = func(b []byte) uint64 { return uint64(bo.Uint32(b)) } + } else { + readPtr = bo.Uint64 + } + vers = readString(x, ptrSize, readPtr, readPtr(data[16:])) + if vers == "" { + return + } + mod = readString(x, ptrSize, readPtr, readPtr(data[16+ptrSize:])) + if len(mod) >= 33 && mod[len(mod)-17] == '\n' { + // Strip module framing. + mod = mod[16 : len(mod)-16] + } else { + mod = "" + } + return vers, mod +} + +// readString returns the string at address addr in the executable x. +func readString(x exe, ptrSize int, readPtr func([]byte) uint64, addr uint64) string { + hdr, err := x.ReadData(addr, uint64(2*ptrSize)) + if err != nil || len(hdr) < 2*ptrSize { + return "" + } + dataAddr := readPtr(hdr) + dataLen := readPtr(hdr[ptrSize:]) + data, err := x.ReadData(dataAddr, dataLen) + if err != nil || uint64(len(data)) < dataLen { + return "" + } + return string(data) +} diff --git a/syft/pkg/cataloger/rpmdb/parse_rpmdb_test.go b/syft/pkg/cataloger/rpmdb/parse_rpmdb_test.go index 7e9720b51..0bad62396 100644 --- a/syft/pkg/cataloger/rpmdb/parse_rpmdb_test.go +++ b/syft/pkg/cataloger/rpmdb/parse_rpmdb_test.go @@ -51,8 +51,8 @@ func (r *rpmdbTestFileResolverMock) RelativeFileByPath(source.Location, string) return nil } -func (r rpmdbTestFileResolverMock) FilesByMIMEType(types ...string) ([]source.Location, error) { - panic("implement me") +func (r *rpmdbTestFileResolverMock) FilesByMIMEType(...string) ([]source.Location, error) { + return nil, fmt.Errorf("not implemented") } func TestParseRpmDB(t *testing.T) { diff --git a/syft/source/directory_resolver.go b/syft/source/directory_resolver.go index b2d8366e7..516981dc6 100644 --- a/syft/source/directory_resolver.go +++ b/syft/source/directory_resolver.go @@ -163,6 +163,7 @@ func (r directoryResolver) addPathToIndex(p string, info os.FileInfo) (string, e if ref != nil { r.refsByMIMEType[metadata.MIMEType] = append(r.refsByMIMEType[metadata.MIMEType], *ref) } + r.metadata[ref.ID()] = metadata return newRoot, nil } diff --git a/syft/source/file_resolver.go b/syft/source/file_resolver.go index fe47d464d..b6ccb4815 100644 --- a/syft/source/file_resolver.go +++ b/syft/source/file_resolver.go @@ -29,7 +29,7 @@ type FilePathResolver interface { FilesByPath(paths ...string) ([]Location, error) // FilesByGlob fetches a set of file references which the given glob matches FilesByGlob(patterns ...string) ([]Location, error) - // FilesByGlob fetches a set of file references which the contents have been classified as one of the given MIME Types + // FilesByMIMEType fetches a set of file references which the contents have been classified as one of the given MIME Types FilesByMIMEType(types ...string) ([]Location, error) // RelativeFileByPath fetches a single file at the given path relative to the layer squash of the given reference. // This is helpful when attempting to find a file that is in the same layer or lower as another file. diff --git a/test/integration/regression_go_bin_scanner_arch_test.go b/test/integration/regression_go_bin_scanner_arch_test.go new file mode 100644 index 000000000..3a76ff335 --- /dev/null +++ b/test/integration/regression_go_bin_scanner_arch_test.go @@ -0,0 +1,48 @@ +package integration + +import ( + "strings" + "testing" + + "github.com/anchore/syft/syft/pkg" +) + +func TestRegressionGoArchDiscovery(t *testing.T) { + const ( + expectedELFPkg = 3 + expectedWINPkg = 3 + expectedMACOSPkg = 3 + ) + // This is a regression test to make sure the way we detect go binary packages + // stays consistent and reproducible as the tool chain evolves + catalog, _, _ := catalogFixtureImage(t, "image-go-bin-arch-coverage") + + var actualELF, actualWIN, actualMACOS int + + for p := range catalog.Enumerate(pkg.GoModulePkg) { + for _, l := range p.Locations { + switch { + case strings.Contains(l.RealPath, "elf"): + actualELF++ + case strings.Contains(l.RealPath, "win"): + actualWIN++ + case strings.Contains(l.RealPath, "macos"): + actualMACOS++ + default: + + } + } + } + + if actualELF != expectedELFPkg { + t.Errorf("unexpected number of elf packages: %d != %d", expectedELFPkg, actualELF) + } + + if actualWIN != expectedWINPkg { + t.Errorf("unexpected number of win packages: %d != %d", expectedWINPkg, actualWIN) + } + + if actualMACOS != expectedMACOSPkg { + t.Errorf("unexpected number of macos packages: %d != %d", expectedMACOSPkg, actualMACOS) + } +} diff --git a/test/integration/test-fixtures/image-go-bin-arch-coverage/Dockerfile b/test/integration/test-fixtures/image-go-bin-arch-coverage/Dockerfile new file mode 100644 index 000000000..7b252568c --- /dev/null +++ b/test/integration/test-fixtures/image-go-bin-arch-coverage/Dockerfile @@ -0,0 +1,16 @@ +FROM golang:latest as builder +WORKDIR /app +COPY go.sum go.mod app.go ./ + +RUN GOOS=linux go build -o elf . +RUN GOOS=windows go build -o win . +RUN GOOS=darwin go build -o macos . + +FROM scratch + +WORKDIR /tmp +COPY --from=builder /app/elf / +COPY --from=builder /app/win / +COPY --from=builder /app/macos / + +ENTRYPOINT ["/elf"] diff --git a/test/integration/test-fixtures/image-go-bin-arch-coverage/app.go b/test/integration/test-fixtures/image-go-bin-arch-coverage/app.go new file mode 100644 index 000000000..8747558d6 --- /dev/null +++ b/test/integration/test-fixtures/image-go-bin-arch-coverage/app.go @@ -0,0 +1,18 @@ +//build:ignore +package main + +import ( + "fmt" + "os" + + "golang.org/x/net/html" + "golang.org/x/term" +) + +var test = html.ErrBufferExceeded + +func main() { + t := term.NewTerminal(os.Stdout, "foo") + t.Write([]byte("hello anchore")) + t.Write([]byte(fmt.Sprintf("%v", test))) +} diff --git a/test/integration/test-fixtures/image-go-bin-arch-coverage/go.mod b/test/integration/test-fixtures/image-go-bin-arch-coverage/go.mod new file mode 100644 index 000000000..4add6f7dc --- /dev/null +++ b/test/integration/test-fixtures/image-go-bin-arch-coverage/go.mod @@ -0,0 +1,14 @@ +module github.com/anchore/test + +go 1.17 + +require ( + golang.org/x/net v0.0.0-20211006190231-62292e806868 + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 +) + +require golang.org/x/sys v0.0.0-20211006194710-c8a6f5223071 // indirect + +exclude golang.org/x/net v0.0.0-20211005215030-d2e5035098b3 + +replace golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 => golang.org/x/term v0.0.0-20210916214954-140adaaadfaf diff --git a/test/integration/test-fixtures/image-go-bin-arch-coverage/go.sum b/test/integration/test-fixtures/image-go-bin-arch-coverage/go.sum new file mode 100644 index 000000000..832264401 --- /dev/null +++ b/test/integration/test-fixtures/image-go-bin-arch-coverage/go.sum @@ -0,0 +1,12 @@ +golang.org/x/net v0.0.0-20211006190231-62292e806868 h1:KlOXYy8wQWTUJYFgkUI40Lzr06ofg5IRXUK5C7qZt1k= +golang.org/x/net v0.0.0-20211006190231-62292e806868/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211006194710-c8a6f5223071 h1:PjhxBct4MZii8FFR8+oeS7QOvxKOTZXgk63EU2XpfJE= +golang.org/x/sys v0.0.0-20211006194710-c8a6f5223071/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210916214954-140adaaadfaf h1:Ihq/mm/suC88gF8WFcVwk+OV6Tq+wyA1O0E5UEvDglI= +golang.org/x/term v0.0.0-20210916214954-140adaaadfaf/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/test/integration/test-fixtures/image-go-bin-arch-coverage/test b/test/integration/test-fixtures/image-go-bin-arch-coverage/test new file mode 100755 index 000000000..a23873125 Binary files /dev/null and b/test/integration/test-fixtures/image-go-bin-arch-coverage/test differ diff --git a/test/integration/test-fixtures/image-large-apk-data/Dockerfile b/test/integration/test-fixtures/image-large-apk-data/Dockerfile index 98ccb7373..8187870a8 100644 --- a/test/integration/test-fixtures/image-large-apk-data/Dockerfile +++ b/test/integration/test-fixtures/image-large-apk-data/Dockerfile @@ -1,5 +1,8 @@ FROM alpine@sha256:d9a7354e3845ea8466bb00b22224d9116b183e594527fb5b6c3d30bc01a20378 + +# we keep these unpinned so that if alpine +# changes our integration tests can adapt RUN apk add --no-cache \ - tzdata=2021a-r0 \ - vim=8.2.2320-r0 \ - alpine-sdk=1.0-r0 + tzdata \ + vim \ + alpine-sdk