diff --git a/cmd/osv-scanner/main.go b/cmd/osv-scanner/main.go index 32ea2f110c2..b9a3ea657c5 100644 --- a/cmd/osv-scanner/main.go +++ b/cmd/osv-scanner/main.go @@ -89,6 +89,11 @@ func run(args []string, stdout, stderr io.Writer) int { Usage: "check subdirectories", Value: false, }, + &cli.BoolFlag{ + Name: "experimental-call-analysis", + Usage: "attempt call analysis on code to detect only active vulnerabilities", + Value: false, + }, &cli.BoolFlag{ Name: "no-ignore", Usage: "also scan files that would be ignored by .gitignore", @@ -106,14 +111,15 @@ func run(args []string, stdout, stderr io.Writer) int { r = output.NewReporter(stdout, stderr, format) vulnResult, err := osvscanner.DoScan(osvscanner.ScannerActions{ - LockfilePaths: context.StringSlice("lockfile"), - SBOMPaths: context.StringSlice("sbom"), - DockerContainerNames: context.StringSlice("docker"), - Recursive: context.Bool("recursive"), - SkipGit: context.Bool("skip-git"), - NoIgnore: context.Bool("no-ignore"), - ConfigOverridePath: context.String("config"), - DirectoryPaths: context.Args().Slice(), + LockfilePaths: context.StringSlice("lockfile"), + SBOMPaths: context.StringSlice("sbom"), + DockerContainerNames: context.StringSlice("docker"), + Recursive: context.Bool("recursive"), + SkipGit: context.Bool("skip-git"), + NoIgnore: context.Bool("no-ignore"), + ConfigOverridePath: context.String("config"), + DirectoryPaths: context.Args().Slice(), + ExperimentalCallAnalysis: context.Bool("experimental-call-analysis"), }, r) if errPrint := r.PrintResult(&vulnResult); errPrint != nil { @@ -132,6 +138,11 @@ func run(args []string, stdout, stderr io.Writer) int { return 1 } + if errors.Is(err, osvscanner.OnlyUncalledVulnerabilitiesFoundErr) { + // TODO: Discuss whether to have a different exit code now that running call analysis is not default + return 2 + } + if errors.Is(err, osvscanner.NoPackagesFoundErr) { r.PrintError("No package sources found, --help for usage information.\n") return 128 diff --git a/docs/working_docs/installation.md b/docs/working_docs/installation.md index 4b40a2a7dd2..23a5b11e5a6 100644 --- a/docs/working_docs/installation.md +++ b/docs/working_docs/installation.md @@ -50,3 +50,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) file. ### SemVer Adherence All releases on the same Major version will be guaranteed to have backward compatible JSON output and CLI arguments. +However, features prefixed with `experimental` (e.g. `--experimental-call-analysis`) might be changed or removed with only a Minor version change. \ No newline at end of file diff --git a/docs/working_docs/output.md b/docs/working_docs/output.md index 454a0475db0..e52513c5c7e 100644 --- a/docs/working_docs/output.md +++ b/docs/working_docs/output.md @@ -118,7 +118,15 @@ osv-scanner --format json -L path/to/lockfile > /path/to/file.json "ids": [ "GHSA-c3h9-896r-86jm", "GO-2021-0053" - ] + ], + // Call stack analysis is done using the `--experimental-call-analysis` flag + // and result is matched against data provided by the advisory to check if + // affected code is actually being executed. + "experimentalAnalysis": { + "GO-2021-0053": { + "called": false + } + } } ] } diff --git a/docs/working_docs/usage.md b/docs/working_docs/usage.md index 58d4008a036..c8d689f99ae 100644 --- a/docs/working_docs/usage.md +++ b/docs/working_docs/usage.md @@ -99,9 +99,33 @@ it should infer the parser based on the filename: osv-scanner --lockfile ':/path/to/my:projects/package-lock.json' ``` +### Scanning with call analysis +Preview +{: .label } + +{: .note } +Features and flags with the `experimental` prefix might change or be removed with only a minor version update. + +Call stack analysis can be performed on some languages to check if the +vulnerable code is actually being executed by your project. If the code +is not being executed, these vulnerabilities will be marked as unexecuted. + +To enable call analysis, call OSV-Scanner with the `--experimental-call-analysis` flag. + +#### Supported languages +- `go` + - Additional dependencies: + - `go` compiler needs to be installed and available on PATH + +#### Example +```bash +osv-scanner --experimental-call-analysis ./my/project/path +``` + ### Scanning a Debian based docker image packages Preview {: .label } + This tool will scrape the list of installed packages in a Debian image and query for vulnerabilities on them. Currently only Debian based docker image scanning is supported. @@ -135,4 +159,4 @@ appropriate osv-scanner flags: ```bash docker run -it -v ${PWD}:/src ghcr.io/google/osv-scanner -L /src/go.mod -``` \ No newline at end of file +``` diff --git a/go.mod b/go.mod index 2b771481ca4..2755351e383 100644 --- a/go.mod +++ b/go.mod @@ -9,12 +9,15 @@ require ( github.com/go-git/go-git/v5 v5.5.2 github.com/google/go-cmp v0.5.9 github.com/jedib0t/go-pretty/v6 v6.4.4 + github.com/kr/pretty v0.3.1 github.com/package-url/packageurl-go v0.1.0 github.com/spdx/tools-golang v0.4.0 github.com/urfave/cli/v2 v2.24.4 golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb golang.org/x/mod v0.8.0 golang.org/x/term v0.5.0 + golang.org/x/tools v0.5.1-0.20230117180257-8aba49bb5ea2 + golang.org/x/vuln v0.0.0-20230118164824-4ec8867cc0e6 gopkg.in/yaml.v3 v3.0.1 ) @@ -29,13 +32,16 @@ require ( github.com/imdario/mergo v0.3.13 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect + github.com/kr/text v0.2.0 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/pjbgf/sha1cd v0.2.3 // indirect github.com/rivo/uniseg v0.2.0 // indirect + github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sergi/go-diff v1.1.0 // indirect github.com/skeema/knownhosts v1.1.0 // indirect github.com/spdx/gordf v0.0.0-20221230105357-b735bd5aac89 // indirect + github.com/stretchr/testify v1.8.1 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect golang.org/x/crypto v0.3.0 // indirect diff --git a/go.sum b/go.sum index 3195318b896..509ea39bfde 100644 --- a/go.sum +++ b/go.sum @@ -14,6 +14,7 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/cloudflare/circl v1.1.0 h1:bZgT/A+cikZnKIwn7xL2OBj012Bmvho/o6RpRvv3GKY= github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= @@ -49,8 +50,9 @@ github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -64,6 +66,7 @@ github.com/package-url/packageurl-go v0.1.0 h1:efWBc98O/dBZRg1pw2xiDzovnlMjCa9NP github.com/package-url/packageurl-go v0.1.0/go.mod h1:C/ApiuWpmbpni4DIOECf6WCjFUZV7O1Fx7VAzrZHgBw= github.com/pjbgf/sha1cd v0.2.3 h1:uKQP/7QOzNtKYH7UTohZLcjF5/55EnTw0jO/Ru4jZwI= github.com/pjbgf/sha1cd v0.2.3/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= @@ -71,6 +74,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= @@ -85,12 +90,15 @@ github.com/spdx/tools-golang v0.4.0 h1:jdhnW8zYelURCbYTphiviFKZkWu51in0E4A1KT2cs github.com/spdx/tools-golang v0.4.0/go.mod h1:VHzvNsKAfAGqs4ZvwRL+7a0dNsL20s7lGui4K9C0xQM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/urfave/cli/v2 v2.24.4 h1:0gyJJEBYtCV87zI/x2nZCPyDxD51K6xM8SkwjHFCNEU= github.com/urfave/cli/v2 v2.24.4/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= @@ -120,6 +128,7 @@ golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -154,6 +163,10 @@ golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.5.1-0.20230117180257-8aba49bb5ea2 h1:v0FhRDmSCNH/0EurAT6T8KRY4aNuUhz6/WwBMxG+gvQ= +golang.org/x/tools v0.5.1-0.20230117180257-8aba49bb5ea2/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= +golang.org/x/vuln v0.0.0-20230118164824-4ec8867cc0e6 h1:XZD8apnMaMVuqE3ZEzf5JJncKMlOsMnnov7U+JRT/d4= +golang.org/x/vuln v0.0.0-20230118164824-4ec8867cc0e6/go.mod h1:cBP4HMKv0X+x96j8IJWCKk0eqpakBmmHjKGSSC0NaYE= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -170,4 +183,6 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.2.2 h1:MNh1AVMyVX23VUHE2O27jm6lNj3vjO5DexS4A1xvnzk= +mvdan.cc/unparam v0.0.0-20211214103731-d0ef000c54e5 h1:Jh3LAeMt1eGpxomyu3jVkmVZWW2MxZ1qIIV2TZ/nRio= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/goreleaser.dockerfile b/goreleaser.dockerfile index 8807cd07d73..c8f4cb67e4e 100644 --- a/goreleaser.dockerfile +++ b/goreleaser.dockerfile @@ -12,11 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM alpine:latest +FROM alpine:3.17 RUN apk add --no-cache \ ca-certificates \ - git + git \ + go # Allow git to run on mounted directories RUN git config --global --add safe.directory '*' diff --git a/internal/govulncheckshim/.goignore b/internal/govulncheckshim/.goignore new file mode 100644 index 00000000000..d4889602bbd --- /dev/null +++ b/internal/govulncheckshim/.goignore @@ -0,0 +1 @@ +fixtures \ No newline at end of file diff --git a/internal/govulncheckshim/client.go b/internal/govulncheckshim/client.go new file mode 100644 index 00000000000..d1740d3a60a --- /dev/null +++ b/internal/govulncheckshim/client.go @@ -0,0 +1,121 @@ +package govulncheckshim + +import ( + "context" + "encoding/json" + "fmt" + "time" + + "golang.org/x/vuln/client" + gvcOSV "golang.org/x/vuln/osv" + + "github.com/google/osv-scanner/pkg/models" +) + +type localSource struct { + vulnList []models.Vulnerability + vulnsByID map[string]*models.Vulnerability + vulnsByAlias map[string][]*models.Vulnerability + vulnsByModule map[string][]*models.Vulnerability + lastModifiedTime time.Time + client.Client +} + +func newClient(vulns []models.Vulnerability) *localSource { + client := localSource{ + vulnList: vulns, + vulnsByID: make(map[string]*models.Vulnerability), + vulnsByAlias: make(map[string][]*models.Vulnerability), + vulnsByModule: make(map[string][]*models.Vulnerability), + lastModifiedTime: time.Unix(0, 0), + } + for idx := range vulns { + // Iterate on reference to avoid copying entire data structure + v := &client.vulnList[idx] + client.vulnsByID[v.ID] = v + for _, alias := range v.Aliases { + client.vulnsByAlias[alias] = append(client.vulnsByAlias[alias], v) + } + for _, affected := range v.Affected { + client.vulnsByModule[affected.Package.Name] = append(client.vulnsByModule[affected.Package.Name], v) + } + if client.lastModifiedTime.Before(v.Modified) { + client.lastModifiedTime = v.Modified + } + } + + return &client +} + +func convertToGvcOSV(osv models.Vulnerability) (gvcOSV.Entry, error) { + val, err := json.Marshal(osv) + if err != nil { + return gvcOSV.Entry{}, fmt.Errorf("failed to convert vuln to JSON: %w", err) + } + response := gvcOSV.Entry{} + err = json.Unmarshal(val, &response) + if err != nil { + return gvcOSV.Entry{}, fmt.Errorf("gvc format is no longer compatible with osv format: %w", err) + } + + return response, nil +} + +func (ls *localSource) GetByModule(ctx context.Context, modulePath string) ([]*gvcOSV.Entry, error) { + //nolint:prealloc // Need to be nil if none exists + var entries []*gvcOSV.Entry + for _, v := range ls.vulnsByModule[modulePath] { + res, err := convertToGvcOSV(*v) + if err != nil { + return nil, err + } + entries = append(entries, &res) + } + + return entries, nil +} + +func (ls *localSource) GetByID(ctx context.Context, id string) (*gvcOSV.Entry, error) { + entry, ok := ls.vulnsByID[id] + if !ok { + //nolint:nilnil // This follows govulncheck's client implementation + // See: https://github.com/golang/vuln/blob/master/client/client.go + return nil, nil + } + response, err := convertToGvcOSV(*entry) + if err != nil { + return nil, err + } + + return &response, nil +} + +func (ls *localSource) GetByAlias(ctx context.Context, alias string) ([]*gvcOSV.Entry, error) { + //nolint:prealloc // Need to be nil if none exists + var entries []*gvcOSV.Entry + + for _, v := range ls.vulnsByAlias[alias] { + res, err := convertToGvcOSV(*v) + if err != nil { + return nil, err + } + entries = append(entries, &res) + } + + return entries, nil +} + +func (ls *localSource) ListIDs(ctx context.Context) ([]string, error) { + //nolint:prealloc // Need to be nil if none exists + var ids []string + for i := range ls.vulnList { + ids = append(ids, ls.vulnList[i].ID) + } + + return ids, nil +} + +func (ls *localSource) LastModifiedTime(context.Context) (time.Time, error) { + // Assume that if anything changes, the index does. + return ls.lastModifiedTime, nil +} diff --git a/internal/govulncheckshim/client_test.go b/internal/govulncheckshim/client_test.go new file mode 100644 index 00000000000..778973ba1bb --- /dev/null +++ b/internal/govulncheckshim/client_test.go @@ -0,0 +1,244 @@ +package govulncheckshim + +import ( + "context" + "encoding/json" + "os" + "path/filepath" + "testing" + + "github.com/google/osv-scanner/pkg/models" +) + +func newTestClient(t *testing.T) *localSource { + t.Helper() + + vulns := []models.Vulnerability{} + entries, err := os.ReadDir("fixtures/client") + if err != nil { + t.Fatal(err) + } + for _, entry := range entries { + file, err := os.Open(filepath.Join("fixtures/client", entry.Name())) + if err != nil { + t.Fatalf("failed to open test fixtures %s", err) + } + newVuln := models.Vulnerability{} + err = json.NewDecoder(file).Decode(&newVuln) + if err != nil { + t.Fatalf("failed to parse test fixtures %s", err) + } + vulns = append(vulns, newVuln) + } + + return newClient(vulns) +} + +func TestGetByID(t *testing.T) { + t.Parallel() + + type IDTestCases struct { + ID string + ReturnValue *struct { + Published string + } + } + + testCases := []IDTestCases{ + { + ID: "GO-2021-0098", + ReturnValue: &struct{ Published string }{ + Published: "2021-04-14", + }, + }, + { + ID: "GO-2022-0569", + ReturnValue: &struct{ Published string }{ + Published: "2022-08-23", + }, + }, + { + ID: "GHSA-vc3p-29h2-gpcp", + ReturnValue: &struct{ Published string }{ + Published: "2022-01-02", + }, + }, + { + ID: "GO-1234-5678", + ReturnValue: nil, + }, + } + + client := newTestClient(t) + for _, x := range testCases { + res, err := client.GetByID(context.Background(), x.ID) + if err != nil { + t.Error(err) + } + if (x.ReturnValue == nil) != (res == nil) { + t.Errorf("Expected %s, found %s", x.ReturnValue, res) + } + + if res == nil { + return + } + + if res.Published.Format("2006-01-02") != x.ReturnValue.Published { + t.Errorf("Expected %s, found %s", x.ReturnValue.Published, res.Published.Format("yyyy-mm-dd")) + } + } +} + +func TestGetByAlias(t *testing.T) { + t.Parallel() + + type AliasTestCases struct { + Alias string + ReturnValue []struct { + ID string + } + } + + testCases := []AliasTestCases{ + { + Alias: "CVE-2021-44716", + ReturnValue: []struct{ ID string }{ + { + ID: "GHSA-vc3p-29h2-gpcp", + }, + }, + }, + { + Alias: "CVE-2021-21237", + ReturnValue: []struct{ ID string }{ + // Order doesn't matter + { + ID: "GHSA-cx3w-xqmc-84g5", + }, + { + ID: "GO-2021-0098", + }, + }, + }, + { + Alias: "GHSA-cx3w-xqmc-84g5", + ReturnValue: []struct{ ID string }{ + // If the alias is the ID, do not return the entry + // TODO: Is this correct behavior? + // { + // ID: "GHSA-cx3w-xqmc-84g5", + // }, + { + ID: "GO-2021-0098", + }, + }, + }, + { + Alias: "GO-1234-5678", + ReturnValue: nil, + }, + } + + client := newTestClient(t) + for _, x := range testCases { + res, err := client.GetByAlias(context.Background(), x.Alias) + if err != nil { + t.Error(err) + } + if (x.ReturnValue == nil) != (res == nil) { + t.Errorf("Expected %v, found %v", x.ReturnValue, res) + } + + if res == nil { + return + } + + if len(res) != len(x.ReturnValue) { + t.Errorf("Expected %v, found %v", x.ReturnValue, res) + } + + for i := range res { + if res[i].ID != x.ReturnValue[i].ID { + t.Errorf("Expected %s, found %s", x.ReturnValue[i].ID, res[i].ID) + } + } + } +} + +func TestGetByModule(t *testing.T) { + t.Parallel() + + type ModuleTestCases struct { + Module string + ReturnValue []struct { + ID string + } + } + + testCases := []ModuleTestCases{ + { + Module: "github.com/beego/beego", + ReturnValue: []struct{ ID string }{ + { + ID: "GO-2022-0569", + }, + }, + }, + { + Module: "github.com/git-lfs/git-lfs", + ReturnValue: []struct{ ID string }{ + // Order doesn't matter + { + ID: "GHSA-cx3w-xqmc-84g5", + }, + { + ID: "GO-2021-0098", + }, + }, + }, + { + Module: "golang.org/x/y/z", + ReturnValue: nil, + }, + } + + client := newTestClient(t) + for _, x := range testCases { + res, err := client.GetByModule(context.Background(), x.Module) + if err != nil { + t.Error(err) + } + if (x.ReturnValue == nil) != (res == nil) { + t.Errorf("Expected %v, found %v", x.ReturnValue, res) + } + + if res == nil { + return + } + + if len(res) != len(x.ReturnValue) { + t.Errorf("Expected %v, found %v", x.ReturnValue, res) + } + + for i := range res { + if res[i].ID != x.ReturnValue[i].ID { + t.Errorf("Expected %s, found %s", x.ReturnValue[i].ID, res[i].ID) + } + } + } +} + +func TestLastModifiedTime(t *testing.T) { + t.Parallel() + + client := newTestClient(t) + clientLastModTime, err := client.LastModifiedTime(context.Background()) + if err != nil { + t.Error(err) + } + + // Last modified time in GHSA-vc3p-29h2-gpcp + if clientLastModTime.Format("2006-01-02") != "2023-02-08" { + t.Errorf("Expected %s, found %s", "2023-02-08", clientLastModTime.Format("2006-01-02")) + } +} diff --git a/internal/govulncheckshim/fixtures/GO-2021-0053.json b/internal/govulncheckshim/fixtures/GO-2021-0053.json new file mode 100644 index 00000000000..176ec555e74 --- /dev/null +++ b/internal/govulncheckshim/fixtures/GO-2021-0053.json @@ -0,0 +1,52 @@ +{ + "id": "GO-2021-0053", + "published": "2021-04-14T20:04:52Z", + "modified": "2023-02-10T16:51:38Z", + "aliases": [ + "CVE-2021-3121", + "GHSA-c3h9-896r-86jm" + ], + "details": "Due to improper bounds checking, maliciously crafted input to generated Unmarshal methods can cause an out-of-bounds panic. If parsing messages from untrusted parties, this may be used as a denial of service vector.", + "affected": [ + { + "package": { + "name": "github.com/gogo/protobuf", + "ecosystem": "Go" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "1.3.2" + } + ] + } + ], + "database_specific": { + "url": "https://pkg.go.dev/vuln/GO-2021-0053" + }, + "ecosystem_specific": { + "imports": [ + { + "path": "github.com/gogo/protobuf/plugin/unmarshal", + "symbols": [ + "unmarshal.Generate", + "unmarshal.field" + ] + } + ] + } + } + ], + "references": [ + { + "type": "FIX", + "url": "https://github.com/gogo/protobuf/commit/b03c65ea87cdc3521ede29f62fe3ce239267c1bc" + } + ], + "schema_version": "1.3.1" +} \ No newline at end of file diff --git a/internal/govulncheckshim/fixtures/GO-2023-1558.json b/internal/govulncheckshim/fixtures/GO-2023-1558.json new file mode 100644 index 00000000000..bffcee522c5 --- /dev/null +++ b/internal/govulncheckshim/fixtures/GO-2023-1558.json @@ -0,0 +1,61 @@ +{ + "id": "GO-2023-1558", + "published": "2023-02-14T19:41:21Z", + "modified": "2023-02-14T19:41:21Z", + "aliases": [ + "CVE-2023-23626", + "GHSA-2h6c-j3gf-xp9r" + ], + "details": "When feeding untrusted user input into the size parameter of `NewBitfield` and FromBytes functions, an attacker can trigger panics.\n\nThis happens when the size is a not a multiple of 8 or is negative.\n\nA workaround is to ensure size%8 == 0 \u0026\u0026 size \u003e= 0 yourself before calling NewBitfield or FromBytes.", + "affected": [ + { + "package": { + "name": "github.com/ipfs/go-bitfield", + "ecosystem": "Go" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "1.1.0" + } + ] + } + ], + "database_specific": { + "url": "https://pkg.go.dev/vuln/GO-2023-1558" + }, + "ecosystem_specific": { + "imports": [ + { + "path": "github.com/ipfs/go-bitfield", + "symbols": [ + "FromBytes", + "NewBitfield" + ] + } + ] + } + } + ], + "references": [ + { + "type": "ADVISORY", + "url": "https://github.com/ipfs/go-bitfield/security/advisories/GHSA-2h6c-j3gf-xp9r" + }, + { + "type": "FIX", + "url": "https://github.com/ipfs/go-bitfield/commit/5e1d256fe043fc4163343ccca83862c69c52e579" + } + ], + "credits": [ + { + "name": "Jorropo" + } + ], + "schema_version": "1.3.1" +} \ No newline at end of file diff --git a/internal/govulncheckshim/fixtures/client/GHSA-cx3w-xqmc-84g5.json b/internal/govulncheckshim/fixtures/client/GHSA-cx3w-xqmc-84g5.json new file mode 100644 index 00000000000..e3b3e899760 --- /dev/null +++ b/internal/govulncheckshim/fixtures/client/GHSA-cx3w-xqmc-84g5.json @@ -0,0 +1,72 @@ +{ + "schema_version": "1.3.0", + "id": "GHSA-cx3w-xqmc-84g5", + "modified": "2022-04-20T16:24:41Z", + "published": "2022-02-15T00:30:37Z", + "aliases": [ + "CVE-2021-21237" + ], + "summary": "Git LFS can execute a Git binary from the current directory on Windows", + "details": "### Impact\nOn Windows, if Git LFS operates on a malicious repository with a git.bat or git.exe file in the current directory, that program would be executed, permitting the attacker to execute arbitrary code. This does not affect Unix systems.\n\nThis is the result of an incomplete fix for CVE-2020-27955.\n\nThis issue occurs because on Windows, [Go includes (and prefers) the current directory when the name of a command run does not contain a directory separator](https://github.com/golang/go/issues/38736).\n\n### Patches\nThis version should be patched in v2.13.2, which will be released in coordination with this security advisory.\n\n### Workarounds\nOther than avoiding untrusted repositories or using a different operating system, there is no workaround.\n\n### References\n_Are there any links users can visit to find out more?_\n\n### For more information\nIf you have any questions or comments about this advisory:\n\n- Start a discussion in [the Git LFS discussion page](https://github.com/git-lfs/git-lfs/discussions).\n- If you cannot open a discussion, please email the core team using their usernames at `github.com`.", + "severity": [ + { + "type": "CVSS_V3", + "score": "CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:C/C:H/I:H/A:N" + } + ], + "affected": [ + { + "package": { + "ecosystem": "Go", + "name": "github.com/git-lfs/git-lfs" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "2.13.2" + } + ] + } + ] + } + ], + "references": [ + { + "type": "WEB", + "url": "https://github.com/git-lfs/git-lfs/security/advisories/GHSA-cx3w-xqmc-84g5" + }, + { + "type": "ADVISORY", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-21237" + }, + { + "type": "WEB", + "url": "https://github.com/git-lfs/git-lfs/commit/fc664697ed2c2081ee9633010de0a7f9debea72a" + }, + { + "type": "WEB", + "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-27955" + }, + { + "type": "PACKAGE", + "url": "https://github.com/git-lfs/git-lfs" + }, + { + "type": "WEB", + "url": "https://github.com/git-lfs/git-lfs/releases/tag/v2.13.2" + } + ], + "database_specific": { + "cwe_ids": [ + "CWE-426", + "CWE-94" + ], + "severity": "HIGH", + "github_reviewed": true + } +} \ No newline at end of file diff --git a/internal/govulncheckshim/fixtures/client/GHSA-vc3p-29h2-gpcp.json b/internal/govulncheckshim/fixtures/client/GHSA-vc3p-29h2-gpcp.json new file mode 100644 index 00000000000..818295760c3 --- /dev/null +++ b/internal/govulncheckshim/fixtures/client/GHSA-vc3p-29h2-gpcp.json @@ -0,0 +1,85 @@ +{ + "schema_version": "1.3.0", + "id": "GHSA-vc3p-29h2-gpcp", + "modified": "2023-02-08T00:37:41Z", + "published": "2022-01-02T00:00:48Z", + "aliases": [ + "CVE-2021-44716" + ], + "summary": "golang.org/x/net/http2 allows uncontrolled memory consumption", + "details": "net/http in Go before 1.16.12 and 1.17.x before 1.17.5 allows uncontrolled memory consumption in the header canonicalization cache via HTTP/2 requests.", + "severity": [ + { + "type": "CVSS_V3", + "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H" + } + ], + "affected": [ + { + "package": { + "ecosystem": "Go", + "name": "golang.org/x/net/http2" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "0.0.0-20211209124913-491a49abca63" + } + ] + } + ] + } + ], + "references": [ + { + "type": "ADVISORY", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-44716" + }, + { + "type": "WEB", + "url": "https://go.dev/cl/369794" + }, + { + "type": "WEB", + "url": "https://go.dev/issue/50058" + }, + { + "type": "WEB", + "url": "https://groups.google.com/g/golang-announce/c/hcmEScgc00k" + }, + { + "type": "WEB", + "url": "https://lists.debian.org/debian-lts-announce/2022/01/msg00016.html" + }, + { + "type": "WEB", + "url": "https://lists.debian.org/debian-lts-announce/2022/01/msg00017.html" + }, + { + "type": "WEB", + "url": "https://pkg.go.dev/vuln/GO-2022-0288" + }, + { + "type": "WEB", + "url": "https://security.gentoo.org/glsa/202208-02" + }, + { + "type": "WEB", + "url": "https://security.netapp.com/advisory/ntap-20220121-0002/" + } + ], + "database_specific": { + "cwe_ids": [ + "CWE-400" + ], + "severity": "HIGH", + "github_reviewed": true, + "github_reviewed_at": "2023-02-08T00:37:41Z", + "nvd_published_at": "2022-01-01T05:15:00Z" + } +} \ No newline at end of file diff --git a/internal/govulncheckshim/fixtures/client/GO-2021-0098.json b/internal/govulncheckshim/fixtures/client/GO-2021-0098.json new file mode 100644 index 00000000000..90181397f41 --- /dev/null +++ b/internal/govulncheckshim/fixtures/client/GO-2021-0098.json @@ -0,0 +1,107 @@ +{ + "id": "GO-2021-0098", + "published": "2021-04-14T20:04:52Z", + "modified": "2023-02-07T16:05:32Z", + "aliases": [ + "CVE-2021-21237", + "GHSA-cx3w-xqmc-84g5" + ], + "details": "Due to the standard library behavior of exec.LookPath on Windows a number of methods may result in arbitrary code execution when cloning or operating on untrusted Git repositories.", + "affected": [ + { + "package": { + "name": "github.com/git-lfs/git-lfs", + "ecosystem": "Go" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "1.5.1-0.20210113180018-fc664697ed2c" + } + ] + } + ], + "database_specific": { + "url": "https://pkg.go.dev/vuln/GO-2021-0098" + }, + "ecosystem_specific": { + "imports": [ + { + "path": "github.com/git-lfs/git-lfs/commands", + "goos": [ + "windows" + ], + "symbols": [ + "PipeCommand", + "PipeMediaCommand", + "Run", + "lockVerifier.Verify", + "singleCheckout.Run", + "singleCheckout.RunToPath", + "uploadContext.NewQueue", + "uploadContext.UploadPointers" + ] + }, + { + "path": "github.com/git-lfs/git-lfs/creds", + "goos": [ + "windows" + ], + "symbols": [ + "AskPassCredentialHelper.Fill", + "AskPassCredentialHelper.getFromProgram", + "CredentialHelperWrapper.FillCreds", + "CredentialHelpers.Approve", + "CredentialHelpers.Fill", + "commandCredentialHelper.Approve" + ] + }, + { + "path": "github.com/git-lfs/git-lfs/lfs", + "goos": [ + "windows" + ], + "symbols": [ + "GitFilter.Clean", + "GitFilter.Smudge", + "GitFilter.SmudgeToFile", + "pipeExtensions" + ] + }, + { + "path": "github.com/git-lfs/git-lfs/lfshttp", + "goos": [ + "windows" + ], + "symbols": [ + "Client.Do", + "Client.DoWithAccess", + "Client.HttpClient", + "Client.NewRequest", + "Client.Transport", + "sshAuthClient.Resolve", + "sshCache.Resolve" + ] + } + ] + } + } + ], + "references": [ + { + "type": "FIX", + "url": "https://github.com/git-lfs/git-lfs/commit/fc664697ed2c2081ee9633010de0a7f9debea72a" + } + ], + "credits": [ + { + "name": "@Ry0taK" + } + ], + "schema_version": "1.3.1" +} \ No newline at end of file diff --git a/internal/govulncheckshim/fixtures/client/GO-2022-0569.json b/internal/govulncheckshim/fixtures/client/GO-2022-0569.json new file mode 100644 index 00000000000..399153588cd --- /dev/null +++ b/internal/govulncheckshim/fixtures/client/GO-2022-0569.json @@ -0,0 +1,316 @@ +{ + "id": "GO-2022-0569", + "published": "2022-08-23T13:24:17Z", + "modified": "2023-02-07T16:05:55Z", + "aliases": [ + "CVE-2022-31836", + "GHSA-95f9-94vc-665h" + ], + "details": "The leafInfo.match() function uses path.join() to deal with wildcard values which can lead to cross directory risk.", + "affected": [ + { + "package": { + "name": "github.com/beego/beego", + "ecosystem": "Go" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "1.12.11" + } + ] + } + ], + "database_specific": { + "url": "https://pkg.go.dev/vuln/GO-2022-0569" + }, + "ecosystem_specific": { + "imports": [ + { + "path": "github.com/beego/beego", + "symbols": [ + "App.Run", + "ControllerRegister.FindPolicy", + "ControllerRegister.FindRouter", + "ControllerRegister.ServeHTTP", + "FilterRouter.ValidRouter", + "InitBeegoBeforeTest", + "Run", + "RunWithMiddleWares", + "TestBeegoInit", + "Tree.Match", + "adminApp.Run" + ] + } + ] + } + }, + { + "package": { + "name": "github.com/beego/beego/v2", + "ecosystem": "Go" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "2.0.0" + }, + { + "fixed": "2.0.4" + } + ] + } + ], + "database_specific": { + "url": "https://pkg.go.dev/vuln/GO-2022-0569" + }, + "ecosystem_specific": { + "imports": [ + { + "path": "github.com/beego/beego/v2/server/web", + "symbols": [ + "AddNamespace", + "AddViewPath", + "Any", + "AutoPrefix", + "AutoRouter", + "BuildTemplate", + "Compare", + "CompareNot", + "Controller.Abort", + "Controller.Bind", + "Controller.BindForm", + "Controller.BindJSON", + "Controller.BindProtobuf", + "Controller.BindXML", + "Controller.BindYAML", + "Controller.CheckXSRFCookie", + "Controller.CustomAbort", + "Controller.Delete", + "Controller.DestroySession", + "Controller.Get", + "Controller.GetBool", + "Controller.GetFile", + "Controller.GetFloat", + "Controller.GetInt", + "Controller.GetInt16", + "Controller.GetInt32", + "Controller.GetInt64", + "Controller.GetInt8", + "Controller.GetSecureCookie", + "Controller.GetString", + "Controller.GetStrings", + "Controller.GetUint16", + "Controller.GetUint32", + "Controller.GetUint64", + "Controller.GetUint8", + "Controller.Head", + "Controller.Input", + "Controller.IsAjax", + "Controller.JSONResp", + "Controller.Options", + "Controller.ParseForm", + "Controller.Patch", + "Controller.Post", + "Controller.Put", + "Controller.Redirect", + "Controller.Render", + "Controller.RenderBytes", + "Controller.RenderString", + "Controller.Resp", + "Controller.SaveToFile", + "Controller.SaveToFileWithBuffer", + "Controller.ServeFormatted", + "Controller.ServeJSON", + "Controller.ServeJSONP", + "Controller.ServeXML", + "Controller.ServeYAML", + "Controller.SessionRegenerateID", + "Controller.SetData", + "Controller.SetSecureCookie", + "Controller.Trace", + "Controller.URLFor", + "Controller.XMLResp", + "Controller.XSRFFormHTML", + "Controller.XSRFToken", + "Controller.YamlResp", + "ControllerRegister.Add", + "ControllerRegister.AddAuto", + "ControllerRegister.AddAutoPrefix", + "ControllerRegister.AddMethod", + "ControllerRegister.AddRouterMethod", + "ControllerRegister.Any", + "ControllerRegister.CtrlAny", + "ControllerRegister.CtrlDelete", + "ControllerRegister.CtrlGet", + "ControllerRegister.CtrlHead", + "ControllerRegister.CtrlOptions", + "ControllerRegister.CtrlPatch", + "ControllerRegister.CtrlPost", + "ControllerRegister.CtrlPut", + "ControllerRegister.Delete", + "ControllerRegister.FindPolicy", + "ControllerRegister.FindRouter", + "ControllerRegister.Get", + "ControllerRegister.GetContext", + "ControllerRegister.Handler", + "ControllerRegister.Head", + "ControllerRegister.Include", + "ControllerRegister.Init", + "ControllerRegister.InsertFilter", + "ControllerRegister.Options", + "ControllerRegister.Patch", + "ControllerRegister.Post", + "ControllerRegister.Put", + "ControllerRegister.ServeHTTP", + "ControllerRegister.URLFor", + "CtrlAny", + "CtrlDelete", + "CtrlGet", + "CtrlHead", + "CtrlOptions", + "CtrlPatch", + "CtrlPost", + "CtrlPut", + "Date", + "DateFormat", + "DateParse", + "Delete", + "Exception", + "ExecuteTemplate", + "ExecuteViewPathTemplate", + "FileSystem.Open", + "FilterRouter.ValidRouter", + "FlashData.Error", + "FlashData.Notice", + "FlashData.Set", + "FlashData.Store", + "FlashData.Success", + "FlashData.Warning", + "Get", + "GetConfig", + "HTML2str", + "Handler", + "Head", + "Htmlquote", + "Htmlunquote", + "HttpServer.Any", + "HttpServer.AutoPrefix", + "HttpServer.AutoRouter", + "HttpServer.CtrlAny", + "HttpServer.CtrlDelete", + "HttpServer.CtrlGet", + "HttpServer.CtrlHead", + "HttpServer.CtrlOptions", + "HttpServer.CtrlPatch", + "HttpServer.CtrlPost", + "HttpServer.CtrlPut", + "HttpServer.Delete", + "HttpServer.Get", + "HttpServer.Handler", + "HttpServer.Head", + "HttpServer.Include", + "HttpServer.InsertFilter", + "HttpServer.LogAccess", + "HttpServer.Options", + "HttpServer.Patch", + "HttpServer.Post", + "HttpServer.PrintTree", + "HttpServer.Put", + "HttpServer.RESTRouter", + "HttpServer.Router", + "HttpServer.RouterWithOpts", + "HttpServer.Run", + "Include", + "InitBeegoBeforeTest", + "InsertFilter", + "LoadAppConfig", + "LogAccess", + "MapGet", + "Namespace.Any", + "Namespace.AutoPrefix", + "Namespace.AutoRouter", + "Namespace.Cond", + "Namespace.CtrlAny", + "Namespace.CtrlDelete", + "Namespace.CtrlGet", + "Namespace.CtrlHead", + "Namespace.CtrlOptions", + "Namespace.CtrlPatch", + "Namespace.CtrlPost", + "Namespace.CtrlPut", + "Namespace.Delete", + "Namespace.Filter", + "Namespace.Get", + "Namespace.Handler", + "Namespace.Head", + "Namespace.Include", + "Namespace.Namespace", + "Namespace.Options", + "Namespace.Patch", + "Namespace.Post", + "Namespace.Put", + "Namespace.Router", + "NewControllerRegister", + "NewControllerRegisterWithCfg", + "NewHttpServerWithCfg", + "NewHttpSever", + "NewNamespace", + "NotNil", + "Options", + "ParseForm", + "Patch", + "Policy", + "Post", + "PrintTree", + "Put", + "RESTRouter", + "ReadFromRequest", + "RenderForm", + "Router", + "RouterWithOpts", + "Run", + "RunWithMiddleWares", + "TestBeegoInit", + "Tree.AddRouter", + "Tree.AddTree", + "Tree.Match", + "URLFor", + "URLMap.GetMap", + "URLMap.GetMapData", + "Walk", + "adminApp.Run", + "adminController.AdminIndex", + "adminController.Healthcheck", + "adminController.ListConf", + "adminController.ProfIndex", + "adminController.PrometheusMetrics", + "adminController.QpsIndex", + "adminController.TaskStatus", + "beegoAppConfig.Bool", + "beegoAppConfig.DefaultBool" + ] + } + ] + } + } + ], + "references": [ + { + "type": "FIX", + "url": "https://github.com/beego/beego/pull/5025" + }, + { + "type": "FIX", + "url": "https://github.com/beego/beego/pull/5025/commits/ea5ae58d40589d249cf577a053e490509de2bf57" + } + ], + "schema_version": "1.3.1" +} \ No newline at end of file diff --git a/internal/govulncheckshim/fixtures/snapshots/govulncheckshim_test.json b/internal/govulncheckshim/fixtures/snapshots/govulncheckshim_test.json new file mode 100755 index 00000000000..6732f439569 --- /dev/null +++ b/internal/govulncheckshim/fixtures/snapshots/govulncheckshim_test.json @@ -0,0 +1,172 @@ +{ + "Vulns": [ + { + "Modules": [ + { + "FixedVersion": "v1.1.0", + "FoundVersion": "v1.0.0", + "Packages": [ + { + "CallStacks": [ + { + "Frames": [ + { + "FuncName": "main", + "PkgPath": "github.com/ossf-tests/osv-e2e", + "Position": { + "Column": 22, + "Filename": "", + "Line": 13, + "Offset": 234 + }, + "RecvType": "" + }, + { + "FuncName": "NewBitfield", + "PkgPath": "github.com/ipfs/go-bitfield", + "Position": { + "Column": 0, + "Filename": "", + "Line": 0, + "Offset": 0 + }, + "RecvType": "" + } + ], + "Summary": "fixtures/test-project/main.go:13:22: github.com/ossf-tests/osv-e2e.main calls github.com/ipfs/go-bitfield.NewBitfield", + "Symbol": "NewBitfield" + } + ], + "Path": "github.com/ipfs/go-bitfield" + } + ], + "Path": "github.com/ipfs/go-bitfield" + } + ], + "OSV": { + "affected": [ + { + "database_specific": { + "url": "https://pkg.go.dev/vuln/GO-2023-1558" + }, + "ecosystem_specific": { + "imports": [ + { + "path": "github.com/ipfs/go-bitfield", + "symbols": [ + "FromBytes", + "NewBitfield" + ] + } + ] + }, + "package": { + "ecosystem": "Go", + "name": "github.com/ipfs/go-bitfield" + }, + "ranges": [ + { + "events": [ + { + "introduced": "0" + }, + { + "fixed": "1.1.0" + } + ], + "type": "SEMVER" + } + ] + } + ], + "aliases": [ + "CVE-2023-23626", + "GHSA-2h6c-j3gf-xp9r" + ], + "details": "When feeding untrusted user input into the size parameter of `NewBitfield` and FromBytes functions, an attacker can trigger panics.\n\nThis happens when the size is a not a multiple of 8 or is negative.\n\nA workaround is to ensure size%8 == 0 \u0026\u0026 size \u003e= 0 yourself before calling NewBitfield or FromBytes.", + "id": "GO-2023-1558", + "modified": "2023-02-14T19:41:21Z", + "published": "2023-02-14T19:41:21Z", + "references": [ + { + "type": "ADVISORY", + "url": "https://github.com/ipfs/go-bitfield/security/advisories/GHSA-2h6c-j3gf-xp9r" + }, + { + "type": "FIX", + "url": "https://github.com/ipfs/go-bitfield/commit/5e1d256fe043fc4163343ccca83862c69c52e579" + } + ], + "schema_version": "1.3.1" + } + }, + { + "Modules": [ + { + "FixedVersion": "v1.3.2", + "FoundVersion": "v1.3.1", + "Packages": [ + { + "CallStacks": null, + "Path": "github.com/gogo/protobuf/plugin/unmarshal" + } + ], + "Path": "github.com/gogo/protobuf" + } + ], + "OSV": { + "affected": [ + { + "database_specific": { + "url": "https://pkg.go.dev/vuln/GO-2021-0053" + }, + "ecosystem_specific": { + "imports": [ + { + "path": "github.com/gogo/protobuf/plugin/unmarshal", + "symbols": [ + "unmarshal.Generate", + "unmarshal.field" + ] + } + ] + }, + "package": { + "ecosystem": "Go", + "name": "github.com/gogo/protobuf" + }, + "ranges": [ + { + "events": [ + { + "introduced": "0" + }, + { + "fixed": "1.3.2" + } + ], + "type": "SEMVER" + } + ] + } + ], + "aliases": [ + "CVE-2021-3121", + "GHSA-c3h9-896r-86jm" + ], + "details": "Due to improper bounds checking, maliciously crafted input to generated Unmarshal methods can cause an out-of-bounds panic. If parsing messages from untrusted parties, this may be used as a denial of service vector.", + "id": "GO-2021-0053", + "modified": "2023-02-10T16:51:38Z", + "published": "2021-04-14T20:04:52Z", + "references": [ + { + "type": "FIX", + "url": "https://github.com/gogo/protobuf/commit/b03c65ea87cdc3521ede29f62fe3ce239267c1bc" + } + ], + "schema_version": "1.3.1" + } + } + ] +} + diff --git a/internal/govulncheckshim/fixtures/test-project/go.mod b/internal/govulncheckshim/fixtures/test-project/go.mod new file mode 100644 index 00000000000..999838ee6ef --- /dev/null +++ b/internal/govulncheckshim/fixtures/test-project/go.mod @@ -0,0 +1,9 @@ +module github.com/ossf-tests/osv-e2e + +go 1.19 + +require github.com/gogo/protobuf v1.3.1 + +require github.com/ipfs/go-bitfield v1.0.0 + +require golang.org/x/image v0.4.0 // indirect diff --git a/internal/govulncheckshim/fixtures/test-project/go.sum b/internal/govulncheckshim/fixtures/test-project/go.sum new file mode 100644 index 00000000000..d4d95224c5c --- /dev/null +++ b/internal/govulncheckshim/fixtures/test-project/go.sum @@ -0,0 +1,33 @@ +github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/ipfs/go-bitfield v1.0.0 h1:y/XHm2GEmD9wKngheWNNCNL0pzrWXZwCdQGv1ikXknQ= +github.com/ipfs/go-bitfield v1.0.0/go.mod h1:N/UiujQy+K+ceU1EF5EkVd1TNqevLrCQMIcAEPrdtus= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/image v0.4.0 h1:x1RWAiZIvERqkltrFjtQP1ycmiR5pmhjtCfVOtdURuQ= +golang.org/x/image v0.4.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/govulncheckshim/fixtures/test-project/main.go b/internal/govulncheckshim/fixtures/test-project/main.go new file mode 100644 index 00000000000..65261ae69b1 --- /dev/null +++ b/internal/govulncheckshim/fixtures/test-project/main.go @@ -0,0 +1,14 @@ +package main + +import ( + "github.com/gogo/protobuf/plugin/unmarshal" + "github.com/gogo/protobuf/version" + "github.com/ipfs/go-bitfield" +) + +func main() { + print(version.AtLeast("v1.2.3")) + unmarshal.NewUnmarshal() + + bitfield.NewBitfield(14) +} diff --git a/internal/govulncheckshim/govulncheckshim.go b/internal/govulncheckshim/govulncheckshim.go new file mode 100644 index 00000000000..9f512c3ef8e --- /dev/null +++ b/internal/govulncheckshim/govulncheckshim.go @@ -0,0 +1,80 @@ +package govulncheckshim + +import ( + "context" + "fmt" + "path/filepath" + "strings" + + "golang.org/x/tools/go/packages" + "golang.org/x/vuln/exp/govulncheck" + "golang.org/x/vuln/vulncheck" + + "github.com/google/osv-scanner/pkg/models" +) + +type packageError struct { + Errors []packages.Error +} + +func (e *packageError) Error() string { + var b strings.Builder + fmt.Fprintln(&b, "Packages contain errors:") + for _, e := range e.Errors { + fmt.Fprintln(&b, e) + } + + return b.String() +} + +// RunGoVulnCheck runs govulncheck with a subset of vulnerabilities identified by osv-scanner +func RunGoVulnCheck(path string, vulns []models.Vulnerability) (*govulncheck.Result, error) { + path, err := filepath.Abs(path) + if err != nil { + return nil, err + } + scanPath := filepath.Join(path, "...") + client := newClient(vulns) + + cfg := &govulncheck.Config{Client: client} + pkgs, err := loadPackages([]string{scanPath}, filepath.Dir(scanPath)) + if err != nil { + return nil, err + } + + return govulncheck.Source(context.Background(), cfg, pkgs) +} + +// loadPackages loads the packages matching patterns at dir using build tags +// provided by tagsFlag. Uses load mode needed for vulncheck analysis. If the +// packages contain errors, a packageError is returned containing a list of +// the errors, along with the packages themselves. +func loadPackages(patterns []string, dir string) ([]*vulncheck.Package, error) { + var buildFlags []string + // TODO: Enable user configurable tags + // if tagsFlag != nil { + // buildFlags = []string{fmt.Sprintf("-tags=%s", strings.Join(tagsFlag, ","))} + // } + + cfg := &packages.Config{Dir: dir, Tests: true} + cfg.Mode |= packages.NeedName | packages.NeedImports | packages.NeedTypes | + packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedDeps | + packages.NeedModule + cfg.BuildFlags = buildFlags + + pkgs, err := packages.Load(cfg, patterns...) + if err != nil { + return nil, err + } + vpkgs := vulncheck.Convert(pkgs) + + var perrs []packages.Error + packages.Visit(pkgs, nil, func(p *packages.Package) { + perrs = append(perrs, p.Errors...) + }) + if len(perrs) > 0 { + err = &packageError{perrs} + } + + return vpkgs, err +} diff --git a/internal/govulncheckshim/govulncheckshim_test.go b/internal/govulncheckshim/govulncheckshim_test.go new file mode 100644 index 00000000000..ef54557ff0d --- /dev/null +++ b/internal/govulncheckshim/govulncheckshim_test.go @@ -0,0 +1,51 @@ +package govulncheckshim + +import ( + "encoding/json" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/google/osv-scanner/internal/testutility" + "github.com/google/osv-scanner/pkg/models" +) + +func Test_RunGoVulnCheck(t *testing.T) { + t.Parallel() + entries, err := os.ReadDir("fixtures") + if err != nil { + t.Errorf("failed to read fixtures dir: %v", err) + } + + vulns := []models.Vulnerability{} + for _, de := range entries { + if !de.Type().IsRegular() { + continue + } + + if !strings.HasSuffix(de.Name(), ".json") { + continue + } + + file, err := os.Open(filepath.Join("fixtures", de.Name())) + if err != nil { + t.Errorf("failed to open fixture vuln files: %v", err) + } + + newVuln := models.Vulnerability{} + err = json.NewDecoder(file).Decode(&newVuln) + if err != nil { + t.Errorf("failed to decode fixture vuln files: %v", err) + } + vulns = append(vulns, newVuln) + } + + res, err := RunGoVulnCheck("fixtures/test-project", vulns) + if err != nil { + t.Errorf("failed to run RunGoVulnCheck: %v", err) + } + + res.Vulns[0].Modules[0].Packages[0].CallStacks[0].Frames[0].Position.Filename = "" + testutility.AssertMatchFixtureJSON(t, "fixtures/snapshots/govulncheckshim_test.json", res) +} diff --git a/internal/output/table.go b/internal/output/table.go index b42c9a1c43c..c363a0b758f 100644 --- a/internal/output/table.go +++ b/internal/output/table.go @@ -19,7 +19,6 @@ func PrintTableResults(vulnResult *models.VulnerabilityResults, outputWriter io. outputTable := table.NewWriter() outputTable.SetOutputMirror(outputWriter) outputTable.AppendHeader(table.Row{"OSV URL (ID In Bold)", "Ecosystem", "Package", "Version", "Source"}) - width, _, err := term.GetSize(int(os.Stdout.Fd())) isTerminal := false if err == nil { // If output is a terminal, set max length to width and add styling @@ -40,6 +39,34 @@ func PrintTableResults(vulnResult *models.VulnerabilityResults, outputWriter io. } func tableBuilder(outputTable table.Writer, vulnResult *models.VulnerabilityResults, addStyling bool) table.Writer { + rows := tableBuilderInner(vulnResult, addStyling, true) + for _, elem := range rows { + outputTable.AppendRow(elem.row, table.RowConfig{AutoMerge: elem.shouldMerge}) + } + + uncalledRows := tableBuilderInner(vulnResult, addStyling, false) + if len(uncalledRows) == 0 { + return outputTable + } + + outputTable.AppendSeparator() + outputTable.AppendRow(table.Row{"Uncalled vulnerabilities"}) + outputTable.AppendSeparator() + + for _, elem := range uncalledRows { + outputTable.AppendRow(elem.row, table.RowConfig{AutoMerge: elem.shouldMerge}) + } + + return outputTable +} + +type tbInnerResponse struct { + row table.Row + shouldMerge bool +} + +func tableBuilderInner(vulnResult *models.VulnerabilityResults, addStyling bool, calledVulns bool) []tbInnerResponse { + allOutputRows := []tbInnerResponse{} // Working directory used to simplify path workingDir, workingDirErr := os.Getwd() for _, sourceRes := range vulnResult.Results { @@ -54,6 +81,10 @@ func tableBuilder(outputTable table.Writer, vulnResult *models.VulnerabilityResu // Merge groups into the same row for _, group := range pkg.Groups { + if group.IsCalled() != calledVulns { + continue + } + outputRow := table.Row{} shouldMerge := false @@ -77,10 +108,13 @@ func tableBuilder(outputTable table.Writer, vulnResult *models.VulnerabilityResu } outputRow = append(outputRow, source.Path) - outputTable.AppendRow(outputRow, table.RowConfig{AutoMerge: shouldMerge}) + allOutputRows = append(allOutputRows, tbInnerResponse{ + row: outputRow, + shouldMerge: shouldMerge, + }) } } } - return outputTable + return allOutputRows } diff --git a/internal/sourceanalysis/fixtures/govulncheckinput.json b/internal/sourceanalysis/fixtures/govulncheckinput.json new file mode 100644 index 00000000000..e70cca41953 --- /dev/null +++ b/internal/sourceanalysis/fixtures/govulncheckinput.json @@ -0,0 +1,169 @@ +{ + "GO-2021-0053": { + "OSV": { + "id": "GO-2021-0053", + "published": "2021-04-14T20:04:52Z", + "modified": "2023-02-10T16:51:38Z", + "aliases": [ + "CVE-2021-3121", + "GHSA-c3h9-896r-86jm" + ], + "details": "Due to improper bounds checking, maliciously crafted input to generated Unmarshal methods can cause an out-of-bounds panic. If parsing messages from untrusted parties, this may be used as a denial of service vector.", + "affected": [ + { + "package": { + "name": "github.com/gogo/protobuf", + "ecosystem": "Go" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "1.3.2" + } + ] + } + ], + "database_specific": { + "url": "https://pkg.go.dev/vuln/GO-2021-0053" + }, + "ecosystem_specific": { + "imports": [ + { + "path": "github.com/gogo/protobuf/plugin/unmarshal", + "symbols": [ + "unmarshal.Generate", + "unmarshal.field" + ] + } + ] + } + } + ], + "references": [ + { + "type": "FIX", + "url": "https://github.com/gogo/protobuf/commit/b03c65ea87cdc3521ede29f62fe3ce239267c1bc" + } + ], + "schema_version": "1.3.0" + }, + "Modules": [ + { + "Path": "github.com/gogo/protobuf", + "FoundVersion": "v1.3.1", + "FixedVersion": "v1.3.2", + "Packages": [ + { + "Path": "github.com/gogo/protobuf/plugin/unmarshal", + "CallStacks": null + } + ] + } + ] + }, + "GO-2023-1558": { + "OSV": { + "id": "GO-2023-1558", + "published": "2023-02-14T19:41:21Z", + "modified": "2023-02-14T19:41:21Z", + "aliases": [ + "CVE-2023-23626", + "GHSA-2h6c-j3gf-xp9r" + ], + "details": "When feeding untrusted user input into the size parameter of `NewBitfield` and FromBytes functions, an attacker can trigger panics.\n\nThis happens when the size is a not a multiple of 8 or is negative.\n\nA workaround is to ensure size%8 == 0 \u0026\u0026 size \u003e= 0 yourself before calling NewBitfield or FromBytes.", + "affected": [ + { + "package": { + "name": "github.com/ipfs/go-bitfield", + "ecosystem": "Go" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "1.1.0" + } + ] + } + ], + "database_specific": { + "url": "https://pkg.go.dev/vuln/GO-2023-1558" + }, + "ecosystem_specific": { + "imports": [ + { + "path": "github.com/ipfs/go-bitfield", + "symbols": [ + "FromBytes", + "NewBitfield" + ] + } + ] + } + } + ], + "references": [ + { + "type": "ADVISORY", + "url": "https://github.com/ipfs/go-bitfield/security/advisories/GHSA-2h6c-j3gf-xp9r" + }, + { + "type": "FIX", + "url": "https://github.com/ipfs/go-bitfield/commit/5e1d256fe043fc4163343ccca83862c69c52e579" + } + ], + "schema_version": "1.3.0" + }, + "Modules": [ + { + "Path": "github.com/ipfs/go-bitfield", + "FoundVersion": "v1.0.0", + "FixedVersion": "v1.1.0", + "Packages": [ + { + "Path": "github.com/ipfs/go-bitfield", + "CallStacks": [ + { + "Symbol": "NewBitfield", + "Summary": "../../internal/govulncheckshim/fixtures/test-project/main.go:13:22: github.com/ossf-tests/osv-e2e.main calls github.com/ipfs/go-bitfield.NewBitfield", + "Frames": [ + { + "PkgPath": "github.com/ossf-tests/osv-e2e", + "FuncName": "main", + "RecvType": "", + "Position": { + "Filename": "/home/rexpan/Documents/Projects/osv-scanner/internal/govulncheckshim/fixtures/test-project/main.go", + "Offset": 234, + "Line": 13, + "Column": 22 + } + }, + { + "PkgPath": "github.com/ipfs/go-bitfield", + "FuncName": "NewBitfield", + "RecvType": "", + "Position": { + "Filename": "", + "Offset": 0, + "Line": 0, + "Column": 0 + } + } + ] + } + ] + } + ] + } + ] + } +} \ No newline at end of file diff --git a/internal/sourceanalysis/fixtures/input.json b/internal/sourceanalysis/fixtures/input.json new file mode 100644 index 00000000000..3f64759ca1c --- /dev/null +++ b/internal/sourceanalysis/fixtures/input.json @@ -0,0 +1,457 @@ +[ + { + "package": { + "name": "github.com/gogo/protobuf", + "version": "1.3.1", + "ecosystem": "Go" + }, + "vulnerabilities": [ + { + "schema_version": "1.3.0", + "id": "GHSA-c3h9-896r-86jm", + "modified": "2022-03-28T20:28:00Z", + "published": "2022-03-28T20:28:00Z", + "aliases": [ + "CVE-2021-3121" + ], + "summary": "Improper Input Validation in GoGo Protobuf", + "details": "An issue was discovered in GoGo Protobuf before 1.3.2. plugin/unmarshal/unmarshal.go lacks certain index validation, aka the \"skippy peanut butter\" issue.", + "affected": [ + { + "package": { + "ecosystem": "Go", + "name": "github.com/gogo/protobuf", + "purl": "pkg:golang/github.com/gogo/protobuf" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "1.3.2" + } + ] + } + ], + "database_specific": { + "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2022/03/GHSA-c3h9-896r-86jm/GHSA-c3h9-896r-86jm.json" + } + } + ], + "references": [ + { + "type": "ADVISORY", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-3121" + }, + { + "type": "WEB", + "url": "https://github.com/gogo/protobuf/commit/b03c65ea87cdc3521ede29f62fe3ce239267c1bc" + }, + { + "type": "WEB", + "url": "https://discuss.hashicorp.com/t/hcsec-2021-23-consul-exposed-to-denial-of-service-in-gogo-protobuf-dependency/29025" + }, + { + "type": "PACKAGE", + "url": "https://github.com/gogo/protobuf" + }, + { + "type": "WEB", + "url": "https://github.com/gogo/protobuf/compare/v1.3.1...v1.3.2" + }, + { + "type": "WEB", + "url": "https://lists.apache.org/thread.html/r68032132c0399c29d6cdc7bd44918535da54060a10a12b1591328bff@%3Cnotifications.skywalking.apache.org%3E" + }, + { + "type": "WEB", + "url": "https://lists.apache.org/thread.html/r88d69555cb74a129a7bf84838073b61259b4a3830190e05a3b87994e@%3Ccommits.pulsar.apache.org%3E" + }, + { + "type": "WEB", + "url": "https://lists.apache.org/thread.html/rc1e9ff22c5641d73701ba56362fb867d40ed287cca000b131dcf4a44@%3Ccommits.pulsar.apache.org%3E" + }, + { + "type": "WEB", + "url": "https://pkg.go.dev/vuln/GO-2021-0053" + }, + { + "type": "WEB", + "url": "https://security.netapp.com/advisory/ntap-20210219-0006/" + } + ], + "database_specific": { + "cwe_ids": [ + "CWE-129", + "CWE-20" + ], + "github_reviewed": true, + "github_reviewed_at": "2022-03-28T20:28:00Z", + "nvd_published_at": "2021-01-11T06:15:00Z", + "severity": "HIGH" + } + }, + { + "schema_version": "1.3.0", + "id": "GO-2021-0053", + "modified": "2023-02-10T16:51:38Z", + "published": "2021-04-14T20:04:52Z", + "aliases": [ + "CVE-2021-3121", + "GHSA-c3h9-896r-86jm" + ], + "summary": "", + "details": "Due to improper bounds checking, maliciously crafted input to generated Unmarshal methods can cause an out-of-bounds panic. If parsing messages from untrusted parties, this may be used as a denial of service vector.", + "affected": [ + { + "package": { + "ecosystem": "Go", + "name": "github.com/gogo/protobuf", + "purl": "pkg:golang/github.com/gogo/protobuf" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "1.3.2" + } + ] + } + ], + "database_specific": { + "source": "https://vuln.go.dev/ID/GO-2021-0053.json", + "url": "https://pkg.go.dev/vuln/GO-2021-0053" + }, + "ecosystem_specific": { + "imports": [ + { + "path": "github.com/gogo/protobuf/plugin/unmarshal", + "symbols": [ + "unmarshal.Generate", + "unmarshal.field" + ] + } + ] + } + } + ], + "references": [ + { + "type": "FIX", + "url": "https://github.com/gogo/protobuf/commit/b03c65ea87cdc3521ede29f62fe3ce239267c1bc" + } + ] + } + ], + "groups": [ + { + "ids": [ + "GHSA-c3h9-896r-86jm", + "GO-2021-0053" + ] + } + ] + }, + { + "package": { + "name": "github.com/ipfs/go-bitfield", + "version": "1.0.0", + "ecosystem": "Go" + }, + "vulnerabilities": [ + { + "schema_version": "1.3.0", + "id": "GHSA-2h6c-j3gf-xp9r", + "modified": "2023-02-10T19:52:45Z", + "published": "2023-02-10T19:52:45Z", + "aliases": [ + "CVE-2023-23626" + ], + "summary": "IPFS go-bitfield vulnerable to DoS via malformed size arguments", + "details": "### Impact\nWhen feeding untrusted user input into the size parameter of `NewBitfield` and `FromBytes` functions, an attacker can trigger `panic`s.\n\nThis happen when the `size` is a not a multiple of `8` or is negative.\nThere were already a note in the `NewBitfield` documentation:\n\u003e ```\n\u003e Panics if size is not a multiple of 8.\n\u003e ````\n\nBut it incomplete and missing from `FromBytes`'s documentation.\n\nThis has been replaced by returning an `(Bitfield, error)` and returning a non nil error if the size is wrong.\n\n### Patches\n- https://github.com/ipfs/go-bitfield/commit/5e1d256fe043fc4163343ccca83862c69c52e579\n\n### Workarounds\n- Ensure `size%8 == 0 \u0026\u0026 size \u003e= 0` yourself before calling `NewBitfield` or `FromBytes`\n\n### References\n- https://github.com/ipfs/go-unixfs/security/advisories/GHSA-q264-w97q-q778\n", + "affected": [ + { + "package": { + "ecosystem": "Go", + "name": "github.com/ipfs/go-bitfield", + "purl": "pkg:golang/github.com/ipfs/go-bitfield" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "1.0.0" + }, + { + "fixed": "1.1.0" + } + ] + } + ], + "versions": [ + "1.0.0" + ], + "database_specific": { + "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2023/02/GHSA-2h6c-j3gf-xp9r/GHSA-2h6c-j3gf-xp9r.json" + } + } + ], + "references": [ + { + "type": "WEB", + "url": "https://github.com/ipfs/go-bitfield/security/advisories/GHSA-2h6c-j3gf-xp9r" + }, + { + "type": "ADVISORY", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-23626" + }, + { + "type": "WEB", + "url": "https://github.com/ipfs/go-bitfield/commit/5e1d256fe043fc4163343ccca83862c69c52e579" + }, + { + "type": "PACKAGE", + "url": "https://github.com/ipfs/go-bitfield" + }, + { + "type": "WEB", + "url": "https://pkg.go.dev/vuln/GO-2023-1558" + } + ], + "database_specific": { + "cwe_ids": [ + "CWE-1284", + "CWE-754" + ], + "github_reviewed": true, + "github_reviewed_at": "2023-02-10T19:52:45Z", + "nvd_published_at": "2023-02-09T21:15:00Z", + "severity": "MODERATE" + } + }, + { + "schema_version": "1.3.0", + "id": "GO-2023-1558", + "modified": "2023-02-14T19:41:21Z", + "published": "2023-02-14T19:41:21Z", + "aliases": [ + "CVE-2023-23626", + "GHSA-2h6c-j3gf-xp9r" + ], + "summary": "", + "details": "When feeding untrusted user input into the size parameter of `NewBitfield` and FromBytes functions, an attacker can trigger panics.\n\nThis happens when the size is a not a multiple of 8 or is negative.\n\nA workaround is to ensure size%8 == 0 \u0026\u0026 size \u003e= 0 yourself before calling NewBitfield or FromBytes.", + "affected": [ + { + "package": { + "ecosystem": "Go", + "name": "github.com/ipfs/go-bitfield", + "purl": "pkg:golang/github.com/ipfs/go-bitfield" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "1.1.0" + } + ] + } + ], + "database_specific": { + "source": "https://vuln.go.dev/ID/GO-2023-1558.json", + "url": "https://pkg.go.dev/vuln/GO-2023-1558" + }, + "ecosystem_specific": { + "imports": [ + { + "path": "github.com/ipfs/go-bitfield", + "symbols": [ + "FromBytes", + "NewBitfield" + ] + } + ] + } + } + ], + "references": [ + { + "type": "ADVISORY", + "url": "https://github.com/ipfs/go-bitfield/security/advisories/GHSA-2h6c-j3gf-xp9r" + }, + { + "type": "FIX", + "url": "https://github.com/ipfs/go-bitfield/commit/5e1d256fe043fc4163343ccca83862c69c52e579" + } + ] + } + ], + "groups": [ + { + "ids": [ + "GHSA-2h6c-j3gf-xp9r", + "GO-2023-1558" + ] + } + ] + }, + { + "package": { + "name": "golang.org/x/image", + "version": "0.4.0", + "ecosystem": "Go" + }, + "vulnerabilities": [ + { + "schema_version": "1.3.0", + "id": "GHSA-qgc7-mgm3-q253", + "modified": "2023-02-17T13:59:44Z", + "published": "2023-02-17T13:59:44Z", + "aliases": [ + "CVE-2022-41727" + ], + "summary": "Uncontrolled Resource Consumption", + "details": "An attacker can craft a malformed TIFF image which will consume a significant amount of memory when passed to DecodeConfig. This could lead to a denial of service.", + "affected": [ + { + "package": { + "ecosystem": "Go", + "name": "golang.org/x/image", + "purl": "pkg:golang/golang.org/x/image" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "0.5.0" + } + ] + } + ], + "database_specific": { + "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2023/02/GHSA-qgc7-mgm3-q253/GHSA-qgc7-mgm3-q253.json" + } + } + ], + "references": [ + { + "type": "ADVISORY", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-41727" + }, + { + "type": "WEB", + "url": "https://go.dev/cl/468195" + }, + { + "type": "WEB", + "url": "https://go.dev/issue/58003" + }, + { + "type": "WEB", + "url": "https://groups.google.com/g/golang-announce/c/ag-FiyjlD5o" + }, + { + "type": "WEB", + "url": "https://pkg.go.dev/vuln/GO-2023-1572" + } + ], + "database_specific": { + "cwe_ids": [ + "CWE-400" + ], + "github_reviewed": true, + "github_reviewed_at": "2023-02-17T13:59:44Z", + "nvd_published_at": null, + "severity": "LOW" + } + }, + { + "schema_version": "1.3.0", + "id": "GO-2023-1572", + "modified": "2023-02-16T22:25:24Z", + "published": "2023-02-16T22:25:24Z", + "aliases": [ + "CVE-2022-41727" + ], + "summary": "", + "details": "An attacker can craft a malformed TIFF image which will consume a significant amount of memory when passed to DecodeConfig. This could lead to a denial of service.", + "affected": [ + { + "package": { + "ecosystem": "Go", + "name": "golang.org/x/image", + "purl": "pkg:golang/golang.org/x/image" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "0.5.0" + } + ] + } + ], + "database_specific": { + "source": "https://vuln.go.dev/ID/GO-2023-1572.json", + "url": "https://pkg.go.dev/vuln/GO-2023-1572" + }, + "ecosystem_specific": { + "imports": [ + { + "path": "golang.org/x/image/tiff", + "symbols": [ + "Decode", + "DecodeConfig", + "decoder.ifdUint", + "newDecoder" + ] + } + ] + } + } + ], + "references": [ + { + "type": "REPORT", + "url": "https://go.dev/issue/58003" + }, + { + "type": "FIX", + "url": "https://go.dev/cl/468195" + }, + { + "type": "WEB", + "url": "https://groups.google.com/g/golang-announce/c/ag-FiyjlD5o" + } + ] + } + ], + "groups": [ + { + "ids": [ + "GHSA-qgc7-mgm3-q253", + "GO-2023-1572" + ] + } + ] + } +] \ No newline at end of file diff --git a/internal/sourceanalysis/fixtures/output.json b/internal/sourceanalysis/fixtures/output.json new file mode 100644 index 00000000000..0b8eb66a627 --- /dev/null +++ b/internal/sourceanalysis/fixtures/output.json @@ -0,0 +1,472 @@ +[ + { + "groups": [ + { + "experimentalAnalysis": { + "GO-2021-0053": { + "called": false + } + }, + "ids": [ + "GHSA-c3h9-896r-86jm", + "GO-2021-0053" + ] + } + ], + "package": { + "ecosystem": "Go", + "name": "github.com/gogo/protobuf", + "version": "1.3.1" + }, + "vulnerabilities": [ + { + "affected": [ + { + "database_specific": { + "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2022/03/GHSA-c3h9-896r-86jm/GHSA-c3h9-896r-86jm.json" + }, + "package": { + "ecosystem": "Go", + "name": "github.com/gogo/protobuf", + "purl": "pkg:golang/github.com/gogo/protobuf" + }, + "ranges": [ + { + "events": [ + { + "introduced": "0" + }, + { + "fixed": "1.3.2" + } + ], + "type": "SEMVER" + } + ] + } + ], + "aliases": [ + "CVE-2021-3121" + ], + "database_specific": { + "cwe_ids": [ + "CWE-129", + "CWE-20" + ], + "github_reviewed": true, + "github_reviewed_at": "2022-03-28T20:28:00Z", + "nvd_published_at": "2021-01-11T06:15:00Z", + "severity": "HIGH" + }, + "details": "An issue was discovered in GoGo Protobuf before 1.3.2. plugin/unmarshal/unmarshal.go lacks certain index validation, aka the \"skippy peanut butter\" issue.", + "id": "GHSA-c3h9-896r-86jm", + "modified": "2022-03-28T20:28:00Z", + "published": "2022-03-28T20:28:00Z", + "references": [ + { + "type": "ADVISORY", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-3121" + }, + { + "type": "WEB", + "url": "https://github.com/gogo/protobuf/commit/b03c65ea87cdc3521ede29f62fe3ce239267c1bc" + }, + { + "type": "WEB", + "url": "https://discuss.hashicorp.com/t/hcsec-2021-23-consul-exposed-to-denial-of-service-in-gogo-protobuf-dependency/29025" + }, + { + "type": "PACKAGE", + "url": "https://github.com/gogo/protobuf" + }, + { + "type": "WEB", + "url": "https://github.com/gogo/protobuf/compare/v1.3.1...v1.3.2" + }, + { + "type": "WEB", + "url": "https://lists.apache.org/thread.html/r68032132c0399c29d6cdc7bd44918535da54060a10a12b1591328bff@%3Cnotifications.skywalking.apache.org%3E" + }, + { + "type": "WEB", + "url": "https://lists.apache.org/thread.html/r88d69555cb74a129a7bf84838073b61259b4a3830190e05a3b87994e@%3Ccommits.pulsar.apache.org%3E" + }, + { + "type": "WEB", + "url": "https://lists.apache.org/thread.html/rc1e9ff22c5641d73701ba56362fb867d40ed287cca000b131dcf4a44@%3Ccommits.pulsar.apache.org%3E" + }, + { + "type": "WEB", + "url": "https://pkg.go.dev/vuln/GO-2021-0053" + }, + { + "type": "WEB", + "url": "https://security.netapp.com/advisory/ntap-20210219-0006/" + } + ], + "schema_version": "1.3.0", + "summary": "Improper Input Validation in GoGo Protobuf" + }, + { + "affected": [ + { + "database_specific": { + "source": "https://vuln.go.dev/ID/GO-2021-0053.json", + "url": "https://pkg.go.dev/vuln/GO-2021-0053" + }, + "ecosystem_specific": { + "imports": [ + { + "path": "github.com/gogo/protobuf/plugin/unmarshal", + "symbols": [ + "unmarshal.Generate", + "unmarshal.field" + ] + } + ] + }, + "package": { + "ecosystem": "Go", + "name": "github.com/gogo/protobuf", + "purl": "pkg:golang/github.com/gogo/protobuf" + }, + "ranges": [ + { + "events": [ + { + "introduced": "0" + }, + { + "fixed": "1.3.2" + } + ], + "type": "SEMVER" + } + ] + } + ], + "aliases": [ + "CVE-2021-3121", + "GHSA-c3h9-896r-86jm" + ], + "details": "Due to improper bounds checking, maliciously crafted input to generated Unmarshal methods can cause an out-of-bounds panic. If parsing messages from untrusted parties, this may be used as a denial of service vector.", + "id": "GO-2021-0053", + "modified": "2023-02-10T16:51:38Z", + "published": "2021-04-14T20:04:52Z", + "references": [ + { + "type": "FIX", + "url": "https://github.com/gogo/protobuf/commit/b03c65ea87cdc3521ede29f62fe3ce239267c1bc" + } + ], + "schema_version": "1.3.0", + "summary": "" + } + ] + }, + { + "groups": [ + { + "experimentalAnalysis": { + "GO-2023-1558": { + "called": true + } + }, + "ids": [ + "GHSA-2h6c-j3gf-xp9r", + "GO-2023-1558" + ] + } + ], + "package": { + "ecosystem": "Go", + "name": "github.com/ipfs/go-bitfield", + "version": "1.0.0" + }, + "vulnerabilities": [ + { + "affected": [ + { + "database_specific": { + "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2023/02/GHSA-2h6c-j3gf-xp9r/GHSA-2h6c-j3gf-xp9r.json" + }, + "package": { + "ecosystem": "Go", + "name": "github.com/ipfs/go-bitfield", + "purl": "pkg:golang/github.com/ipfs/go-bitfield" + }, + "ranges": [ + { + "events": [ + { + "introduced": "1.0.0" + }, + { + "fixed": "1.1.0" + } + ], + "type": "SEMVER" + } + ], + "versions": [ + "1.0.0" + ] + } + ], + "aliases": [ + "CVE-2023-23626" + ], + "database_specific": { + "cwe_ids": [ + "CWE-1284", + "CWE-754" + ], + "github_reviewed": true, + "github_reviewed_at": "2023-02-10T19:52:45Z", + "nvd_published_at": "2023-02-09T21:15:00Z", + "severity": "MODERATE" + }, + "details": "### Impact\nWhen feeding untrusted user input into the size parameter of `NewBitfield` and `FromBytes` functions, an attacker can trigger `panic`s.\n\nThis happen when the `size` is a not a multiple of `8` or is negative.\nThere were already a note in the `NewBitfield` documentation:\n\u003e ```\n\u003e Panics if size is not a multiple of 8.\n\u003e ````\n\nBut it incomplete and missing from `FromBytes`'s documentation.\n\nThis has been replaced by returning an `(Bitfield, error)` and returning a non nil error if the size is wrong.\n\n### Patches\n- https://github.com/ipfs/go-bitfield/commit/5e1d256fe043fc4163343ccca83862c69c52e579\n\n### Workarounds\n- Ensure `size%8 == 0 \u0026\u0026 size \u003e= 0` yourself before calling `NewBitfield` or `FromBytes`\n\n### References\n- https://github.com/ipfs/go-unixfs/security/advisories/GHSA-q264-w97q-q778\n", + "id": "GHSA-2h6c-j3gf-xp9r", + "modified": "2023-02-10T19:52:45Z", + "published": "2023-02-10T19:52:45Z", + "references": [ + { + "type": "WEB", + "url": "https://github.com/ipfs/go-bitfield/security/advisories/GHSA-2h6c-j3gf-xp9r" + }, + { + "type": "ADVISORY", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-23626" + }, + { + "type": "WEB", + "url": "https://github.com/ipfs/go-bitfield/commit/5e1d256fe043fc4163343ccca83862c69c52e579" + }, + { + "type": "PACKAGE", + "url": "https://github.com/ipfs/go-bitfield" + }, + { + "type": "WEB", + "url": "https://pkg.go.dev/vuln/GO-2023-1558" + } + ], + "schema_version": "1.3.0", + "summary": "IPFS go-bitfield vulnerable to DoS via malformed size arguments" + }, + { + "affected": [ + { + "database_specific": { + "source": "https://vuln.go.dev/ID/GO-2023-1558.json", + "url": "https://pkg.go.dev/vuln/GO-2023-1558" + }, + "ecosystem_specific": { + "imports": [ + { + "path": "github.com/ipfs/go-bitfield", + "symbols": [ + "FromBytes", + "NewBitfield" + ] + } + ] + }, + "package": { + "ecosystem": "Go", + "name": "github.com/ipfs/go-bitfield", + "purl": "pkg:golang/github.com/ipfs/go-bitfield" + }, + "ranges": [ + { + "events": [ + { + "introduced": "0" + }, + { + "fixed": "1.1.0" + } + ], + "type": "SEMVER" + } + ] + } + ], + "aliases": [ + "CVE-2023-23626", + "GHSA-2h6c-j3gf-xp9r" + ], + "details": "When feeding untrusted user input into the size parameter of `NewBitfield` and FromBytes functions, an attacker can trigger panics.\n\nThis happens when the size is a not a multiple of 8 or is negative.\n\nA workaround is to ensure size%8 == 0 \u0026\u0026 size \u003e= 0 yourself before calling NewBitfield or FromBytes.", + "id": "GO-2023-1558", + "modified": "2023-02-14T19:41:21Z", + "published": "2023-02-14T19:41:21Z", + "references": [ + { + "type": "ADVISORY", + "url": "https://github.com/ipfs/go-bitfield/security/advisories/GHSA-2h6c-j3gf-xp9r" + }, + { + "type": "FIX", + "url": "https://github.com/ipfs/go-bitfield/commit/5e1d256fe043fc4163343ccca83862c69c52e579" + } + ], + "schema_version": "1.3.0", + "summary": "" + } + ] + }, + { + "groups": [ + { + "experimentalAnalysis": { + "GO-2023-1572": { + "called": false + } + }, + "ids": [ + "GHSA-qgc7-mgm3-q253", + "GO-2023-1572" + ] + } + ], + "package": { + "ecosystem": "Go", + "name": "golang.org/x/image", + "version": "0.4.0" + }, + "vulnerabilities": [ + { + "affected": [ + { + "database_specific": { + "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2023/02/GHSA-qgc7-mgm3-q253/GHSA-qgc7-mgm3-q253.json" + }, + "package": { + "ecosystem": "Go", + "name": "golang.org/x/image", + "purl": "pkg:golang/golang.org/x/image" + }, + "ranges": [ + { + "events": [ + { + "introduced": "0" + }, + { + "fixed": "0.5.0" + } + ], + "type": "SEMVER" + } + ] + } + ], + "aliases": [ + "CVE-2022-41727" + ], + "database_specific": { + "cwe_ids": [ + "CWE-400" + ], + "github_reviewed": true, + "github_reviewed_at": "2023-02-17T13:59:44Z", + "nvd_published_at": null, + "severity": "LOW" + }, + "details": "An attacker can craft a malformed TIFF image which will consume a significant amount of memory when passed to DecodeConfig. This could lead to a denial of service.", + "id": "GHSA-qgc7-mgm3-q253", + "modified": "2023-02-17T13:59:44Z", + "published": "2023-02-17T13:59:44Z", + "references": [ + { + "type": "ADVISORY", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-41727" + }, + { + "type": "WEB", + "url": "https://go.dev/cl/468195" + }, + { + "type": "WEB", + "url": "https://go.dev/issue/58003" + }, + { + "type": "WEB", + "url": "https://groups.google.com/g/golang-announce/c/ag-FiyjlD5o" + }, + { + "type": "WEB", + "url": "https://pkg.go.dev/vuln/GO-2023-1572" + } + ], + "schema_version": "1.3.0", + "summary": "Uncontrolled Resource Consumption" + }, + { + "affected": [ + { + "database_specific": { + "source": "https://vuln.go.dev/ID/GO-2023-1572.json", + "url": "https://pkg.go.dev/vuln/GO-2023-1572" + }, + "ecosystem_specific": { + "imports": [ + { + "path": "golang.org/x/image/tiff", + "symbols": [ + "Decode", + "DecodeConfig", + "decoder.ifdUint", + "newDecoder" + ] + } + ] + }, + "package": { + "ecosystem": "Go", + "name": "golang.org/x/image", + "purl": "pkg:golang/golang.org/x/image" + }, + "ranges": [ + { + "events": [ + { + "introduced": "0" + }, + { + "fixed": "0.5.0" + } + ], + "type": "SEMVER" + } + ] + } + ], + "aliases": [ + "CVE-2022-41727" + ], + "details": "An attacker can craft a malformed TIFF image which will consume a significant amount of memory when passed to DecodeConfig. This could lead to a denial of service.", + "id": "GO-2023-1572", + "modified": "2023-02-16T22:25:24Z", + "published": "2023-02-16T22:25:24Z", + "references": [ + { + "type": "REPORT", + "url": "https://go.dev/issue/58003" + }, + { + "type": "FIX", + "url": "https://go.dev/cl/468195" + }, + { + "type": "WEB", + "url": "https://groups.google.com/g/golang-announce/c/ag-FiyjlD5o" + } + ], + "schema_version": "1.3.0", + "summary": "" + } + ] + } +] \ No newline at end of file diff --git a/internal/sourceanalysis/fixtures/vulnbyid.json b/internal/sourceanalysis/fixtures/vulnbyid.json new file mode 100644 index 00000000000..5f831740cd1 --- /dev/null +++ b/internal/sourceanalysis/fixtures/vulnbyid.json @@ -0,0 +1,407 @@ +{ + "GHSA-2h6c-j3gf-xp9r": { + "schema_version": "1.3.0", + "id": "GHSA-2h6c-j3gf-xp9r", + "modified": "2023-02-10T19:52:45Z", + "published": "2023-02-10T19:52:45Z", + "aliases": [ + "CVE-2023-23626" + ], + "summary": "IPFS go-bitfield vulnerable to DoS via malformed size arguments", + "details": "### Impact\nWhen feeding untrusted user input into the size parameter of `NewBitfield` and `FromBytes` functions, an attacker can trigger `panic`s.\n\nThis happen when the `size` is a not a multiple of `8` or is negative.\nThere were already a note in the `NewBitfield` documentation:\n\u003e ```\n\u003e Panics if size is not a multiple of 8.\n\u003e ````\n\nBut it incomplete and missing from `FromBytes`'s documentation.\n\nThis has been replaced by returning an `(Bitfield, error)` and returning a non nil error if the size is wrong.\n\n### Patches\n- https://github.com/ipfs/go-bitfield/commit/5e1d256fe043fc4163343ccca83862c69c52e579\n\n### Workarounds\n- Ensure `size%8 == 0 \u0026\u0026 size \u003e= 0` yourself before calling `NewBitfield` or `FromBytes`\n\n### References\n- https://github.com/ipfs/go-unixfs/security/advisories/GHSA-q264-w97q-q778\n", + "affected": [ + { + "package": { + "ecosystem": "Go", + "name": "github.com/ipfs/go-bitfield", + "purl": "pkg:golang/github.com/ipfs/go-bitfield" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "1.0.0" + }, + { + "fixed": "1.1.0" + } + ] + } + ], + "versions": [ + "1.0.0" + ], + "database_specific": { + "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2023/02/GHSA-2h6c-j3gf-xp9r/GHSA-2h6c-j3gf-xp9r.json" + } + } + ], + "references": [ + { + "type": "WEB", + "url": "https://github.com/ipfs/go-bitfield/security/advisories/GHSA-2h6c-j3gf-xp9r" + }, + { + "type": "ADVISORY", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-23626" + }, + { + "type": "WEB", + "url": "https://github.com/ipfs/go-bitfield/commit/5e1d256fe043fc4163343ccca83862c69c52e579" + }, + { + "type": "PACKAGE", + "url": "https://github.com/ipfs/go-bitfield" + }, + { + "type": "WEB", + "url": "https://pkg.go.dev/vuln/GO-2023-1558" + } + ], + "database_specific": { + "cwe_ids": [ + "CWE-1284", + "CWE-754" + ], + "github_reviewed": true, + "github_reviewed_at": "2023-02-10T19:52:45Z", + "nvd_published_at": "2023-02-09T21:15:00Z", + "severity": "MODERATE" + } + }, + "GHSA-c3h9-896r-86jm": { + "schema_version": "1.3.0", + "id": "GHSA-c3h9-896r-86jm", + "modified": "2022-03-28T20:28:00Z", + "published": "2022-03-28T20:28:00Z", + "aliases": [ + "CVE-2021-3121" + ], + "summary": "Improper Input Validation in GoGo Protobuf", + "details": "An issue was discovered in GoGo Protobuf before 1.3.2. plugin/unmarshal/unmarshal.go lacks certain index validation, aka the \"skippy peanut butter\" issue.", + "affected": [ + { + "package": { + "ecosystem": "Go", + "name": "github.com/gogo/protobuf", + "purl": "pkg:golang/github.com/gogo/protobuf" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "1.3.2" + } + ] + } + ], + "database_specific": { + "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2022/03/GHSA-c3h9-896r-86jm/GHSA-c3h9-896r-86jm.json" + } + } + ], + "references": [ + { + "type": "ADVISORY", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-3121" + }, + { + "type": "WEB", + "url": "https://github.com/gogo/protobuf/commit/b03c65ea87cdc3521ede29f62fe3ce239267c1bc" + }, + { + "type": "WEB", + "url": "https://discuss.hashicorp.com/t/hcsec-2021-23-consul-exposed-to-denial-of-service-in-gogo-protobuf-dependency/29025" + }, + { + "type": "PACKAGE", + "url": "https://github.com/gogo/protobuf" + }, + { + "type": "WEB", + "url": "https://github.com/gogo/protobuf/compare/v1.3.1...v1.3.2" + }, + { + "type": "WEB", + "url": "https://lists.apache.org/thread.html/r68032132c0399c29d6cdc7bd44918535da54060a10a12b1591328bff@%3Cnotifications.skywalking.apache.org%3E" + }, + { + "type": "WEB", + "url": "https://lists.apache.org/thread.html/r88d69555cb74a129a7bf84838073b61259b4a3830190e05a3b87994e@%3Ccommits.pulsar.apache.org%3E" + }, + { + "type": "WEB", + "url": "https://lists.apache.org/thread.html/rc1e9ff22c5641d73701ba56362fb867d40ed287cca000b131dcf4a44@%3Ccommits.pulsar.apache.org%3E" + }, + { + "type": "WEB", + "url": "https://pkg.go.dev/vuln/GO-2021-0053" + }, + { + "type": "WEB", + "url": "https://security.netapp.com/advisory/ntap-20210219-0006/" + } + ], + "database_specific": { + "cwe_ids": [ + "CWE-129", + "CWE-20" + ], + "github_reviewed": true, + "github_reviewed_at": "2022-03-28T20:28:00Z", + "nvd_published_at": "2021-01-11T06:15:00Z", + "severity": "HIGH" + } + }, + "GHSA-qgc7-mgm3-q253": { + "schema_version": "1.3.0", + "id": "GHSA-qgc7-mgm3-q253", + "modified": "2023-02-17T13:59:44Z", + "published": "2023-02-17T13:59:44Z", + "aliases": [ + "CVE-2022-41727" + ], + "summary": "Uncontrolled Resource Consumption", + "details": "An attacker can craft a malformed TIFF image which will consume a significant amount of memory when passed to DecodeConfig. This could lead to a denial of service.", + "affected": [ + { + "package": { + "ecosystem": "Go", + "name": "golang.org/x/image", + "purl": "pkg:golang/golang.org/x/image" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "0.5.0" + } + ] + } + ], + "database_specific": { + "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2023/02/GHSA-qgc7-mgm3-q253/GHSA-qgc7-mgm3-q253.json" + } + } + ], + "references": [ + { + "type": "ADVISORY", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-41727" + }, + { + "type": "WEB", + "url": "https://go.dev/cl/468195" + }, + { + "type": "WEB", + "url": "https://go.dev/issue/58003" + }, + { + "type": "WEB", + "url": "https://groups.google.com/g/golang-announce/c/ag-FiyjlD5o" + }, + { + "type": "WEB", + "url": "https://pkg.go.dev/vuln/GO-2023-1572" + } + ], + "database_specific": { + "cwe_ids": [ + "CWE-400" + ], + "github_reviewed": true, + "github_reviewed_at": "2023-02-17T13:59:44Z", + "nvd_published_at": null, + "severity": "LOW" + } + }, + "GO-2021-0053": { + "schema_version": "1.3.0", + "id": "GO-2021-0053", + "modified": "2023-02-10T16:51:38Z", + "published": "2021-04-14T20:04:52Z", + "aliases": [ + "CVE-2021-3121", + "GHSA-c3h9-896r-86jm" + ], + "summary": "", + "details": "Due to improper bounds checking, maliciously crafted input to generated Unmarshal methods can cause an out-of-bounds panic. If parsing messages from untrusted parties, this may be used as a denial of service vector.", + "affected": [ + { + "package": { + "ecosystem": "Go", + "name": "github.com/gogo/protobuf", + "purl": "pkg:golang/github.com/gogo/protobuf" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "1.3.2" + } + ] + } + ], + "database_specific": { + "source": "https://vuln.go.dev/ID/GO-2021-0053.json", + "url": "https://pkg.go.dev/vuln/GO-2021-0053" + }, + "ecosystem_specific": { + "imports": [ + { + "path": "github.com/gogo/protobuf/plugin/unmarshal", + "symbols": [ + "unmarshal.Generate", + "unmarshal.field" + ] + } + ] + } + } + ], + "references": [ + { + "type": "FIX", + "url": "https://github.com/gogo/protobuf/commit/b03c65ea87cdc3521ede29f62fe3ce239267c1bc" + } + ] + }, + "GO-2023-1558": { + "schema_version": "1.3.0", + "id": "GO-2023-1558", + "modified": "2023-02-14T19:41:21Z", + "published": "2023-02-14T19:41:21Z", + "aliases": [ + "CVE-2023-23626", + "GHSA-2h6c-j3gf-xp9r" + ], + "summary": "", + "details": "When feeding untrusted user input into the size parameter of `NewBitfield` and FromBytes functions, an attacker can trigger panics.\n\nThis happens when the size is a not a multiple of 8 or is negative.\n\nA workaround is to ensure size%8 == 0 \u0026\u0026 size \u003e= 0 yourself before calling NewBitfield or FromBytes.", + "affected": [ + { + "package": { + "ecosystem": "Go", + "name": "github.com/ipfs/go-bitfield", + "purl": "pkg:golang/github.com/ipfs/go-bitfield" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "1.1.0" + } + ] + } + ], + "database_specific": { + "source": "https://vuln.go.dev/ID/GO-2023-1558.json", + "url": "https://pkg.go.dev/vuln/GO-2023-1558" + }, + "ecosystem_specific": { + "imports": [ + { + "path": "github.com/ipfs/go-bitfield", + "symbols": [ + "FromBytes", + "NewBitfield" + ] + } + ] + } + } + ], + "references": [ + { + "type": "ADVISORY", + "url": "https://github.com/ipfs/go-bitfield/security/advisories/GHSA-2h6c-j3gf-xp9r" + }, + { + "type": "FIX", + "url": "https://github.com/ipfs/go-bitfield/commit/5e1d256fe043fc4163343ccca83862c69c52e579" + } + ] + }, + "GO-2023-1572": { + "schema_version": "1.3.0", + "id": "GO-2023-1572", + "modified": "2023-02-22T20:13:12Z", + "published": "2023-02-16T22:25:24Z", + "aliases": [ + "CVE-2022-41727", + "GHSA-qgc7-mgm3-q253" + ], + "summary": "", + "details": "An attacker can craft a malformed TIFF image which will consume a significant amount of memory when passed to DecodeConfig. This could lead to a denial of service.", + "affected": [ + { + "package": { + "ecosystem": "Go", + "name": "golang.org/x/image", + "purl": "pkg:golang/golang.org/x/image" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "0.5.0" + } + ] + } + ], + "database_specific": { + "source": "https://vuln.go.dev/ID/GO-2023-1572.json", + "url": "https://pkg.go.dev/vuln/GO-2023-1572" + }, + "ecosystem_specific": { + "imports": [ + { + "path": "golang.org/x/image/tiff", + "symbols": [ + "Decode", + "DecodeConfig", + "decoder.ifdUint", + "newDecoder" + ] + } + ] + } + } + ], + "references": [ + { + "type": "REPORT", + "url": "https://go.dev/issue/58003" + }, + { + "type": "FIX", + "url": "https://go.dev/cl/468195" + }, + { + "type": "WEB", + "url": "https://groups.google.com/g/golang-announce/c/ag-FiyjlD5o" + } + ] + } +} \ No newline at end of file diff --git a/internal/sourceanalysis/go.go b/internal/sourceanalysis/go.go new file mode 100644 index 00000000000..a2a599ab073 --- /dev/null +++ b/internal/sourceanalysis/go.go @@ -0,0 +1,85 @@ +package sourceanalysis + +import ( + "fmt" + "path/filepath" + + "github.com/google/osv-scanner/internal/govulncheckshim" + "github.com/google/osv-scanner/internal/output" + "github.com/google/osv-scanner/pkg/models" + "golang.org/x/exp/slices" + "golang.org/x/vuln/exp/govulncheck" +) + +func goAnalysis(r *output.Reporter, pkgs []models.PackageVulns, source models.SourceInfo) { + vulns, vulnsByID := vulnsFromAllPkgs(pkgs) + res, err := govulncheckshim.RunGoVulnCheck(filepath.Dir(source.Path), vulns) + if err != nil { + // TODO: Better method to identify the type of error and give advice specific to the error + r.PrintError( + fmt.Sprintf("Failed to run code analysis (govulncheck) on '%s' because %s\n"+ + "(the Go toolchain is required)\n", source.Path, err.Error())) + + return + } + gvcResByVulnID := map[string]*govulncheck.Vuln{} + for _, v := range res.Vulns { + gvcResByVulnID[v.OSV.ID] = v + } + matchAnalysisWithPackageVulns(pkgs, gvcResByVulnID, vulnsByID) +} + +func matchAnalysisWithPackageVulns(pkgs []models.PackageVulns, gvcResByVulnID map[string]*govulncheck.Vuln, vulnsByID map[string]models.Vulnerability) { + for _, pv := range pkgs { + // Use index to keep reference to original element in slice + for groupIdx := range pv.Groups { + for _, vulnID := range pv.Groups[groupIdx].IDs { + analysis := &pv.Groups[groupIdx].ExperimentalAnalysis + if *analysis == nil { + *analysis = make(map[string]models.AnalysisInfo) + } + + gvcVuln, ok := gvcResByVulnID[vulnID] + if !ok { // If vulnerability not found, check if it contain any source information + fillNotImportedAnalysisInfo(vulnsByID, vulnID, pv, analysis) + continue + } + // Module list is unlikely to be very big, linear search is fine + containsModule := slices.ContainsFunc(gvcVuln.Modules, func(module *govulncheck.Module) bool { + return module.Path == pv.Package.Name + }) + + if !containsModule { + // Code does not import module, so definitely not called + (*analysis)[vulnID] = models.AnalysisInfo{ + Called: false, + } + } else { + // Code does import module, check if it's called + (*analysis)[vulnID] = models.AnalysisInfo{ + Called: gvcVuln.IsCalled(), + } + } + } + } + } +} + +// fillNotImportedAnalysisInfo checks for any source information in advisories, and sets called to false +func fillNotImportedAnalysisInfo(vulnsByID map[string]models.Vulnerability, vulnID string, pv models.PackageVulns, analysis *map[string]models.AnalysisInfo) { + for _, v := range vulnsByID[vulnID].Affected { + // TODO: Compare versions to see if this is the correct affected element + // ver, err := semantic.Parse(pv.Package.Version, semantic.SemverVersion) + if v.Package.Name != pv.Package.Name { + continue + } + _, hasImportsField := v.EcosystemSpecific["imports"] + if hasImportsField { + // If there is source information, then analysis has been performed, and + // code does not import the vulnerable package, so definitely not called + (*analysis)[vulnID] = models.AnalysisInfo{ + Called: false, + } + } + } +} diff --git a/internal/sourceanalysis/go_test.go b/internal/sourceanalysis/go_test.go new file mode 100644 index 00000000000..6723f192e97 --- /dev/null +++ b/internal/sourceanalysis/go_test.go @@ -0,0 +1,20 @@ +package sourceanalysis + +import ( + "testing" + + "github.com/google/osv-scanner/internal/testutility" + "github.com/google/osv-scanner/pkg/models" + "golang.org/x/vuln/exp/govulncheck" +) + +func Test_matchAnalysisWithPackageVulns(t *testing.T) { + t.Parallel() + + pkgs := testutility.LoadJSONFixture[[]models.PackageVulns](t, "fixtures/input.json") + gvcResByVulnID := testutility.LoadJSONFixture[map[string]*govulncheck.Vuln](t, "fixtures/govulncheckinput.json") + vulnsByID := testutility.LoadJSONFixture[map[string]models.Vulnerability](t, "fixtures/vulnbyid.json") + + matchAnalysisWithPackageVulns(pkgs, gvcResByVulnID, vulnsByID) + testutility.AssertMatchFixtureJSON(t, "fixtures/output.json", pkgs) +} diff --git a/internal/sourceanalysis/sourceanalysis.go b/internal/sourceanalysis/sourceanalysis.go new file mode 100644 index 00000000000..121233b9016 --- /dev/null +++ b/internal/sourceanalysis/sourceanalysis.go @@ -0,0 +1,33 @@ +package sourceanalysis + +import ( + "path/filepath" + + "github.com/google/osv-scanner/internal/output" + "github.com/google/osv-scanner/pkg/models" +) + +// vulnsFromAllPkgs returns the flattened list of unique vulnerabilities +func vulnsFromAllPkgs(pkgs []models.PackageVulns) ([]models.Vulnerability, map[string]models.Vulnerability) { + flatVulns := map[string]models.Vulnerability{} + for _, pv := range pkgs { + for _, vuln := range pv.Vulnerabilities { + flatVulns[vuln.ID] = vuln + } + } + + vulns := []models.Vulnerability{} + for _, v := range flatVulns { + vulns = append(vulns, v) + } + + return vulns, flatVulns +} + +// Run runs the language specific analyzers on the code given packages and source info +func Run(r *output.Reporter, source models.SourceInfo, pkgs []models.PackageVulns) { + // GoVulnCheck + if source.Type == "lockfile" && filepath.Base(source.Path) == "go.mod" { + goAnalysis(r, pkgs, source) + } +} diff --git a/internal/testutility/utility.go b/internal/testutility/utility.go new file mode 100644 index 00000000000..ff6497983a5 --- /dev/null +++ b/internal/testutility/utility.go @@ -0,0 +1,63 @@ +package testutility + +import ( + "encoding/json" + "os" + "reflect" + "strings" + "testing" + + "github.com/kr/pretty" +) + +// LoadJSONFixture loads a JSON fixture file and returns the decoded version. +func LoadJSONFixture[V any](t *testing.T, path string) V { + t.Helper() + file, err := os.Open(path) + if err != nil { + t.Fatalf("Failed to open fixture: %s", err) + } + var value V + err = json.NewDecoder(file).Decode(&value) + if err != nil { + t.Fatalf("Failed to parse fixture: %s", err) + } + + return value +} + +// AssertMatchFixtureJSON matches the JSON at path with the value val, failing if not equal, printing out the difference. +func AssertMatchFixtureJSON[V any](t *testing.T, path string, val V) { + t.Helper() + fileA, err := os.ReadFile(path) + if err != nil { + t.Fatalf("Failed to open fixture: %s", err) + } + + var elem V + err = json.Unmarshal(fileA, &elem) + if err != nil { + t.Fatalf("Failed to unmarshal val: %s", err) + } + + if !reflect.DeepEqual(val, elem) { + t.Errorf("Not equal: \n%s", strings.Join(pretty.Diff(val, elem), "\n")) + } +} + +// CreateJSONFixture creates a JSON file at path of the value val, +// can be used with AssertMatchFixtureJSON to compare against future values. +func CreateJSONFixture[V any](t *testing.T, path string, val V) { + t.Helper() + file, err := os.Open(path) + if err != nil { + t.Fatalf("Failed to open file to write: %s", err) + } + + encoder := json.NewEncoder(file) + encoder.SetIndent("", " ") + err = encoder.Encode(val) + if err != nil { + t.Fatalf("Failed to encode val: %s", err) + } +} diff --git a/pkg/models/results.go b/pkg/models/results.go index 457cf07e07e..76f63b8d9f4 100644 --- a/pkg/models/results.go +++ b/pkg/models/results.go @@ -1,6 +1,10 @@ package models -import "time" +import ( + "time" + + "golang.org/x/exp/slices" +) // Combined vulnerabilities found for the scanned packages type VulnerabilityResults struct { @@ -13,10 +17,13 @@ func (vulns *VulnerabilityResults) Flatten() []VulnerabilityFlattened { for _, res := range vulns.Results { for _, pkg := range res.Packages { for _, v := range pkg.Vulnerabilities { + // groupIdx should never be -1 since vulnerabilities should always be in one group + groupIdx := slices.IndexFunc(pkg.Groups, func(g GroupInfo) bool { return slices.Contains(g.IDs, v.ID) }) results = append(results, VulnerabilityFlattened{ Source: res.Source, Package: pkg.Package, Vulnerability: v, + GroupInfo: pkg.Groups[groupIdx], }) } } @@ -30,6 +37,7 @@ type VulnerabilityFlattened struct { Source SourceInfo Package PackageInfo Vulnerability Vulnerability + GroupInfo GroupInfo } type Vulnerability struct { @@ -91,6 +99,28 @@ type PackageVulns struct { type GroupInfo struct { IDs []string `json:"ids"` + // Map of Vulnerability IDs to AnalysisInfo + ExperimentalAnalysis map[string]AnalysisInfo `json:"experimentalAnalysis,omitempty"` +} + +// IsCalled returns true if any analysis performed determines that the vulnerability is being called +// Also returns true if no analysis is performed +func (groupInfo *GroupInfo) IsCalled() bool { + if len(groupInfo.ExperimentalAnalysis) == 0 { + return true + } + + for _, analysis := range groupInfo.ExperimentalAnalysis { + if analysis.Called { + return true + } + } + + return false +} + +type AnalysisInfo struct { + Called bool `json:"called"` } // Specific package information diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go index f7f33b108c6..6b1d8e53e03 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -32,6 +32,8 @@ type ScannerActions struct { NoIgnore bool DockerContainerNames []string ConfigOverridePath string + + ExperimentalCallAnalysis bool } // NoPackagesFoundErr for when no packages is found during a scan. @@ -42,6 +44,9 @@ var NoPackagesFoundErr = errors.New("no packages found in scan") //nolint:errname,stylecheck // Would require version major bump to change var VulnerabilitiesFoundErr = errors.New("vulnerabilities found") +//nolint:errname,stylecheck // Would require version bump to change +var OnlyUncalledVulnerabilitiesFoundErr = errors.New("only uncalled vulnerabilities found") + // scanDir walks through the given directory to try to find any relevant files // These include: // - Any lockfiles with scanLockfile @@ -455,16 +460,22 @@ func DoScan(actions ScannerActions, r *output.Reporter) (models.VulnerabilityRes if filtered > 0 { r.PrintText(fmt.Sprintf("Filtered %d vulnerabilities from output\n", filtered)) } - hydratedResp, err := osv.Hydrate(resp) if err != nil { return models.VulnerabilityResults{}, fmt.Errorf("failed to hydrate OSV response: %w", err) } - vulnerabilityResults := groupResponseBySource(r, query, hydratedResp) + vulnerabilityResults := groupResponseBySource(r, query, hydratedResp, actions.ExperimentalCallAnalysis) // if vulnerability exists it should return error if len(vulnerabilityResults.Results) > 0 { - return vulnerabilityResults, VulnerabilitiesFoundErr + // If any vulnerabilities are called, then we return VulnerabilitiesFoundErr + for _, vf := range vulnerabilityResults.Flatten() { + if vf.GroupInfo.IsCalled() { + return vulnerabilityResults, VulnerabilitiesFoundErr + } + } + // Otherwise return OnlyUncalledVulnerabilitiesFoundErr + return vulnerabilityResults, OnlyUncalledVulnerabilitiesFoundErr } return vulnerabilityResults, nil diff --git a/pkg/osvscanner/vulnerability_result.go b/pkg/osvscanner/vulnerability_result.go index 5196aae5970..fab5381a77a 100644 --- a/pkg/osvscanner/vulnerability_result.go +++ b/pkg/osvscanner/vulnerability_result.go @@ -5,6 +5,7 @@ import ( "sort" "github.com/google/osv-scanner/internal/output" + "github.com/google/osv-scanner/internal/sourceanalysis" "github.com/google/osv-scanner/pkg/grouper" "github.com/google/osv-scanner/pkg/models" "github.com/google/osv-scanner/pkg/osv" @@ -12,7 +13,7 @@ import ( // groupResponseBySource converts raw OSV API response into structured vulnerability information // grouped by source location. -func groupResponseBySource(r *output.Reporter, query osv.BatchedQuery, resp *osv.HydratedBatchedResponse) models.VulnerabilityResults { +func groupResponseBySource(r *output.Reporter, query osv.BatchedQuery, resp *osv.HydratedBatchedResponse, callAnalysis bool) models.VulnerabilityResults { output := models.VulnerabilityResults{ Results: []models.PackageSource{}, } @@ -48,11 +49,15 @@ func groupResponseBySource(r *output.Reporter, query osv.BatchedQuery, resp *osv pkg.Vulnerabilities = response.Vulns - pkg.Groups = grouper.Group(grouper.ConvertVulnerabilityToIDAliases(pkg.Vulnerabilities)) + groups := grouper.Group(grouper.ConvertVulnerabilityToIDAliases(pkg.Vulnerabilities)) + pkg.Groups = groups groupedBySource[query.Source] = append(groupedBySource[query.Source], pkg) } for source, packages := range groupedBySource { + if callAnalysis { + sourceanalysis.Run(r, source, packages) + } output.Results = append(output.Results, models.PackageSource{ Source: source, Packages: packages, diff --git a/pkg/osvscanner/vulnerability_result_internal_test.go b/pkg/osvscanner/vulnerability_result_internal_test.go index 97f5621d007..79e860b7ab9 100644 --- a/pkg/osvscanner/vulnerability_result_internal_test.go +++ b/pkg/osvscanner/vulnerability_result_internal_test.go @@ -12,9 +12,10 @@ import ( func Test_groupResponseBySource(t *testing.T) { t.Parallel() type args struct { - r *output.Reporter - query osv.BatchedQuery - resp *osv.HydratedBatchedResponse + r *output.Reporter + query osv.BatchedQuery + resp *osv.HydratedBatchedResponse + callAnalysis bool } tests := []struct { name string @@ -25,7 +26,7 @@ func Test_groupResponseBySource(t *testing.T) { tt := tt // Reinitialize for t.Parallel() t.Run(tt.name, func(t *testing.T) { t.Parallel() - if got := groupResponseBySource(tt.args.r, tt.args.query, tt.args.resp); !reflect.DeepEqual(got, tt.want) { + if got := groupResponseBySource(tt.args.r, tt.args.query, tt.args.resp, tt.args.callAnalysis); !reflect.DeepEqual(got, tt.want) { t.Errorf("groupResponse() = %v, want %v", got, tt.want) } })