mirror of
https://github.com/anchore/grype
synced 2024-11-10 06:34:13 +00:00
Add matching by CPE (#40)
* Commit just to share progress, needs to be squashed/fixed-up once working. Signed-off-by: Zach Hill <zach@anchore.com> * minor fixes * add cpe obj * add cpe matching * report cpe in search key * add verbose logging for matches; bump vulnscan-db ver * add dev profiler option; tweak logging * test support for CPE URI bindings addresses https://github.com/anchore/vulnscan/pull/40#discussion_r455389937 * rename nvdv2 to nvd * reduce scope of cpe matching to non-distro packages * normalize nil constraint strings Co-authored-by: Zach Hill <zach@anchore.com>
This commit is contained in:
parent
afb8597aa2
commit
bbff869499
35 changed files with 1072 additions and 131 deletions
9
.gitignore
vendored
9
.gitignore
vendored
|
@ -1,9 +1,14 @@
|
|||
*.tar.gz
|
||||
*.profile
|
||||
.server
|
||||
Dockerfile
|
||||
/metadata.json
|
||||
/listing.json
|
||||
.vscode/
|
||||
.server/
|
||||
*.db
|
||||
*.db-journal
|
||||
!**/test-fixtures/**/*.db
|
||||
*.tar
|
||||
*tar.gz
|
||||
.idea/
|
||||
*.log
|
||||
.images
|
||||
|
|
|
@ -22,7 +22,6 @@ linters:
|
|||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- maligned
|
||||
- misspell
|
||||
- nakedret
|
||||
- nolintlint
|
||||
|
@ -48,6 +47,7 @@ linters:
|
|||
# - gomnd # this is too aggressive
|
||||
# - interfacer # this is a good idea, but is no longer supported and is prone to false positives
|
||||
# - lll # without a way to specify per-line exception cases, this is not usable
|
||||
# - maligned # this is an excellent linter, but tricky to optimize and we are not sensitive to memory layout optimizations
|
||||
# - nestif
|
||||
# - testpackage
|
||||
# - wsl
|
21
cmd/root.go
21
cmd/root.go
|
@ -3,6 +3,7 @@ package cmd
|
|||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime/pprof"
|
||||
|
||||
"github.com/anchore/imgbom/imgbom"
|
||||
_distro "github.com/anchore/imgbom/imgbom/distro"
|
||||
|
@ -30,7 +31,25 @@ var rootCmd = &cobra.Command{
|
|||
}),
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
os.Exit(runDefaultCmd(cmd, args))
|
||||
if appConfig.Dev.ProfileCPU {
|
||||
f, err := os.Create("cpu.profile")
|
||||
if err != nil {
|
||||
log.Errorf("unable to create CPU profile: %+v", err)
|
||||
} else {
|
||||
err := pprof.StartCPUProfile(f)
|
||||
if err != nil {
|
||||
log.Errorf("unable to start CPU profile: %+v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exitCode := runDefaultCmd(cmd, args)
|
||||
|
||||
if appConfig.Dev.ProfileCPU {
|
||||
pprof.StopCPUProfile()
|
||||
}
|
||||
|
||||
os.Exit(exitCode)
|
||||
},
|
||||
}
|
||||
|
||||
|
|
2
go.mod
2
go.mod
|
@ -9,6 +9,8 @@ require (
|
|||
github.com/anchore/imgbom v0.0.0-20200713170720-e8d11eec6992
|
||||
github.com/anchore/siren-db v0.0.0-20200716152335-9bc4580f72a1
|
||||
github.com/anchore/stereoscope v0.0.0-20200706164556-7cf39d7f4639
|
||||
github.com/facebookincubator/nvdtools v0.1.4-0.20200622182922-aed862a62ae6
|
||||
github.com/go-test/deep v1.0.7
|
||||
github.com/hashicorp/go-getter v1.4.1
|
||||
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
|
||||
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d
|
||||
|
|
45
go.sum
45
go.sum
|
@ -144,7 +144,6 @@ github.com/aws/aws-sdk-go v1.31.6 h1:nKjQbpXhdImctBh1e0iLg9iQW/X297LPPuY/9f92R2k
|
|||
github.com/aws/aws-sdk-go v1.31.6/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
|
||||
|
@ -184,7 +183,6 @@ github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDG
|
|||
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ=
|
||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
|
||||
|
@ -237,11 +235,12 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
|
|||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/facebookincubator/nvdtools v0.1.4-0.20200622182922-aed862a62ae6 h1:+GR1Gkrl/JervFT1aKR4kzG8T10QWYMSKfYfhCJX0vU=
|
||||
github.com/facebookincubator/nvdtools v0.1.4-0.20200622182922-aed862a62ae6/go.mod h1:0/FIVnSEl9YHXLq3tKBPpKaI0iUceDhdSHPlIwIX44Y=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
|
@ -275,7 +274,6 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG
|
|||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-test/deep v1.0.6 h1:UHSEyLZUwX9Qoi99vVwvewiMC8mM2bf7XEM2nqvzEn8=
|
||||
github.com/go-test/deep v1.0.6/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8=
|
||||
github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M=
|
||||
github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8=
|
||||
|
@ -356,12 +354,10 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ
|
|||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1 h1:/exdXoGamhu5ONeUJH0deniYLWYvQwW66yvlfiiKTu0=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-containerregistry v0.0.0-20200430153450-5cbd060f5c92/go.mod h1:pD1UFYs7MCAx+ZLShBdttcaOSbyc8F9Na/9IZLNwJeA=
|
||||
github.com/google/go-containerregistry v0.1.0 h1:hL5mVw7cTX3SBr64Arpv+cJH93L+Z9Q6WjckImYLB3g=
|
||||
github.com/google/go-containerregistry v0.1.0/go.mod h1:npTSyywOeILcgWqd+rvtzGWflIPPcBQhYoOONaY4ltM=
|
||||
github.com/google/go-containerregistry v0.1.1 h1:AG8FSAfXglim2l5qSrqp5VK2Xl03PiBf25NiTGGamws=
|
||||
github.com/google/go-containerregistry v0.1.1/go.mod h1:npTSyywOeILcgWqd+rvtzGWflIPPcBQhYoOONaY4ltM=
|
||||
|
@ -384,7 +380,6 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
|
|||
github.com/google/rpmpack v0.0.0-20191226140753-aa36bfddb3a0/go.mod h1:RaTPr0KUf2K7fnZYLNDrr8rxAamWs3iNywJLtQ2AzBg=
|
||||
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/wire v0.3.0/go.mod h1:i1DMg/Lu8Sz5yYl25iOdmc5CT5qusaa+zmRWs16741s=
|
||||
github.com/google/wire v0.4.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
|
||||
|
@ -483,7 +478,6 @@ github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeY
|
|||
github.com/jmoiron/sqlx v1.2.1-0.20190826204134-d7d95172beb5/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
|
||||
github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0=
|
||||
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
|
@ -496,7 +490,6 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X
|
|||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcMpsXCp3lJ03zYT1PkRd3kQGPn9GVg=
|
||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
|
@ -555,7 +548,6 @@ github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
|
|||
github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
|
@ -565,7 +557,6 @@ github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/
|
|||
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
|
||||
github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
|
||||
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
|
@ -580,7 +571,6 @@ github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eI
|
|||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.3.1 h1:cCBH2gTD2K0OtLlv/Y5H01VQCqmlDxz30kS5Y5bqfLA=
|
||||
github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
|
@ -628,7 +618,6 @@ github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59P
|
|||
github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.8.0 h1:Keo9qb7iRJs2voHvunFtuuYFsbWeOBh8/P9v/kVMFtw=
|
||||
github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs=
|
||||
|
@ -647,20 +636,16 @@ github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndr
|
|||
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
|
||||
|
@ -699,7 +684,6 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
|
|||
github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo=
|
||||
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v1.0.0 h1:UVQPSSmc3qtTi+zPPkCXvZX9VvW/xT/NsRvKfwY81a8=
|
||||
github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
|
||||
|
@ -711,13 +695,10 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k
|
|||
github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE=
|
||||
github.com/sourcegraph/go-diff v0.5.3/go.mod h1:v9JDtjCE4HHHCZGId75rg8gkKKa98RVjBcBGsVmMmak=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/afero v1.3.2 h1:GDarE4TJQI52kYSbSAmLiId1Elfj+xgSDqrUZxFhxlU=
|
||||
github.com/spf13/afero v1.3.2/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
|
||||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
|
@ -726,14 +707,12 @@ github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3
|
|||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
|
||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
|
@ -749,7 +728,6 @@ github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRci
|
|||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.0 h1:jlIyCplCJFULU/01vCkhKuTyc3OorI3bJFuw6obfgho=
|
||||
github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
|
@ -791,7 +769,6 @@ github.com/wagoodman/go-partybus v0.0.0-20200526224238-eb215533f07d/go.mod h1:JP
|
|||
github.com/wagoodman/go-progress v0.0.0-20200621122631-1a2120f0695a/go.mod h1:jLXFoL31zFaHKAAyZUh+sxiTDFe1L1ZHrcK2T1itVKA=
|
||||
github.com/wagoodman/go-progress v0.0.0-20200621153512-2778c704bf22 h1:GYaiTP0ywrCjJ4qMxxCg+YKPSDMeFJg6i1X9X55LJCA=
|
||||
github.com/wagoodman/go-progress v0.0.0-20200621153512-2778c704bf22/go.mod h1:jLXFoL31zFaHKAAyZUh+sxiTDFe1L1ZHrcK2T1itVKA=
|
||||
github.com/wagoodman/jotframe v0.0.0-20200622123948-2995cbd43525 h1:fGlwSBQrl9/axciK2+gJ9q86SeQYJpbPx4vOrExvZXY=
|
||||
github.com/wagoodman/jotframe v0.0.0-20200622123948-2995cbd43525/go.mod h1:DzXZ1wfRedNhC3KQTick8Gf3CEPMFHsP5k4R/ldjKtw=
|
||||
github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
|
||||
github.com/xanzy/go-gitlab v0.32.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
|
||||
|
@ -802,7 +779,6 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
|||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738 h1:VcrIfasaLFkyjk6KNlXQSzO+B0fZcnECiDrKJsfxka0=
|
||||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
||||
go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
|
@ -841,7 +817,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
|||
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw=
|
||||
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM=
|
||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
|
@ -865,7 +840,6 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTk
|
|||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
|
@ -875,7 +849,6 @@ golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU
|
|||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
|
@ -919,7 +892,6 @@ golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2 h1:eDrdRpKgkcCqKZQwyZRyeFZgfqt37SL7Kv3tok06cKE=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM=
|
||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
|
@ -936,7 +908,6 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -962,7 +933,6 @@ golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190620070143-6f217b454f45/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -990,9 +960,7 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121 h1:rITEj+UZHYC927n8GT97eC3zrpzXdb/voyeOuVKS46o=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 h1:OjiUf46hAmXblsZdnoSXsEUSKU8r1UEzcL5RVZ4gO9Y=
|
||||
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200610111108-226ff32320da h1:bGb80FudwxpeucJUjPYJXuJ8Hk91vNtfvrymzwiei38=
|
||||
golang.org/x/sys v0.0.0-20200610111108-226ff32320da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -1004,7 +972,6 @@ golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
|||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI=
|
||||
|
@ -1061,7 +1028,6 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK
|
|||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204192400-7124308813f3/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200210192313-1ace956b0e17 h1:a/Fd23DJvg1CaeDH0dYHahE+hCI0v9rFgxSNIThoUcM=
|
||||
golang.org/x/tools v0.0.0-20200210192313-1ace956b0e17/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
|
@ -1140,11 +1106,8 @@ google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfG
|
|||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200519141106-08726f379972/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece h1:1YM0uhfumvoDu9sx8+RyWwTI63zoCQvI23IYFRlvte0=
|
||||
google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200604104852-0b0486081ffb h1:ek2py5bOqzR7MR/6obzk0rXUgYCLmjyLnaO9ssT+l6w=
|
||||
google.golang.org/genproto v0.0.0-20200604104852-0b0486081ffb/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200615140333-fd031eab31e7 h1:1N7l1PuXZwEK7OhHdmKQROOM75PnUjABGwvVRbLBgFk=
|
||||
google.golang.org/genproto v0.0.0-20200615140333-fd031eab31e7/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
|
@ -1168,7 +1131,6 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ
|
|||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
|
||||
|
@ -1187,7 +1149,6 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
|
|||
gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww=
|
||||
|
@ -1200,7 +1161,6 @@ gopkg.in/warnings.v0 v0.1.1/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRN
|
|||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
@ -1215,7 +1175,6 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh
|
|||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8=
|
||||
|
|
|
@ -26,7 +26,8 @@ type Application struct {
|
|||
Quiet bool `mapstructure:"quiet"`
|
||||
Log Logging `mapstructure:"log"`
|
||||
CliOptions CliOnlyOptions
|
||||
Db Database `mapstructure:"db"`
|
||||
Db Database `mapstructure:"db"`
|
||||
Dev Development `mapstructure:"dev"`
|
||||
}
|
||||
|
||||
type Logging struct {
|
||||
|
@ -42,6 +43,10 @@ type Database struct {
|
|||
UpdateOnStartup bool `mapstructure:"update-on-startup"`
|
||||
}
|
||||
|
||||
type Development struct {
|
||||
ProfileCPU bool `mapstructure:"profile-cpu"`
|
||||
}
|
||||
|
||||
func (d Database) ToCuratorConfig() db.Config {
|
||||
return db.Config{
|
||||
DbDir: d.Dir,
|
||||
|
@ -59,6 +64,7 @@ func setNonCliDefaultValues(v *viper.Viper) {
|
|||
v.SetDefault("db.update-url", "http://localhost:5000/listing.json")
|
||||
// TODO: set this to true before release
|
||||
v.SetDefault("db.update-on-startup", false)
|
||||
v.SetDefault("dev.profile-cpu", false)
|
||||
}
|
||||
|
||||
func LoadConfigFromFile(v *viper.Viper, cliOpts *CliOnlyOptions) (*Application, error) {
|
||||
|
@ -97,7 +103,7 @@ func (cfg *Application) Build() error {
|
|||
|
||||
if cfg.Quiet {
|
||||
// TODO: this is bad: quiet option trumps all other logging options
|
||||
// we should be able to quiet the consol logging and leave file logging alone...
|
||||
// we should be able to quiet the console logging and leave file logging alone...
|
||||
// ... this will be an enhancement for later
|
||||
cfg.Log.LevelOpt = zapcore.PanicLevel
|
||||
} else {
|
||||
|
@ -132,7 +138,7 @@ func readConfig(v *viper.Viper, configPath string) error {
|
|||
v.SetEnvPrefix(internal.ApplicationName)
|
||||
// allow for nested options to be specified via environment variables
|
||||
// e.g. pod.context = APPNAME_POD_CONTEXT
|
||||
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
|
||||
|
||||
// use explicitly the given user config
|
||||
if configPath != "" {
|
||||
|
|
140
vulnscan/cpe/cpe.go
Normal file
140
vulnscan/cpe/cpe.go
Normal file
|
@ -0,0 +1,140 @@
|
|||
package cpe
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/anchore/imgbom/imgbom/pkg"
|
||||
"github.com/anchore/vulnscan/internal"
|
||||
"github.com/facebookincubator/nvdtools/wfn"
|
||||
)
|
||||
|
||||
// TODO: would be great to allow these to be overridden by user data/config
|
||||
var targetSoftware = map[pkg.Language][]string{
|
||||
pkg.Java: {
|
||||
"java",
|
||||
"maven",
|
||||
"jenkins",
|
||||
"cloudbees_jenkins",
|
||||
},
|
||||
//pkg.JavaScript: {
|
||||
// "node.js",
|
||||
//},
|
||||
pkg.Python: {
|
||||
"python",
|
||||
},
|
||||
pkg.Ruby: {
|
||||
"ruby",
|
||||
"rails",
|
||||
},
|
||||
}
|
||||
|
||||
const ANY = "*"
|
||||
|
||||
type CPE = wfn.Attributes
|
||||
|
||||
func New(cpeStr string) (CPE, error) {
|
||||
value, err := wfn.Parse(cpeStr)
|
||||
// we need to compare the raw data since we are constructing CPEs in other locations
|
||||
value.Vendor = wfn.StripSlashes(value.Vendor)
|
||||
value.Product = wfn.StripSlashes(value.Product)
|
||||
value.Language = wfn.StripSlashes(value.Language)
|
||||
value.Version = wfn.StripSlashes(value.Version)
|
||||
value.TargetSW = wfn.StripSlashes(value.TargetSW)
|
||||
value.Part = wfn.StripSlashes(value.Part)
|
||||
value.Edition = wfn.StripSlashes(value.Edition)
|
||||
value.Other = wfn.StripSlashes(value.Other)
|
||||
value.SWEdition = wfn.StripSlashes(value.SWEdition)
|
||||
value.TargetHW = wfn.StripSlashes(value.TargetHW)
|
||||
value.Update = wfn.StripSlashes(value.Update)
|
||||
|
||||
if value == nil || err != nil {
|
||||
return CPE{}, fmt.Errorf("failed to parse CPE (%s): %w", cpeStr, err)
|
||||
}
|
||||
return *value, nil
|
||||
}
|
||||
|
||||
func NewSlice(cpeStrs ...string) ([]CPE, error) {
|
||||
ret := make([]CPE, len(cpeStrs))
|
||||
for idx, c := range cpeStrs {
|
||||
value, err := New(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret[idx] = value
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Generate Create a list of CPEs, trying to guess the vendor, product tuple and setting TargetSoftware if possible
|
||||
func Generate(p *pkg.Package) ([]CPE, error) {
|
||||
targetSoftwares := candidateTargetSoftwareAttrs(p)
|
||||
vendors := candidateVendors(p)
|
||||
products := candidateProducts(p)
|
||||
|
||||
keys := internal.NewStringSet()
|
||||
cpes := make([]CPE, 0)
|
||||
for _, product := range products {
|
||||
for _, vendor := range vendors {
|
||||
for _, targetSw := range targetSoftwares {
|
||||
// prevent duplicate entries...
|
||||
key := fmt.Sprintf("%s|%s|%s|%s", product, vendor, p.Version, targetSw)
|
||||
if keys.Contains(key) {
|
||||
continue
|
||||
}
|
||||
keys.Add(key)
|
||||
|
||||
// add a new entry...
|
||||
candidateCpe := wfn.NewAttributesWithAny()
|
||||
candidateCpe.Product = product
|
||||
candidateCpe.Vendor = vendor
|
||||
candidateCpe.Version = p.Version
|
||||
candidateCpe.TargetSW = targetSw
|
||||
|
||||
cpes = append(cpes, *candidateCpe)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cpes, nil
|
||||
}
|
||||
|
||||
func candidateTargetSoftwareAttrs(p *pkg.Package) []string {
|
||||
// TODO: expand with package metadata (from type assert)
|
||||
mappedNames := targetSoftware[p.Language]
|
||||
|
||||
if mappedNames == nil {
|
||||
mappedNames = []string{}
|
||||
}
|
||||
|
||||
attrs := make([]string, len(mappedNames))
|
||||
copy(attrs, targetSoftware[p.Language])
|
||||
// last element is the any match, present for all
|
||||
attrs = append(attrs, ANY)
|
||||
|
||||
return attrs
|
||||
}
|
||||
|
||||
func candidateVendors(p *pkg.Package) []string {
|
||||
// TODO: expand with package metadata (from type assert)
|
||||
ret := []string{p.Name}
|
||||
if p.Language == pkg.Python {
|
||||
ret = append(ret, fmt.Sprintf("python-%s", p.Name))
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func candidateProducts(p *pkg.Package) []string {
|
||||
// TODO: expand with package metadata (from type assert)
|
||||
return []string{p.Name}
|
||||
}
|
||||
|
||||
func MatchWithoutVersion(c CPE, candidates []CPE) []CPE {
|
||||
results := make([]CPE, 0)
|
||||
for _, candidate := range candidates {
|
||||
canCopy := candidate
|
||||
if c.MatchWithoutVersion(&canCopy) {
|
||||
results = append(results, candidate)
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
219
vulnscan/cpe/cpe_test.go
Normal file
219
vulnscan/cpe/cpe_test.go
Normal file
|
@ -0,0 +1,219 @@
|
|||
package cpe
|
||||
|
||||
import (
|
||||
"github.com/anchore/imgbom/imgbom/pkg"
|
||||
"github.com/sergi/go-diff/diffmatchpatch"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func must(c CPE, e error) CPE {
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected CPE
|
||||
}{
|
||||
{
|
||||
name: "gocase",
|
||||
input: `cpe:/a:10web:form_maker:1.0.0::~~~wordpress~~`,
|
||||
expected: must(New(`cpe:2.3:a:10web:form_maker:1.0.0:*:*:*:*:wordpress:*:*`)),
|
||||
},
|
||||
{
|
||||
name: "dashes",
|
||||
input: `cpe:/a:7-zip:7-zip:4.56:beta:~~~windows~~`,
|
||||
expected: must(New(`cpe:2.3:a:7-zip:7-zip:4.56:beta:*:*:*:windows:*:*`)),
|
||||
},
|
||||
{
|
||||
name: "URL escape characters",
|
||||
input: `cpe:/a:%240.99_kindle_books_project:%240.99_kindle_books:6::~~~android~~`,
|
||||
expected: must(New(`cpe:2.3:a:$0.99_kindle_books_project:$0.99_kindle_books:6:*:*:*:*:android:*:*`)),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
actual, err := New(test.input)
|
||||
if err != nil {
|
||||
t.Fatalf("got an error while creating CPE: %+v", err)
|
||||
}
|
||||
|
||||
if actual.BindToFmtString() != test.expected.BindToFmtString() {
|
||||
t.Errorf("mismatched entries:\n\texpected:%+v\n\t actual:%+v\n", test.expected.BindToFmtString(), actual.BindToFmtString())
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
p pkg.Package
|
||||
expected []CPE
|
||||
}{
|
||||
{
|
||||
name: "simple package",
|
||||
p: pkg.Package{
|
||||
Name: "name",
|
||||
Version: "3.2",
|
||||
FoundBy: "some-analyzer",
|
||||
Language: pkg.Java,
|
||||
Type: pkg.DebPkg,
|
||||
},
|
||||
expected: []CPE{
|
||||
must(New("cpe:2.3:*:name:name:3.2:*:*:*:*:java:*:*")),
|
||||
must(New("cpe:2.3:*:name:name:3.2:*:*:*:*:maven:*:*")),
|
||||
must(New("cpe:2.3:*:name:name:3.2:*:*:*:*:jenkins:*:*")),
|
||||
must(New("cpe:2.3:*:name:name:3.2:*:*:*:*:cloudbees_jenkins:*:*")),
|
||||
must(New("cpe:2.3:*:name:name:3.2:*:*:*:*:*:*:*")),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
actual, err := Generate(&test.p)
|
||||
if err != nil {
|
||||
t.Fatalf("got an error while generating CPEs: %+v", err)
|
||||
}
|
||||
|
||||
if len(actual) != len(test.expected) {
|
||||
for _, e := range actual {
|
||||
t.Errorf(" unexpected entry: %+v", e.BindToFmtString())
|
||||
}
|
||||
t.Fatalf("unexpected number of entries: %d", len(actual))
|
||||
}
|
||||
|
||||
for idx, a := range actual {
|
||||
e := test.expected[idx]
|
||||
if a.BindToFmtString() != e.BindToFmtString() {
|
||||
t.Errorf("mismatched entries @ %d:\n\texpected:%+v\n\t actual:%+v\n", idx, e.BindToFmtString(), a.BindToFmtString())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchWithoutVersion(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
compare CPE
|
||||
candidates []CPE
|
||||
expected []CPE
|
||||
}{
|
||||
{
|
||||
name: "GoCase",
|
||||
compare: must(New("cpe:2.3:*:python-requests:requests:2.3.0:*:*:*:*:python:*:*")),
|
||||
candidates: []CPE{
|
||||
must(New("cpe:2.3:a:python-requests:requests:2.2.1:*:*:*:*:*:*:*")),
|
||||
},
|
||||
expected: []CPE{
|
||||
must(New("cpe:2.3:a:python-requests:requests:2.2.1:*:*:*:*:*:*:*")),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "IgnoreVersion",
|
||||
compare: must(New("cpe:2.3:*:name:name:3.2:*:*:*:*:java:*:*")),
|
||||
candidates: []CPE{
|
||||
must(New("cpe:2.3:*:name:name:3.2:*:*:*:*:java:*:*")),
|
||||
must(New("cpe:2.3:*:name:name:3.3:*:*:*:*:java:*:*")),
|
||||
must(New("cpe:2.3:*:name:name:5.5:*:*:*:*:java:*:*")),
|
||||
},
|
||||
expected: []CPE{
|
||||
must(New("cpe:2.3:*:name:name:3.2:*:*:*:*:java:*:*")),
|
||||
must(New("cpe:2.3:*:name:name:3.3:*:*:*:*:java:*:*")),
|
||||
must(New("cpe:2.3:*:name:name:5.5:*:*:*:*:java:*:*")),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "MatchByTargetSW",
|
||||
compare: must(New("cpe:2.3:*:name:name:3.2:*:*:*:*:java:*:*")),
|
||||
candidates: []CPE{
|
||||
must(New("cpe:2.3:*:name:name:3.2:*:*:*:*:java:*:*")),
|
||||
must(New("cpe:2.3:*:name:name:3.2:*:*:*:*:maven:*:*")),
|
||||
must(New("cpe:2.3:*:name:name:3.2:*:*:*:*:jenkins:*:*")),
|
||||
must(New("cpe:2.3:*:name:name:3.2:*:*:*:*:cloudbees_jenkins:*:*")),
|
||||
must(New("cpe:2.3:*:name:name:3.2:*:*:*:*:*:*:*")),
|
||||
},
|
||||
expected: []CPE{
|
||||
must(New("cpe:2.3:*:name:name:3.2:*:*:*:*:java:*:*")),
|
||||
must(New("cpe:2.3:*:name:name:3.2:*:*:*:*:*:*:*")),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "MatchByName",
|
||||
compare: must(New("cpe:2.3:*:name:name5:3.2:*:*:*:*:java:*:*")),
|
||||
candidates: []CPE{
|
||||
must(New("cpe:2.3:*:name:name1:3.2:*:*:*:*:java:*:*")),
|
||||
must(New("cpe:2.3:*:name:name2:3.2:*:*:*:*:java:*:*")),
|
||||
must(New("cpe:2.3:*:name:name3:3.2:*:*:*:*:java:*:*")),
|
||||
must(New("cpe:2.3:*:name:name4:3.2:*:*:*:*:java:*:*")),
|
||||
must(New("cpe:2.3:*:name:name5:3.2:*:*:*:*:*:*:*")),
|
||||
},
|
||||
expected: []CPE{
|
||||
must(New("cpe:2.3:*:name:name5:3.2:*:*:*:*:*:*:*")),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "MatchByVendor",
|
||||
compare: must(New("cpe:2.3:*:name3:name:3.2:*:*:*:*:java:*:*")),
|
||||
candidates: []CPE{
|
||||
must(New("cpe:2.3:*:name1:name:3.2:*:*:*:*:java:*:*")),
|
||||
must(New("cpe:2.3:*:name3:name:3.2:*:*:*:*:jaba-no-bother:*:*")),
|
||||
must(New("cpe:2.3:*:name3:name:3.2:*:*:*:*:java:*:*")),
|
||||
must(New("cpe:2.3:*:name4:name:3.2:*:*:*:*:java:*:*")),
|
||||
must(New("cpe:2.3:*:name5:name:3.2:*:*:*:*:*:*:*")),
|
||||
},
|
||||
expected: []CPE{
|
||||
must(New("cpe:2.3:*:name3:name:3.2:*:*:*:*:java:*:*")),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "MatchAnyVendorOrTargetSW",
|
||||
compare: must(New("cpe:2.3:*:*:name:3.2:*:*:*:*:*:*:*")),
|
||||
candidates: []CPE{
|
||||
must(New("cpe:2.3:*:name1:name:3.2:*:*:*:*:java:*:*")),
|
||||
must(New("cpe:2.3:*:name3:name:3.2:*:*:*:*:jaba-no-bother:*:*")),
|
||||
must(New("cpe:2.3:*:name3:name:3.2:*:*:*:*:java:*:*")),
|
||||
must(New("cpe:2.3:*:name4:name:3.2:*:*:*:*:java:*:*")),
|
||||
must(New("cpe:2.3:*:name5:name:3.2:*:*:*:*:*:*:*")),
|
||||
must(New("cpe:2.3:*:name5:NOMATCH:3.2:*:*:*:*:*:*:*")),
|
||||
},
|
||||
expected: []CPE{
|
||||
must(New("cpe:2.3:*:name1:name:3.2:*:*:*:*:java:*:*")),
|
||||
must(New("cpe:2.3:*:name3:name:3.2:*:*:*:*:jaba-no-bother:*:*")),
|
||||
must(New("cpe:2.3:*:name3:name:3.2:*:*:*:*:java:*:*")),
|
||||
must(New("cpe:2.3:*:name4:name:3.2:*:*:*:*:java:*:*")),
|
||||
must(New("cpe:2.3:*:name5:name:3.2:*:*:*:*:*:*:*")),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
actual := MatchWithoutVersion(test.compare, test.candidates)
|
||||
|
||||
if len(actual) != len(test.expected) {
|
||||
for _, e := range actual {
|
||||
t.Errorf(" unexpected entry: %+v", e.BindToFmtString())
|
||||
}
|
||||
t.Fatalf("unexpected number of entries: %d", len(actual))
|
||||
}
|
||||
|
||||
for idx, a := range actual {
|
||||
e := test.expected[idx]
|
||||
if a.BindToFmtString() != e.BindToFmtString() {
|
||||
dmp := diffmatchpatch.New()
|
||||
diffs := dmp.DiffMain(a.BindToFmtString(), e.BindToFmtString(), true)
|
||||
t.Errorf("mismatched entries @ %d:\n\texpected:%+v\n\t actual:%+v\n\t diff:%+v\n", idx, e.BindToFmtString(), a.BindToFmtString(), dmp.DiffPrettyText(diffs))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -22,3 +22,7 @@ type Match struct {
|
|||
func (m Match) String() string {
|
||||
return fmt.Sprintf("Match(pkg=%s vuln=%s confidence=%f type='%s' key='%s' foundBy='%s')", m.Package, m.Vulnerability.String(), m.Confidence, m.Type, m.SearchKey, m.Matcher)
|
||||
}
|
||||
|
||||
func (m Match) Summary() string {
|
||||
return fmt.Sprintf("vuln='%s' confidence=%0.2f type='%s' key='%s' foundBy='%s')", m.Vulnerability.ID, m.Confidence, m.Type, m.SearchKey, m.Matcher)
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ const (
|
|||
UnknownMatchType Type = iota
|
||||
ExactDirectMatch
|
||||
ExactIndirectMatch
|
||||
FuzzyMatch
|
||||
)
|
||||
|
||||
type Type int
|
||||
|
@ -14,6 +15,7 @@ var typeStr = []string{
|
|||
"UnknownMatchType",
|
||||
"Exact-Direct Match",
|
||||
"Exact-Indirect Match",
|
||||
"Fuzzy Match",
|
||||
}
|
||||
|
||||
func ParseType(userStr string) Type {
|
||||
|
|
|
@ -19,6 +19,18 @@ func (m *Matcher) Name() string {
|
|||
return "bundler-matcher"
|
||||
}
|
||||
|
||||
func (m *Matcher) Match(store vulnerability.Provider, d distro.Distro, p *pkg.Package) ([]match.Match, error) {
|
||||
return common.FindMatchesByPackageLanguage(store, p.Language, p, m.Name())
|
||||
func (m *Matcher) Match(store vulnerability.Provider, _ distro.Distro, p *pkg.Package) ([]match.Match, error) {
|
||||
var matches = make([]match.Match, 0)
|
||||
langMatches, err := common.FindMatchesByPackageLanguage(store, p.Language, p, m.Name())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
matches = append(matches, langMatches...)
|
||||
|
||||
cpeMatches, err := common.FindMatchesByPackageCPE(store, p, m.Name())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
matches = append(matches, cpeMatches...)
|
||||
return matches, nil
|
||||
}
|
||||
|
|
52
vulnscan/matcher/common/cpe_matchers.go
Normal file
52
vulnscan/matcher/common/cpe_matchers.go
Normal file
|
@ -0,0 +1,52 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/anchore/imgbom/imgbom/pkg"
|
||||
"github.com/anchore/vulnscan/vulnscan/match"
|
||||
"github.com/anchore/vulnscan/vulnscan/version"
|
||||
"github.com/anchore/vulnscan/vulnscan/vulnerability"
|
||||
)
|
||||
|
||||
func FindMatchesByPackageCPE(store vulnerability.ProviderByCPE, p *pkg.Package, matcherName string) ([]match.Match, error) {
|
||||
verObj, err := version.NewVersionFromPkg(p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("matcher failed to parse version pkg='%s' ver='%s': %w", p.Name, p.Version, err)
|
||||
}
|
||||
|
||||
matches := make([]match.Match, 0)
|
||||
vulnSet := vulnerability.NewSet()
|
||||
|
||||
for _, cpe := range verObj.CPEs() {
|
||||
allPkgVulns, err := store.GetByCPE(cpe)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("matcher failed to fetch by CPE pkg='%s': %w", p.Name, err)
|
||||
}
|
||||
|
||||
for _, vuln := range allPkgVulns {
|
||||
if vulnSet.Contains(vuln) {
|
||||
continue
|
||||
}
|
||||
vulnSet.Add(vuln)
|
||||
|
||||
// if the constraint it met, then the given package has the vulnerability
|
||||
isPackageVulnerable, err := vuln.Constraint.Satisfied(verObj)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cpe matcher failed to check constraint='%s' version='%s': %w", vuln.Constraint, verObj, err)
|
||||
}
|
||||
|
||||
if isPackageVulnerable {
|
||||
matches = append(matches, match.Match{
|
||||
Type: match.FuzzyMatch,
|
||||
Confidence: 0.9, // TODO: this is hard coded for now
|
||||
Vulnerability: *vuln,
|
||||
Package: p,
|
||||
Matcher: matcherName,
|
||||
SearchKey: fmt.Sprintf("cpe[%s] constraint[%s]", cpe.BindToFmtString(), vuln.Constraint.String()),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return matches, err
|
||||
}
|
187
vulnscan/matcher/common/cpe_matchers_test.go
Normal file
187
vulnscan/matcher/common/cpe_matchers_test.go
Normal file
|
@ -0,0 +1,187 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"github.com/anchore/imgbom/imgbom/pkg"
|
||||
"github.com/anchore/vulnscan/internal"
|
||||
"github.com/anchore/vulnscan/vulnscan/cpe"
|
||||
"github.com/anchore/vulnscan/vulnscan/match"
|
||||
"github.com/anchore/vulnscan/vulnscan/version"
|
||||
"github.com/anchore/vulnscan/vulnscan/vulnerability"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func must(c cpe.CPE, e error) cpe.CPE {
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
type mockCPEProvider struct {
|
||||
data map[string]map[string][]*vulnerability.Vulnerability
|
||||
}
|
||||
|
||||
func newMockProviderByCPE() *mockCPEProvider {
|
||||
pr := mockCPEProvider{
|
||||
data: make(map[string]map[string][]*vulnerability.Vulnerability),
|
||||
}
|
||||
pr.stub()
|
||||
return &pr
|
||||
}
|
||||
|
||||
func (pr *mockCPEProvider) stub() {
|
||||
pr.data["nvd"] = map[string][]*vulnerability.Vulnerability{
|
||||
"activerecord": {
|
||||
{
|
||||
Constraint: version.MustGetConstraint("< 3.7.6", version.SemanticFormat),
|
||||
ID: "CVE-2017-fake-1",
|
||||
CPEs: []cpe.CPE{
|
||||
must(cpe.New("cpe:2.3:*:activerecord:activerecord:*:*:*:*:*:rails:*:*")),
|
||||
},
|
||||
},
|
||||
{
|
||||
Constraint: version.MustGetConstraint("< 3.7.4", version.SemanticFormat),
|
||||
ID: "CVE-2017-fake-2",
|
||||
CPEs: []cpe.CPE{
|
||||
must(cpe.New("cpe:2.3:*:activerecord:activerecord:*:*:*:*:*:ruby:*:*")),
|
||||
},
|
||||
},
|
||||
{
|
||||
Constraint: version.MustGetConstraint("= 4.0.1", version.SemanticFormat),
|
||||
ID: "CVE-2017-fake-3",
|
||||
CPEs: []cpe.CPE{
|
||||
must(cpe.New("cpe:2.3:*:couldntgetthisrightcouldyou:activerecord:4.0.1:*:*:*:*:*:*:*")),
|
||||
},
|
||||
},
|
||||
},
|
||||
"awesome": {
|
||||
{
|
||||
Constraint: version.MustGetConstraint("< 98SP3", version.UnknownFormat),
|
||||
ID: "CVE-2017-fake-4",
|
||||
CPEs: []cpe.CPE{
|
||||
must(cpe.New("cpe:2.3:*:awesome:awesome:*:*:*:*:*:*:*:*")),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (pr *mockCPEProvider) GetByCPE(c cpe.CPE) ([]*vulnerability.Vulnerability, error) {
|
||||
return pr.data["nvd"][c.Product], nil
|
||||
}
|
||||
|
||||
func TestFindMatchesByPackageCPE(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
p pkg.Package
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
name: "match from range",
|
||||
p: pkg.Package{
|
||||
Name: "activerecord",
|
||||
Version: "3.7.5",
|
||||
Language: pkg.Ruby,
|
||||
Type: pkg.BundlerPkg,
|
||||
},
|
||||
expected: []string{
|
||||
"CVE-2017-fake-1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple matches",
|
||||
p: pkg.Package{
|
||||
Name: "activerecord",
|
||||
Version: "3.7.3",
|
||||
Language: pkg.Ruby,
|
||||
Type: pkg.BundlerPkg,
|
||||
},
|
||||
expected: []string{
|
||||
"CVE-2017-fake-1",
|
||||
"CVE-2017-fake-2",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "exact match",
|
||||
p: pkg.Package{
|
||||
Name: "activerecord",
|
||||
Version: "4.0.1",
|
||||
Language: pkg.Ruby,
|
||||
Type: pkg.BundlerPkg,
|
||||
},
|
||||
expected: []string{
|
||||
"CVE-2017-fake-3",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no match",
|
||||
p: pkg.Package{
|
||||
Name: "couldntgetthisrightcouldyou",
|
||||
Version: "4.0.1",
|
||||
Language: pkg.Ruby,
|
||||
Type: pkg.BundlerPkg,
|
||||
},
|
||||
expected: []string{},
|
||||
},
|
||||
{
|
||||
name: "fuzzy version match",
|
||||
p: pkg.Package{
|
||||
Name: "awesome",
|
||||
Version: "98SE1",
|
||||
},
|
||||
expected: []string{
|
||||
"CVE-2017-fake-4",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
|
||||
store := newMockProviderByCPE()
|
||||
actual, err := FindMatchesByPackageCPE(store, &test.p, "SOME_OTHER_MATCHER")
|
||||
if err != nil {
|
||||
t.Fatalf("error while finding matches: %+v", err)
|
||||
}
|
||||
|
||||
if len(actual) != len(test.expected) {
|
||||
for _, a := range actual {
|
||||
t.Errorf(" entry: %+v", a)
|
||||
}
|
||||
t.Fatalf("unexpected matches count: %d", len(actual))
|
||||
}
|
||||
|
||||
foundCVEs := internal.NewStringSet()
|
||||
|
||||
for _, a := range actual {
|
||||
foundCVEs.Add(a.Vulnerability.ID)
|
||||
|
||||
if a.Type != match.FuzzyMatch {
|
||||
t.Error("fuzzy match not indicated")
|
||||
}
|
||||
|
||||
if a.Package.Name != test.p.Name {
|
||||
t.Errorf("failed to capture correct original package: %s", a.Package.Name)
|
||||
}
|
||||
|
||||
if a.Matcher != "SOME_OTHER_MATCHER" {
|
||||
t.Errorf("failed to capture matcher name: %s", a.Matcher)
|
||||
}
|
||||
|
||||
if a.IndirectPackage != nil {
|
||||
t.Fatalf("should not have captured indirect package")
|
||||
}
|
||||
|
||||
if a.Confidence != 0.9 {
|
||||
t.Fatalf("unexpected confidence: %f", a.Confidence)
|
||||
}
|
||||
}
|
||||
|
||||
for _, id := range test.expected {
|
||||
if !foundCVEs.Contains(id) {
|
||||
t.Errorf("missing CVE: %s", id)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,8 +1,11 @@
|
|||
// nolint:dupl
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/anchore/vulnscan/vulnscan/version"
|
||||
|
||||
"github.com/anchore/imgbom/imgbom/distro"
|
||||
"github.com/anchore/imgbom/imgbom/pkg"
|
||||
"github.com/anchore/vulnscan/vulnscan/match"
|
||||
|
@ -10,15 +13,35 @@ import (
|
|||
)
|
||||
|
||||
func FindMatchesByPackageDistro(store vulnerability.ProviderByDistro, d distro.Distro, p *pkg.Package, matcherName string) ([]match.Match, error) {
|
||||
allPkgVulns, err := store.GetByDistro(d, p)
|
||||
verObj, err := version.NewVersionFromPkg(p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("distro matcher failed to fetch distro='%s' pkg='%s': %w", d, p.Name, err)
|
||||
return nil, fmt.Errorf("matcher failed to parse version pkg='%s' ver='%s': %w", p.Name, p.Version, err)
|
||||
}
|
||||
|
||||
matches, err := FindMatchesForPackage(allPkgVulns, p, matcherName)
|
||||
for idx := range matches {
|
||||
// explicitly set the search key to indicate a distro match
|
||||
matches[idx].SearchKey = fmt.Sprintf("distro=[%s] pkg=[%s:%s]", d, p.Name, p.Version)
|
||||
allPkgVulns, err := store.GetByDistro(d, p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("matcher failed to fetch distro='%s' pkg='%s': %w", d, p.Name, err)
|
||||
}
|
||||
|
||||
matches := make([]match.Match, 0)
|
||||
for _, vuln := range allPkgVulns {
|
||||
// if the constraint it met, then the given package has the vulnerability
|
||||
isPackageVulnerable, err := vuln.Constraint.Satisfied(verObj)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("distro matcher failed to check constraint='%s' version='%s': %w", vuln.Constraint, verObj, err)
|
||||
}
|
||||
|
||||
if isPackageVulnerable {
|
||||
matches = append(matches, match.Match{
|
||||
Type: match.ExactDirectMatch,
|
||||
Confidence: 1.0, // TODO: this is hard coded for now
|
||||
Vulnerability: *vuln,
|
||||
Package: p,
|
||||
Matcher: matcherName,
|
||||
SearchKey: fmt.Sprintf("distro[%s] constraint[%s]", d, vuln.Constraint.String()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return matches, err
|
||||
}
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/anchore/imgbom/imgbom/pkg"
|
||||
"github.com/anchore/vulnscan/vulnscan/match"
|
||||
"github.com/anchore/vulnscan/vulnscan/version"
|
||||
"github.com/anchore/vulnscan/vulnscan/vulnerability"
|
||||
)
|
||||
|
||||
func FindMatchesForPackage(allPkgVulns []*vulnerability.Vulnerability, p *pkg.Package, matcherName string) ([]match.Match, error) {
|
||||
matches := make([]match.Match, 0)
|
||||
verObj, err := version.NewVersionFromPkg(p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("matcher failed to parse version pkg='%s' ver='%s': %w", p.Name, p.Version, err)
|
||||
}
|
||||
|
||||
for _, vuln := range allPkgVulns {
|
||||
// if the constraint it met, then the given package has the vulnerability
|
||||
isPackageVulnerable, err := vuln.Constraint.Satisfied(verObj)
|
||||
if err != nil {
|
||||
// TODO: not enough information (cannot back track constraint object)
|
||||
return nil, fmt.Errorf("language matcher failed to check constraint='%s' version='%s': %w", vuln.Constraint, verObj, err)
|
||||
}
|
||||
|
||||
if isPackageVulnerable {
|
||||
matches = append(matches, match.Match{
|
||||
Type: match.ExactDirectMatch,
|
||||
Confidence: 1.0, // TODO: this is hard coded for now
|
||||
Vulnerability: *vuln,
|
||||
Package: p,
|
||||
Matcher: matcherName,
|
||||
})
|
||||
}
|
||||
}
|
||||
return matches, nil
|
||||
}
|
|
@ -1,23 +1,46 @@
|
|||
// nolint:dupl
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/anchore/vulnscan/vulnscan/version"
|
||||
|
||||
"github.com/anchore/imgbom/imgbom/pkg"
|
||||
"github.com/anchore/vulnscan/vulnscan/match"
|
||||
"github.com/anchore/vulnscan/vulnscan/vulnerability"
|
||||
)
|
||||
|
||||
func FindMatchesByPackageLanguage(store vulnerability.ProviderByLanguage, l pkg.Language, p *pkg.Package, matcherName string) ([]match.Match, error) {
|
||||
allPkgVulns, err := store.GetByLanguage(l, p)
|
||||
verObj, err := version.NewVersionFromPkg(p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("language matcher failed to fetch language='%s' pkg='%s': %w", l, p.Name, err)
|
||||
return nil, fmt.Errorf("matcher failed to parse version pkg='%s' ver='%s': %w", p.Name, p.Version, err)
|
||||
}
|
||||
|
||||
matches, err := FindMatchesForPackage(allPkgVulns, p, matcherName)
|
||||
for idx := range matches {
|
||||
// explicitly set the search key to indicate a language match
|
||||
matches[idx].SearchKey = fmt.Sprintf("language=[%s] pkg=[%s:%s]", l, p.Name, p.Version)
|
||||
allPkgVulns, err := store.GetByLanguage(l, p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("matcher failed to fetch language='%s' pkg='%s': %w", l, p.Name, err)
|
||||
}
|
||||
|
||||
matches := make([]match.Match, 0)
|
||||
for _, vuln := range allPkgVulns {
|
||||
// if the constraint it met, then the given package has the vulnerability
|
||||
isPackageVulnerable, err := vuln.Constraint.Satisfied(verObj)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("language matcher failed to check constraint='%s' version='%s': %w", vuln.Constraint, verObj, err)
|
||||
}
|
||||
|
||||
if isPackageVulnerable {
|
||||
matches = append(matches, match.Match{
|
||||
Type: match.ExactDirectMatch,
|
||||
Confidence: 1.0, // TODO: this is hard coded for now
|
||||
Vulnerability: *vuln,
|
||||
Package: p,
|
||||
Matcher: matcherName,
|
||||
SearchKey: fmt.Sprintf("language[%s] constraint[%s]", l, vuln.Constraint.String()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return matches, err
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"github.com/anchore/imgbom/imgbom/distro"
|
||||
"github.com/anchore/imgbom/imgbom/pkg"
|
||||
"github.com/anchore/vulnscan/internal/log"
|
||||
"github.com/anchore/vulnscan/vulnscan/match"
|
||||
"github.com/anchore/vulnscan/vulnscan/matcher/bundler"
|
||||
"github.com/anchore/vulnscan/vulnscan/matcher/dpkg"
|
||||
"github.com/anchore/vulnscan/vulnscan/matcher/java"
|
||||
|
@ -51,15 +52,18 @@ func (c *controller) add(matchers ...Matcher) {
|
|||
func (c *controller) findMatches(s vulnerability.Provider, o distro.Distro, packages ...*pkg.Package) result.Result {
|
||||
res := result.NewResult()
|
||||
for _, p := range packages {
|
||||
log.Debugf("searching for vulnerability matches for pkg=%s", p)
|
||||
|
||||
matchers, ok := c.matchers[p.Type]
|
||||
if !ok {
|
||||
log.Errorf("no matchers available for package type=%s pkg=%s", p.Type, p)
|
||||
log.Errorf("no matchers available for package pkg=%s", p)
|
||||
}
|
||||
for _, m := range matchers {
|
||||
matches, err := m.Match(s, o, p)
|
||||
if err != nil {
|
||||
log.Errorf("matcher failed for pkg=%s: %+v", p, err)
|
||||
} else {
|
||||
logMatches(p, matches)
|
||||
res.Add(p, matches...)
|
||||
}
|
||||
}
|
||||
|
@ -70,3 +74,16 @@ func (c *controller) findMatches(s vulnerability.Provider, o distro.Distro, pack
|
|||
func FindMatches(s vulnerability.Provider, o distro.Distro, packages ...*pkg.Package) result.Result {
|
||||
return controllerInstance.findMatches(s, o, packages...)
|
||||
}
|
||||
|
||||
func logMatches(p *pkg.Package, matches []match.Match) {
|
||||
if len(matches) > 0 {
|
||||
log.Debugf("found %d vulnerabilities for pkg=%s", len(matches), p)
|
||||
for idx, m := range matches {
|
||||
var branch = "├──"
|
||||
if idx == len(matches)-1 {
|
||||
branch = "└──"
|
||||
}
|
||||
log.Debugf(" %s %s", branch, m.Summary())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,18 @@ func (m *Matcher) Name() string {
|
|||
return "python-matcher"
|
||||
}
|
||||
|
||||
func (m *Matcher) Match(store vulnerability.Provider, d distro.Distro, p *pkg.Package) ([]match.Match, error) {
|
||||
return common.FindMatchesByPackageLanguage(store, p.Language, p, m.Name())
|
||||
func (m *Matcher) Match(store vulnerability.Provider, _ distro.Distro, p *pkg.Package) ([]match.Match, error) {
|
||||
var matches = make([]match.Match, 0)
|
||||
langMatches, err := common.FindMatchesByPackageLanguage(store, p.Language, p, m.Name())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
matches = append(matches, langMatches...)
|
||||
|
||||
cpeMatches, err := common.FindMatchesByPackageCPE(store, p, m.Name())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
matches = append(matches, cpeMatches...)
|
||||
return matches, nil
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"io"
|
||||
|
||||
"github.com/anchore/imgbom/imgbom/pkg"
|
||||
"github.com/anchore/vulnscan/internal/log"
|
||||
"github.com/anchore/vulnscan/vulnscan/result"
|
||||
)
|
||||
|
||||
|
@ -20,9 +19,16 @@ func NewPresenter() *Presenter {
|
|||
// ResultObj is a single item for the JSON array reported
|
||||
type ResultObj struct {
|
||||
Cve string `json:"cve"`
|
||||
FoundBy FoundBy `json:"found-by"`
|
||||
Package Package `json:"package"`
|
||||
}
|
||||
|
||||
// FoundBy contains all data that indicates how the result match was found
|
||||
type FoundBy struct {
|
||||
Matcher string `json:"matcher"`
|
||||
SearchKey string `json:"search-key"`
|
||||
}
|
||||
|
||||
// Package is a nested JSON object from ResultObj
|
||||
type Package struct {
|
||||
Name string `json:"name"`
|
||||
|
@ -35,22 +41,23 @@ func (pres *Presenter) Present(output io.Writer, catalog *pkg.Catalog, results r
|
|||
doc := make([]ResultObj, 0)
|
||||
|
||||
for match := range results.Enumerate() {
|
||||
pkg := catalog.Package(match.Package.ID())
|
||||
|
||||
p := catalog.Package(match.Package.ID())
|
||||
doc = append(
|
||||
doc,
|
||||
ResultObj{
|
||||
Cve: match.Vulnerability.ID,
|
||||
Package: Package{Name: pkg.Name, Version: pkg.Version, Type: pkg.Type.String()}},
|
||||
Cve: match.Vulnerability.ID,
|
||||
FoundBy: FoundBy{
|
||||
Matcher: match.Matcher,
|
||||
SearchKey: match.SearchKey,
|
||||
},
|
||||
Package: Package{Name: p.Name, Version: p.Version, Type: p.Type.String()},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
bytes, err := json.Marshal(&doc)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("failed to marshal json (presenter=json): %w", err)
|
||||
}
|
||||
|
||||
_, err = output.Write(bytes)
|
||||
return err
|
||||
enc := json.NewEncoder(output)
|
||||
// prevent > and < from being escaped in the payload
|
||||
enc.SetEscapeHTML(false)
|
||||
enc.SetIndent("", " ")
|
||||
return enc.Encode(&doc)
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
[]
|
||||
[]
|
||||
|
|
|
@ -1 +1,26 @@
|
|||
[{"cve":"CVE-1999-0001","package":{"name":"package-1","version":"1.0.1","type":"deb"}},{"cve":"CVE-1999-0002","package":{"name":"package-1","version":"1.0.1","type":"deb"}}]
|
||||
[
|
||||
{
|
||||
"cve": "CVE-1999-0001",
|
||||
"found-by": {
|
||||
"matcher": "",
|
||||
"search-key": ""
|
||||
},
|
||||
"package": {
|
||||
"name": "package-1",
|
||||
"version": "1.0.1",
|
||||
"type": "deb"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cve": "CVE-1999-0002",
|
||||
"found-by": {
|
||||
"matcher": "",
|
||||
"search-key": ""
|
||||
},
|
||||
"package": {
|
||||
"name": "package-1",
|
||||
"version": "1.0.1",
|
||||
"type": "deb"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -23,6 +23,7 @@ func GetConstraint(constStr string, format Format) (Constraint, error) {
|
|||
return nil, fmt.Errorf("could not find constraint for given format: %s", format)
|
||||
}
|
||||
|
||||
// MustGetConstraint is meant for testing only, do not use within the library
|
||||
func MustGetConstraint(constStr string, format Format) Constraint {
|
||||
constraint, err := GetConstraint(constStr, format)
|
||||
if err != nil {
|
||||
|
|
|
@ -81,7 +81,7 @@ func (c debConstraint) Satisfied(version *Version) (bool, error) {
|
|||
|
||||
func (c debConstraint) String() string {
|
||||
if c.raw == "" {
|
||||
return "[no constraint] (deb)"
|
||||
return "none (deb)"
|
||||
}
|
||||
return fmt.Sprintf("%s (deb)", c.raw)
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ func (c *testCase) name() string {
|
|||
}
|
||||
|
||||
func (c *testCase) assert(t *testing.T, format Format, constraint Constraint) {
|
||||
t.Helper()
|
||||
|
||||
verObj, err := NewVersion(c.version, format)
|
||||
if !errors.Is(err, c.createErr) {
|
||||
t.Fatalf("unexpected create error: '%+v'!='%+v'", err, c.createErr)
|
||||
|
|
|
@ -71,7 +71,7 @@ func (c rpmConstraint) Satisfied(version *Version) (bool, error) {
|
|||
|
||||
func (c rpmConstraint) String() string {
|
||||
if c.raw == "" {
|
||||
return "--- (rpm)"
|
||||
return "none (rpm)"
|
||||
}
|
||||
return fmt.Sprintf("%s (rpm)", c.raw)
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ type semanticConstraint struct {
|
|||
|
||||
func newSemanticConstraint(constStr string) (semanticConstraint, error) {
|
||||
normalized := normalizer.Replace(constStr)
|
||||
|
||||
constraints, err := hashiVer.NewConstraint(normalized)
|
||||
if err != nil {
|
||||
return semanticConstraint{}, err
|
||||
|
@ -48,5 +49,8 @@ func (c semanticConstraint) Satisfied(version *Version) (bool, error) {
|
|||
}
|
||||
|
||||
func (c semanticConstraint) String() string {
|
||||
return fmt.Sprintf("%s (semantic)", c.raw)
|
||||
if c.raw == "" {
|
||||
return "none (semver)"
|
||||
}
|
||||
return fmt.Sprintf("%s (semver)", c.raw)
|
||||
}
|
||||
|
|
|
@ -43,6 +43,29 @@ func TestVersionSemantic(t *testing.T) {
|
|||
{version: "2.3.1+meta", constraint: "< 2.0", satisfied: false},
|
||||
{version: "2.3.1+meta", constraint: "< 2", satisfied: false},
|
||||
{version: "2.3.1+meta", constraint: "< 2, > 3", satisfied: false},
|
||||
// from https://github.com/hashicorp/go-version/issues/61
|
||||
// and https://semver.org/#spec-item-11
|
||||
// A larger set of pre-release fields has a higher precedence than a smaller set, if all of the preceding identifiers are equal.
|
||||
{version: "1.0.0-alpha", constraint: "> 1.0.0-alpha.1", satisfied: false},
|
||||
{version: "1.0.0-alpha", constraint: "< 1.0.0-alpha.1", satisfied: true},
|
||||
{version: "1.0.0-alpha.1", constraint: "> 1.0.0-alpha.beta", satisfied: false},
|
||||
{version: "1.0.0-alpha.1", constraint: "< 1.0.0-alpha.beta", satisfied: true},
|
||||
{version: "1.0.0-alpha.beta", constraint: "> 1.0.0-beta", satisfied: false},
|
||||
{version: "1.0.0-alpha.beta", constraint: "< 1.0.0-beta", satisfied: true},
|
||||
{version: "1.0.0-beta", constraint: "> 1.0.0-beta.2", satisfied: false},
|
||||
{version: "1.0.0-beta", constraint: "< 1.0.0-beta.2", satisfied: true},
|
||||
{version: "1.0.0-beta.2", constraint: "> 1.0.0-beta.11", satisfied: false},
|
||||
{version: "1.0.0-beta.2", constraint: "< 1.0.0-beta.11", satisfied: true},
|
||||
{version: "1.0.0-beta.11", constraint: "> 1.0.0-rc.1", satisfied: false},
|
||||
{version: "1.0.0-beta.11", constraint: "< 1.0.0-rc.1", satisfied: true},
|
||||
{version: "1.0.0-rc.1", constraint: "> 1.0.0", satisfied: false},
|
||||
{version: "1.0.0-rc.1", constraint: "< 1.0.0", satisfied: true},
|
||||
{version: "1.0.0-alpha.1", constraint: "> 1.0.0-alpha.1", satisfied: false},
|
||||
{version: "1.0.0-alpha.2", constraint: "> 1.0.0-alpha.1", satisfied: true},
|
||||
{version: "1.2.0-beta", constraint: ">1.0, <2.0", satisfied: true},
|
||||
{version: "1.2.0-beta", constraint: ">1.0", satisfied: true},
|
||||
{version: "1.2.0-beta", constraint: "<2.0", satisfied: true},
|
||||
{version: "1.2.0", constraint: ">1.0, <2.0", satisfied: true},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
|
||||
hashiVer "github.com/anchore/go-version"
|
||||
"github.com/anchore/imgbom/imgbom/pkg"
|
||||
"github.com/anchore/vulnscan/vulnscan/cpe"
|
||||
deb "github.com/knqyf263/go-deb-version"
|
||||
)
|
||||
|
||||
|
@ -15,9 +16,10 @@ type Version struct {
|
|||
}
|
||||
|
||||
type rich struct {
|
||||
semVer *hashiVer.Version
|
||||
debVer *deb.Version
|
||||
rpmVer *rpmVersion
|
||||
cpeVers []cpe.CPE
|
||||
semVer *hashiVer.Version
|
||||
debVer *deb.Version
|
||||
rpmVer *rpmVersion
|
||||
}
|
||||
|
||||
func NewVersion(raw string, format Format) (*Version, error) {
|
||||
|
@ -35,7 +37,17 @@ func NewVersion(raw string, format Format) (*Version, error) {
|
|||
}
|
||||
|
||||
func NewVersionFromPkg(p *pkg.Package) (*Version, error) {
|
||||
return NewVersion(p.Version, FormatFromPkgType(p.Type))
|
||||
ver, err := NewVersion(p.Version, FormatFromPkgType(p.Type))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cpes, err := cpe.Generate(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ver.rich.cpeVers = cpes
|
||||
return ver, nil
|
||||
}
|
||||
|
||||
func (v *Version) populate() error {
|
||||
|
@ -59,6 +71,10 @@ func (v *Version) populate() error {
|
|||
return fmt.Errorf("no rich version populated (format=%s)", v.Format)
|
||||
}
|
||||
|
||||
func (v Version) CPEs() []cpe.CPE {
|
||||
return v.rich.cpeVers
|
||||
}
|
||||
|
||||
func (v Version) String() string {
|
||||
return fmt.Sprintf("%s (%s)", v.Raw, v.Format)
|
||||
}
|
||||
|
|
|
@ -60,3 +60,7 @@ func languageNamespaces(l pkg.Language) map[string]namer {
|
|||
}
|
||||
return namespaces
|
||||
}
|
||||
|
||||
func cpeNamespaces() []string {
|
||||
return []string{"nvd"}
|
||||
}
|
||||
|
|
|
@ -3,11 +3,13 @@ package vulnerability
|
|||
import (
|
||||
"github.com/anchore/imgbom/imgbom/distro"
|
||||
"github.com/anchore/imgbom/imgbom/pkg"
|
||||
"github.com/anchore/vulnscan/vulnscan/cpe"
|
||||
)
|
||||
|
||||
type Provider interface {
|
||||
ProviderByDistro
|
||||
ProviderByLanguage
|
||||
ProviderByCPE
|
||||
}
|
||||
|
||||
type ProviderByDistro interface {
|
||||
|
@ -17,3 +19,7 @@ type ProviderByDistro interface {
|
|||
type ProviderByLanguage interface {
|
||||
GetByLanguage(pkg.Language, *pkg.Package) ([]*Vulnerability, error)
|
||||
}
|
||||
|
||||
type ProviderByCPE interface {
|
||||
GetByCPE(cpe.CPE) ([]*Vulnerability, error)
|
||||
}
|
||||
|
|
20
vulnscan/vulnerability/set.go
Normal file
20
vulnscan/vulnerability/set.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
package vulnerability
|
||||
|
||||
type Set map[string]struct{}
|
||||
|
||||
func NewSet() Set {
|
||||
return make(Set)
|
||||
}
|
||||
|
||||
func (s Set) Add(v *Vulnerability) {
|
||||
s[v.hash()] = struct{}{}
|
||||
}
|
||||
|
||||
func (s Set) Remove(v *Vulnerability) {
|
||||
delete(s, v.hash())
|
||||
}
|
||||
|
||||
func (s Set) Contains(v *Vulnerability) bool {
|
||||
_, ok := s[v.hash()]
|
||||
return ok
|
||||
}
|
|
@ -6,7 +6,9 @@ import (
|
|||
"github.com/anchore/imgbom/imgbom/distro"
|
||||
"github.com/anchore/imgbom/imgbom/pkg"
|
||||
"github.com/anchore/siren-db/pkg/db"
|
||||
"github.com/anchore/vulnscan/vulnscan/cpe"
|
||||
"github.com/anchore/vulnscan/vulnscan/version"
|
||||
"github.com/facebookincubator/nvdtools/wfn"
|
||||
)
|
||||
|
||||
type StoreProvider struct {
|
||||
|
@ -80,3 +82,52 @@ func (pr *StoreProvider) GetByLanguage(l pkg.Language, p *pkg.Package) ([]*Vulne
|
|||
|
||||
return vulns, nil
|
||||
}
|
||||
|
||||
func (pr *StoreProvider) GetByCPE(requestCPE cpe.CPE) ([]*Vulnerability, error) {
|
||||
vulns := make([]*Vulnerability, 0)
|
||||
|
||||
namespaces := cpeNamespaces()
|
||||
if namespaces == nil {
|
||||
return nil, fmt.Errorf("no store namespaces found for arbitrary CPEs")
|
||||
}
|
||||
|
||||
if requestCPE.Product == wfn.Any || requestCPE.Product == wfn.NA {
|
||||
return nil, fmt.Errorf("product name is required")
|
||||
}
|
||||
|
||||
for _, namespace := range namespaces {
|
||||
// TODO: should we encode vendor + name? or leave it as just package name (product)?
|
||||
|
||||
allPkgVulns, err := pr.store.GetVulnerability(namespace, requestCPE.Product)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("provider failed to fetch namespace='%s' product='%s': %w", namespace, requestCPE.Product, err)
|
||||
}
|
||||
|
||||
for _, vuln := range allPkgVulns {
|
||||
format := version.ParseFormat(vuln.VersionFormat)
|
||||
|
||||
constraint, err := version.GetConstraint(vuln.VersionConstraint, format)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("provider failed to parse cpe='%s' constraint='%s' format='%s': %w", requestCPE.BindToFmtString(), vuln.VersionConstraint, format, err)
|
||||
}
|
||||
|
||||
vulnCPEs, err := cpe.NewSlice(vuln.CPEs...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// compare the request CPE to the potential matches (excluding version, which is handled downstream)
|
||||
candidateMatchCpes := cpe.MatchWithoutVersion(requestCPE, vulnCPEs)
|
||||
|
||||
if len(candidateMatchCpes) > 0 {
|
||||
vulns = append(vulns, &Vulnerability{
|
||||
Constraint: constraint,
|
||||
CPEs: candidateMatchCpes,
|
||||
ID: vuln.ID,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return vulns, nil
|
||||
}
|
||||
|
|
|
@ -33,10 +33,50 @@ func (d *mockStore) stub() {
|
|||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (d *mockStore) AddVulnerability(v *db.Vulnerability) error {
|
||||
return nil
|
||||
d.data["nvd"] = map[string][]db.Vulnerability{
|
||||
"activerecord": {
|
||||
{
|
||||
PackageName: "activerecord",
|
||||
Namespace: "nvd",
|
||||
VersionConstraint: "< 3.7.6",
|
||||
ID: "CVE-2014-fake-3",
|
||||
VersionFormat: "unknown",
|
||||
CPEs: []string{
|
||||
"cpe:2.3:*:activerecord:activerecord:*:*:*:*:*:rails:*:*",
|
||||
},
|
||||
},
|
||||
{
|
||||
PackageName: "activerecord",
|
||||
Namespace: "nvd",
|
||||
VersionConstraint: "< 3.7.4",
|
||||
ID: "CVE-2014-fake-4",
|
||||
VersionFormat: "unknown",
|
||||
CPEs: []string{
|
||||
"cpe:2.3:*:activerecord:activerecord:*:*:something:*:*:ruby:*:*",
|
||||
},
|
||||
},
|
||||
{
|
||||
PackageName: "activerecord",
|
||||
Namespace: "nvd",
|
||||
VersionConstraint: "= 4.0.1",
|
||||
ID: "CVE-2014-fake-5",
|
||||
VersionFormat: "unknown",
|
||||
CPEs: []string{
|
||||
"cpe:2.3:*:couldntgetthisrightcouldyou:activerecord:4.0.1:*:*:*:*:*:*:*",
|
||||
},
|
||||
},
|
||||
{
|
||||
PackageName: "activerecord",
|
||||
Namespace: "nvd",
|
||||
VersionConstraint: "< 98SP3",
|
||||
ID: "CVE-2014-fake-6",
|
||||
VersionFormat: "unknown",
|
||||
CPEs: []string{
|
||||
"cpe:2.3:*:awesome:awesome:*:*:*:*:*:*:*:*",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (d *mockStore) GetVulnerability(namespace, name string) ([]db.Vulnerability, error) {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package vulnerability
|
||||
|
||||
import (
|
||||
"github.com/anchore/vulnscan/vulnscan/cpe"
|
||||
"github.com/go-test/deep"
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/imgbom/imgbom/distro"
|
||||
|
@ -50,3 +52,92 @@ func TestGetByDistro(t *testing.T) {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
func must(c cpe.CPE, e error) cpe.CPE {
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func TestGetByCPE(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
cpe cpe.CPE
|
||||
expected []Vulnerability
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
name: "match from name and target SW",
|
||||
cpe: must(cpe.New("cpe:2.3:*:activerecord:activerecord:*:*:*:*:*:ruby:*:*")),
|
||||
expected: []Vulnerability{
|
||||
{
|
||||
Constraint: version.MustGetConstraint("< 3.7.4", version.UnknownFormat),
|
||||
ID: "CVE-2014-fake-4",
|
||||
CPEs: []cpe.CPE{
|
||||
must(cpe.New("cpe:2.3:*:activerecord:activerecord:*:*:something:*:*:ruby:*:*")),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "match from vendor & name",
|
||||
cpe: must(cpe.New("cpe:2.3:*:activerecord:activerecord:*:*:*:*:*:*:*:*")),
|
||||
expected: []Vulnerability{
|
||||
{
|
||||
Constraint: version.MustGetConstraint("< 3.7.6", version.UnknownFormat),
|
||||
ID: "CVE-2014-fake-3",
|
||||
CPEs: []cpe.CPE{
|
||||
must(cpe.New("cpe:2.3:*:activerecord:activerecord:*:*:*:*:*:rails:*:*")),
|
||||
},
|
||||
},
|
||||
{
|
||||
Constraint: version.MustGetConstraint("< 3.7.4", version.UnknownFormat),
|
||||
ID: "CVE-2014-fake-4",
|
||||
CPEs: []cpe.CPE{
|
||||
must(cpe.New("cpe:2.3:*:activerecord:activerecord:*:*:something:*:*:ruby:*:*")),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "dont allow any name",
|
||||
cpe: must(cpe.New("cpe:2.3:*:couldntgetthisrightcouldyou:*:*:*:*:*:*:*:*:*")),
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
|
||||
provider := NewProviderFromStore(newMockStore())
|
||||
|
||||
actual, err := provider.GetByCPE(test.cpe)
|
||||
if err != nil && !test.err {
|
||||
t.Fatalf("expected no err, got: %+v", err)
|
||||
} else if err == nil && test.err {
|
||||
t.Fatalf("expected an err, got none")
|
||||
}
|
||||
|
||||
if len(actual) != len(test.expected) {
|
||||
for _, a := range actual {
|
||||
t.Errorf(" entry: %+v", a)
|
||||
}
|
||||
t.Fatalf("unexpected length: %d", len(actual))
|
||||
}
|
||||
|
||||
for idx, vuln := range actual {
|
||||
diffs := deep.Equal(&test.expected[idx], vuln)
|
||||
if len(diffs) > 0 {
|
||||
for _, d := range diffs {
|
||||
t.Errorf("diff: %+v", d)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,14 +3,21 @@ package vulnerability
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/anchore/vulnscan/vulnscan/cpe"
|
||||
|
||||
"github.com/anchore/vulnscan/vulnscan/version"
|
||||
)
|
||||
|
||||
type Vulnerability struct {
|
||||
Constraint version.Constraint
|
||||
CPEs []cpe.CPE
|
||||
ID string
|
||||
}
|
||||
|
||||
func (v Vulnerability) String() string {
|
||||
return fmt.Sprintf("Vuln(id=%s constraint='%s')", v.ID, v.Constraint)
|
||||
}
|
||||
|
||||
func (v *Vulnerability) hash() string {
|
||||
return fmt.Sprintf("%s|%s|%+v", v.ID, v.Constraint, v.CPEs)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue