vulndb: delete
Delete the vulndb module. Users should now use
golang.org/x/vuln/cmd/govulncheck.
Change-Id: Ia592a36f410fd7aef35a45b077cb2cbbf4bec38c
Reviewed-on: https://go-review.googlesource.com/c/exp/+/395534
Reviewed-by: Zvonimir Pavlinovic <zpavlinovic@google.com>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
Trust: Julie Qiu <julie@golang.org>
diff --git a/vulndb/go.mod b/vulndb/go.mod
deleted file mode 100644
index 4c7d6fc..0000000
--- a/vulndb/go.mod
+++ /dev/null
@@ -1,15 +0,0 @@
-// Deprecated: use golang.org/x/vuln instead.
-module golang.org/x/exp/vulndb
-
-go 1.17
-
-require (
- golang.org/x/tools v0.0.0-20210910171127-a568412ca0e6
- golang.org/x/vuln v0.0.0-20211109030331-63d5d8171d01
-)
-
-require (
- golang.org/x/mod v0.5.0 // indirect
- golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf // indirect
- golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
-)
diff --git a/vulndb/go.sum b/vulndb/go.sum
deleted file mode 100644
index db8f9cf..0000000
--- a/vulndb/go.sum
+++ /dev/null
@@ -1,102 +0,0 @@
-github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
-github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
-github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
-github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
-github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
-github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
-github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
-github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
-github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
-github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
-github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
-github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
-github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0=
-github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc=
-github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
-github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
-github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
-github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
-github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
-github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
-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/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
-github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
-github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-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/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
-github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
-golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
-golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.5.0 h1:UG21uOlmZabA4fW5i7ZX6bjw1xELEGg/ZLgZq9auk/Q=
-golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-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-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
-golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-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-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/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-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k=
-golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-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.0.0-20210910171127-a568412ca0e6 h1:O8E8PvYQ1HtZae5hPC9lyLiVuGlqL50yMQW7Y6oRaXQ=
-golang.org/x/tools v0.0.0-20210910171127-a568412ca0e6/go.mod h1:YD9qOF0M9xpSpdWTBbzEl5e/RnCefISl8E5Noe10jFM=
-golang.org/x/vuln v0.0.0-20211109030331-63d5d8171d01 h1:aohfMrFbzRmVFllsufGifzVZzGJHuCrdmXA88J2FX9Q=
-golang.org/x/vuln v0.0.0-20211109030331-63d5d8171d01/go.mod h1:vGjwvr4zd0JNGaeuScTP00A/lDhN8Ao3GFprqIUiIcM=
-golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
-gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/vulndb/govulncheck/cache.go b/vulndb/govulncheck/cache.go
deleted file mode 100644
index d0295a7..0000000
--- a/vulndb/govulncheck/cache.go
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "encoding/json"
- "go/build"
- "io/ioutil"
- "os"
- "path/filepath"
- "time"
-
- "golang.org/x/vuln/osv"
-)
-
-// NOTE: this cache implementation should be kept internal to the go tooling
-// (i.e. cmd/go/internal/something) so that the vulndb cache is owned by the
-// go command. Also it is currently NOT CONCURRENCY SAFE since it does not
-// implement file locking. If ported to the stdlib it should use
-// cmd/go/internal/lockedfile.
-
-// The cache uses a single JSON index file for each vulnerability database
-// which contains the map from packages to the time the last
-// vulnerability for that package was added/modified and the time that
-// the index was retrieved from the vulnerability database. The JSON
-// format is as follows:
-//
-// $GOPATH/pkg/mod/cache/download/vulndb/{db hostname}/indexes/index.json
-// {
-// Retrieved time.Time
-// Index osv.DBIndex
-// }
-//
-// Each package also has a JSON file which contains the array of vulnerability
-// entries for the package. The JSON format is as follows:
-//
-// $GOPATH/pkg/mod/cache/download/vulndb/{db hostname}/{import path}/vulns.json
-// []*osv.Entry
-
-// fsCache is file-system cache implementing osv.Cache
-// TODO: make cache thread-safe
-type fsCache struct {
- rootDir string
-}
-
-// use cfg.GOMODCACHE available in cmd/go/internal?
-var defaultCacheRoot = filepath.Join(build.Default.GOPATH, "/pkg/mod/cache/download/vulndb")
-
-func defaultCache() *fsCache {
- return &fsCache{rootDir: defaultCacheRoot}
-}
-
-type cachedIndex struct {
- Retrieved time.Time
- Index osv.DBIndex
-}
-
-func (c *fsCache) ReadIndex(dbName string) (osv.DBIndex, time.Time, error) {
- b, err := ioutil.ReadFile(filepath.Join(c.rootDir, dbName, "index.json"))
- if err != nil {
- if os.IsNotExist(err) {
- return nil, time.Time{}, nil
- }
- return nil, time.Time{}, err
- }
- var index cachedIndex
- if err := json.Unmarshal(b, &index); err != nil {
- return nil, time.Time{}, err
- }
- return index.Index, index.Retrieved, nil
-}
-
-func (c *fsCache) WriteIndex(dbName string, index osv.DBIndex, retrieved time.Time) error {
- path := filepath.Join(c.rootDir, dbName)
- if err := os.MkdirAll(path, 0755); err != nil {
- return err
- }
- j, err := json.Marshal(cachedIndex{
- Index: index,
- Retrieved: retrieved,
- })
- if err != nil {
- return err
- }
- if err := ioutil.WriteFile(filepath.Join(path, "index.json"), j, 0666); err != nil {
- return err
- }
- return nil
-}
-
-func (c *fsCache) ReadEntries(dbName string, p string) ([]*osv.Entry, error) {
- b, err := ioutil.ReadFile(filepath.Join(c.rootDir, dbName, p, "vulns.json"))
- if err != nil {
- if os.IsNotExist(err) {
- return nil, nil
- }
- return nil, err
- }
- var entries []*osv.Entry
- if err := json.Unmarshal(b, &entries); err != nil {
- return nil, err
- }
- return entries, nil
-}
-
-func (c *fsCache) WriteEntries(dbName string, p string, entries []*osv.Entry) error {
- path := filepath.Join(c.rootDir, dbName, p)
- if err := os.MkdirAll(path, 0777); err != nil {
- return err
- }
- j, err := json.Marshal(entries)
- if err != nil {
- return err
- }
- if err := ioutil.WriteFile(filepath.Join(path, "vulns.json"), j, 0666); err != nil {
- return err
- }
- return nil
-}
diff --git a/vulndb/govulncheck/cache_test.go b/vulndb/govulncheck/cache_test.go
deleted file mode 100644
index 0a85bc4..0000000
--- a/vulndb/govulncheck/cache_test.go
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "os"
- "path/filepath"
- "reflect"
- "testing"
- "time"
-
- "golang.org/x/vuln/osv"
-)
-
-func TestCache(t *testing.T) {
- tmpDir := t.TempDir()
-
- cache := &fsCache{rootDir: tmpDir}
- dbName := "vulndb.golang.org"
-
- _, _, err := cache.ReadIndex(dbName)
- if err != nil {
- t.Fatalf("ReadIndex failed for non-existent database: %v", err)
- }
-
- if err = os.Mkdir(filepath.Join(tmpDir, dbName), 0777); err != nil {
- t.Fatalf("os.Mkdir failed: %v", err)
- }
- _, _, err = cache.ReadIndex(dbName)
- if err != nil {
- t.Fatalf("ReadIndex failed for database without cached index: %v", err)
- }
-
- now := time.Now()
- expectedIdx := osv.DBIndex{
- "a.vuln.example.com": time.Time{}.Add(time.Hour),
- "b.vuln.example.com": time.Time{}.Add(time.Hour * 2),
- "c.vuln.example.com": time.Time{}.Add(time.Hour * 3),
- }
- if err = cache.WriteIndex(dbName, expectedIdx, now); err != nil {
- t.Fatalf("WriteIndex failed to write index: %v", err)
- }
-
- idx, retrieved, err := cache.ReadIndex(dbName)
- if err != nil {
- t.Fatalf("ReadIndex failed for database with cached index: %v", err)
- }
- if !reflect.DeepEqual(idx, expectedIdx) {
- t.Errorf("ReadIndex returned unexpected index, got:\n%s\nwant:\n%s", idx, expectedIdx)
- }
- if !retrieved.Equal(now) {
- t.Errorf("ReadIndex returned unexpected retrieved: got %s, want %s", retrieved, now)
- }
-
- if _, err = cache.ReadEntries(dbName, "vuln.example.com"); err != nil {
- t.Fatalf("ReadEntires failed for non-existent package: %v", err)
- }
-
- expectedEntries := []*osv.Entry{
- {ID: "001"},
- {ID: "002"},
- {ID: "003"},
- }
- if err := cache.WriteEntries(dbName, "vuln.example.com", expectedEntries); err != nil {
- t.Fatalf("WriteEntries failed: %v", err)
- }
-
- entries, err := cache.ReadEntries(dbName, "vuln.example.com")
- if err != nil {
- t.Fatalf("ReadEntries failed for cached package: %v", err)
- }
- if !reflect.DeepEqual(entries, expectedEntries) {
- t.Errorf("ReadEntries returned unexpected entries, got:\n%v\nwant:\n%v", entries, expectedEntries)
- }
-}
diff --git a/vulndb/govulncheck/main.go b/vulndb/govulncheck/main.go
deleted file mode 100644
index 5934976..0000000
--- a/vulndb/govulncheck/main.go
+++ /dev/null
@@ -1,270 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Command govulncheck reports known vulnerabilities filed in a vulnerability database
-// (see https://golang.org/design/draft-vulndb) that affect a given package or binary.
-//
-// It uses static analysis or the binary's symbol table to narrow down reports to only
-// those that potentially affect the application.
-//
-// WARNING WARNING WARNING
-//
-// govulncheck is still experimental and neither its output or the vulnerability
-// database should be relied on to be stable or comprehensive.
-package main
-
-import (
- "encoding/json"
- "flag"
- "fmt"
- "go/build"
- "log"
- "os"
- "runtime"
- "strings"
-
- "golang.org/x/exp/vulndb/internal/audit"
- "golang.org/x/exp/vulndb/internal/binscan"
- "golang.org/x/tools/go/buildutil"
- "golang.org/x/tools/go/packages"
- "golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/vuln/client"
-)
-
-var (
- jsonFlag = flag.Bool("json", false, "")
- verboseFlag = flag.Bool("verbose", false, "")
- importsFlag = flag.Bool("imports", false, "")
- allFlag = flag.Bool("all", false, "")
- testsFlag = flag.Bool("tests", false, "")
-)
-
-const usage = `govulncheck: identify known vulnerabilities by call graph traversal.
-
-Usage:
-
- govulncheck [-imports] [-json] [-all] [-tests] [-tags] {package pattern...}
-
- govulncheck {binary path}
-
-Flags:
-
- -imports Perform a broad scan with more false positives, which reports all
- vulnerabilities found in any transitively imported package, regardless
- of whether they are reachable.
-
- -json Print vulnerability findings in JSON format.
-
- -all Show all representative findings for each vulnerability. A best effort
- is made to order findings by relevance. When false [default], show only
- the most relevant finding.
-
- -verbose Print progress information.
-
- -tags Comma-separated list of build tags.
-
- -tests Boolean flag indicating if test files should be analyzed too.
-
-govulncheck can be used with either one or more package patterns (i.e. golang.org/x/crypto/...
-or ./...) or with a single path to a Go binary. In the latter case module and symbol
-information will be extracted from the binary in order to detect vulnerable symbols
-and the -imports flag is disregarded.
-
-The environment variable GOVULNDB can be set to a comma-separate list of vulnerability
-database URLs, with http://, https://, or file:// protocols. Entries from multiple
-databases are merged.
-`
-
-func init() {
- flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
-}
-
-func main() {
- flag.Usage = func() { fmt.Fprint(os.Stderr, usage) }
- flag.Parse()
-
- if len(flag.Args()) == 0 {
- fmt.Fprint(os.Stderr, usage)
- os.Exit(1)
- }
-
- dbs := []string{"https://storage.googleapis.com/go-vulndb"}
- if GOVULNDB := os.Getenv("GOVULNDB"); GOVULNDB != "" {
- dbs = strings.Split(GOVULNDB, ",")
- }
- dbClient, err := client.NewClient(dbs, client.Options{HTTPCache: defaultCache()})
- if err != nil {
- fmt.Fprintf(os.Stderr, "govulncheck: %s\n", err)
- os.Exit(1)
- }
-
- cfg := &packages.Config{
- Mode: packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles | packages.NeedImports | packages.NeedTypes | packages.NeedTypesSizes | packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedDeps | packages.NeedModule,
- Tests: *testsFlag,
- BuildFlags: []string{fmt.Sprintf("-tags=%s", strings.Join(build.Default.BuildTags, ","))},
- }
-
- r, err := run(cfg, flag.Args(), *importsFlag, dbClient)
- if err != nil {
- fmt.Fprintf(os.Stderr, "govulncheck: %s\n", err)
- os.Exit(1)
- }
-
- if !*allFlag {
- r = projectToSingleFinding(r)
- }
-
- writeOut(r, *jsonFlag)
-}
-
-func projectToSingleFinding(r *audit.Results) *audit.Results {
- nr := &audit.Results{
- SearchMode: r.SearchMode,
- Vulnerabilities: r.Vulnerabilities,
- VulnFindings: make(map[string][]audit.Finding),
- }
-
- for id, findings := range r.VulnFindings {
- if len(findings) > 0 {
- nr.VulnFindings[id] = []audit.Finding{findings[0]}
- }
- }
-
- return nr
-}
-
-func writeOut(r *audit.Results, toJson bool) {
- if !toJson {
- os.Stdout.Write([]byte(r.String()))
- return
- }
-
- b, err := json.MarshalIndent(r, "", "\t")
- if err != nil {
- fmt.Fprintf(os.Stderr, "govulncheck: %s\n", err)
- os.Exit(1)
- }
- os.Stdout.Write(b)
- os.Stdout.Write([]byte{'\n'})
-}
-
-// extractModules collects modules in `pkgs` up to uniqueness of
-// module path and version.
-func extractModules(pkgs []*packages.Package) []*packages.Module {
- modMap := map[string]*packages.Module{}
- modKey := func(mod *packages.Module) string {
- if mod.Replace != nil {
- return fmt.Sprintf("%s@%s", mod.Replace.Path, mod.Replace.Version)
- }
- return fmt.Sprintf("%s@%s", mod.Path, mod.Version)
- }
-
- seen := map[*packages.Package]bool{}
- var extract func(*packages.Package, map[string]*packages.Module)
- extract = func(pkg *packages.Package, modMap map[string]*packages.Module) {
- if pkg == nil || seen[pkg] {
- return
- }
- if pkg.Module != nil {
- modMap[modKey(pkg.Module)] = pkg.Module
- }
- seen[pkg] = true
- for _, imp := range pkg.Imports {
- extract(imp, modMap)
- }
- }
- for _, pkg := range pkgs {
- extract(pkg, modMap)
- }
-
- modules := []*packages.Module{}
- for _, mod := range modMap {
- modules = append(modules, mod)
- }
- return modules
-}
-
-func isFile(path string) bool {
- s, err := os.Stat(path)
- if err != nil {
- return false
- }
- return !s.IsDir()
-}
-
-func run(cfg *packages.Config, patterns []string, importsOnly bool, dbClient client.Client) (*audit.Results, error) {
- if len(patterns) == 1 && isFile(patterns[0]) {
- modules, symbols, err := binscan.ExtractPackagesAndSymbols(patterns[0])
- if err != nil {
- return nil, err
- }
-
- vulns, err := audit.FetchVulnerabilities(dbClient, modules)
- if err != nil {
- return nil, fmt.Errorf("failed to load vulnerability dbs: %v", err)
- }
- vulns = vulns.Filter(lookupEnv("GOOS", runtime.GOOS), lookupEnv("GOARCH", runtime.GOARCH))
-
- results := audit.VulnerablePackageSymbols(symbols, vulns)
- return &results, nil
- }
-
- // Load packages.
- if *verboseFlag {
- log.Println("loading packages...")
- }
- pkgs, err := packages.Load(cfg, patterns...)
- if err != nil {
- return nil, err
- }
- if packages.PrintErrors(pkgs) > 0 {
- return nil, fmt.Errorf("packages contain errors")
- }
- if *verboseFlag {
- log.Printf("\t%d loaded packages\n", len(pkgs))
- }
-
- // Load database.
- if *verboseFlag {
- log.Println("loading database...")
- }
-
- modVulns, err := audit.FetchVulnerabilities(dbClient, extractModules(pkgs))
- if err != nil {
- return nil, fmt.Errorf("failed to fetch vulnerabilities: %v", err)
- }
- modVulns = modVulns.Filter(lookupEnv("GOOS", runtime.GOOS), lookupEnv("GOARCH", runtime.GOARCH))
- if *verboseFlag {
- log.Printf("\t%d known vulnerabilities.\n", modVulns.Num())
- }
-
- // Load SSA.
- if *verboseFlag {
- log.Println("building ssa...")
- }
- prog, ssaPkgs := ssautil.AllPackages(pkgs, 0)
- prog.Build()
- if *verboseFlag {
- log.Println("\tbuilt ssa")
- }
-
- // Compute the findings.
- if *verboseFlag {
- log.Println("detecting vulnerabilities...")
- }
- var results audit.Results
- if importsOnly {
- results = audit.VulnerableImports(ssaPkgs, modVulns)
- } else {
- results = audit.VulnerableSymbols(ssaPkgs, modVulns)
- }
- return &results, nil
-}
-
-func lookupEnv(key string, defaultValue string) (value string) {
- if v, ok := os.LookupEnv(key); ok {
- return v
- }
- return defaultValue
-}
diff --git a/vulndb/internal/audit/detect.go b/vulndb/internal/audit/detect.go
deleted file mode 100644
index 993f84b..0000000
--- a/vulndb/internal/audit/detect.go
+++ /dev/null
@@ -1,305 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package audit finds vulnerabilities affecting Go packages.
-package audit
-
-import (
- "fmt"
- "go/token"
- "sort"
- "strings"
-
- "golang.org/x/tools/go/packages"
- "golang.org/x/vuln/osv"
-)
-
-// Preamble with types and common functionality used by vulnerability detection mechanisms in detect_*.go files.
-
-// SearchType represents a type of an audit search: call graph, imports, or binary.
-type SearchType int
-
-// enum values for SearchType.
-const (
- CallGraphSearch SearchType = iota
- ImportsSearch
- BinarySearch
-)
-
-// Results contains the information on findings and identified vulnerabilities by audit search.
-type Results struct {
- SearchMode SearchType
-
- // TODO: identify vulnerability with <ID, package, symbol>?
- // Vulnerabilities in dependent modules.
- Vulnerabilities []osv.Entry
-
- VulnFindings map[string][]Finding // vuln.ID -> findings
-}
-
-// String method for results.
-func (r Results) String() string {
- sort.Slice(r.Vulnerabilities, func(i, j int) bool { return r.Vulnerabilities[i].ID < r.Vulnerabilities[j].ID })
-
- rStr := ""
- for _, v := range r.Vulnerabilities {
- findings := r.VulnFindings[v.ID]
- if len(findings) == 0 {
- // TODO: add messages for such cases too?
- continue
- }
-
- var aliases string
- if len(v.Aliases) != 0 {
- aliases = fmt.Sprintf(" (%s)", strings.Join(v.Aliases, ", "))
- }
- rStr += fmt.Sprintf("Findings for vulnerability: %s%s:\n\n", v.ID, aliases)
-
- for _, finding := range findings {
- rStr += finding.String() + "\n"
- }
- }
- return rStr
-}
-
-// addFindings adds a findings `f` for vulnerability `v`.
-func (r Results) addFinding(v osv.Entry, f Finding) {
- r.VulnFindings[v.ID] = append(r.VulnFindings[v.ID], f)
-}
-
-// sort orders findings for each vulnerability based on its
-// perceived usefulness to the user.
-func (r Results) sort() {
- for _, fs := range r.VulnFindings {
- sort.SliceStable(fs, func(i int, j int) bool { return findingCompare(&fs[i], &fs[j]) })
- }
-}
-
-// Finding represents a finding for the use of a vulnerable symbol or an imported vulnerable package.
-// Provides info on symbol location and the trace leading up to the symbol use.
-type Finding struct {
- Symbol string
- Position *token.Position `json:",omitempty"`
- Type SymbolType
- Trace []TraceElem
-
- // Approximate measure for indicating how understandable the finding is to the client.
- // The smaller the weight, the more understandable is the finding.
- weight int
-
- // Approximate measure for indicating confidence in finding being a true positive. The
- // smaller the value, the bigger the confidence.
- confidence int
-}
-
-// String method for findings.
-func (f Finding) String() string {
- traceStr := traceString(f.Trace)
-
- var pos string
- if f.Position != nil {
- pos = fmt.Sprintf(" (%s)", f.Position)
- }
-
- return fmt.Sprintf("Trace:\n%s%s\n%s\n", f.Symbol, pos, traceStr)
-}
-
-func traceString(trace []TraceElem) string {
- // traces are typically short, so string builders are not necessary
- traceStr := ""
- for i := len(trace) - 1; i >= 0; i-- {
- traceStr += trace[i].String() + "\n"
- }
- return traceStr
-}
-
-// SymbolType represents a type of a symbol use: function, global, or an import statement.
-type SymbolType int
-
-// enum values for SymbolType.
-const (
- FunctionType SymbolType = iota
- ImportType
- GlobalType
-)
-
-// TraceElem represents an entry in the finding trace. Represents a function call or an import statement.
-type TraceElem struct {
- Description string
- Position *token.Position `json:",omitempty"`
-}
-
-// String method for trace elements.
-func (e TraceElem) String() string {
- if e.Position == nil {
- return e.Description
- }
- return fmt.Sprintf("%s (%s)", e.Description, e.Position)
-}
-
-// MarshalText implements the encoding.TextMarshaler interface.
-func (s SymbolType) MarshalText() ([]byte, error) {
- var name string
- switch s {
- default:
- name = "unrecognized"
- case FunctionType:
- name = "function"
- case ImportType:
- name = "import"
- case GlobalType:
- name = "global"
- }
- return []byte(name), nil
-}
-
-type modVulns struct {
- mod *packages.Module
- vulns []*osv.Entry
-}
-
-type ModuleVulnerabilities []modVulns
-
-func matchesPlatform(os, arch string, e osv.EcosystemSpecific) bool {
- matchesOS := len(e.GOOS) == 0
- matchesArch := len(e.GOARCH) == 0
- for _, o := range e.GOOS {
- if os == o {
- matchesOS = true
- break
- }
- }
- for _, a := range e.GOARCH {
- if arch == a {
- matchesArch = true
- break
- }
- }
- return matchesOS && matchesArch
-}
-
-func (mv ModuleVulnerabilities) Filter(os, arch string) ModuleVulnerabilities {
- var filteredMod ModuleVulnerabilities
- for _, mod := range mv {
- module := mod.mod
- modVersion := module.Version
- if module.Replace != nil {
- modVersion = module.Replace.Version
- }
- // TODO: if modVersion == "", try vcs to get the version?
- var filteredVulns []*osv.Entry
- for _, v := range mod.vulns {
- var filteredAffected []osv.Affected
- for _, a := range v.Affected {
- // A module version is affected if
- // - it is included in one of the affected version ranges
- // - and module version is not ""
- // The latter means the module version is not available, so
- // we don't want to spam users with potential false alarms.
- // TODO: issue warning for "" cases above?
- affected := modVersion != "" && a.Ranges.AffectsSemver(modVersion) && matchesPlatform(os, arch, a.EcosystemSpecific)
- if affected {
- filteredAffected = append(filteredAffected, a)
- }
- }
- if len(filteredAffected) == 0 {
- continue
- }
- newV := *v
- newV.Affected = filteredAffected
- filteredVulns = append(filteredVulns, &newV)
- }
- filteredMod = append(filteredMod, modVulns{
- mod: module,
- vulns: filteredVulns,
- })
- }
- return filteredMod
-}
-
-func (mv ModuleVulnerabilities) Num() int {
- var num int
- for _, m := range mv {
- num += len(m.vulns)
- }
- return num
-}
-
-// VulnsForPackage returns the vulnerabilities for the module which is the most
-// specific prefix of importPath, or nil if there is no matching module with
-// vulnerabilities.
-func (mv ModuleVulnerabilities) VulnsForPackage(importPath string) []*osv.Entry {
- var mostSpecificMod *modVulns
- for _, mod := range mv {
- md := mod
- if strings.HasPrefix(importPath, md.mod.Path) {
- if mostSpecificMod == nil || len(mostSpecificMod.mod.Path) < len(md.mod.Path) {
- mostSpecificMod = &md
- }
- }
- }
-
- if mostSpecificMod == nil {
- return nil
- }
-
- if mostSpecificMod.mod.Replace != nil {
- importPath = fmt.Sprintf("%s%s", mostSpecificMod.mod.Replace.Path, strings.TrimPrefix(importPath, mostSpecificMod.mod.Path))
- }
- vulns := mostSpecificMod.vulns
- packageVulns := []*osv.Entry{}
- for _, v := range vulns {
- for _, a := range v.Affected {
- if a.Package.Name == importPath {
- packageVulns = append(packageVulns, v)
- break
- }
- }
- }
- return packageVulns
-}
-
-// VulnsForSymbol returns vulnerabilities for `symbol` in `mv.VulnsForPackage(importPath)`.
-func (mv ModuleVulnerabilities) VulnsForSymbol(importPath, symbol string) []*osv.Entry {
- vulns := mv.VulnsForPackage(importPath)
- if vulns == nil {
- return nil
- }
-
- symbolVulns := []*osv.Entry{}
- for _, v := range vulns {
- vulnLoop:
- for _, a := range v.Affected {
- if a.Package.Name != importPath {
- continue
- }
- if len(a.EcosystemSpecific.Symbols) == 0 {
- symbolVulns = append(symbolVulns, v)
- continue vulnLoop
- }
- for _, s := range a.EcosystemSpecific.Symbols {
- if s == symbol {
- symbolVulns = append(symbolVulns, v)
- continue vulnLoop
- }
- }
- }
- }
- return symbolVulns
-}
-
-// Vulns returns vulnerabilities for all modules in `mv`.
-func (mv ModuleVulnerabilities) Vulns() []*osv.Entry {
- var vulns []*osv.Entry
- seen := make(map[string]bool)
- for _, mv := range mv {
- for _, v := range mv.vulns {
- if !seen[v.ID] {
- vulns = append(vulns, v)
- seen[v.ID] = true
- }
- }
- }
- return vulns
-}
diff --git a/vulndb/internal/audit/detect_binary.go b/vulndb/internal/audit/detect_binary.go
deleted file mode 100644
index b8e2a9b..0000000
--- a/vulndb/internal/audit/detect_binary.go
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package audit
-
-import (
- "fmt"
-)
-
-// VulnerablePackageSymbols returns a list of vulnerability findings for per-package symbols
-// in packageSymbols, given the `modVulns` vulnerabilities.
-//
-// Findings for each vulnerability are sorted by estimated usefulness to the user and do not
-// have an associated trace.
-func VulnerablePackageSymbols(packageSymbols map[string][]string, modVulns ModuleVulnerabilities) Results {
- results := Results{
- SearchMode: BinarySearch,
- Vulnerabilities: serialize(modVulns.Vulns()),
- VulnFindings: make(map[string][]Finding),
- }
- if len(modVulns) == 0 {
- return results
- }
-
- for pkg, symbols := range packageSymbols {
- for _, symbol := range symbols {
- vulns := modVulns.VulnsForSymbol(pkg, symbol)
- for _, v := range serialize(vulns) {
- results.addFinding(v, Finding{
- Symbol: fmt.Sprintf("%s.%s", pkg, symbol),
- Type: GlobalType,
- })
- }
- }
- }
-
- results.sort()
- return results
-}
diff --git a/vulndb/internal/audit/detect_callgraph.go b/vulndb/internal/audit/detect_callgraph.go
deleted file mode 100644
index 983b37a..0000000
--- a/vulndb/internal/audit/detect_callgraph.go
+++ /dev/null
@@ -1,352 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package audit
-
-import (
- "container/list"
- "fmt"
- "go/token"
- "log"
- "strings"
- "sync"
-
- "golang.org/x/tools/go/callgraph"
- "golang.org/x/tools/go/packages"
- "golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/ssa/ssautil"
-
- "golang.org/x/tools/go/callgraph/cha"
- "golang.org/x/tools/go/callgraph/vta"
-)
-
-// VulnerableSymbols returns vulnerability findings for symbols transitively reachable
-// through the callgraph built using VTA analysis from the entry points of pkgs, given
-// 'modVulns' vulnerabilities.
-//
-// Returns all findings reachable from pkgs while analyzing each package only once,
-// preferring findings of shorter import traces. For instance, given call chains
-// A() -> B() -> V
-// A() -> D() -> B() -> V
-// D() -> B() -> V
-// where A and D are top level packages and V is a vulnerable symbol, VulnerableSymbols
-// can return either
-// A() -> B() -> V
-// or
-// D() -> B() -> V
-// as traces of transitively using a vulnerable symbol V.
-//
-// Findings for each vulnerability are sorted by estimated usefulness to the user.
-//
-// Panics if packages in pkgs do not belong to the same program.
-func VulnerableSymbols(pkgs []*ssa.Package, modVulns ModuleVulnerabilities) Results {
- results := Results{
- SearchMode: CallGraphSearch,
- Vulnerabilities: serialize(modVulns.Vulns()),
- VulnFindings: make(map[string][]Finding),
- }
- if len(modVulns) == 0 {
- return results
- }
-
- prog := pkgsProgram(pkgs)
- if prog == nil {
- panic("packages in pkgs must belong to a single common program")
- }
- entries := entryPoints(pkgs)
- callGraph := callGraph(prog, entries)
-
- queue := list.New()
- for _, entry := range entries {
- queue.PushBack(&callChain{f: entry})
- }
-
- seen := make(map[*ssa.Function]bool)
- for queue.Len() > 0 {
- front := queue.Front()
- v := front.Value.(*callChain)
- queue.Remove(front)
-
- if seen[v.f] {
- continue
- }
- seen[v.f] = true
-
- calls := funcVulnsAndCalls(v, modVulns, &results, callGraph)
- for _, call := range calls {
- queue.PushBack(call)
- }
- }
-
- results.sort()
- return results
-}
-
-// callGraph builds a call graph of prog based on VTA analysis.
-func callGraph(prog *ssa.Program, entries []*ssa.Function) *callgraph.Graph {
- entrySlice := make(map[*ssa.Function]bool)
- for _, e := range entries {
- entrySlice[e] = true
- }
- initial := cha.CallGraph(prog)
- allFuncs := ssautil.AllFunctions(prog)
-
- fslice := forwardReachableFrom(entrySlice, initial)
- // Keep only actually linked functions.
- pruneSlice(fslice, allFuncs)
- vtaCg := vta.CallGraph(fslice, initial)
-
- // Repeat the process once more, this time using
- // the produced VTA call graph as the base graph.
- fslice = forwardReachableFrom(entrySlice, vtaCg)
- pruneSlice(fslice, allFuncs)
-
- return vta.CallGraph(fslice, vtaCg)
-}
-
-func entryPoints(topPackages []*ssa.Package) []*ssa.Function {
- var entries []*ssa.Function
- for _, pkg := range topPackages {
- if pkg.Pkg.Name() == "main" {
- // for "main" packages the only valid entry points are the "main"
- // function and any "init#" functions, even if there are other
- // exported functions or types. similarly to isEntry it should be
- // safe to ignore the validity of the main or init# signatures,
- // since the compiler will reject malformed definitions,
- // and the init function is synthetic
- entries = append(entries, memberFuncs(pkg.Members["main"], pkg.Prog)...)
- for name, member := range pkg.Members {
- if strings.HasPrefix(name, "init#") || name == "init" {
- entries = append(entries, memberFuncs(member, pkg.Prog)...)
- }
- }
- continue
- }
- for _, member := range pkg.Members {
- for _, f := range memberFuncs(member, pkg.Prog) {
- if isEntry(f) {
- entries = append(entries, f)
- }
- }
- }
- }
- return entries
-}
-
-func isEntry(f *ssa.Function) bool {
- // it should be safe to ignore checking that the signature of the "init" function
- // is valid, since it is synthetic
- if f.Name() == "init" && f.Synthetic == "package initializer" {
- return true
- }
-
- return f.Synthetic == "" && f.Object() != nil && f.Object().Exported()
-}
-
-// callChain helps doing BFS over package call graph while remembering the call stack.
-type callChain struct {
- // nil for entry points of the chain.
- call ssa.CallInstruction
- f *ssa.Function
- parent *callChain
-}
-
-func (chain *callChain) trace() []TraceElem {
- if chain == nil {
- return nil
- }
-
- var pos *token.Position
- desc := fmt.Sprintf("%s.%s(...)", pkgPath(chain.f), dbFuncName(chain.f))
- if chain.call != nil {
- pos = instrPosition(chain.call)
- if unresolved(chain.call) {
- // In case of a statically unresolved call site, communicate to the client
- // that this was approximately resolved to chain.f.
- desc = fmt.Sprintf("%s(...) [approx. resolved to %s]", callName(chain.call), chain.f)
- }
- } else {
- // No call information means the function is an entry point.
- pos = funcPosition(chain.f)
- }
-
- return append(chain.parent.trace(), TraceElem{Description: desc, Position: pos})
-}
-
-// weight computes an approximate measure of how easy is to understand the call
-// chain when presented to the client as a trace. The smaller the value, the more
-// understendeable the chain is. Currently defined as the number of unresolved
-// call sites in the chain.
-func (chain *callChain) weight() int {
- if chain == nil || chain.call == nil {
- return 0
- }
-
- callWeight := 0
- if unresolved(chain.call) {
- callWeight = 1
- }
- return callWeight + chain.parent.weight()
-}
-
-// for assesing confidence level of findings.
-var stdPackages = make(map[string]bool)
-var loadStdsOnce sync.Once
-
-func isStdPackage(pkg *ssa.Package) bool {
- if pkg == nil || pkg.Pkg == nil {
- return false
- }
-
- loadStdsOnce.Do(func() {
- pkgs, err := packages.Load(nil, "std")
- if err != nil {
- log.Printf("warning: unable to fetch list of std packages, ordering of findings might be affected: %v", err)
- }
-
- for _, p := range pkgs {
- stdPackages[p.PkgPath] = true
- }
- })
- return stdPackages[pkg.Pkg.Path()]
-}
-
-// confidence computes an approximate measure of whether the `chain`
-// represents a true finding. Currently, it equals the number of call
-// sites in `chain` that go through standard libraries. Such findings
-// have been experimentally shown to often result in false positives.
-//
-// TODO: add tests for trace confidence computation involving std libs.
-func (chain *callChain) confidence() int {
- if chain == nil || chain.call == nil {
- return 0
- }
-
- callConfidence := 0
- if isStdPackage(chain.call.Parent().Pkg) {
- callConfidence = 1
- }
- return callConfidence + chain.parent.confidence()
-}
-
-// funcVulnsAndCalls adds symbol findings to results for
-// function at the top of chain and next calls to analyze.
-func funcVulnsAndCalls(chain *callChain, modVulns ModuleVulnerabilities, results *Results, callGraph *callgraph.Graph) []*callChain {
- var calls []*callChain
- for _, b := range chain.f.Blocks {
- for _, instr := range b.Instrs {
- // First collect all findings for globals except callees in function call statements.
- globalFindings(globalUses(instr), chain, modVulns, results)
-
- // Callees are handled separately to produce call findings rather than global findings.
- site, ok := instr.(ssa.CallInstruction)
- if !ok {
- continue
- }
-
- for _, callee := range siteCallees(site, callGraph) {
- c := &callChain{call: site, f: callee, parent: chain}
- calls = append(calls, c)
- callFinding(c, modVulns, results)
- }
- }
- }
- return calls
-}
-
-// globalFindings adds findings for vulnerable globals among globalUses to results.
-// Assumes each use in globalUses is a use of a global variable. Can generate
-// duplicates when globalUses contains duplicates.
-func globalFindings(globalUses []*ssa.Value, chain *callChain, modVulns ModuleVulnerabilities, results *Results) {
- if underRelatedVuln(chain, modVulns) {
- return
- }
-
- for _, o := range globalUses {
- g := (*o).(*ssa.Global)
- vulns := modVulns.VulnsForSymbol(g.Package().Pkg.Path(), g.Name())
- for _, v := range serialize(vulns) {
- results.addFinding(v, Finding{
- Symbol: fmt.Sprintf("%s.%s", g.Package().Pkg.Path(), g.Name()),
- Trace: chain.trace(),
- Position: valPosition(*o, chain.f),
- Type: GlobalType,
- weight: chain.weight(),
- confidence: chain.confidence()})
- }
- }
-}
-
-// callFinding adds findings to results for the call made at the top of the chain.
-// If there is no vulnerability or no call information, then nil is returned.
-// TODO(zpavlinovic): remove ssa info from higher-order calls.
-func callFinding(chain *callChain, modVulns ModuleVulnerabilities, results *Results) {
- if underRelatedVuln(chain, modVulns) {
- return
- }
-
- callee := chain.f
- call := chain.call
- if callee == nil || call == nil {
- return
- }
-
- c := chain
- if !unresolved(call) {
- // If the last call is a resolved callsite, remove the edge from the trace as that
- // information is provided in the symbol field.
- c = c.parent
- }
-
- // some synthetic functions might not have an associated package
- if callee.Package() == nil || callee.Package().Pkg == nil {
- return
- }
-
- vulns := modVulns.VulnsForSymbol(callee.Package().Pkg.Path(), dbFuncName(callee))
- for _, v := range serialize(vulns) {
- results.addFinding(v, Finding{
- Symbol: fmt.Sprintf("%s.%s", callee.Package().Pkg.Path(), dbFuncName(callee)),
- Trace: c.trace(),
- Position: instrPosition(call),
- Type: FunctionType,
- weight: c.weight(),
- confidence: c.confidence()})
- }
-}
-
-// Checks if a potential vulnerability in chain.f is analyzed only because
-// a previous vulnerability in the same package as chain.f has been seen.
-// For instance, for the chain P1:A -> P2:B -> P2:C where both B and C are
-// vulnerable, the function returns true since B is already vulnerable and
-// has hence been reported. Clients are likely not interested in vulnerabilties
-// inside of a function that is already deemed vulnerable. This is an optimization
-// step to stop flooding of findings when a package has a lot of known vulnerable
-// symbols (e.g., all of them).
-//
-// Note that for P1:A -> P2:B -> P3:D -> P2:C the function returns false. This
-// is because C is called from D that comes from a different package.
-func underRelatedVuln(chain *callChain, modVulns ModuleVulnerabilities) bool {
- pkg := pkgPath(chain.f)
-
- c := chain
- for {
- c = c.parent
- // Analyze the immediate substack related to pkg.
- if c == nil || pkgPath(c.f) != pkg {
- break
- }
-
- // some synthetic functions might not have an associated package
- if c.f.Pkg == nil || c.f.Pkg.Pkg == nil {
- continue
- }
-
- // TODO: can we optimize using the information on findings already reported?
- if len(modVulns.VulnsForSymbol(c.f.Pkg.Pkg.Path(), dbFuncName(c.f))) > 0 {
- return true
- }
- }
- return false
-}
diff --git a/vulndb/internal/audit/detect_callgraph_test.go b/vulndb/internal/audit/detect_callgraph_test.go
deleted file mode 100644
index 17789ec..0000000
--- a/vulndb/internal/audit/detect_callgraph_test.go
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package audit
-
-import (
- "go/token"
- "reflect"
- "testing"
-
- "golang.org/x/tools/go/packages"
- "golang.org/x/tools/go/packages/packagestest"
- "golang.org/x/vuln/osv"
-)
-
-func TestSymbolVulnDetectionVTA(t *testing.T) {
- pkgs, modVulns := testContext(t)
- results := VulnerableSymbols(pkgs, modVulns)
-
- if results.SearchMode != CallGraphSearch {
- t.Errorf("want call graph search mode; got %v", results.SearchMode)
- }
-
- // There should be four call chains reported with VTA-VTA version, in the following order,
- // for vuln.VG and vuln.VulnData.Vuln vulnerabilities:
- // vuln.VG:
- // T:T1() -> vuln.VG [use of global at line 4]
- // T:T2() -> vuln.Vuln() [approx.resolved] -> vuln.VG [use of global at vuln.go:4]
- // vuln.VulnData.Vuln:
- // T:T1() -> A:A1() -> vuln.VulnData.Vuln() [call at A.go:14]
- // T:T1() -> vuln.VulnData.Vuln() [approx. resolved] [call at testdata.go:13]
- // Without VTA-VTA, we would alse have the following false positive:
- // T:T2() -> vuln.VulnData.Vuln() [approx. resolved] [call at testdata.go:26]
- for _, test := range []struct {
- vulnId string
- findings []Finding
- }{
- {vulnId: "V1", findings: []Finding{
- {
- Symbol: "thirdparty.org/vulnerabilities/vuln.VulnData.Vuln",
- Trace: []TraceElem{
- {Description: "command-line-arguments.T1(...)", Position: &token.Position{Line: 11, Filename: "T.go"}},
- {Description: "a.org/A.A1(...)", Position: &token.Position{Line: 14, Filename: "T.go"}}},
- Type: FunctionType,
- Position: &token.Position{Line: 15, Filename: "A.go"},
- weight: 0,
- },
- {
- Symbol: "thirdparty.org/vulnerabilities/vuln.VulnData.Vuln",
- Trace: []TraceElem{
- {Description: "command-line-arguments.T1(...)", Position: &token.Position{Line: 11, Filename: "T.go"}},
- {Description: "a.org/A.I.Vuln(...) [approx. resolved to (thirdparty.org/vulnerabilities/vuln.VulnData).Vuln]", Position: &token.Position{Line: 14, Filename: "T.go"}}},
- Type: FunctionType,
- Position: &token.Position{Line: 14, Filename: "T.go"},
- weight: 1,
- },
- }},
- {vulnId: "V2", findings: []Finding{
- {
- Symbol: "thirdparty.org/vulnerabilities/vuln.VG",
- Trace: []TraceElem{
- {Description: "command-line-arguments.T1(...)", Position: &token.Position{Line: 11, Filename: "T.go"}},
- },
- Type: GlobalType,
- Position: &token.Position{Line: 5, Filename: "vuln.go"},
- weight: 0,
- },
- {
- Symbol: "thirdparty.org/vulnerabilities/vuln.VG",
- Trace: []TraceElem{
- {Description: "command-line-arguments.T2(...)", Position: &token.Position{Line: 20, Filename: "T.go"}},
- {Description: "command-line-arguments.t0(...) [approx. resolved to thirdparty.org/vulnerabilities/vuln.Vuln]", Position: &token.Position{Line: 22, Filename: "T.go"}},
- },
- Type: GlobalType,
- Position: &token.Position{Line: 5, Filename: "vuln.go"},
- weight: 1,
- },
- }},
- } {
- got := projectFindings(results.VulnFindings[test.vulnId])
- if !reflect.DeepEqual(test.findings, got) {
- t.Errorf("want %v findings (projected); got %v", test.findings, got)
- }
- }
-}
-
-func TestInitReachability(t *testing.T) {
- e := packagestest.Export(t, packagestest.Modules, []packagestest.Module{
- {
- Name: "golang.org/inittest",
- Files: map[string]interface{}{"main.go": `
- package main
-
- import "example.com/vuln"
-
- func main() {
- vuln.Foo() // benign
- }
- `},
- },
- {
- Name: "example.com@v1.1.1",
- Files: map[string]interface{}{"vuln/vuln.go": `
- package vuln
-
- func init() {
- Bad() // bad
- }
-
- func Foo() {}
- func Bad() {}
- `},
- },
- })
- defer e.Cleanup()
-
- _, pkgs, _, err := loadAndBuildPackages(e, "/inittest/main.go")
- if err != nil {
- t.Fatal(err)
- }
-
- modVulns := ModuleVulnerabilities{
- {
- mod: &packages.Module{Path: "example.com", Version: "v1.1.1"},
- vulns: []*osv.Entry{
- {
- ID: "V3",
- Affected: []osv.Affected{{
- Package: osv.Package{Name: "example.com/vuln"},
- Ranges: osv.Affects{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Introduced: "1.0.0"}, {Fixed: "1.1.2"}}}},
- EcosystemSpecific: osv.EcosystemSpecific{Symbols: []string{"Bad"}},
- }},
- },
- },
- },
- }
- results := VulnerableSymbols(pkgs, modVulns)
-
- if results.SearchMode != CallGraphSearch {
- t.Errorf("want call graph search mode; got %v", results.SearchMode)
- }
-
- want := []Finding{
- {
- Symbol: "example.com/vuln.Bad",
- Trace: []TraceElem{
- {Description: "command-line-arguments.init(...)", Position: &token.Position{}},
- {Description: "example.com/vuln.init(...)", Position: &token.Position{}},
- {Description: "example.com/vuln.init#1(...)", Position: &token.Position{}}},
- Type: FunctionType,
- Position: &token.Position{Line: 5, Filename: "vuln.go"},
- weight: 0,
- },
- }
- if got := projectFindings(results.VulnFindings["V3"]); !reflect.DeepEqual(want, got) {
- t.Errorf("want %v findings (projected); got %v", want, got)
- }
-}
diff --git a/vulndb/internal/audit/detect_imports.go b/vulndb/internal/audit/detect_imports.go
deleted file mode 100644
index 4e2a78e..0000000
--- a/vulndb/internal/audit/detect_imports.go
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package audit
-
-import (
- "container/list"
- "go/types"
-
- "golang.org/x/tools/go/ssa"
-)
-
-// VulnerableImports returns vulnerability findings for packages imported by `pkgs`
-// given the vulnerability and platform info captured in `env`.
-//
-// Returns all findings reachable from `pkgs` while analyzing each package only once, preferring
-// findings of shorter import traces. For instance, given import chains
-// A -> B -> V
-// A -> D -> B -> V
-// D -> B -> V
-// where A and D are top level packages and V is a vulnerable package, VulnerableImports can return either
-// A -> B -> V
-// or
-// D -> B -> V
-// as traces of importing a vulnerable package V.
-//
-// Findings for each vulnerability are sorted by estimated usefulness to the user.
-func VulnerableImports(pkgs []*ssa.Package, modVulns ModuleVulnerabilities) Results {
- results := Results{
- SearchMode: ImportsSearch,
- Vulnerabilities: serialize(modVulns.Vulns()),
- VulnFindings: make(map[string][]Finding),
- }
- if len(modVulns) == 0 {
- return results
- }
-
- seen := make(map[string]bool)
- queue := list.New()
- for _, pkg := range pkgs {
- queue.PushBack(&importChain{pkg: pkg.Pkg})
- }
-
- for queue.Len() > 0 {
- front := queue.Front()
- c := front.Value.(*importChain)
- queue.Remove(front)
-
- pkg := c.pkg
- if pkg == nil {
- continue
- }
-
- if seen[pkg.Path()] {
- continue
- }
- seen[pkg.Path()] = true
-
- for _, imp := range pkg.Imports() {
- vulns := modVulns.VulnsForPackage(imp.Path())
- for _, v := range serialize(vulns) {
- t := c.trace()
- results.addFinding(v, Finding{
- Symbol: imp.Path(),
- Type: ImportType,
- Trace: t,
- weight: len(t)})
- }
- queue.PushBack(&importChain{pkg: imp, parent: c})
- }
- }
-
- results.sort()
- return results
-}
-
-// importChain helps doing BFS over package imports while remembering import chains.
-type importChain struct {
- pkg *types.Package
- parent *importChain
-}
-
-func (chain *importChain) trace() []TraceElem {
- if chain == nil {
- return nil
- }
- return append(chain.parent.trace(), TraceElem{Description: chain.pkg.Path()})
-}
diff --git a/vulndb/internal/audit/detect_imports_test.go b/vulndb/internal/audit/detect_imports_test.go
deleted file mode 100644
index e0f3968..0000000
--- a/vulndb/internal/audit/detect_imports_test.go
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package audit
-
-import (
- "reflect"
- "testing"
-)
-
-func TestImportedPackageVulnDetection(t *testing.T) {
- pkgs, modVulns := testContext(t)
- results := VulnerableImports(pkgs, modVulns)
-
- if results.SearchMode != ImportsSearch {
- t.Errorf("want import search mode; got %v", results.SearchMode)
- }
-
- // There should be two chains reported in the following order
- // for two of the thirdparty.org test vulnerabilities:
- // T -> vuln
- // T -> A -> vuln
- for _, test := range []struct {
- vulnId string
- findings []Finding
- }{
- {vulnId: "V1", findings: []Finding{
- {
- Symbol: "thirdparty.org/vulnerabilities/vuln",
- Trace: []TraceElem{{Description: "command-line-arguments"}},
- Type: ImportType,
- weight: 1,
- },
- {
- Symbol: "thirdparty.org/vulnerabilities/vuln",
- Trace: []TraceElem{{Description: "command-line-arguments"}, {Description: "a.org/A"}},
- Type: ImportType,
- weight: 2,
- },
- }},
- {vulnId: "V2", findings: []Finding{
- {
- Symbol: "thirdparty.org/vulnerabilities/vuln",
- Trace: []TraceElem{{Description: "command-line-arguments"}},
- Type: ImportType,
- weight: 1,
- },
- {
- Symbol: "thirdparty.org/vulnerabilities/vuln",
- Trace: []TraceElem{{Description: "command-line-arguments"}, {Description: "a.org/A"}},
- Type: ImportType,
- weight: 2,
- },
- }},
- } {
- got := projectFindings(results.VulnFindings[test.vulnId])
- if !reflect.DeepEqual(test.findings, got) {
- t.Errorf("want %v findings (projected); got %v", test.findings, got)
- }
- }
-}
diff --git a/vulndb/internal/audit/detect_test.go b/vulndb/internal/audit/detect_test.go
deleted file mode 100644
index 6c54732..0000000
--- a/vulndb/internal/audit/detect_test.go
+++ /dev/null
@@ -1,238 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package audit
-
-import (
- "fmt"
- "reflect"
- "testing"
-
- "golang.org/x/tools/go/packages"
- "golang.org/x/vuln/osv"
-)
-
-func moduleVulnerabilitiesToString(mv ModuleVulnerabilities) string {
- var s string
- for _, m := range mv {
- s += fmt.Sprintf("mod: %v\n", m.mod)
- for _, v := range m.vulns {
- s += fmt.Sprintf("\t%v\n", v)
- }
- }
- return s
-}
-
-func TestFilterVulns(t *testing.T) {
- mv := ModuleVulnerabilities{
- {
- mod: &packages.Module{
- Path: "example.mod/a",
- Version: "v1.0.0",
- },
- vulns: []*osv.Entry{
- {ID: "a", Affected: []osv.Affected{
- {Ranges: osv.Affects{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Fixed: "2.0.0"}}}}},
- {Ranges: osv.Affects{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Fixed: "1.0.0"}}}}}, // should be filtered out
- }},
- {ID: "b", Affected: []osv.Affected{{Ranges: osv.Affects{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Introduced: "1.0.1"}}}}, EcosystemSpecific: osv.EcosystemSpecific{GOOS: []string{"windows", "linux"}}}}},
- {ID: "c", Affected: []osv.Affected{{Ranges: osv.Affects{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Introduced: "1.0.1"}, {Fixed: "1.0.1"}}}}, EcosystemSpecific: osv.EcosystemSpecific{GOARCH: []string{"arm64", "amd64"}}}}},
- {ID: "d", Affected: []osv.Affected{{EcosystemSpecific: osv.EcosystemSpecific{GOOS: []string{"windows"}}}}},
- },
- },
- {
- mod: &packages.Module{
- Path: "example.mod/b",
- Version: "v1.0.0",
- },
- vulns: []*osv.Entry{
- {ID: "e", Affected: []osv.Affected{{EcosystemSpecific: osv.EcosystemSpecific{GOARCH: []string{"arm64"}}}}},
- {ID: "f", Affected: []osv.Affected{{EcosystemSpecific: osv.EcosystemSpecific{GOOS: []string{"linux"}}}}},
- {ID: "g", Affected: []osv.Affected{{EcosystemSpecific: osv.EcosystemSpecific{GOARCH: []string{"amd64"}}, Ranges: osv.Affects{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Introduced: "0.0.1"}, {Fixed: "2.0.1"}}}}}}},
- {ID: "h", Affected: []osv.Affected{{EcosystemSpecific: osv.EcosystemSpecific{GOOS: []string{"windows"}, GOARCH: []string{"amd64"}}}}},
- },
- },
- {
- mod: &packages.Module{
- Path: "example.mod/c",
- },
- vulns: []*osv.Entry{
- {ID: "i", Affected: []osv.Affected{{EcosystemSpecific: osv.EcosystemSpecific{GOARCH: []string{"amd64"}}, Ranges: osv.Affects{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Introduced: "0.0.0"}}}}}}},
- {ID: "j", Affected: []osv.Affected{{EcosystemSpecific: osv.EcosystemSpecific{GOARCH: []string{"amd64"}}, Ranges: osv.Affects{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Fixed: "3.0.0"}}}}}}},
- {ID: "k"},
- },
- },
- {
- mod: &packages.Module{
- Path: "example.mod/d",
- Version: "v1.2.0",
- },
- vulns: []*osv.Entry{
- {ID: "l", Affected: []osv.Affected{
- {EcosystemSpecific: osv.EcosystemSpecific{GOOS: []string{"windows"}}}, // should be filtered out
- {EcosystemSpecific: osv.EcosystemSpecific{GOOS: []string{"linux"}}},
- }},
- },
- },
- }
-
- expected := ModuleVulnerabilities{
- {
- mod: &packages.Module{
- Path: "example.mod/a",
- Version: "v1.0.0",
- },
- vulns: []*osv.Entry{
- {ID: "a", Affected: []osv.Affected{{Ranges: osv.Affects{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Fixed: "2.0.0"}}}}}}},
- {ID: "c", Affected: []osv.Affected{{EcosystemSpecific: osv.EcosystemSpecific{GOARCH: []string{"arm64", "amd64"}}, Ranges: osv.Affects{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Introduced: "1.0.1"}, {Fixed: "1.0.1"}}}}}}},
- },
- },
- {
- mod: &packages.Module{
- Path: "example.mod/b",
- Version: "v1.0.0",
- },
- vulns: []*osv.Entry{
- {ID: "f", Affected: []osv.Affected{{EcosystemSpecific: osv.EcosystemSpecific{GOOS: []string{"linux"}}}}},
- {ID: "g", Affected: []osv.Affected{{EcosystemSpecific: osv.EcosystemSpecific{GOARCH: []string{"amd64"}}, Ranges: osv.Affects{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Introduced: "0.0.1"}, {Fixed: "2.0.1"}}}}}}},
- },
- },
- {
- mod: &packages.Module{
- Path: "example.mod/c",
- },
- },
- {
- mod: &packages.Module{
- Path: "example.mod/d",
- Version: "v1.2.0",
- },
- vulns: []*osv.Entry{
- {ID: "l", Affected: []osv.Affected{{EcosystemSpecific: osv.EcosystemSpecific{GOOS: []string{"linux"}}}}},
- },
- },
- }
-
- filtered := mv.Filter("linux", "amd64")
- if !reflect.DeepEqual(filtered, expected) {
- t.Fatalf("Filter returned unexpected results, got:\n%s\nwant:\n%s", moduleVulnerabilitiesToString(filtered), moduleVulnerabilitiesToString(expected))
- }
-}
-
-func vulnsToString(vulns []*osv.Entry) string {
- var s string
- for _, v := range vulns {
- s += fmt.Sprintf("\t%v\n", v)
- }
- return s
-}
-
-func TestVulnsForPackage(t *testing.T) {
- mv := ModuleVulnerabilities{
- {
- mod: &packages.Module{
- Path: "example.mod/a",
- Version: "v1.0.0",
- },
- vulns: []*osv.Entry{
- {ID: "a", Affected: []osv.Affected{{Package: osv.Package{Name: "example.mod/a/b/c"}}}},
- },
- },
- {
- mod: &packages.Module{
- Path: "example.mod/a/b",
- Version: "v1.0.0",
- },
- vulns: []*osv.Entry{
- {ID: "b", Affected: []osv.Affected{{Package: osv.Package{Name: "example.mod/a/b/c"}}}},
- },
- },
- {
- mod: &packages.Module{
- Path: "example.mod/d",
- Version: "v0.0.1",
- },
- vulns: []*osv.Entry{
- {ID: "d", Affected: []osv.Affected{{Package: osv.Package{Name: "example.mod/d"}}}},
- },
- },
- }
-
- filtered := mv.VulnsForPackage("example.mod/a/b/c")
- expected := []*osv.Entry{
- {ID: "b", Affected: []osv.Affected{{Package: osv.Package{Name: "example.mod/a/b/c"}}}},
- }
-
- if !reflect.DeepEqual(filtered, expected) {
- t.Fatalf("VulnsForPackage returned unexpected results, got:\n%s\nwant:\n%s", vulnsToString(filtered), vulnsToString(expected))
- }
-}
-
-func TestVulnsForPackageReplaced(t *testing.T) {
- mv := ModuleVulnerabilities{
- {
- mod: &packages.Module{
- Path: "example.mod/a",
- Version: "v1.0.0",
- },
- vulns: []*osv.Entry{
- {ID: "a", Affected: []osv.Affected{{Package: osv.Package{Name: "example.mod/a/b/c"}}}},
- },
- },
- {
- mod: &packages.Module{
- Path: "example.mod/a/b",
- Replace: &packages.Module{
- Path: "example.mod/b",
- },
- Version: "v1.0.0",
- },
- vulns: []*osv.Entry{
- {ID: "c", Affected: []osv.Affected{{Package: osv.Package{Name: "example.mod/b/c"}}}},
- },
- },
- }
-
- filtered := mv.VulnsForPackage("example.mod/a/b/c")
- expected := []*osv.Entry{
- {ID: "c", Affected: []osv.Affected{{Package: osv.Package{Name: "example.mod/b/c"}}}},
- }
-
- if !reflect.DeepEqual(filtered, expected) {
- t.Fatalf("VulnsForPackage returned unexpected results, got:\n%s\nwant:\n%s", vulnsToString(filtered), vulnsToString(expected))
- }
-}
-
-func TestVulnsForSymbol(t *testing.T) {
- mv := ModuleVulnerabilities{
- {
- mod: &packages.Module{
- Path: "example.mod/a",
- Version: "v1.0.0",
- },
- vulns: []*osv.Entry{
- {ID: "a", Affected: []osv.Affected{{Package: osv.Package{Name: "example.mod/a/b/c"}}}},
- },
- },
- {
- mod: &packages.Module{
- Path: "example.mod/a/b",
- Version: "v1.0.0",
- },
- vulns: []*osv.Entry{
- {ID: "b", Affected: []osv.Affected{{Package: osv.Package{Name: "example.mod/a/b/c"}, EcosystemSpecific: osv.EcosystemSpecific{Symbols: []string{"a"}}}}},
- {ID: "c", Affected: []osv.Affected{{Package: osv.Package{Name: "example.mod/a/b/c"}, EcosystemSpecific: osv.EcosystemSpecific{Symbols: []string{"b"}}}}},
- },
- },
- }
-
- filtered := mv.VulnsForSymbol("example.mod/a/b/c", "a")
- expected := []*osv.Entry{
- {ID: "b", Affected: []osv.Affected{{Package: osv.Package{Name: "example.mod/a/b/c"}, EcosystemSpecific: osv.EcosystemSpecific{Symbols: []string{"a"}}}}},
- }
-
- if !reflect.DeepEqual(filtered, expected) {
- t.Fatalf("VulnsForPackage returned unexpected results, got:\n%s\nwant:\n%s", vulnsToString(filtered), vulnsToString(expected))
- }
-}
diff --git a/vulndb/internal/audit/helpers_test.go b/vulndb/internal/audit/helpers_test.go
deleted file mode 100644
index 0e65a1f..0000000
--- a/vulndb/internal/audit/helpers_test.go
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package audit
-
-import (
- "go/token"
- "io/ioutil"
- "path"
- "path/filepath"
- "strings"
- "testing"
-
- "golang.org/x/tools/go/packages"
- "golang.org/x/tools/go/packages/packagestest"
- "golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/vuln/osv"
-)
-
-// Loads test program and environment with the following import structure
-// T
-// / | \
-// A | B
-// \ | /
-// \ | A
-// \ | /
-// vuln
-// where `vuln` is a package containing some vulnerabilities. The definition
-// of T can be found in testdata/top_package.go, A is in testdata/a_dep.go,
-// B is in testdata/b_dep.go, and vuln is in testdata/vuln.go.
-//
-// The program has the following vulnerabilities that should be reported
-// T:T1() -> vuln.VG
-// T:T1() -> A:A1() -> vuln.VulnData.Vuln()
-// T:T2() -> vuln.Vuln() [approx.resolved] -> vuln.VG
-// T:T1() -> vuln.VulnData.Vuln() [approx. resolved]
-//
-// The following vulnerability should not be reported as it is redundant:
-// T:T1() -> A:A1() -> B:B1() -> vuln.VulnData.Vuln()
-func testContext(t *testing.T) ([]*ssa.Package, ModuleVulnerabilities) {
- e := packagestest.Export(t, packagestest.Modules, []packagestest.Module{
- {
- Name: "golang.org/vulntest",
- Files: map[string]interface{}{"T/T.go": readFile(t, "testdata/top_package.go")},
- },
- {
- Name: "a.org@v1.1.1",
- Files: map[string]interface{}{"A/A.go": readFile(t, "testdata/a_dep.go")},
- },
- {
- Name: "b.org@v1.2.2",
- Files: map[string]interface{}{"B/B.go": readFile(t, "testdata/b_dep.go")},
- },
- {
- Name: "thirdparty.org/vulnerabilities@v1.0.1",
- Files: map[string]interface{}{"vuln/vuln.go": readFile(t, "testdata/vuln.go")},
- },
- })
- defer e.Cleanup()
-
- _, ssaPkgs, _, err := loadAndBuildPackages(e, "/vulntest/T/T.go")
- if err != nil {
- t.Fatal(err)
- }
- if len(ssaPkgs) != 1 {
- t.Errorf("want 1 top level SSA package; got %d", len(ssaPkgs))
- }
-
- modVulns := ModuleVulnerabilities{
- {
- mod: &packages.Module{Path: "thirdparty.org/vulnerabilities", Version: "v1.0.1"},
- vulns: []*osv.Entry{
- {
- ID: "V1",
- Affected: []osv.Affected{{
- Package: osv.Package{Name: "thirdparty.org/vulnerabilities/vuln"},
- Ranges: osv.Affects{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Introduced: "1.0.0"}, {Fixed: "1.0.4"}, {Introduced: "1.1.2"}}}},
- EcosystemSpecific: osv.EcosystemSpecific{Symbols: []string{"VulnData.Vuln", "VulnData.VulnOnPtr"}},
- }},
- },
- {
- ID: "V2",
- Affected: []osv.Affected{{
- Package: osv.Package{Name: "thirdparty.org/vulnerabilities/vuln"},
- Ranges: osv.Affects{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Introduced: "1.0.1"}, {Fixed: "1.0.2"}}}},
- EcosystemSpecific: osv.EcosystemSpecific{Symbols: []string{"VG"}},
- }},
- },
- },
- },
- }
-
- return ssaPkgs, modVulns
-}
-
-func loadAndBuildPackages(e *packagestest.Exported, file string) (*ssa.Program, []*ssa.Package, []*packages.Package, error) {
- e.Config.Mode |= packages.NeedModule | packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles | packages.NeedImports | packages.NeedTypes | packages.NeedTypesSizes | packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedDeps
- // Get the path to the test file.
- filepath := path.Join(e.Temp(), file)
- pkgs, err := packages.Load(e.Config, filepath)
- if err != nil {
- return nil, nil, nil, err
- }
-
- prog, ssaPkgs := ssautil.AllPackages(pkgs, 0)
- prog.Build()
- return prog, ssaPkgs, pkgs, nil
-}
-
-// projectPosition simplifies position to only filename and location info.
-func projectPosition(pos *token.Position) *token.Position {
- if pos == nil {
- return nil
- }
- fname := pos.Filename
- if fname != "" {
- fname = filepath.Base(fname)
- }
- return &token.Position{Line: pos.Line, Filename: fname}
-}
-
-// projectTrace simplifies traces for testing comparison purposes
-// by simplifying position info.
-func projectTrace(trace []TraceElem) []TraceElem {
- var nt []TraceElem
- for _, e := range trace {
- nt = append(nt, TraceElem{Description: e.Description, Position: projectPosition(e.Position)})
- }
- return nt
-}
-
-// projectFindings simplifies findings for testing comparison purposes. Traces
-// are removed their position info, finding's position only contains file and
-// line info, and vulnerabilities only have package path.
-func projectFindings(findings []Finding) []Finding {
- var nfs []Finding
- for _, f := range findings {
- nf := Finding{
- Type: f.Type,
- Symbol: f.Symbol,
- Position: projectPosition(f.Position),
- Trace: projectTrace(f.Trace),
- weight: f.weight,
- }
- nfs = append(nfs, nf)
- }
- return nfs
-}
-
-func readFile(t *testing.T, path string) string {
- content, err := ioutil.ReadFile(path)
- if err != nil {
- t.Fatalf("failed to load code from `%v`: %v", path, err)
- }
- return strings.ReplaceAll(string(content), "// go:build ignore", "")
-}
diff --git a/vulndb/internal/audit/order.go b/vulndb/internal/audit/order.go
deleted file mode 100644
index 522e3ab..0000000
--- a/vulndb/internal/audit/order.go
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package audit
-
-import (
- "fmt"
- "strings"
-)
-
-// findingCompare compares two findings in terms of their estimated value to the user.
-// Findings of shorter traces generally come earlier in the ordering.
-//
-// Two findings produced by audit call graph search are lexicographically ordered by:
-// 1) their estimated level of confidence in being a true positive, 2) the length of
-// their traces, and 3) the number of unresolved call sites in the traces.
-func findingCompare(finding1, finding2 *Finding) bool {
- if finding1.confidence != finding2.confidence {
- return finding1.confidence < finding2.confidence
- }
-
- if len(finding1.Trace) != len(finding2.Trace) {
- return len(finding1.Trace) < len(finding2.Trace)
- }
-
- if finding1.weight != finding2.weight {
- return finding1.weight < finding2.weight
- }
- // At this point we just need to make sure the ordering is deterministic.
- // TODO(zpavlinovic): is there a more meaningful ordering?
- return findingStrCompare(finding1, finding2)
-}
-
-// findingStrCompare compares string representation of findings pointwise by their fields.
-func findingStrCompare(finding1, finding2 *Finding) bool {
- if cmp := strings.Compare(finding1.Symbol, finding2.Symbol); cmp != 0 {
- return cmp < 0
- }
-
- if cmp := strings.Compare(fmt.Sprintf("%v", finding1.Type), fmt.Sprintf("%v", finding2.Type)); cmp != 0 {
- return cmp < 0
- }
-
- if cmp := strings.Compare(fmt.Sprintf("%v", finding1.Position), fmt.Sprintf("%v", finding2.Position)); cmp != 0 {
- return cmp < 0
- }
-
- return strings.Compare(fmt.Sprintf("%v", finding1.Trace), fmt.Sprintf("%v", finding2.Trace)) <= 0
-}
diff --git a/vulndb/internal/audit/order_test.go b/vulndb/internal/audit/order_test.go
deleted file mode 100644
index 18506ce..0000000
--- a/vulndb/internal/audit/order_test.go
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package audit
-
-import (
- "reflect"
- "sort"
- "testing"
-)
-
-func TestFindingsOrdering(t *testing.T) {
- f1 := Finding{Trace: []TraceElem{
- {Description: "T1"},
- },
- }
- f2 := Finding{Trace: []TraceElem{
- {Description: "T1"},
- {Description: "T2"},
- },
- }
- f3 := Finding{Trace: []TraceElem{
- {Description: "T1"}},
- confidence: 1,
- }
- f4 := Finding{Trace: []TraceElem{
- {Description: "T1"}},
- confidence: 1,
- weight: 2,
- }
-
- finds := []Finding{f4, f3, f2, f1}
- sort.SliceStable(finds, func(i int, j int) bool { return findingCompare(&finds[i], &finds[j]) })
- if want := []Finding{f1, f2, f3, f4}; !reflect.DeepEqual(finds, want) {
- t.Errorf("want ordering %v; got %v", want, finds)
- }
-}
diff --git a/vulndb/internal/audit/slicing.go b/vulndb/internal/audit/slicing.go
deleted file mode 100644
index b976f16..0000000
--- a/vulndb/internal/audit/slicing.go
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package audit
-
-import (
- "golang.org/x/tools/go/callgraph"
- "golang.org/x/tools/go/ssa"
-)
-
-// forwardReachableFrom computes the set of functions forward reachable from `sources`.
-// A function f is reachable from a function g if f is an anonymous function defined
-// in g or a function called in g as given by the callgraph `cg`.
-func forwardReachableFrom(sources map[*ssa.Function]bool, cg *callgraph.Graph) map[*ssa.Function]bool {
- m := make(map[*ssa.Function]bool)
- for s := range sources {
- forward(s, cg, m)
- }
- return m
-}
-
-func forward(f *ssa.Function, cg *callgraph.Graph, seen map[*ssa.Function]bool) {
- if _, ok := seen[f]; ok {
- return
- }
- seen[f] = true
- var buf [10]*ssa.Value // avoid alloc in common case
- for _, b := range f.Blocks {
- for _, instr := range b.Instrs {
- switch i := instr.(type) {
- case ssa.CallInstruction:
- for _, c := range siteCallees(i, cg) {
- forward(c, cg, seen)
- }
- default:
- for _, op := range i.Operands(buf[:0]) {
- if fn, ok := (*op).(*ssa.Function); ok {
- forward(fn, cg, seen)
- }
- }
- }
- }
- }
-}
-
-// pruneSlice removes functions in `slice` that are in `toPrune`.
-func pruneSlice(slice map[*ssa.Function]bool, toPrune map[*ssa.Function]bool) {
- for f := range slice {
- if _, ok := toPrune[f]; !ok {
- delete(slice, f)
- }
- }
-}
diff --git a/vulndb/internal/audit/slicing_test.go b/vulndb/internal/audit/slicing_test.go
deleted file mode 100644
index 782b6c3..0000000
--- a/vulndb/internal/audit/slicing_test.go
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package audit
-
-import (
- "reflect"
- "testing"
-
- "golang.org/x/tools/go/callgraph/cha"
- "golang.org/x/tools/go/packages/packagestest"
- "golang.org/x/tools/go/ssa"
-)
-
-// funcsToString returns a set of function names for `funcs`.
-func funcsToString(funcs map[*ssa.Function]bool) map[string]bool {
- fs := make(map[string]bool)
- for f := range funcs {
- fs[dbFuncName(f)] = true
- }
- return fs
-}
-
-func TestSlicing(t *testing.T) {
- e := packagestest.Export(t, packagestest.Modules, []packagestest.Module{
- {
- Name: "some/module",
- Files: map[string]interface{}{"slice/slice.go": readFile(t, "testdata/slice.go")},
- },
- })
- prog, pkgs, _, err := loadAndBuildPackages(e, "/module/slice/slice.go")
- if err != nil {
- t.Fatal(err)
- }
-
- pkg := pkgs[0]
- sources := map[*ssa.Function]bool{pkg.Func("Apply"): true, pkg.Func("Do"): true}
- fs := funcsToString(forwardReachableFrom(sources, cha.CallGraph(prog)))
- want := map[string]bool{
- "Apply": true,
- "Apply$1": true,
- "X": true,
- "Y": true,
- "Do": true,
- "Do$1": true,
- "Do$1$1": true,
- "debug": true,
- "A.Foo": true,
- "B.Foo": true,
- }
- if !reflect.DeepEqual(want, fs) {
- t.Errorf("want %v; got %v", want, fs)
- }
-}
diff --git a/vulndb/internal/audit/testdata/a_dep.go b/vulndb/internal/audit/testdata/a_dep.go
deleted file mode 100644
index 56e3664..0000000
--- a/vulndb/internal/audit/testdata/a_dep.go
+++ /dev/null
@@ -1,43 +0,0 @@
-// go:build ignore
-
-package A
-
-import (
- "thirdparty.org/vulnerabilities/vuln"
-)
-
-type I interface {
- Vuln()
-}
-
-func A1() I {
- v := vuln.VulnData{}
- v.Vuln() // vuln use
- return v
-}
-
-func A2() func() {
- return vuln.Vuln
-}
-
-func A3() func() {
- return func() {}
-}
-
-type vulnWrap struct {
- V I
-}
-
-func A4(f func(i I)) vulnWrap {
- f(vuln.VulnData{})
- return vulnWrap{}
-}
-
-func doWrap(i I) {
- w := vulnWrap{}
- w.V = i
-}
-
-// Part of a test program consisting of packages found
-// in top_package.go, b_dep.go, and vuln.go. For more
-// details, see testProgAndEnv function in helpers_test.go.
diff --git a/vulndb/internal/audit/testdata/b_dep.go b/vulndb/internal/audit/testdata/b_dep.go
deleted file mode 100644
index 2090059..0000000
--- a/vulndb/internal/audit/testdata/b_dep.go
+++ /dev/null
@@ -1,22 +0,0 @@
-// go:build ignore
-
-package B
-
-import (
- "a.org/A"
-)
-
-type internal struct{}
-
-func (i internal) Vuln() {}
-
-func B1() {
- A.A1() // transitive vuln use but should not be reported
- var i A.I
- i = internal{}
- i.Vuln() // no vuln use
-}
-
-// Part of a test program consisting of packages found in
-// vuln.go, a_dep.go, and b_dep.go. For more details, see
-// testProgAndEnv function in helpers_test.go.
diff --git a/vulndb/internal/audit/testdata/slice.go b/vulndb/internal/audit/testdata/slice.go
deleted file mode 100644
index 5fbbe68..0000000
--- a/vulndb/internal/audit/testdata/slice.go
+++ /dev/null
@@ -1,57 +0,0 @@
-// go:build ignore
-
-package testdata
-
-func X() {}
-func Y() {}
-
-// not reachable
-func id(i int) int {
- return i
-}
-
-// not reachable
-func inc(i int) int {
- return i + 1
-}
-
-func Apply(b bool, h func()) {
- if b {
- func() {
- print("applied")
- }()
- return
- }
- h()
-}
-
-type I interface {
- Foo()
-}
-
-type A struct{}
-
-func (a A) Foo() {}
-
-// not reachable
-func (a A) Bar() {}
-
-type B struct{}
-
-func (b B) Foo() {}
-
-func debug(s string) {
- print(s)
-}
-
-func Do(i I, input string) {
- debug(input)
-
- i.Foo()
-
- func(x string) {
- func(l int) {
- print(l)
- }(len(x))
- }(input)
-}
diff --git a/vulndb/internal/audit/testdata/top_package.go b/vulndb/internal/audit/testdata/top_package.go
deleted file mode 100644
index 8b045b8..0000000
--- a/vulndb/internal/audit/testdata/top_package.go
+++ /dev/null
@@ -1,34 +0,0 @@
-// go:build ignore
-
-package T
-
-import (
- "a.org/A"
- "b.org/B"
- "thirdparty.org/vulnerabilities/vuln"
-)
-
-func T1(x bool) {
- print(vuln.VG) // vuln use
- if x {
- A.A1().Vuln() // vuln use
- } else {
- B.B1() // no vuln use
- }
-}
-
-func T2(x bool) {
- if x {
- A.A2()() // vuln use. The return value of A.A2() is stored in register t0
- } else {
- A.A3()()
- w := A.A4(benign)
- w.V.Vuln() // no vuln use with vta-vta
- }
-}
-
-func benign(i A.I) {}
-
-// Part of a test program consisting of packages found in
-// vuln.go, a_dep.go, and b_dep.go. For more details,
-// see testProgAndEnv function in helpers_test.go
diff --git a/vulndb/internal/audit/testdata/vuln.go b/vulndb/internal/audit/testdata/vuln.go
deleted file mode 100644
index c981eff..0000000
--- a/vulndb/internal/audit/testdata/vuln.go
+++ /dev/null
@@ -1,17 +0,0 @@
-// go:build ignore
-
-package vuln
-
-var VG int
-
-type VulnData struct{}
-
-func (v VulnData) Vuln() {}
-
-func Vuln() {
- print(VG)
-}
-
-// Part of a test program consisting of packages found in
-// top_package.go, a_dep.go, and b_dep.go. For more details,
-// see testProgAndEnv function in helpers_test.go.
diff --git a/vulndb/internal/audit/utils.go b/vulndb/internal/audit/utils.go
deleted file mode 100644
index 76394ef..0000000
--- a/vulndb/internal/audit/utils.go
+++ /dev/null
@@ -1,213 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package audit
-
-import (
- "bytes"
- "fmt"
- "go/token"
- "go/types"
- "strings"
-
- "golang.org/x/tools/go/callgraph"
- "golang.org/x/tools/go/types/typeutil"
-
- "golang.org/x/tools/go/ssa"
-
- "golang.org/x/vuln/osv"
-)
-
-// instrPosition gives the position of `instr`. Returns empty token.Position
-// if no file information on `instr` is available.
-func instrPosition(instr ssa.Instruction) *token.Position {
- pos := instr.Parent().Prog.Fset.Position(instr.Pos())
- return &pos
-}
-
-// valPosition gives the position of `v` inside of `f`. Assumes `v` is used in
-// `f`. Returns empty token.Position if no file information on `f` is available.
-func valPosition(v ssa.Value, f *ssa.Function) *token.Position {
- pos := f.Prog.Fset.Position(v.Pos())
- return &pos
-}
-
-// funcPosition gives the position of `f`. Returns empty token.Position
-// if no file information on `f` is available.
-func funcPosition(f *ssa.Function) *token.Position {
- pos := f.Prog.Fset.Position(f.Pos())
- return &pos
-}
-
-// siteCallees computes a set of callees for call site `call` given program `callgraph`.
-func siteCallees(call ssa.CallInstruction, callgraph *callgraph.Graph) []*ssa.Function {
- var matches []*ssa.Function
-
- node := callgraph.Nodes[call.Parent()]
- if node == nil {
- return nil
- }
-
- for _, edge := range node.Out {
- callee := edge.Callee.Func
- // Some callgraph analyses, such as CHA, might return synthetic (interface)
- // methods as well as the concrete methods. Skip such synthetic functions.
- if edge.Site == call {
- matches = append(matches, callee)
- }
- }
- return matches
-}
-
-func callName(call ssa.CallInstruction) string {
- if !call.Common().IsInvoke() {
- return fmt.Sprintf("%s.%s", call.Parent().Pkg.Pkg.Path(), call.Common().Value.Name())
- }
- buf := new(bytes.Buffer)
- types.WriteType(buf, call.Common().Value.Type(), nil)
- return fmt.Sprintf("%s.%s", buf, call.Common().Method.Name())
-}
-
-func unresolved(call ssa.CallInstruction) bool {
- if call == nil {
- return false
- }
- return call.Common().StaticCallee() == nil
-}
-
-// pkgsProgram returns the single common program to which all pkgs belong, if such.
-// Otherwise, returns nil.
-func pkgsProgram(pkgs []*ssa.Package) *ssa.Program {
- var prog *ssa.Program
- for _, pkg := range pkgs {
- if prog == nil {
- prog = pkg.Prog
- } else if prog != pkg.Prog {
- return nil
- }
- }
- return prog
-}
-
-// globalUses returns a list of global uses by an instruction.
-// Global function callees are disregarded as they are preferred as call uses.
-func globalUses(instr ssa.Instruction) []*ssa.Value {
- ops := instr.Operands(nil)
- if _, ok := instr.(ssa.CallInstruction); ok {
- ops = ops[1:]
- }
-
- var glbs []*ssa.Value
- for _, o := range ops {
- if _, ok := (*o).(*ssa.Global); ok {
- glbs = append(glbs, o)
- }
- }
- return glbs
-}
-
-// Computes function name consistent with the function namings used in vulnerability
-// databases. Effectively, a qualified name of a function local to its enclosing package.
-// If a receiver is a pointer, this information is not encoded in the resulting name. The
-// name of anonymous functions is simply "". The function names are unique subject to the
-// enclosing package, but not globally.
-//
-// Examples:
-// func (a A) foo (...) {...} -> A.foo
-// func foo(...) {...} -> foo
-// func (b *B) bar (...) {...} -> B.bar
-func dbFuncName(f *ssa.Function) string {
- var typeFormat func(t types.Type) string
- typeFormat = func(t types.Type) string {
- switch tt := t.(type) {
- case *types.Pointer:
- return typeFormat(tt.Elem())
- case *types.Named:
- return tt.Obj().Name()
- default:
- return types.TypeString(t, func(p *types.Package) string { return "" })
- }
- }
- selectBound := func(f *ssa.Function) types.Type {
- // If f is a "bound" function introduced by ssa for a given type, return the type.
- // When "f" is a "bound" function, it will have 1 free variable of that type within
- // the function. This is subject to change when ssa changes.
- if len(f.FreeVars) == 1 && strings.HasPrefix(f.Synthetic, "bound ") {
- return f.FreeVars[0].Type()
- }
- return nil
- }
- selectThunk := func(f *ssa.Function) types.Type {
- // If f is a "thunk" function introduced by ssa for a given type, return the type.
- // When "f" is a "thunk" function, the first parameter will have that type within
- // the function. This is subject to change when ssa changes.
- params := f.Signature.Params() // params.Len() == 1 then params != nil.
- if strings.HasPrefix(f.Synthetic, "thunk ") && params.Len() >= 1 {
- if first := params.At(0); first != nil {
- return first.Type()
- }
- }
- return nil
- }
- var qprefix string
- if recv := f.Signature.Recv(); recv != nil {
- qprefix = typeFormat(recv.Type())
- } else if btype := selectBound(f); btype != nil {
- qprefix = typeFormat(btype)
- } else if ttype := selectThunk(f); ttype != nil {
- qprefix = typeFormat(ttype)
- }
-
- if qprefix == "" {
- return f.Name()
- }
- return qprefix + "." + f.Name()
-}
-
-// memberFuncs returns functions associated with the `member`:
-// 1) `member` itself if `member` is a function
-// 2) `member` methods if `member` is a type
-// 3) empty list otherwise
-func memberFuncs(member ssa.Member, prog *ssa.Program) []*ssa.Function {
- switch t := member.(type) {
- case *ssa.Type:
- methods := typeutil.IntuitiveMethodSet(t.Type(), &prog.MethodSets)
- var funcs []*ssa.Function
- for _, m := range methods {
- if f := prog.MethodValue(m); f != nil {
- funcs = append(funcs, f)
- }
- }
- return funcs
- case *ssa.Function:
- return []*ssa.Function{t}
- default:
- return nil
- }
-}
-
-// Returns the path of a package `f` belongs to. Covers both
-// the case when `f` is an anonymous and a synthetic function.
-func pkgPath(f *ssa.Function) string {
- // Handle all user defined functions.
- if p := f.Package(); p != nil && p.Pkg != nil {
- return p.Pkg.Path()
- }
- // Cover synthetic functions as well.
- if o := f.Object(); o != nil && o.Pkg() != nil {
- return o.Pkg().Path()
- }
- // Not reachable in principle.
- return ""
-}
-
-// serialize transforms []*osv.Entry into []osv.Entry as to
-// allow serialization of Finding.
-func serialize(vulns []*osv.Entry) []osv.Entry {
- var vs []osv.Entry
- for _, v := range vulns {
- vs = append(vs, *v)
- }
- return vs
-}
diff --git a/vulndb/internal/audit/vulnerability.go b/vulndb/internal/audit/vulnerability.go
deleted file mode 100644
index ea1125c..0000000
--- a/vulndb/internal/audit/vulnerability.go
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package audit
-
-import (
- "go/build"
- "os"
- "path/filepath"
- "strings"
-
- "golang.org/x/tools/go/packages"
- "golang.org/x/vuln/client"
-)
-
-// FetchVulnerabilities fetches vulnerabilities that affect the supplied modules.
-func FetchVulnerabilities(client client.Client, modules []*packages.Module) (ModuleVulnerabilities, error) {
- mv := ModuleVulnerabilities{}
- for _, mod := range modules {
- modPath := mod.Path
- if mod.Replace != nil {
- modPath = mod.Replace.Path
- }
-
- // skip loading vulns for local imports
- if isLocal(mod) {
- // TODO: what if client has its own db
- // with local vulns?
- continue
- }
- vulns, err := client.GetByModule(modPath)
- if err != nil {
- return nil, err
- }
- if len(vulns) == 0 {
- continue
- }
- mv = append(mv, modVulns{
- mod: mod,
- vulns: vulns,
- })
- }
- return mv, nil
-}
-
-func isLocal(mod *packages.Module) bool {
- modDir := mod.Dir
- if mod.Replace != nil {
- modDir = mod.Replace.Dir
- }
- return modDir != "" && !strings.HasPrefix(modDir, modCacheDirectory())
-}
-
-func modCacheDirectory() string {
- var modCacheDir string
- // TODO: define modCacheDir using cmd/go/internal/cfg.GOMODCACHE
- if modCacheDir = os.Getenv("GOMODCACHE"); modCacheDir == "" {
- if modCacheDir = os.Getenv("GOPATH"); modCacheDir == "" {
- modCacheDir = build.Default.GOPATH
- }
- modCacheDir = filepath.Join(modCacheDir, "pkg", "mod")
- }
- return modCacheDir
-}
diff --git a/vulndb/internal/audit/vulnerability_test.go b/vulndb/internal/audit/vulnerability_test.go
deleted file mode 100644
index 5761772..0000000
--- a/vulndb/internal/audit/vulnerability_test.go
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package audit
-
-import (
- "reflect"
- "testing"
-
- "golang.org/x/tools/go/packages"
- "golang.org/x/vuln/client"
- "golang.org/x/vuln/osv"
-)
-
-type mockClient struct {
- client.Client
- ret map[string][]*osv.Entry
-}
-
-func (mc *mockClient) GetByModule(a string) ([]*osv.Entry, error) {
- return mc.ret[a], nil
-}
-
-func TestFetchVulnerabilities(t *testing.T) {
- mc := &mockClient{
- ret: map[string][]*osv.Entry{
- "example.mod/a": {{ID: "a", Affected: []osv.Affected{{Package: osv.Package{Name: "example.mod/a"}, Ranges: osv.Affects{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Fixed: "2.0.0"}}}}}}}},
- "example.mod/b": {{ID: "b", Affected: []osv.Affected{{Package: osv.Package{Name: "example.mod/b"}, Ranges: osv.Affects{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Fixed: "1.1.1"}}}}}}}},
- "example.mod/d": {{ID: "c", Affected: []osv.Affected{{Package: osv.Package{Name: "example.mod/d"}, Ranges: osv.Affects{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Fixed: "2.0.0"}}}}}}}},
- "example.mod/e": {{ID: "e", Affected: []osv.Affected{{Package: osv.Package{Name: "example.mod/e"}, Ranges: osv.Affects{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Fixed: "2.2.0"}}}}}}}},
- },
- }
-
- mv, err := FetchVulnerabilities(mc, []*packages.Module{
- {Path: "example.mod/a", Dir: modCacheDirectory(), Version: "v1.0.0"},
- {Path: "example.mod/b", Dir: modCacheDirectory(), Version: "v1.0.4"},
- {Path: "example.mod/c", Replace: &packages.Module{Path: "example.mod/d", Dir: modCacheDirectory(), Version: "v1.0.0"}, Version: "v2.0.0"},
- {Path: "example.mod/e", Replace: &packages.Module{Path: "../local/example.mod/d", Dir: modCacheDirectory(), Version: "v1.0.1"}, Version: "v2.1.0"},
- })
- if err != nil {
- t.Fatalf("FetchVulnerabilities failed: %s", err)
- }
-
- expected := ModuleVulnerabilities{
- {
- mod: &packages.Module{Path: "example.mod/a", Dir: modCacheDirectory(), Version: "v1.0.0"},
- vulns: []*osv.Entry{
- {ID: "a", Affected: []osv.Affected{{Package: osv.Package{Name: "example.mod/a"}, Ranges: osv.Affects{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Fixed: "2.0.0"}}}}}}},
- },
- },
- {
- mod: &packages.Module{Path: "example.mod/b", Dir: modCacheDirectory(), Version: "v1.0.4"},
- vulns: []*osv.Entry{
- {ID: "b", Affected: []osv.Affected{{Package: osv.Package{Name: "example.mod/b"}, Ranges: osv.Affects{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Fixed: "1.1.1"}}}}}}},
- },
- },
- {
- mod: &packages.Module{Path: "example.mod/c", Replace: &packages.Module{Path: "example.mod/d", Dir: modCacheDirectory(), Version: "v1.0.0"}, Version: "v2.0.0"},
- vulns: []*osv.Entry{
- {ID: "c", Affected: []osv.Affected{{Package: osv.Package{Name: "example.mod/d"}, Ranges: osv.Affects{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Fixed: "2.0.0"}}}}}}},
- },
- },
- }
- if !reflect.DeepEqual(mv, expected) {
- t.Fatalf("FetchVulnerabilities returned unexpected results, got:\n%s\nwant:\n%s", moduleVulnerabilitiesToString(mv), moduleVulnerabilitiesToString(expected))
- }
-}
diff --git a/vulndb/internal/binscan/exe.go b/vulndb/internal/binscan/exe.go
deleted file mode 100644
index e705ebe..0000000
--- a/vulndb/internal/binscan/exe.go
+++ /dev/null
@@ -1,381 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package binscan
-
-// This file is a somewhat modified version of cmd/go/internal/version/exe.go
-// that adds functionality for extracting the PCLN table.
-
-import (
- "bytes"
- "debug/elf"
- "debug/macho"
- "debug/pe"
- "encoding/binary"
- "fmt"
-
- // "internal/xcoff"
- "io"
- "os"
-)
-
-// An exe is a generic interface to an OS executable (ELF, Mach-O, PE, XCOFF).
-type exe interface {
- // Close closes the underlying file.
- Close() error
-
- // ReadData reads and returns up to size byte starting at virtual address addr.
- ReadData(addr, size uint64) ([]byte, error)
-
- // DataStart returns the writable data segment start address.
- DataStart() uint64
-
- PCLNTab() ([]byte, uint64)
-}
-
-// openExe opens file and returns it as an exe.
-func openExe(file string) (exe, error) {
- f, err := os.Open(file)
- if err != nil {
- return nil, err
- }
- data := make([]byte, 16)
- if _, err := io.ReadFull(f, data); err != nil {
- return nil, err
- }
- f.Seek(0, 0)
- if bytes.HasPrefix(data, []byte("\x7FELF")) {
- e, err := elf.NewFile(f)
- if err != nil {
- f.Close()
- return nil, err
- }
- return &elfExe{f, e}, nil
- }
- if bytes.HasPrefix(data, []byte("MZ")) {
- e, err := pe.NewFile(f)
- if err != nil {
- f.Close()
- return nil, err
- }
- return &peExe{f, e}, nil
- }
- if bytes.HasPrefix(data, []byte("\xFE\xED\xFA")) || bytes.HasPrefix(data[1:], []byte("\xFA\xED\xFE")) {
- e, err := macho.NewFile(f)
- if err != nil {
- f.Close()
- return nil, err
- }
- return &machoExe{f, e}, nil
- }
- // TODO(rolandshoemaker): we cannot support XCOFF files due to the usage of internal/xcoff.
- // Once this code is moved into the stdlib, this support can be re-enabled.
- // if bytes.HasPrefix(data, []byte{0x01, 0xDF}) || bytes.HasPrefix(data, []byte{0x01, 0xF7}) {
- // e, err := xcoff.NewFile(f)
- // if err != nil {
- // f.Close()
- // return nil, err
- // }
- // return &xcoffExe{f, e}, nil
-
- // }
- return nil, fmt.Errorf("unrecognized executable format")
-}
-
-// elfExe is the ELF implementation of the exe interface.
-type elfExe struct {
- os *os.File
- f *elf.File
-}
-
-func (x *elfExe) Close() error {
- return x.os.Close()
-}
-
-func (x *elfExe) ReadData(addr, size uint64) ([]byte, error) {
- for _, prog := range x.f.Progs {
- if prog.Vaddr <= addr && addr <= prog.Vaddr+prog.Filesz-1 {
- n := prog.Vaddr + prog.Filesz - addr
- if n > size {
- n = size
- }
- data := make([]byte, n)
- _, err := prog.ReadAt(data, int64(addr-prog.Vaddr))
- if err != nil {
- return nil, err
- }
- return data, nil
- }
- }
- return nil, fmt.Errorf("address not mapped")
-}
-
-func (x *elfExe) DataStart() uint64 {
- for _, s := range x.f.Sections {
- if s.Name == ".go.buildinfo" {
- return s.Addr
- }
- }
- for _, p := range x.f.Progs {
- if p.Type == elf.PT_LOAD && p.Flags&(elf.PF_X|elf.PF_W) == elf.PF_W {
- return p.Vaddr
- }
- }
- return 0
-}
-
-const go12magic = 0xfffffffb
-const go116magic = 0xfffffffa
-
-func (x *elfExe) PCLNTab() ([]byte, uint64) {
- var offset uint64
- text := x.f.Section(".text")
- if text != nil {
- offset = text.Offset
- }
- pclntab := x.f.Section(".gopclntab")
- if pclntab == nil {
- pclntab = x.f.Section(".data.rel.ro.gopclntab")
- if pclntab == nil {
- pclntab = x.f.Section(".data.rel.ro")
- if pclntab == nil {
- return nil, 0
- }
- // Possibly the PCLN table has been stuck in the .data.rel.ro section, but without
- // its own section header. We can search for for the start by looking for the four
- // byte magic and the go magic.
- b, err := pclntab.Data()
- if err != nil {
- return nil, 0
- }
- // TODO(rolandshoemaker): I'm not sure if the 16 byte increment during the search is
- // actually correct. During testing it worked, but that may be because I got lucky
- // with the binary I was using, and we need to do four byte jumps to exhaustively
- // search the section?
- for i := 0; i < len(b); i += 16 {
- if len(b)-i > 16 && b[i+4] == 0 && b[i+5] == 0 &&
- (b[i+6] == 1 || b[i+6] == 2 || b[i+6] == 4) &&
- (b[i+7] == 4 || b[i+7] == 8) {
- // Also check for the go magic
- leMagic := binary.LittleEndian.Uint32(b[i:])
- beMagic := binary.BigEndian.Uint32(b[i:])
- switch {
- case leMagic == go12magic:
- fallthrough
- case beMagic == go12magic:
- fallthrough
- case leMagic == go116magic:
- fallthrough
- case beMagic == go116magic:
- return b[i:], offset
- }
- }
- }
- }
- }
- b, err := pclntab.Data()
- if err != nil {
- return nil, 0
- }
- return b, offset
-}
-
-// peExe is the PE (Windows Portable Executable) implementation of the exe interface.
-type peExe struct {
- os *os.File
- f *pe.File
-}
-
-func (x *peExe) Close() error {
- return x.os.Close()
-}
-
-func (x *peExe) imageBase() uint64 {
- switch oh := x.f.OptionalHeader.(type) {
- case *pe.OptionalHeader32:
- return uint64(oh.ImageBase)
- case *pe.OptionalHeader64:
- return oh.ImageBase
- }
- return 0
-}
-
-func (x *peExe) ReadData(addr, size uint64) ([]byte, error) {
- addr -= x.imageBase()
- for _, sect := range x.f.Sections {
- if uint64(sect.VirtualAddress) <= addr && addr <= uint64(sect.VirtualAddress+sect.Size-1) {
- n := uint64(sect.VirtualAddress+sect.Size) - addr
- if n > size {
- n = size
- }
- data := make([]byte, n)
- _, err := sect.ReadAt(data, int64(addr-uint64(sect.VirtualAddress)))
- if err != nil {
- return nil, err
- }
- return data, nil
- }
- }
- return nil, fmt.Errorf("address not mapped")
-}
-
-func (x *peExe) DataStart() uint64 {
- // Assume data is first writable section.
- const (
- IMAGE_SCN_CNT_CODE = 0x00000020
- IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040
- IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080
- IMAGE_SCN_MEM_EXECUTE = 0x20000000
- IMAGE_SCN_MEM_READ = 0x40000000
- IMAGE_SCN_MEM_WRITE = 0x80000000
- IMAGE_SCN_MEM_DISCARDABLE = 0x2000000
- IMAGE_SCN_LNK_NRELOC_OVFL = 0x1000000
- IMAGE_SCN_ALIGN_32BYTES = 0x600000
- )
- for _, sect := range x.f.Sections {
- if sect.VirtualAddress != 0 && sect.Size != 0 &&
- sect.Characteristics&^IMAGE_SCN_ALIGN_32BYTES == IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE {
- return uint64(sect.VirtualAddress) + x.imageBase()
- }
- }
- return 0
-}
-
-func (x *peExe) PCLNTab() ([]byte, uint64) {
- var textOffset uint64
- for _, section := range x.f.Sections {
- if section.Name == ".text" {
- textOffset = uint64(section.Offset)
- break
- }
- }
- var start, end int64
- var section int
- for _, symbol := range x.f.Symbols {
- if symbol.Name == "runtime.pclntab" {
- start = int64(symbol.Value)
- section = int(symbol.SectionNumber - 1)
- } else if symbol.Name == "runtime.epclntab" {
- end = int64(symbol.Value)
- break
- }
- }
- if start == 0 || end == 0 {
- return nil, 0
- }
- offset := int64(x.f.Sections[section].Offset) + start
- size := end - start
-
- pclntab := make([]byte, size)
- if _, err := x.os.ReadAt(pclntab, offset); err != nil {
- return nil, 0
- }
-
- return pclntab, textOffset
-}
-
-// machoExe is the Mach-O (Apple macOS/iOS) implementation of the exe interface.
-type machoExe struct {
- os *os.File
- f *macho.File
-}
-
-func (x *machoExe) Close() error {
- return x.os.Close()
-}
-
-func (x *machoExe) ReadData(addr, size uint64) ([]byte, error) {
- for _, load := range x.f.Loads {
- seg, ok := load.(*macho.Segment)
- if !ok {
- continue
- }
- if seg.Addr <= addr && addr <= seg.Addr+seg.Filesz-1 {
- if seg.Name == "__PAGEZERO" {
- continue
- }
- n := seg.Addr + seg.Filesz - addr
- if n > size {
- n = size
- }
- data := make([]byte, n)
- _, err := seg.ReadAt(data, int64(addr-seg.Addr))
- if err != nil {
- return nil, err
- }
- return data, nil
- }
- }
- return nil, fmt.Errorf("address not mapped")
-}
-
-func (x *machoExe) DataStart() uint64 {
- // Look for section named "__go_buildinfo".
- for _, sec := range x.f.Sections {
- if sec.Name == "__go_buildinfo" {
- return sec.Addr
- }
- }
- // Try the first non-empty writable segment.
- const RW = 3
- for _, load := range x.f.Loads {
- seg, ok := load.(*macho.Segment)
- if ok && seg.Addr != 0 && seg.Filesz != 0 && seg.Prot == RW && seg.Maxprot == RW {
- return seg.Addr
- }
- }
- return 0
-}
-
-func (x *machoExe) PCLNTab() ([]byte, uint64) {
- var textOffset uint64
- text := x.f.Section("__text")
- if text != nil {
- textOffset = uint64(text.Offset)
- }
- pclntab := x.f.Section("__gopclntab")
- if pclntab == nil {
- return nil, 0
- }
- b, err := pclntab.Data()
- if err != nil {
- return nil, 0
- }
- return b, textOffset
-}
-
-// TODO(rolandshoemaker): we cannot support XCOFF files due to the usage of internal/xcoff.
-// Once this code is moved into the stdlib, this support can be re-enabled.
-
-// // xcoffExe is the XCOFF (AIX eXtended COFF) implementation of the exe interface.
-// type xcoffExe struct {
-// os *os.File
-// f *xcoff.File
-// }
-//
-// func (x *xcoffExe) Close() error {
-// return x.os.Close()
-// }
-//
-// func (x *xcoffExe) ReadData(addr, size uint64) ([]byte, error) {
-// for _, sect := range x.f.Sections {
-// if uint64(sect.VirtualAddress) <= addr && addr <= uint64(sect.VirtualAddress+sect.Size-1) {
-// n := uint64(sect.VirtualAddress+sect.Size) - addr
-// if n > size {
-// n = size
-// }
-// data := make([]byte, n)
-// _, err := sect.ReadAt(data, int64(addr-uint64(sect.VirtualAddress)))
-// if err != nil {
-// return nil, err
-// }
-// return data, nil
-// }
-// }
-// return nil, fmt.Errorf("address not mapped")
-// }
-//
-// func (x *xcoffExe) DataStart() uint64 {
-// return x.f.SectionByType(xcoff.STYP_DATA).VirtualAddress
-// }
diff --git a/vulndb/internal/binscan/scan.go b/vulndb/internal/binscan/scan.go
deleted file mode 100644
index a88740d..0000000
--- a/vulndb/internal/binscan/scan.go
+++ /dev/null
@@ -1,243 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package binscan contains methods for parsing Go binary files for the purpose
-// of extracting module dependency and symbol table information.
-package binscan
-
-// Code in this package is dervied from src/cmd/go/internal/version/version.go
-// and cmd/go/internal/version/exe.go.
-
-import (
- "bytes"
- "debug/gosym"
- "encoding/binary"
- "errors"
- "fmt"
- "net/url"
- "runtime/debug"
- "strings"
-
- "golang.org/x/tools/go/packages"
-)
-
-// buildInfoMagic, findVers, and readString are copied from
-// cmd/go/internal/version
-
-// The build info blob left by the linker is identified by
-// a 16-byte header, consisting of buildInfoMagic (14 bytes),
-// the binary's pointer size (1 byte),
-// and whether the binary is big endian (1 byte).
-var buildInfoMagic = []byte("\xff Go buildinf:")
-
-// findVers finds and returns the Go version and module version information
-// in the executable x.
-func findVers(x exe) string {
- // Read the first 64kB of text to find the build info blob.
- text := x.DataStart()
- data, err := x.ReadData(text, 64*1024)
- if err != nil {
- return ""
- }
- for ; !bytes.HasPrefix(data, buildInfoMagic); data = data[32:] {
- if len(data) < 32 {
- return ""
- }
- }
-
- // Decode the blob.
- ptrSize := int(data[14])
- bigEndian := data[15] != 0
- var bo binary.ByteOrder
- if bigEndian {
- bo = binary.BigEndian
- } else {
- bo = binary.LittleEndian
- }
- var readPtr func([]byte) uint64
- if ptrSize == 4 {
- readPtr = func(b []byte) uint64 { return uint64(bo.Uint32(b)) }
- } else {
- readPtr = bo.Uint64
- }
- vers := readString(x, ptrSize, readPtr, readPtr(data[16:]))
- if vers == "" {
- return ""
- }
- mod := readString(x, ptrSize, readPtr, readPtr(data[16+ptrSize:]))
- if len(mod) >= 33 && mod[len(mod)-17] == '\n' {
- // Strip module framing.
- mod = mod[16 : len(mod)-16]
- } else {
- mod = ""
- }
- return mod
-}
-
-// readString returns the string at address addr in the executable x.
-func readString(x exe, ptrSize int, readPtr func([]byte) uint64, addr uint64) string {
- hdr, err := x.ReadData(addr, uint64(2*ptrSize))
- if err != nil || len(hdr) < 2*ptrSize {
- return ""
- }
- dataAddr := readPtr(hdr)
- dataLen := readPtr(hdr[ptrSize:])
- data, err := x.ReadData(dataAddr, dataLen)
- if err != nil || uint64(len(data)) < dataLen {
- return ""
- }
- return string(data)
-}
-
-// readBuildInfo is copied from runtime/debug
-func readBuildInfo(data string) (*debug.BuildInfo, bool) {
- if len(data) == 0 {
- return nil, false
- }
-
- const (
- pathLine = "path\t"
- modLine = "mod\t"
- depLine = "dep\t"
- repLine = "=>\t"
- )
-
- readEntryFirstLine := func(elem []string) (debug.Module, bool) {
- if len(elem) != 2 && len(elem) != 3 {
- return debug.Module{}, false
- }
- sum := ""
- if len(elem) == 3 {
- sum = elem[2]
- }
- return debug.Module{
- Path: elem[0],
- Version: elem[1],
- Sum: sum,
- }, true
- }
-
- var (
- info = &debug.BuildInfo{}
- last *debug.Module
- line string
- ok bool
- )
- // Reverse of cmd/go/internal/modload.PackageBuildInfo
- for len(data) > 0 {
- i := strings.IndexByte(data, '\n')
- if i < 0 {
- break
- }
- line, data = data[:i], data[i+1:]
- switch {
- case strings.HasPrefix(line, pathLine):
- elem := line[len(pathLine):]
- info.Path = elem
- case strings.HasPrefix(line, modLine):
- elem := strings.Split(line[len(modLine):], "\t")
- last = &info.Main
- *last, ok = readEntryFirstLine(elem)
- if !ok {
- return nil, false
- }
- case strings.HasPrefix(line, depLine):
- elem := strings.Split(line[len(depLine):], "\t")
- last = new(debug.Module)
- info.Deps = append(info.Deps, last)
- *last, ok = readEntryFirstLine(elem)
- if !ok {
- return nil, false
- }
- case strings.HasPrefix(line, repLine):
- elem := strings.Split(line[len(repLine):], "\t")
- if len(elem) != 3 {
- return nil, false
- }
- if last == nil {
- return nil, false
- }
- last.Replace = &debug.Module{
- Path: elem[0],
- Version: elem[1],
- Sum: elem[2],
- }
- last = nil
- }
- }
- return info, true
-}
-
-func debugModulesToPackagesModules(debugModules []*debug.Module) []*packages.Module {
- packagesModules := make([]*packages.Module, len(debugModules))
- for i, mod := range debugModules {
- packagesModules[i] = &packages.Module{
- Path: mod.Path,
- Version: mod.Version,
- }
- if mod.Replace != nil {
- packagesModules[i].Replace = &packages.Module{
- Path: mod.Replace.Path,
- Version: mod.Replace.Version,
- }
- }
- }
- return packagesModules
-}
-
-// ExtractPackagesAndSymbols extracts the symbols, packages, and their associated module versions
-// from a Go binary. Stripped binaries are not supported.
-func ExtractPackagesAndSymbols(binPath string) ([]*packages.Module, map[string][]string, error) {
- x, err := openExe(binPath)
- if err != nil {
- return nil, nil, err
- }
-
- pclntab, textOffset := x.PCLNTab()
- if pclntab == nil {
- // TODO(roland): if we have build information, but not PCLN table, we should be able to
- // fall back to much higher granularity vulnerability checking.
- return nil, nil, errors.New("unable to load the PCLN table")
- }
- lineTab := gosym.NewLineTable(pclntab, textOffset)
- if lineTab == nil {
- return nil, nil, errors.New("invalid line table")
- }
- tab, err := gosym.NewTable(nil, lineTab)
- if err != nil {
- return nil, nil, err
- }
-
- packageSymbols := map[string][]string{}
- for _, f := range tab.Funcs {
- if f.Func == nil {
- continue
- }
- symName := f.Func.BaseName()
- if r := f.Func.ReceiverName(); r != "" {
- if strings.HasPrefix(r, "(*") {
- r = strings.Trim(r, "(*)")
- }
- symName = fmt.Sprintf("%s.%s", r, symName)
- }
-
- pkgName := f.Func.PackageName()
- if pkgName == "" {
- continue
- }
- pkgName, err := url.PathUnescape(pkgName)
- if err != nil {
- return nil, nil, err
- }
-
- packageSymbols[pkgName] = append(packageSymbols[pkgName], symName)
- }
-
- bi, ok := readBuildInfo(findVers(x))
- if !ok {
- return nil, nil, err
- }
-
- return debugModulesToPackagesModules(bi.Deps), packageSymbols, nil
-}