Compare commits

..

57 Commits

Author SHA1 Message Date
kazuminn
4c04acbd9e feat(report) : Differences between vulnerability patched items (#1157)
* add plusDiff() and minusDiff()
* add plusDiff minusDiff test

Co-authored-by: Kota Kanbe <kotakanbe@gmail.com>
2021-02-10 06:55:48 +09:00
Kota Kanbe
1c4f231572 fix(scan): ignore rpm -qf exit status (#1168) 2021-02-09 17:26:12 +09:00
Kota Kanbe
51b8e169d2 fix(scan): warning if lsof command not found (#1167) 2021-02-07 07:28:45 +09:00
Kota Kanbe
b4611ae9b7 fix(scan): fix yum-ps warning Failed to exec which -bash (#1166) 2021-02-07 07:23:12 +09:00
Kota Kanbe
cd6722017b fix(scan): yum-ps err Failed to find the package (#1165) 2021-02-06 08:42:06 +09:00
Kota Kanbe
290edffccf fix(log): output version to log for debugging purpose (#1163) 2021-02-04 07:47:56 +09:00
Kota Kanbe
64a6222bf9 fix(report): set created_at and updated_at of trivy to json (#1162) 2021-02-03 17:52:44 +09:00
Kota Kanbe
adb686b7c9 fix(report): set created_at and updated_at of wpscan.com to json (#1161) 2021-02-03 16:41:44 +09:00
Kota Kanbe
d4af341b0f fix(report): remove duplicated refreshing logic when report with -diff (#1160) 2021-02-03 07:37:19 +09:00
Kota Kanbe
fea7e93c8d chore: fix comment (#1158) 2021-02-02 06:06:49 +09:00
sadayuki-matsuno
8b6b8d0f2e feat(wordpress): define API limit exceed error for wpscan.com (#1155)
* feat(wordpress) specify wp err

* fix typo, chagne const name

Co-authored-by: Kota Kanbe <kotakanbe@gmail.com>
2021-01-30 09:53:41 +09:00
Kota Kanbe
4dcbd865cc fix(report): set http timeout 10 sec (#1154)
* fix(report): set http timeout 10 sec

* fix: add an error handling
2021-01-30 09:40:33 +09:00
Kota Kanbe
39b19444fe Merge branch 'master' of github.com:future-architect/vuls 2021-01-28 16:24:14 +09:00
Kota Kanbe
644d5a5462 fix(report): remove retry logic for wpscan.com (#1151)
* fix(saas) change saas upload s3 key (#1116)

* fix(report): remove retry logic for wpscan.com

Co-authored-by: sadayuki-matsuno <sadayuki.matsuno@gmail.com>
2021-01-28 16:21:33 +09:00
Kota Kanbe
8e18451e3f Merge branch 'master' of github.com:future-architect/vuls 2021-01-28 08:24:23 +09:00
Kota Kanbe
3dbdd01f97 fix(report): wordrpess scanning skipped when package is emtpy (#1150) 2021-01-28 08:24:03 +09:00
sadayuki-matsuno
a89079c005 fix(saas) change saas upload s3 key (#1116) 2021-01-28 08:20:13 +09:00
sadayuki-matsuno
a8c0926b4f fix(saas) change saas upload s3 key (#1116) 2021-01-27 14:43:09 +09:00
Kota Kanbe
dd2959a31b fix(eol): add eol for alpine 3.13 (#1149) 2021-01-27 12:52:07 +09:00
Kota Kanbe
51099f42c3 fix(tui): runtime panic when tui with docker-base-setup (#1148)
* fix(tui): runtime panic when tui with docker-base-setup

* pass test case
2021-01-26 09:40:26 +09:00
Kota Kanbe
63f170cc7a fix(report): set severity in Red Hat OVAL to both CVSS v3 and v2 #1146 (#1147) 2021-01-26 07:58:59 +09:00
Kota Kanbe
3c1489e588 feat(report): range notion calc by severity when no-cvss-score (#1145) 2021-01-25 13:22:55 +09:00
Kota Kanbe
e4f1e03f62 feat(github): display GitHub Security Advisory details (#1143) 2021-01-24 09:15:04 +09:00
Kota Kanbe
83d48ec990 Create codeql-analysis.yml 2021-01-24 09:06:13 +09:00
Kota Kanbe
b20d2b2684 fix(scan): skip wordpress scan for preudo servers (#1142) 2021-01-21 07:11:55 +09:00
Kota Kanbe
2b918c70ae fix(scan): config dump nocolor in debug mode. (#1141) 2021-01-21 06:38:37 +09:00
Kota Kanbe
1100c133ba feat(config): Default values for WordPress scanning to be set in config.toml (#1140)
* chore: update go mod

* fix(wordpress): set default if defined in config.toml
2021-01-21 06:22:25 +09:00
Kota Kanbe
88899f0e89 refactor: around CheckHTTPHealth (#1139) 2021-01-20 07:41:29 +09:00
Kota Kanbe
59dc0059bc fix(model): omit changelog from json if empty (#1137) 2021-01-19 09:01:35 +09:00
Kota Kanbe
986fb304c0 fix(scan): add --nogpgcheck to dnf mod list to avoid Error: Cache-only enabled but no cache for *** (#1136) 2021-01-19 08:05:20 +09:00
Kota Kanbe
d6435d2885 fix(xml): remove -format-xml #1068 (#1134) 2021-01-18 04:38:00 +09:00
shopper
affb456499 fix(email.go):Fix runtime error(invalid memory address) (#1133) 2021-01-18 04:08:14 +09:00
Kota Kanbe
705ed0a0ac fix(discover): change config.toml template (#1132) 2021-01-16 07:58:46 +09:00
Kota Kanbe
dfffe5b508 fix(config): err occurs when host not set in local-scan-mode (#1129)
If host is not set in local scan mode, an error occurs.
2021-01-14 09:22:04 +09:00
Shigechika AIKAWA
fca102edba fix dnf prompt and ssh user (#1126) 2021-01-14 08:22:06 +09:00
Kota Kanbe
554b6345a2 chore: go mod update (#1127) 2021-01-14 08:12:47 +09:00
Kota Kanbe
aa954dc84c fix(scan): kindness msg when no-cache err on dnf mod list (#1128) 2021-01-14 08:12:35 +09:00
Kota Kanbe
b5506a1368 chore: go mod update (#1125) 2021-01-13 11:56:35 +09:00
Kota Kanbe
0b55f94828 Improve implementation around config (#1122)
* refactor config

* fix saas config

* feat(config): scanmodule for each server in config.toml

* feat(config): enable to specify containersOnly in config.toml

* add new keys of config.toml to discover.go

* fix summary output, logging
2021-01-13 08:46:27 +09:00
Kota Kanbe
a67052f48c fix(scan): err detecting EOL for alpine Linux (#1124) 2021-01-12 20:10:22 +09:00
Kota Kanbe
6eff6a9329 feat(report): display EOL information to scan summary (#1120)
* feat(report): display EOL information to scan summary

* detect Amazon linux EOL
2021-01-09 07:58:55 +09:00
Kota Kanbe
69d32d4511 feat(report): add a err code to wpscan.com API error (#1119) 2021-01-07 14:57:49 +09:00
Kota Kanbe
d7a613b710 chore: go mod update (#1118) 2021-01-07 08:02:29 +09:00
sadayuki-matsuno
669c019287 fix(cvecontent) Fixed not to split empty string (#1117) 2021-01-06 15:52:55 +09:00
Shigechika AIKAWA
fcc4901a10 fix(scan): Failed to parse CentOS Stream (#1098) 2021-01-06 14:57:19 +09:00
Kota Kanbe
4359503484 fix(redhat): possibility of false positives on RHEL (#1115) 2021-01-06 13:33:08 +09:00
Kota Kanbe
b13f93a2d3 feat(scan): support dnf modules (#1114)
* feat(scan): support dnf modules

* change dnf module list --installed to --enabled

* chore: refactor

* feat(report): detect logic for dnf modularity label

* fix func name

* chore: update go mods
2021-01-06 11:36:41 +09:00
Kota Kanbe
8405e0fad6 refactor(gost): Duplicate code into function (#1110)
* refactor(gost): Duplicate code into function

* fix
2020-12-30 08:33:30 +09:00
Kota Kanbe
aceb3f1826 fix(scan): add an error case for rpm -qa (#1109) 2020-12-30 08:05:14 +09:00
Kota Kanbe
a206675f3e fix(wordpress): remove cache because not permitted. (#1107) 2020-12-29 07:25:58 +09:00
Kota Kanbe
f4253d74ae fix(wordpress): wpscan.com unmarshal error (#1106)
* refactor(report): remove Integration.apply

* add an err check

* fix(wordpress): wpscan.com unmarshal error

* fix warnings
2020-12-29 07:11:04 +09:00
Kota Kanbe
aaea15e516 refactor(report): remove Integration.apply (#1105)
* refactor(report): remove Integration.apply

* add an err check
2020-12-29 06:59:48 +09:00
Kota Kanbe
83d1f80959 chore(report): remove stride and hipchat support (#1104) 2020-12-26 08:52:45 +09:00
Kota Kanbe
a33cff8f13 fix(reprot): use SQLite3 in current dir if not specified (#1103) 2020-12-26 08:24:17 +09:00
Kota Kanbe
8679759f60 chore: fix typo (#1102) 2020-12-26 08:23:02 +09:00
Kota Kanbe
53deaee3d7 refactor(config): remove DependencyCheckXMLPath in config.toml (#1100) 2020-12-25 06:38:00 +09:00
Kota Kanbe
5a14a58fe4 refactor(nvdxml): Remove codes related to NVD xml(deprecated) (#1099) 2020-12-25 06:16:14 +09:00
92 changed files with 3847 additions and 3231 deletions

67
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@@ -0,0 +1,67 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '32 20 * * 0'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'go' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@@ -92,7 +92,7 @@ Vuls is a tool created to solve the problems listed above. It has the following
- [RustSec Advisory Database](https://github.com/RustSec/advisory-db)
- WordPress
- [WPVulnDB](https://wpvulndb.com/api)
- [wpscan](https://wpscan.com/api)
### Scan mode
@@ -177,6 +177,10 @@ For more information such as Installation, Tutorial, Usage, visit [vuls.io](http
kotakanbe ([@kotakanbe](https://twitter.com/kotakanbe)) created vuls and [these fine people](https://github.com/future-architect/vuls/graphs/contributors) have contributed.
## Contribute
see [vulsdoc](https://vuls.io/docs/en/how-to-contribute.html)
----
## Stargazers over time

View File

@@ -29,7 +29,7 @@ func main() {
flag.Parse()
if *v {
fmt.Printf("vuls %s %s\n", config.Version, config.Revision)
fmt.Printf("vuls-%s-%s\n", config.Version, config.Revision)
os.Exit(int(subcommands.ExitSuccess))
}

32
config/chatworkconf.go Normal file
View File

@@ -0,0 +1,32 @@
package config
import (
"github.com/asaskevich/govalidator"
"golang.org/x/xerrors"
)
// ChatWorkConf is ChatWork config
type ChatWorkConf struct {
APIToken string `json:"-"`
Room string `json:"-"`
}
// Validate validates configuration
func (c *ChatWorkConf) Validate() (errs []error) {
if !Conf.ToChatWork {
return
}
if len(c.Room) == 0 {
errs = append(errs, xerrors.New("chatWorkConf.room must not be empty"))
}
if len(c.APIToken) == 0 {
errs = append(errs, xerrors.New("chatWorkConf.ApiToken must not be empty"))
}
_, err := govalidator.ValidateStruct(c)
if err != nil {
errs = append(errs, err)
}
return
}

File diff suppressed because it is too large Load Diff

74
config/exploitconf.go Normal file
View File

@@ -0,0 +1,74 @@
package config
import (
"fmt"
"os"
"path/filepath"
"time"
"github.com/parnurzeal/gorequest"
"golang.org/x/xerrors"
)
// ExploitConf is exploit config
type ExploitConf struct {
// DB type for exploit dictionary (sqlite3, mysql, postgres or redis)
Type string
// http://exploit-dictionary.com:1324 or DB connection string
URL string `json:"-"`
// /path/to/exploit.sqlite3
SQLite3Path string `json:"-"`
}
func (cnf *ExploitConf) setDefault() {
if cnf.Type == "" {
cnf.Type = "sqlite3"
}
if cnf.URL == "" && cnf.SQLite3Path == "" {
wd, _ := os.Getwd()
cnf.SQLite3Path = filepath.Join(wd, "go-exploitdb.sqlite3")
}
}
const exploitDBType = "EXPLOITDB_TYPE"
const exploitDBURL = "EXPLOITDB_URL"
const exploitDBPATH = "EXPLOITDB_SQLITE3_PATH"
// Init set options with the following priority.
// 1. Environment variable
// 2. config.toml
func (cnf *ExploitConf) Init() {
if os.Getenv(exploitDBType) != "" {
cnf.Type = os.Getenv(exploitDBType)
}
if os.Getenv(exploitDBURL) != "" {
cnf.URL = os.Getenv(exploitDBURL)
}
if os.Getenv(exploitDBPATH) != "" {
cnf.SQLite3Path = os.Getenv(exploitDBPATH)
}
cnf.setDefault()
}
// IsFetchViaHTTP returns wether fetch via http
func (cnf *ExploitConf) IsFetchViaHTTP() bool {
return Conf.Exploit.Type == "http"
}
// CheckHTTPHealth do health check
func (cnf *ExploitConf) CheckHTTPHealth() error {
if !cnf.IsFetchViaHTTP() {
return nil
}
url := fmt.Sprintf("%s/health", cnf.URL)
resp, _, errs := gorequest.New().Timeout(10 * time.Second).Get(url).End()
// resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
// resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return xerrors.Errorf("Failed to connect to exploit server. url: %s, errs: %s", url, errs)
}
return nil
}

74
config/gocvedictconf.go Normal file
View File

@@ -0,0 +1,74 @@
package config
import (
"fmt"
"os"
"path/filepath"
"time"
"github.com/parnurzeal/gorequest"
"golang.org/x/xerrors"
)
// GoCveDictConf is go-cve-dictionary config
type GoCveDictConf struct {
// DB type of CVE dictionary (sqlite3, mysql, postgres or redis)
Type string
// http://cve-dictionary.com:1323 or DB connection string
URL string `json:"-"`
// /path/to/cve.sqlite3
SQLite3Path string `json:"-"`
}
func (cnf *GoCveDictConf) setDefault() {
if cnf.Type == "" {
cnf.Type = "sqlite3"
}
if cnf.URL == "" && cnf.SQLite3Path == "" {
wd, _ := os.Getwd()
cnf.SQLite3Path = filepath.Join(wd, "cve.sqlite3")
}
}
const cveDBType = "CVEDB_TYPE"
const cveDBURL = "CVEDB_URL"
const cveDBPATH = "CVEDB_SQLITE3_PATH"
// Init set options with the following priority.
// 1. Environment variable
// 2. config.toml
func (cnf *GoCveDictConf) Init() {
if os.Getenv(cveDBType) != "" {
cnf.Type = os.Getenv(cveDBType)
}
if os.Getenv(cveDBURL) != "" {
cnf.URL = os.Getenv(cveDBURL)
}
if os.Getenv(cveDBPATH) != "" {
cnf.SQLite3Path = os.Getenv(cveDBPATH)
}
cnf.setDefault()
}
// IsFetchViaHTTP returns wether fetch via http
func (cnf *GoCveDictConf) IsFetchViaHTTP() bool {
return Conf.CveDict.Type == "http"
}
// CheckHTTPHealth checks http server status
func (cnf *GoCveDictConf) CheckHTTPHealth() error {
if !cnf.IsFetchViaHTTP() {
return nil
}
url := fmt.Sprintf("%s/health", cnf.URL)
resp, _, errs := gorequest.New().Timeout(10 * time.Second).SetDebug(Conf.Debug).Get(url).End()
// resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return xerrors.Errorf("Failed to request to CVE server. url: %s, errs: %s",
url, errs)
}
return nil
}

74
config/gostconf.go Normal file
View File

@@ -0,0 +1,74 @@
package config
import (
"fmt"
"os"
"path/filepath"
"time"
"github.com/parnurzeal/gorequest"
"golang.org/x/xerrors"
)
// GostConf is gost config
type GostConf struct {
// DB type for gost dictionary (sqlite3, mysql, postgres or redis)
Type string
// http://gost-dictionary.com:1324 or DB connection string
URL string `json:"-"`
// /path/to/gost.sqlite3
SQLite3Path string `json:"-"`
}
func (cnf *GostConf) setDefault() {
if cnf.Type == "" {
cnf.Type = "sqlite3"
}
if cnf.URL == "" && cnf.SQLite3Path == "" {
wd, _ := os.Getwd()
cnf.SQLite3Path = filepath.Join(wd, "gost.sqlite3")
}
}
const gostDBType = "GOSTDB_TYPE"
const gostDBURL = "GOSTDB_URL"
const gostDBPATH = "GOSTDB_SQLITE3_PATH"
// Init set options with the following priority.
// 1. Environment variable
// 2. config.toml
func (cnf *GostConf) Init() {
if os.Getenv(gostDBType) != "" {
cnf.Type = os.Getenv(gostDBType)
}
if os.Getenv(gostDBURL) != "" {
cnf.URL = os.Getenv(gostDBURL)
}
if os.Getenv(gostDBPATH) != "" {
cnf.SQLite3Path = os.Getenv(gostDBPATH)
}
cnf.setDefault()
}
// IsFetchViaHTTP returns wether fetch via http
func (cnf *GostConf) IsFetchViaHTTP() bool {
return Conf.Gost.Type == "http"
}
// CheckHTTPHealth do health check
func (cnf *GostConf) CheckHTTPHealth() error {
if !cnf.IsFetchViaHTTP() {
return nil
}
url := fmt.Sprintf("%s/health", cnf.URL)
resp, _, errs := gorequest.New().Timeout(10 * time.Second).Get(url).End()
// resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
// resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return xerrors.Errorf("Failed to connect to gost server. url: %s, errs: %s", url, errs)
}
return nil
}

76
config/govaldictconf.go Normal file
View File

@@ -0,0 +1,76 @@
package config
import (
"fmt"
"os"
"path/filepath"
"time"
"github.com/parnurzeal/gorequest"
"golang.org/x/xerrors"
)
// GovalDictConf is goval-dictionary config
type GovalDictConf struct {
// DB type of OVAL dictionary (sqlite3, mysql, postgres or redis)
Type string
// http://goval-dictionary.com:1324 or DB connection string
URL string `json:"-"`
// /path/to/oval.sqlite3
SQLite3Path string `json:"-"`
}
func (cnf *GovalDictConf) setDefault() {
if cnf.Type == "" {
cnf.Type = "sqlite3"
}
if cnf.URL == "" && cnf.SQLite3Path == "" {
wd, _ := os.Getwd()
cnf.SQLite3Path = filepath.Join(wd, "oval.sqlite3")
}
}
const govalType = "OVALDB_TYPE"
const govalURL = "OVALDB_URL"
const govalPATH = "OVALDB_SQLITE3_PATH"
// Init set options with the following priority.
// 1. Environment variable
// 2. config.toml
func (cnf *GovalDictConf) Init() {
if os.Getenv(govalType) != "" {
cnf.Type = os.Getenv(govalType)
}
if os.Getenv(govalURL) != "" {
cnf.URL = os.Getenv(govalURL)
}
if os.Getenv(govalPATH) != "" {
cnf.SQLite3Path = os.Getenv(govalPATH)
}
cnf.setDefault()
}
// IsFetchViaHTTP returns wether fetch via http
func (cnf *GovalDictConf) IsFetchViaHTTP() bool {
return Conf.OvalDict.Type == "http"
}
// CheckHTTPHealth do health check
func (cnf *GovalDictConf) CheckHTTPHealth() error {
if !cnf.IsFetchViaHTTP() {
return nil
}
url := fmt.Sprintf("%s/health", cnf.URL)
resp, _, errs := gorequest.New().Timeout(10 * time.Second).Get(url).End()
// resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
// resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return xerrors.Errorf("Failed to request to OVAL server. url: %s, errs: %s",
url, errs)
}
return nil
}

38
config/httpconf.go Normal file
View File

@@ -0,0 +1,38 @@
package config
import (
"os"
"github.com/asaskevich/govalidator"
)
// HTTPConf is HTTP config
type HTTPConf struct {
URL string `valid:"url" json:"-"`
}
// Validate validates configuration
func (c *HTTPConf) Validate() (errs []error) {
if !Conf.ToHTTP {
return nil
}
if _, err := govalidator.ValidateStruct(c); err != nil {
errs = append(errs, err)
}
return errs
}
const httpKey = "VULS_HTTP_URL"
// Init set options with the following priority.
// 1. Environment variable
// 2. config.toml
func (c *HTTPConf) Init(toml HTTPConf) {
if os.Getenv(httpKey) != "" {
c.URL = os.Getenv(httpKey)
}
if toml.URL != "" {
c.URL = toml.URL
}
}

73
config/metasploitconf.go Normal file
View File

@@ -0,0 +1,73 @@
package config
import (
"fmt"
"os"
"path/filepath"
"github.com/parnurzeal/gorequest"
"golang.org/x/xerrors"
)
// MetasploitConf is metasploit config
type MetasploitConf struct {
// DB type for metasploit dictionary (sqlite3, mysql, postgres or redis)
Type string
// http://metasploit-dictionary.com:1324 or DB connection string
URL string `json:"-"`
// /path/to/metasploit.sqlite3
SQLite3Path string `json:"-"`
}
func (cnf *MetasploitConf) setDefault() {
if cnf.Type == "" {
cnf.Type = "sqlite3"
}
if cnf.URL == "" && cnf.SQLite3Path == "" {
wd, _ := os.Getwd()
cnf.SQLite3Path = filepath.Join(wd, "go-msfdb.sqlite3")
}
}
const metasploitDBType = "METASPLOITDB_TYPE"
const metasploitDBURL = "METASPLOITDB_URL"
const metasploitDBPATH = "METASPLOITDB_SQLITE3_PATH"
// Init set options with the following priority.
// 1. Environment variable
// 2. config.toml
func (cnf *MetasploitConf) Init() {
if os.Getenv(metasploitDBType) != "" {
cnf.Type = os.Getenv(metasploitDBType)
}
if os.Getenv(metasploitDBURL) != "" {
cnf.URL = os.Getenv(metasploitDBURL)
}
if os.Getenv(metasploitDBPATH) != "" {
cnf.SQLite3Path = os.Getenv(metasploitDBPATH)
}
cnf.setDefault()
}
// IsFetchViaHTTP returns wether fetch via http
func (cnf *MetasploitConf) IsFetchViaHTTP() bool {
return Conf.Metasploit.Type == "http"
}
// CheckHTTPHealth do health check
func (cnf *MetasploitConf) CheckHTTPHealth() error {
if !cnf.IsFetchViaHTTP() {
return nil
}
url := fmt.Sprintf("%s/health", cnf.URL)
resp, _, errs := gorequest.New().Get(url).End()
// resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
// resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return xerrors.Errorf("Failed to connect to metasploit server. url: %s, errs: %s", url, errs)
}
return nil
}

249
config/os.go Normal file
View File

@@ -0,0 +1,249 @@
package config
import (
"fmt"
"strings"
"time"
)
const (
// RedHat is
RedHat = "redhat"
// Debian is
Debian = "debian"
// Ubuntu is
Ubuntu = "ubuntu"
// CentOS is
CentOS = "centos"
// Fedora is
// Fedora = "fedora"
// Amazon is
Amazon = "amazon"
// Oracle is
Oracle = "oracle"
// FreeBSD is
FreeBSD = "freebsd"
// Raspbian is
Raspbian = "raspbian"
// Windows is
Windows = "windows"
// OpenSUSE is
OpenSUSE = "opensuse"
// OpenSUSELeap is
OpenSUSELeap = "opensuse.leap"
// SUSEEnterpriseServer is
SUSEEnterpriseServer = "suse.linux.enterprise.server"
// SUSEEnterpriseDesktop is
SUSEEnterpriseDesktop = "suse.linux.enterprise.desktop"
// SUSEOpenstackCloud is
SUSEOpenstackCloud = "suse.openstack.cloud"
// Alpine is
Alpine = "alpine"
// ServerTypePseudo is used for ServerInfo.Type, r.Family
ServerTypePseudo = "pseudo"
)
// EOL has End-of-Life information
type EOL struct {
StandardSupportUntil time.Time
ExtendedSupportUntil time.Time
Ended bool
}
// IsStandardSupportEnded checks now is under standard support
func (e EOL) IsStandardSupportEnded(now time.Time) bool {
return e.Ended ||
!e.ExtendedSupportUntil.IsZero() && e.StandardSupportUntil.IsZero() ||
!e.StandardSupportUntil.IsZero() && now.After(e.StandardSupportUntil)
}
// IsExtendedSuppportEnded checks now is under extended support
func (e EOL) IsExtendedSuppportEnded(now time.Time) bool {
if e.Ended {
return true
}
if e.StandardSupportUntil.IsZero() && e.ExtendedSupportUntil.IsZero() {
return false
}
return !e.ExtendedSupportUntil.IsZero() && now.After(e.ExtendedSupportUntil) ||
e.ExtendedSupportUntil.IsZero() && now.After(e.StandardSupportUntil)
}
// GetEOL return EOL information for the OS-release passed by args
// https://github.com/aquasecurity/trivy/blob/master/pkg/detector/ospkg/redhat/redhat.go#L20
func GetEOL(family, release string) (eol EOL, found bool) {
switch family {
case Amazon:
rel := "2"
if isAmazonLinux1(release) {
rel = "1"
}
eol, found = map[string]EOL{
"1": {StandardSupportUntil: time.Date(2023, 6, 30, 23, 59, 59, 0, time.UTC)},
"2": {},
}[rel]
case RedHat:
// https://access.redhat.com/support/policy/updates/errata
eol, found = map[string]EOL{
"3": {Ended: true},
"4": {Ended: true},
"5": {Ended: true},
"6": {
StandardSupportUntil: time.Date(2020, 11, 30, 23, 59, 59, 0, time.UTC),
ExtendedSupportUntil: time.Date(2024, 6, 30, 23, 59, 59, 0, time.UTC),
},
"7": {
StandardSupportUntil: time.Date(2024, 6, 30, 23, 59, 59, 0, time.UTC),
},
"8": {
StandardSupportUntil: time.Date(2029, 5, 31, 23, 59, 59, 0, time.UTC),
},
}[major(release)]
case CentOS:
// https://en.wikipedia.org/wiki/CentOS#End-of-support_schedule
// TODO Stream
eol, found = map[string]EOL{
"3": {Ended: true},
"4": {Ended: true},
"5": {Ended: true},
"6": {Ended: true},
"7": {StandardSupportUntil: time.Date(2024, 6, 30, 23, 59, 59, 0, time.UTC)},
"8": {StandardSupportUntil: time.Date(2021, 12, 31, 23, 59, 59, 0, time.UTC)},
}[major(release)]
case Oracle:
eol, found = map[string]EOL{
// Source:
// https://www.oracle.com/a/ocom/docs/elsp-lifetime-069338.pdf
// https://community.oracle.com/docs/DOC-917964
"3": {Ended: true},
"4": {Ended: true},
"5": {Ended: true},
"6": {
StandardSupportUntil: time.Date(2021, 3, 1, 23, 59, 59, 0, time.UTC),
ExtendedSupportUntil: time.Date(2024, 3, 1, 23, 59, 59, 0, time.UTC),
},
"7": {
StandardSupportUntil: time.Date(2024, 7, 1, 23, 59, 59, 0, time.UTC),
},
"8": {
StandardSupportUntil: time.Date(2029, 7, 1, 23, 59, 59, 0, time.UTC),
},
}[major(release)]
case Debian:
eol, found = map[string]EOL{
// https://wiki.debian.org/LTS
"6": {Ended: true},
"7": {Ended: true},
"8": {Ended: true},
"9": {StandardSupportUntil: time.Date(2022, 6, 30, 23, 59, 59, 0, time.UTC)},
"10": {StandardSupportUntil: time.Date(2024, 6, 30, 23, 59, 59, 0, time.UTC)},
}[major(release)]
case Raspbian:
// Not found
eol, found = map[string]EOL{}[major(release)]
case Ubuntu:
// https://wiki.ubuntu.com/Releases
eol, found = map[string]EOL{
"14.10": {Ended: true},
"14.04": {
ExtendedSupportUntil: time.Date(2022, 4, 1, 23, 59, 59, 0, time.UTC),
},
"15.04": {Ended: true},
"16.10": {Ended: true},
"17.04": {Ended: true},
"17.10": {Ended: true},
"16.04": {
StandardSupportUntil: time.Date(2021, 4, 1, 23, 59, 59, 0, time.UTC),
ExtendedSupportUntil: time.Date(2024, 4, 1, 23, 59, 59, 0, time.UTC),
},
"18.04": {
StandardSupportUntil: time.Date(2023, 4, 1, 23, 59, 59, 0, time.UTC),
ExtendedSupportUntil: time.Date(2028, 4, 1, 23, 59, 59, 0, time.UTC),
},
"18.10": {Ended: true},
"19.04": {Ended: true},
"19.10": {Ended: true},
"20.04": {
StandardSupportUntil: time.Date(2025, 4, 1, 23, 59, 59, 0, time.UTC),
},
"21.04": {
StandardSupportUntil: time.Date(2022, 1, 1, 23, 59, 59, 0, time.UTC),
},
"21.10": {
StandardSupportUntil: time.Date(2022, 7, 1, 23, 59, 59, 0, time.UTC),
},
}[release]
case SUSEEnterpriseServer:
//TODO
case Alpine:
// https://github.com/aquasecurity/trivy/blob/master/pkg/detector/ospkg/alpine/alpine.go#L19
// https://wiki.alpinelinux.org/wiki/Alpine_Linux:Releases
eol, found = map[string]EOL{
"2.0": {Ended: true},
"2.1": {Ended: true},
"2.2": {Ended: true},
"2.3": {Ended: true},
"2.4": {Ended: true},
"2.5": {Ended: true},
"2.6": {Ended: true},
"2.7": {Ended: true},
"3.0": {Ended: true},
"3.1": {Ended: true},
"3.2": {Ended: true},
"3.3": {Ended: true},
"3.4": {Ended: true},
"3.5": {Ended: true},
"3.6": {Ended: true},
"3.7": {Ended: true},
"3.8": {Ended: true},
"3.9": {Ended: true},
"3.10": {StandardSupportUntil: time.Date(2021, 5, 1, 23, 59, 59, 0, time.UTC)},
"3.11": {StandardSupportUntil: time.Date(2021, 11, 1, 23, 59, 59, 0, time.UTC)},
"3.12": {StandardSupportUntil: time.Date(2022, 5, 1, 23, 59, 59, 0, time.UTC)},
"3.13": {StandardSupportUntil: time.Date(2022, 11, 1, 23, 59, 59, 0, time.UTC)},
}[majorDotMinor(release)]
case FreeBSD:
// https://www.freebsd.org/security/
eol, found = map[string]EOL{
"7": {Ended: true},
"8": {Ended: true},
"9": {Ended: true},
"10": {Ended: true},
"11": {StandardSupportUntil: time.Date(2021, 9, 30, 23, 59, 59, 0, time.UTC)},
"12": {StandardSupportUntil: time.Date(2024, 6, 30, 23, 59, 59, 0, time.UTC)},
}[major(release)]
}
return
}
func major(osVer string) (majorVersion string) {
return strings.Split(osVer, ".")[0]
}
func majorDotMinor(osVer string) (majorDotMinor string) {
ss := strings.SplitN(osVer, ".", 3)
if len(ss) == 1 {
return osVer
}
return fmt.Sprintf("%s.%s", ss[0], ss[1])
}
func isAmazonLinux1(osRelease string) bool {
return len(strings.Fields(osRelease)) == 1
}

373
config/os_test.go Normal file
View File

@@ -0,0 +1,373 @@
package config
import (
"testing"
"time"
)
func TestEOL_IsStandardSupportEnded(t *testing.T) {
type fields struct {
family string
release string
}
tests := []struct {
name string
fields fields
now time.Time
found bool
stdEnded bool
extEnded bool
}{
// Amazon Linux
{
name: "amazon linux 1 supported",
fields: fields{family: Amazon, release: "2018.03"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: true,
},
{
name: "amazon linux 1 eol on 2023-6-30",
fields: fields{family: Amazon, release: "2018.03"},
now: time.Date(2023, 7, 1, 23, 59, 59, 0, time.UTC),
stdEnded: true,
extEnded: true,
found: true,
},
{
name: "amazon linux 2 supported",
fields: fields{family: Amazon, release: "2 (Karoo)"},
now: time.Date(2023, 7, 1, 23, 59, 59, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: true,
},
//RHEL
{
name: "RHEL7 supported",
fields: fields{family: RedHat, release: "7"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: true,
},
{
name: "RHEL8 supported",
fields: fields{family: RedHat, release: "8"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: true,
},
{
name: "RHEL6 eol",
fields: fields{family: RedHat, release: "6"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: true,
extEnded: false,
found: true,
},
{
name: "RHEL9 not found",
fields: fields{family: RedHat, release: "9"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: false,
},
//CentOS
{
name: "CentOS 7 supported",
fields: fields{family: CentOS, release: "7"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: true,
},
{
name: "CentOS 8 supported",
fields: fields{family: CentOS, release: "8"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: true,
},
{
name: "CentOS 6 eol",
fields: fields{family: CentOS, release: "6"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: true,
extEnded: true,
found: true,
},
{
name: "CentOS 9 not found",
fields: fields{family: CentOS, release: "9"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: false,
},
//Oracle
{
name: "Oracle Linux 7 supported",
fields: fields{family: Oracle, release: "7"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: true,
},
{
name: "Oracle Linux 8 supported",
fields: fields{family: Oracle, release: "8"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: true,
},
{
name: "Oracle Linux 6 eol",
fields: fields{family: Oracle, release: "6"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: true,
},
{
name: "Oracle Linux 9 not found",
fields: fields{family: Oracle, release: "9"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: false,
},
//Ubuntu
{
name: "Ubuntu 18.04 supported",
fields: fields{family: Ubuntu, release: "18.04"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: true,
},
{
name: "Ubuntu 18.04 ext supported",
fields: fields{family: Ubuntu, release: "18.04"},
now: time.Date(2025, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: true,
extEnded: false,
found: true,
},
{
name: "Ubuntu 16.04 supported",
fields: fields{family: Ubuntu, release: "18.04"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: true,
},
{
name: "Ubuntu 14.04 eol",
fields: fields{family: Ubuntu, release: "14.04"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: true,
extEnded: false,
found: true,
},
{
name: "Ubuntu 14.10 eol",
fields: fields{family: Ubuntu, release: "14.10"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: true,
extEnded: true,
found: true,
},
{
name: "Ubuntu 12.10 not found",
fields: fields{family: Ubuntu, release: "12.10"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
found: false,
stdEnded: false,
extEnded: false,
},
{
name: "Ubuntu 21.04 supported",
fields: fields{family: Ubuntu, release: "21.04"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
found: true,
stdEnded: false,
extEnded: false,
},
//Debian
{
name: "Debian 9 supported",
fields: fields{family: Debian, release: "9"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: true,
},
{
name: "Debian 10 supported",
fields: fields{family: Debian, release: "10"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: true,
},
{
name: "Debian 8 supported",
fields: fields{family: Debian, release: "8"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: true,
extEnded: true,
found: true,
},
{
name: "Debian 11 supported",
fields: fields{family: Debian, release: "11"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: false,
},
//alpine
{
name: "alpine 3.10 supported",
fields: fields{family: Alpine, release: "3.10"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: true,
},
{
name: "Alpine 3.11 supported",
fields: fields{family: Alpine, release: "3.11"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: true,
},
{
name: "Alpine 3.12 supported",
fields: fields{family: Alpine, release: "3.12"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: true,
},
{
name: "Alpine 3.9 eol",
fields: fields{family: Alpine, release: "3.9"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: true,
extEnded: true,
found: true,
},
{
name: "Alpine 3.14 not found",
fields: fields{family: Alpine, release: "3.14"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: false,
},
// freebsd
{
name: "freebsd 11 supported",
fields: fields{family: FreeBSD, release: "11"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: true,
},
{
name: "freebsd 11 eol on 2021-9-30",
fields: fields{family: FreeBSD, release: "11"},
now: time.Date(2021, 10, 1, 23, 59, 59, 0, time.UTC),
stdEnded: true,
extEnded: true,
found: true,
},
{
name: "freebsd 12 supported",
fields: fields{family: FreeBSD, release: "12"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: true,
},
{
name: "freebsd 10 eol",
fields: fields{family: FreeBSD, release: "10"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: true,
extEnded: true,
found: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
eol, found := GetEOL(tt.fields.family, tt.fields.release)
if found != tt.found {
t.Errorf("GetEOL.found = %v, want %v", found, tt.found)
}
if found {
if got := eol.IsStandardSupportEnded(tt.now); got != tt.stdEnded {
t.Errorf("EOL.IsStandardSupportEnded() = %v, want %v", got, tt.stdEnded)
}
if got := eol.IsExtendedSuppportEnded(tt.now); got != tt.extEnded {
t.Errorf("EOL.IsExtendedSupportEnded() = %v, want %v", got, tt.extEnded)
}
}
})
}
}
func Test_majorDotMinor(t *testing.T) {
type args struct {
osVer string
}
tests := []struct {
name string
args args
wantMajorDotMinor string
}{
{
name: "empty",
args: args{
osVer: "",
},
wantMajorDotMinor: "",
},
{
name: "major",
args: args{
osVer: "3",
},
wantMajorDotMinor: "3",
},
{
name: "major dot minor",
args: args{
osVer: "3.1",
},
wantMajorDotMinor: "3.1",
},
{
name: "major dot minor dot release",
args: args{
osVer: "3.1.4",
},
wantMajorDotMinor: "3.1",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if gotMajorDotMinor := majorDotMinor(tt.args.osVer); gotMajorDotMinor != tt.wantMajorDotMinor {
t.Errorf("majorDotMinor() = %v, want %v", gotMajorDotMinor, tt.wantMajorDotMinor)
}
})
}
}

34
config/saasconf.go Normal file
View File

@@ -0,0 +1,34 @@
package config
import (
"github.com/asaskevich/govalidator"
"golang.org/x/xerrors"
)
// SaasConf is FutureVuls config
type SaasConf struct {
GroupID int64 `json:"-"`
Token string `json:"-"`
URL string `json:"-"`
}
// Validate validates configuration
func (c *SaasConf) Validate() (errs []error) {
if c.GroupID == 0 {
errs = append(errs, xerrors.New("GroupID must not be empty"))
}
if len(c.Token) == 0 {
errs = append(errs, xerrors.New("Token must not be empty"))
}
if len(c.URL) == 0 {
errs = append(errs, xerrors.New("URL must not be empty"))
}
_, err := govalidator.ValidateStruct(c)
if err != nil {
errs = append(errs, err)
}
return
}

110
config/scanmode.go Normal file
View File

@@ -0,0 +1,110 @@
package config
import (
"strings"
"golang.org/x/xerrors"
)
// ScanMode has a type of scan mode. fast, fast-root, deep and offline
type ScanMode struct {
flag byte
}
const (
// Fast is fast scan mode
Fast = byte(1 << iota)
// FastRoot is scanmode
FastRoot
// Deep is scanmode
Deep
// Offline is scanmode
Offline
fastStr = "fast"
fastRootStr = "fast-root"
deepStr = "deep"
offlineStr = "offline"
)
// Set mode
func (s *ScanMode) Set(f byte) {
s.flag |= f
}
// IsFast return whether scan mode is fast
func (s ScanMode) IsFast() bool {
return s.flag&Fast == Fast
}
// IsFastRoot return whether scan mode is fastroot
func (s ScanMode) IsFastRoot() bool {
return s.flag&FastRoot == FastRoot
}
// IsDeep return whether scan mode is deep
func (s ScanMode) IsDeep() bool {
return s.flag&Deep == Deep
}
// IsOffline return whether scan mode is offline
func (s ScanMode) IsOffline() bool {
return s.flag&Offline == Offline
}
func (s *ScanMode) ensure() error {
numTrue := 0
for _, b := range []bool{s.IsFast(), s.IsFastRoot(), s.IsDeep()} {
if b {
numTrue++
}
}
if numTrue == 0 {
s.Set(Fast)
} else if s.IsDeep() && s.IsOffline() {
return xerrors.New("Don't specify both of deep and offline")
} else if numTrue != 1 {
return xerrors.New("Specify only one of offline, fast, fast-root or deep")
}
return nil
}
func (s ScanMode) String() string {
ss := ""
if s.IsFast() {
ss = fastStr
} else if s.IsFastRoot() {
ss = fastRootStr
} else if s.IsDeep() {
ss = deepStr
}
if s.IsOffline() {
ss += " " + offlineStr
}
return ss + " mode"
}
func setScanMode(server *ServerInfo, d ServerInfo) error {
if len(server.ScanMode) == 0 {
server.ScanMode = Conf.Default.ScanMode
}
for _, m := range server.ScanMode {
switch strings.ToLower(m) {
case fastStr:
server.Mode.Set(Fast)
case fastRootStr:
server.Mode.Set(FastRoot)
case deepStr:
server.Mode.Set(Deep)
case offlineStr:
server.Mode.Set(Offline)
default:
return xerrors.Errorf("scanMode: %s of %s is invalid. Specify -fast, -fast-root, -deep or offline",
m, server.ServerName)
}
}
if err := server.Mode.ensure(); err != nil {
return xerrors.Errorf("%s in %s", err, server.ServerName)
}
return nil
}

97
config/scanmodule.go Normal file
View File

@@ -0,0 +1,97 @@
package config
import (
"strings"
"golang.org/x/xerrors"
)
// ScanModule has a type of scan module
type ScanModule struct {
flag byte
}
const (
// OSPkg is scanmodule
OSPkg = byte(1 << iota)
// WordPress is scanmodule
WordPress
// Lockfile is scanmodule
Lockfile
// Port is scanmodule
Port
osPkgStr = "ospkg"
wordPressStr = "wordpress"
lockfileStr = "lockfile"
portStr = "port"
)
var allModules = []string{osPkgStr, wordPressStr, lockfileStr, portStr}
// Set module
func (s *ScanModule) Set(f byte) {
s.flag |= f
}
// IsScanOSPkg return whether scanning os pkg
func (s ScanModule) IsScanOSPkg() bool {
return s.flag&OSPkg == OSPkg
}
// IsScanWordPress return whether scanning wordpress
func (s ScanModule) IsScanWordPress() bool {
return s.flag&WordPress == WordPress
}
// IsScanLockFile whether scanning lock file
func (s ScanModule) IsScanLockFile() bool {
return s.flag&Lockfile == Lockfile
}
// IsScanPort whether scanning listening ports
func (s ScanModule) IsScanPort() bool {
return s.flag&Port == Port
}
// IsZero return the struct value are all false
func (s ScanModule) IsZero() bool {
return !(s.IsScanOSPkg() || s.IsScanWordPress() || s.IsScanLockFile() || s.IsScanPort())
}
func (s *ScanModule) ensure() error {
if s.IsZero() {
s.Set(OSPkg)
s.Set(WordPress)
s.Set(Lockfile)
s.Set(Port)
} else if !s.IsScanOSPkg() && s.IsScanPort() {
return xerrors.New("When specifying the Port, Specify OSPkg as well")
}
return nil
}
func setScanModules(server *ServerInfo, d ServerInfo) error {
if len(server.ScanModules) == 0 {
server.ScanModules = d.ScanModules
}
for _, m := range server.ScanModules {
switch strings.ToLower(m) {
case osPkgStr:
server.Module.Set(OSPkg)
case wordPressStr:
server.Module.Set(WordPress)
case lockfileStr:
server.Module.Set(Lockfile)
case portStr:
server.Module.Set(Port)
default:
return xerrors.Errorf("scanMode: %s of %s is invalid. Specify %s",
m, server.ServerName, allModules)
}
}
if err := server.Module.ensure(); err != nil {
return xerrors.Errorf("%s in %s", err, server.ServerName)
}
return nil
}

65
config/scanmodule_test.go Normal file
View File

@@ -0,0 +1,65 @@
package config
import (
"testing"
)
func TestScanModule_IsZero(t *testing.T) {
tests := []struct {
name string
modes []byte
want bool
}{
{
name: "not zero",
modes: []byte{OSPkg},
want: false,
},
{
name: "zero",
modes: []byte{},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := ScanModule{}
for _, b := range tt.modes {
s.Set(b)
}
if got := s.IsZero(); got != tt.want {
t.Errorf("ScanModule.IsZero() = %v, want %v", got, tt.want)
}
})
}
}
func TestScanModule_validate(t *testing.T) {
tests := []struct {
name string
modes []byte
wantErr bool
}{
{
name: "valid",
modes: []byte{},
wantErr: false,
},
{
name: "err",
modes: []byte{WordPress, Lockfile, Port},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := ScanModule{}
for _, b := range tt.modes {
s.Set(b)
}
if err := s.ensure(); (err != nil) != tt.wantErr {
t.Errorf("ScanModule.validate() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

51
config/slackconf.go Normal file
View File

@@ -0,0 +1,51 @@
package config
import (
"strings"
"github.com/asaskevich/govalidator"
"golang.org/x/xerrors"
)
// SlackConf is slack config
type SlackConf struct {
HookURL string `valid:"url" json:"-" toml:"hookURL,omitempty"`
LegacyToken string `json:"-" toml:"legacyToken,omitempty"`
Channel string `json:"-" toml:"channel,omitempty"`
IconEmoji string `json:"-" toml:"iconEmoji,omitempty"`
AuthUser string `json:"-" toml:"authUser,omitempty"`
NotifyUsers []string `toml:"notifyUsers,omitempty" json:"-"`
Text string `json:"-"`
}
// Validate validates configuration
func (c *SlackConf) Validate() (errs []error) {
if !Conf.ToSlack {
return
}
if len(c.HookURL) == 0 && len(c.LegacyToken) == 0 {
errs = append(errs, xerrors.New("slack.hookURL or slack.LegacyToken must not be empty"))
}
if len(c.Channel) == 0 {
errs = append(errs, xerrors.New("slack.channel must not be empty"))
} else {
if !(strings.HasPrefix(c.Channel, "#") ||
c.Channel == "${servername}") {
errs = append(errs, xerrors.Errorf(
"channel's prefix must be '#', channel: %s", c.Channel))
}
}
if len(c.AuthUser) == 0 {
errs = append(errs, xerrors.New("slack.authUser must not be empty"))
}
_, err := govalidator.ValidateStruct(c)
if err != nil {
errs = append(errs, err)
}
return
}

65
config/smtpconf.go Normal file
View File

@@ -0,0 +1,65 @@
package config
import (
"github.com/asaskevich/govalidator"
"golang.org/x/xerrors"
)
// SMTPConf is smtp config
type SMTPConf struct {
SMTPAddr string `toml:"smtpAddr,omitempty" json:"-"`
SMTPPort string `toml:"smtpPort,omitempty" valid:"port" json:"-"`
User string `toml:"user,omitempty" json:"-"`
Password string `toml:"password,omitempty" json:"-"`
From string `toml:"from,omitempty" json:"-"`
To []string `toml:"to,omitempty" json:"-"`
Cc []string `toml:"cc,omitempty" json:"-"`
SubjectPrefix string `toml:"subjectPrefix,omitempty" json:"-"`
}
func checkEmails(emails []string) (errs []error) {
for _, addr := range emails {
if len(addr) == 0 {
return
}
if ok := govalidator.IsEmail(addr); !ok {
errs = append(errs, xerrors.Errorf("Invalid email address. email: %s", addr))
}
}
return
}
// Validate SMTP configuration
func (c *SMTPConf) Validate() (errs []error) {
if !Conf.ToEmail {
return
}
// Check Emails fromat
emails := []string{}
emails = append(emails, c.From)
emails = append(emails, c.To...)
emails = append(emails, c.Cc...)
if emailErrs := checkEmails(emails); 0 < len(emailErrs) {
errs = append(errs, emailErrs...)
}
if len(c.SMTPAddr) == 0 {
errs = append(errs, xerrors.New("email.smtpAddr must not be empty"))
}
if len(c.SMTPPort) == 0 {
errs = append(errs, xerrors.New("email.smtpPort must not be empty"))
}
if len(c.To) == 0 {
errs = append(errs, xerrors.New("email.To required at least one address"))
}
if len(c.From) == 0 {
errs = append(errs, xerrors.New("email.From required at least one address"))
}
_, err := govalidator.ValidateStruct(c)
if err != nil {
errs = append(errs, err)
}
return
}

129
config/syslogconf.go Normal file
View File

@@ -0,0 +1,129 @@
package config
import (
"errors"
"log/syslog"
"github.com/asaskevich/govalidator"
"golang.org/x/xerrors"
)
// SyslogConf is syslog config
type SyslogConf struct {
Protocol string `json:"-"`
Host string `valid:"host" json:"-"`
Port string `valid:"port" json:"-"`
Severity string `json:"-"`
Facility string `json:"-"`
Tag string `json:"-"`
Verbose bool `json:"-"`
}
// Validate validates configuration
func (c *SyslogConf) Validate() (errs []error) {
if !Conf.ToSyslog {
return nil
}
// If protocol is empty, it will connect to the local syslog server.
if len(c.Protocol) > 0 && c.Protocol != "tcp" && c.Protocol != "udp" {
errs = append(errs, errors.New(`syslog.protocol must be "tcp" or "udp"`))
}
// Default port: 514
if c.Port == "" {
c.Port = "514"
}
if _, err := c.GetSeverity(); err != nil {
errs = append(errs, err)
}
if _, err := c.GetFacility(); err != nil {
errs = append(errs, err)
}
if _, err := govalidator.ValidateStruct(c); err != nil {
errs = append(errs, err)
}
return errs
}
// GetSeverity gets severity
func (c *SyslogConf) GetSeverity() (syslog.Priority, error) {
if c.Severity == "" {
return syslog.LOG_INFO, nil
}
switch c.Severity {
case "emerg":
return syslog.LOG_EMERG, nil
case "alert":
return syslog.LOG_ALERT, nil
case "crit":
return syslog.LOG_CRIT, nil
case "err":
return syslog.LOG_ERR, nil
case "warning":
return syslog.LOG_WARNING, nil
case "notice":
return syslog.LOG_NOTICE, nil
case "info":
return syslog.LOG_INFO, nil
case "debug":
return syslog.LOG_DEBUG, nil
default:
return -1, xerrors.Errorf("Invalid severity: %s", c.Severity)
}
}
// GetFacility gets facility
func (c *SyslogConf) GetFacility() (syslog.Priority, error) {
if c.Facility == "" {
return syslog.LOG_AUTH, nil
}
switch c.Facility {
case "kern":
return syslog.LOG_KERN, nil
case "user":
return syslog.LOG_USER, nil
case "mail":
return syslog.LOG_MAIL, nil
case "daemon":
return syslog.LOG_DAEMON, nil
case "auth":
return syslog.LOG_AUTH, nil
case "syslog":
return syslog.LOG_SYSLOG, nil
case "lpr":
return syslog.LOG_LPR, nil
case "news":
return syslog.LOG_NEWS, nil
case "uucp":
return syslog.LOG_UUCP, nil
case "cron":
return syslog.LOG_CRON, nil
case "authpriv":
return syslog.LOG_AUTHPRIV, nil
case "ftp":
return syslog.LOG_FTP, nil
case "local0":
return syslog.LOG_LOCAL0, nil
case "local1":
return syslog.LOG_LOCAL1, nil
case "local2":
return syslog.LOG_LOCAL2, nil
case "local3":
return syslog.LOG_LOCAL3, nil
case "local4":
return syslog.LOG_LOCAL4, nil
case "local5":
return syslog.LOG_LOCAL5, nil
case "local6":
return syslog.LOG_LOCAL6, nil
case "local7":
return syslog.LOG_LOCAL7, nil
default:
return -1, xerrors.Errorf("Invalid facility: %s", c.Facility)
}
}

32
config/telegramconf.go Normal file
View File

@@ -0,0 +1,32 @@
package config
import (
"github.com/asaskevich/govalidator"
"golang.org/x/xerrors"
)
// TelegramConf is Telegram config
type TelegramConf struct {
Token string `json:"-"`
ChatID string `json:"-"`
}
// Validate validates configuration
func (c *TelegramConf) Validate() (errs []error) {
if !Conf.ToTelegram {
return
}
if len(c.ChatID) == 0 {
errs = append(errs, xerrors.New("TelegramConf.ChatID must not be empty"))
}
if len(c.Token) == 0 {
errs = append(errs, xerrors.New("TelegramConf.Token must not be empty"))
}
_, err := govalidator.ValidateStruct(c)
if err != nil {
errs = append(errs, err)
}
return
}

View File

@@ -15,270 +15,212 @@ type TOMLLoader struct {
// Load load the configuration TOML file specified by path arg.
func (c TOMLLoader) Load(pathToToml, keyPass string) error {
var conf Config
if _, err := toml.DecodeFile(pathToToml, &conf); err != nil {
if _, err := toml.DecodeFile(pathToToml, &Conf); err != nil {
return err
}
Conf.EMail = conf.EMail
Conf.Slack = conf.Slack
Conf.Stride = conf.Stride
Conf.HipChat = conf.HipChat
Conf.ChatWork = conf.ChatWork
Conf.Telegram = conf.Telegram
Conf.Saas = conf.Saas
Conf.Syslog = conf.Syslog
Conf.HTTP = conf.HTTP
Conf.AWS = conf.AWS
Conf.Azure = conf.Azure
Conf.CveDict = conf.CveDict
Conf.OvalDict = conf.OvalDict
Conf.Gost = conf.Gost
Conf.Exploit = conf.Exploit
Conf.Metasploit = conf.Metasploit
d := conf.Default
Conf.Default = d
servers := make(map[string]ServerInfo)
if keyPass != "" {
d.KeyPassword = keyPass
Conf.Default.KeyPassword = keyPass
}
Conf.CveDict.Init()
Conf.OvalDict.Init()
Conf.Gost.Init()
Conf.Exploit.Init()
Conf.Metasploit.Init()
index := 0
for serverName, v := range conf.Servers {
if 0 < len(v.KeyPassword) {
return xerrors.Errorf("[Deprecated] KEYPASSWORD IN CONFIG FILE ARE UNSECURE. REMOVE THEM IMMEDIATELY FOR A SECURITY REASONS. THEY WILL BE REMOVED IN A FUTURE RELEASE: %s", serverName)
for name, server := range Conf.Servers {
server.ServerName = name
if 0 < len(server.KeyPassword) {
return xerrors.Errorf("[Deprecated] KEYPASSWORD IN CONFIG FILE ARE UNSECURE. REMOVE THEM IMMEDIATELY FOR A SECURITY REASONS. THEY WILL BE REMOVED IN A FUTURE RELEASE: %s", name)
}
s := ServerInfo{ServerName: serverName}
if v.Type != ServerTypePseudo {
s.Host = v.Host
if len(s.Host) == 0 {
return xerrors.Errorf("%s is invalid. host is empty", serverName)
}
s.JumpServer = v.JumpServer
if len(s.JumpServer) == 0 {
s.JumpServer = d.JumpServer
}
switch {
case v.Port != "":
s.Port = v.Port
case d.Port != "":
s.Port = d.Port
default:
s.Port = "22"
}
switch {
case v.User != "":
s.User = v.User
case d.User != "":
s.User = d.User
default:
if s.Port != "local" {
return xerrors.Errorf("%s is invalid. User is empty", serverName)
}
}
s.SSHConfigPath = v.SSHConfigPath
if len(s.SSHConfigPath) == 0 {
s.SSHConfigPath = d.SSHConfigPath
}
s.KeyPath = v.KeyPath
if len(s.KeyPath) == 0 {
s.KeyPath = d.KeyPath
}
s.KeyPassword = v.KeyPassword
if len(s.KeyPassword) == 0 {
s.KeyPassword = d.KeyPassword
}
if err := setDefaultIfEmpty(&server, Conf.Default); err != nil {
return xerrors.Errorf("Failed to set default value to config. server: %s, err: %w", name, err)
}
s.ScanMode = v.ScanMode
if len(s.ScanMode) == 0 {
s.ScanMode = d.ScanMode
if len(s.ScanMode) == 0 {
s.ScanMode = []string{"fast"}
}
}
for _, m := range s.ScanMode {
switch m {
case "fast":
s.Mode.Set(Fast)
case "fast-root":
s.Mode.Set(FastRoot)
case "deep":
s.Mode.Set(Deep)
case "offline":
s.Mode.Set(Offline)
default:
return xerrors.Errorf("scanMode: %s of %s is invalid. Specify -fast, -fast-root, -deep or offline", m, serverName)
}
}
if err := s.Mode.validate(); err != nil {
return xerrors.Errorf("%s in %s", err, serverName)
if err := setScanMode(&server, Conf.Default); err != nil {
return xerrors.Errorf("Failed to set ScanMode: %w", err)
}
s.CpeNames = v.CpeNames
if len(s.CpeNames) == 0 {
s.CpeNames = d.CpeNames
if err := setScanModules(&server, Conf.Default); err != nil {
return xerrors.Errorf("Failed to set ScanModule: %w", err)
}
s.Lockfiles = v.Lockfiles
if len(s.Lockfiles) == 0 {
s.Lockfiles = d.Lockfiles
if len(server.CpeNames) == 0 {
server.CpeNames = Conf.Default.CpeNames
}
s.FindLock = v.FindLock
for i, n := range s.CpeNames {
for i, n := range server.CpeNames {
uri, err := toCpeURI(n)
if err != nil {
return xerrors.Errorf("Failed to parse CPENames %s in %s, err: %w", n, serverName, err)
return xerrors.Errorf("Failed to parse CPENames %s in %s, err: %w", n, name, err)
}
s.CpeNames[i] = uri
server.CpeNames[i] = uri
}
s.ContainersIncluded = v.ContainersIncluded
if len(s.ContainersIncluded) == 0 {
s.ContainersIncluded = d.ContainersIncluded
}
s.ContainersExcluded = v.ContainersExcluded
if len(s.ContainersExcluded) == 0 {
s.ContainersExcluded = d.ContainersExcluded
}
s.ContainerType = v.ContainerType
if len(s.ContainerType) == 0 {
s.ContainerType = d.ContainerType
}
s.Containers = v.Containers
for contName, cont := range s.Containers {
cont.IgnoreCves = append(cont.IgnoreCves, d.IgnoreCves...)
s.Containers[contName] = cont
}
if len(v.DependencyCheckXMLPath) != 0 || len(d.DependencyCheckXMLPath) != 0 {
return xerrors.Errorf("[DEPRECATED] dependencyCheckXMLPath IS DEPRECATED. USE owaspDCXMLPath INSTEAD: %s", serverName)
}
s.OwaspDCXMLPath = v.OwaspDCXMLPath
if len(s.OwaspDCXMLPath) == 0 {
s.OwaspDCXMLPath = d.OwaspDCXMLPath
}
s.Memo = v.Memo
if s.Memo == "" {
s.Memo = d.Memo
}
s.IgnoreCves = v.IgnoreCves
for _, cve := range d.IgnoreCves {
for _, cve := range Conf.Default.IgnoreCves {
found := false
for _, c := range s.IgnoreCves {
for _, c := range server.IgnoreCves {
if cve == c {
found = true
break
}
}
if !found {
s.IgnoreCves = append(s.IgnoreCves, cve)
server.IgnoreCves = append(server.IgnoreCves, cve)
}
}
s.IgnorePkgsRegexp = v.IgnorePkgsRegexp
for _, pkg := range d.IgnorePkgsRegexp {
for _, pkg := range Conf.Default.IgnorePkgsRegexp {
found := false
for _, p := range s.IgnorePkgsRegexp {
for _, p := range server.IgnorePkgsRegexp {
if pkg == p {
found = true
break
}
}
if !found {
s.IgnorePkgsRegexp = append(s.IgnorePkgsRegexp, pkg)
server.IgnorePkgsRegexp = append(server.IgnorePkgsRegexp, pkg)
}
}
for _, reg := range s.IgnorePkgsRegexp {
for _, reg := range server.IgnorePkgsRegexp {
_, err := regexp.Compile(reg)
if err != nil {
return xerrors.Errorf("Failed to parse %s in %s. err: %w", reg, serverName, err)
return xerrors.Errorf("Failed to parse %s in %s. err: %w", reg, name, err)
}
}
for contName, cont := range s.Containers {
for contName, cont := range server.Containers {
for _, reg := range cont.IgnorePkgsRegexp {
_, err := regexp.Compile(reg)
if err != nil {
return xerrors.Errorf("Failed to parse %s in %s@%s. err: %w",
reg, contName, serverName, err)
reg, contName, name, err)
}
}
}
opt := map[string]interface{}{}
for k, v := range d.Optional {
opt[k] = v
for ownerRepo, githubSetting := range server.GitHubRepos {
if ss := strings.Split(ownerRepo, "/"); len(ss) != 2 {
return xerrors.Errorf("Failed to parse GitHub owner/repo: %s in %s",
ownerRepo, name)
}
if githubSetting.Token == "" {
return xerrors.Errorf("GitHub owner/repo: %s in %s token is empty",
ownerRepo, name)
}
}
for k, v := range v.Optional {
opt[k] = v
}
s.Optional = opt
s.Enablerepo = v.Enablerepo
if len(s.Enablerepo) == 0 {
s.Enablerepo = d.Enablerepo
if len(server.Enablerepo) == 0 {
server.Enablerepo = Conf.Default.Enablerepo
}
if len(s.Enablerepo) != 0 {
for _, repo := range s.Enablerepo {
if len(server.Enablerepo) != 0 {
for _, repo := range server.Enablerepo {
switch repo {
case "base", "updates":
// nop
default:
return xerrors.Errorf(
"For now, enablerepo have to be base or updates: %s, servername: %s",
s.Enablerepo, serverName)
"For now, enablerepo have to be base or updates: %s",
server.Enablerepo)
}
}
}
s.GitHubRepos = v.GitHubRepos
for ownerRepo, githubSetting := range s.GitHubRepos {
if ss := strings.Split(ownerRepo, "/"); len(ss) != 2 {
return xerrors.Errorf("Failed to parse GitHub owner/repo: %s in %s",
ownerRepo, serverName)
}
if githubSetting.Token == "" {
return xerrors.Errorf("GitHub owner/repo: %s in %s token is empty",
ownerRepo, serverName)
}
}
s.UUIDs = v.UUIDs
s.Type = v.Type
s.WordPress.WPVulnDBToken = v.WordPress.WPVulnDBToken
s.WordPress.CmdPath = v.WordPress.CmdPath
s.WordPress.DocRoot = v.WordPress.DocRoot
s.WordPress.OSUser = v.WordPress.OSUser
s.WordPress.IgnoreInactive = v.WordPress.IgnoreInactive
s.IgnoredJSONKeys = v.IgnoredJSONKeys
if len(s.IgnoredJSONKeys) == 0 {
s.IgnoredJSONKeys = d.IgnoredJSONKeys
}
s.LogMsgAnsiColor = Colors[index%len(Colors)]
server.LogMsgAnsiColor = Colors[index%len(Colors)]
index++
servers[serverName] = s
Conf.Servers[name] = server
}
Conf.Servers = servers
return nil
}
func setDefaultIfEmpty(server *ServerInfo, d ServerInfo) error {
if server.Type != ServerTypePseudo {
if len(server.Host) == 0 {
return xerrors.Errorf("server.host is empty")
}
if len(server.JumpServer) == 0 {
server.JumpServer = Conf.Default.JumpServer
}
if server.Port == "" {
if Conf.Default.Port != "" {
server.Port = Conf.Default.Port
} else {
server.Port = "22"
}
}
if server.User == "" {
server.User = Conf.Default.User
if server.User == "" && server.Port != "local" {
return xerrors.Errorf("server.user is empty")
}
}
if server.SSHConfigPath == "" {
server.SSHConfigPath = Conf.Default.SSHConfigPath
}
if server.KeyPath == "" {
server.KeyPath = Conf.Default.KeyPath
}
if server.KeyPassword == "" {
server.KeyPassword = Conf.Default.KeyPassword
}
}
if len(server.Lockfiles) == 0 {
server.Lockfiles = Conf.Default.Lockfiles
}
if len(server.ContainersIncluded) == 0 {
server.ContainersIncluded = Conf.Default.ContainersIncluded
}
if len(server.ContainersExcluded) == 0 {
server.ContainersExcluded = Conf.Default.ContainersExcluded
}
if server.ContainerType == "" {
server.ContainerType = Conf.Default.ContainerType
}
for contName, cont := range server.Containers {
cont.IgnoreCves = append(cont.IgnoreCves, Conf.Default.IgnoreCves...)
server.Containers[contName] = cont
}
if server.OwaspDCXMLPath == "" {
server.OwaspDCXMLPath = Conf.Default.OwaspDCXMLPath
}
if server.Memo == "" {
server.Memo = Conf.Default.Memo
}
if server.WordPress == nil {
server.WordPress = Conf.Default.WordPress
if server.WordPress == nil {
server.WordPress = &WordPressConf{}
}
}
if len(server.IgnoredJSONKeys) == 0 {
server.IgnoredJSONKeys = Conf.Default.IgnoredJSONKeys
}
opt := map[string]interface{}{}
for k, v := range Conf.Default.Optional {
opt[k] = v
}
for k, v := range server.Optional {
opt[k] = v
}
server.Optional = opt
return nil
}

View File

@@ -57,12 +57,24 @@ func Parse(vulnJSON []byte, scanResult *models.ScanResult) (result *models.ScanR
return references[i].Link < references[j].Link
})
var published time.Time
if vuln.PublishedDate != nil {
published = *vuln.PublishedDate
}
var lastModified time.Time
if vuln.LastModifiedDate != nil {
lastModified = *vuln.LastModifiedDate
}
vulnInfo.CveContents = models.CveContents{
models.Trivy: models.CveContent{
Cvss3Severity: vuln.Severity,
References: references,
Title: vuln.Title,
Summary: vuln.Description,
Published: published,
LastModified: lastModified,
},
}
// do only if image type is Vuln

View File

@@ -16,6 +16,12 @@ func (e Error) Error() string {
var (
// ErrFailedToAccessGithubAPI is error of github alert's api access
ErrFailedToAccessGithubAPI ErrorCode = "ErrFailedToAccessGithubAPI"
// ErrFailedToAccessWpScan is error of wpscan.com api access
ErrFailedToAccessWpScan ErrorCode = "ErrFailedToAccessWpScan"
// ErrWpScanAPILimitExceeded is error of wpscan.com api limit exceeded
ErrWpScanAPILimitExceeded ErrorCode = "ErrWpScanAPILimitExceeded"
)
// New :

View File

@@ -4,26 +4,22 @@ package exploit
import (
"encoding/json"
"fmt"
"net/http"
cnf "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"github.com/mozqnet/go-exploitdb/db"
exploitmodels "github.com/mozqnet/go-exploitdb/models"
"github.com/parnurzeal/gorequest"
"golang.org/x/xerrors"
"github.com/vulsio/go-exploitdb/db"
exploitmodels "github.com/vulsio/go-exploitdb/models"
)
// FillWithExploit fills exploit information that has in Exploit
func FillWithExploit(driver db.DB, r *models.ScanResult) (nExploitCve int, err error) {
if cnf.Conf.Exploit.IsFetchViaHTTP() {
func FillWithExploit(driver db.DB, r *models.ScanResult, cnf *config.ExploitConf) (nExploitCve int, err error) {
if cnf.IsFetchViaHTTP() {
var cveIDs []string
for cveID := range r.ScannedCves {
cveIDs = append(cveIDs, cveID)
}
prefix, _ := util.URLPathJoin(cnf.Conf.Exploit.URL, "cves")
prefix, _ := util.URLPathJoin(cnf.URL, "cves")
responses, err := getCvesViaHTTP(cveIDs, prefix)
if err != nil {
return 0, err
@@ -87,33 +83,3 @@ func ConvertToModels(es []*exploitmodels.Exploit) (exploits []models.Exploit) {
}
return exploits
}
// CheckHTTPHealth do health check
func CheckHTTPHealth() error {
if !cnf.Conf.Exploit.IsFetchViaHTTP() {
return nil
}
url := fmt.Sprintf("%s/health", cnf.Conf.Exploit.URL)
var errs []error
var resp *http.Response
resp, _, errs = gorequest.New().Get(url).End()
// resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
// resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return xerrors.Errorf("Failed to connect to exploit server. url: %s, errs: %w", url, errs)
}
return nil
}
// CheckIfExploitFetched checks if oval entries are in DB by family, release.
func CheckIfExploitFetched(driver db.DB, osFamily string) (fetched bool, err error) {
//TODO
return true, nil
}
// CheckIfExploitFresh checks if oval entries are fresh enough
func CheckIfExploitFresh(driver db.DB, osFamily string) (ok bool, err error) {
//TODO
return true, nil
}

View File

@@ -85,13 +85,13 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er
count, retryMax := 0, 3
f := func() (err error) {
// resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
resp, body, errs = gorequest.New().Get(url).End()
resp, body, errs = gorequest.New().Timeout(10 * time.Second).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
count++
if count == retryMax {
return nil
}
return xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %w", url, resp, errs)
return xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %s", url, resp, errs)
}
return nil
}

View File

@@ -15,12 +15,14 @@ import (
"golang.org/x/oauth2"
)
// FillGitHubSecurityAlerts access to owner/repo on GitHub and fetch security alerts of the repository via GitHub API v4 GraphQL and then set to the given ScanResult.
// DetectGitHubSecurityAlerts access to owner/repo on GitHub and fetch security alerts of the repository via GitHub API v4 GraphQL and then set to the given ScanResult.
// https://help.github.com/articles/about-security-alerts-for-vulnerable-dependencies/
func FillGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string) (nCVEs int, err error) {
//TODO move to report
func DetectGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string) (nCVEs int, err error) {
src := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: token},
)
//TODO Proxy
httpClient := oauth2.NewClient(context.Background(), src)
// TODO Use `https://github.com/shurcooL/githubv4` if the tool supports vulnerabilityAlerts Endpoint
@@ -31,10 +33,12 @@ func FillGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string) (
for {
jsonStr := fmt.Sprintf(jsonfmt, owner, repo, 100, after)
req, err := http.NewRequest("POST",
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
req, err := http.NewRequestWithContext(ctx, http.MethodPost,
"https://api.github.com/graphql",
bytes.NewBuffer([]byte(jsonStr)),
)
defer cancel()
if err != nil {
return 0, err
}
@@ -65,10 +69,8 @@ func FillGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string) (
// util.Log.Debugf("%s", pp.Sprint(alerts))
// util.Log.Debugf("%s", string(body))
if alerts.Data.Repository.URL == "" {
return 0, errof.New(
errof.ErrFailedToAccessGithubAPI,
fmt.Sprintf("Failed to access to GitHub API. Response: %s", string(body)),
)
return 0, errof.New(errof.ErrFailedToAccessGithubAPI,
fmt.Sprintf("Failed to access to GitHub API. Response: %s", string(body)))
}
for _, v := range alerts.Data.Repository.VulnerabilityAlerts.Edges {
@@ -102,20 +104,39 @@ func FillGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string) (
cveIDs = other
}
refs := []models.Reference{}
for _, r := range v.Node.SecurityAdvisory.References {
refs = append(refs, models.Reference{Link: r.URL})
}
for _, cveID := range cveIDs {
cveContent := models.CveContent{
Type: models.GitHub,
CveID: cveID,
Title: v.Node.SecurityAdvisory.Summary,
Summary: v.Node.SecurityAdvisory.Description,
Cvss2Severity: v.Node.SecurityVulnerability.Severity,
Cvss3Severity: v.Node.SecurityVulnerability.Severity,
SourceLink: v.Node.SecurityAdvisory.Permalink,
References: refs,
Published: v.Node.SecurityAdvisory.PublishedAt,
LastModified: v.Node.SecurityAdvisory.UpdatedAt,
}
if val, ok := r.ScannedCves[cveID]; ok {
val.GitHubSecurityAlerts = val.GitHubSecurityAlerts.Add(m)
val.CveContents[models.GitHub] = cveContent
r.ScannedCves[cveID] = val
nCVEs++
} else {
v := models.VulnInfo{
CveID: cveID,
Confidences: models.Confidences{models.GitHubMatch},
GitHubSecurityAlerts: models.GitHubSecurityAlerts{m},
CveContents: models.NewCveContents(cveContent),
}
r.ScannedCves[cveID] = v
nCVEs++
}
nCVEs++
}
}
if !alerts.Data.Repository.VulnerabilityAlerts.PageInfo.HasNextPage {

54
go.mod
View File

@@ -2,35 +2,21 @@ module github.com/future-architect/vuls
go 1.15
replace (
gopkg.in/mattn/go-colorable.v0 => github.com/mattn/go-colorable v0.1.0
gopkg.in/mattn/go-isatty.v0 => github.com/mattn/go-isatty v0.0.6
)
require (
github.com/Azure/azure-sdk-for-go v49.1.0+incompatible
github.com/Azure/go-autorest/autorest v0.11.15 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.10 // indirect
github.com/Azure/azure-sdk-for-go v50.2.0+incompatible
github.com/BurntSushi/toml v0.3.1
github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91
github.com/aquasecurity/fanal v0.0.0-20201218050947-981a0510f9cb
github.com/aquasecurity/trivy v0.14.0
github.com/aquasecurity/trivy-db v0.0.0-20201220084758-2d91316c83fa
github.com/aquasecurity/fanal v0.0.0-20210119051230-28c249da7cfd
github.com/aquasecurity/trivy v0.15.0
github.com/aquasecurity/trivy-db v0.0.0-20210121143430-2a5c54036a86
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef
github.com/aws/aws-sdk-go v1.36.12
github.com/aws/aws-sdk-go v1.36.31
github.com/boltdb/bolt v1.3.1
github.com/briandowns/spinner v1.12.0 // indirect
github.com/caarlos0/env/v6 v6.4.0 // indirect
github.com/cenkalti/backoff v2.2.1+incompatible
github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21
github.com/emersion/go-smtp v0.14.0
github.com/goccy/go-yaml v1.8.4 // indirect
github.com/golang/protobuf v1.4.3 // indirect
github.com/google/subcommands v1.2.0
github.com/google/wire v0.4.0 // indirect
github.com/gosuri/uitable v0.0.4
github.com/grokify/html-strip-tags-go v0.0.0-20200923094847-079d207a09f1 // indirect
github.com/hashicorp/go-uuid v1.0.2
github.com/hashicorp/go-version v1.2.1
github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c
@@ -41,40 +27,24 @@ require (
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d
github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936
github.com/knqyf263/gost v0.1.7
github.com/kotakanbe/go-cve-dictionary v0.5.6
github.com/kotakanbe/go-cve-dictionary v0.5.7
github.com/kotakanbe/go-pingscanner v0.1.0
github.com/kotakanbe/goval-dictionary v0.2.16
github.com/kotakanbe/goval-dictionary v0.3.1
github.com/kotakanbe/logrus-prefixed-formatter v0.0.0-20180123152602-928f7356cb96
github.com/magiconair/properties v1.8.4 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/mapstructure v1.4.0 // indirect
github.com/mozqnet/go-exploitdb v0.1.2
github.com/nlopes/slack v0.6.0
github.com/nsf/termbox-go v0.0.0-20201124104050-ed494de23a00 // indirect
github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1 // indirect
github.com/olekukonko/tablewriter v0.0.4
github.com/parnurzeal/gorequest v0.2.16
github.com/pelletier/go-toml v1.8.1 // indirect
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
github.com/sirupsen/logrus v1.7.0
github.com/spf13/afero v1.5.1
github.com/spf13/cast v1.3.1 // indirect
github.com/spf13/cobra v1.1.1
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/viper v1.7.1 // indirect
github.com/takuzoo3868/go-msfdb v0.1.3
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.16.0 // indirect
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5
golang.org/x/sys v0.0.0-20201218084310-7d0127a74742 // indirect
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58 // indirect
github.com/vulsio/go-exploitdb v0.1.4
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
golang.org/x/oauth2 v0.0.0-20210125201302-af13f521f196
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
google.golang.org/appengine v1.6.7 // indirect
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
honnef.co/go/tools v0.1.0 // indirect
k8s.io/utils v0.0.0-20201110183641-67b214c5f920
k8s.io/utils v0.0.0-20210111153108-fddb29f9d009
)

243
go.sum
View File

@@ -35,46 +35,31 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/Azure/azure-sdk-for-go v35.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go v38.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go v49.1.0+incompatible h1:Sz6TnzkzwsiEgD84Vujpjo0vEox9/UMeyLVWBCkZwQ4=
github.com/Azure/azure-sdk-for-go v49.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go v50.2.0+incompatible h1:w9EF1btRfLLWbNEp6XvkMjeA6nQ3e1GZ2KNDqB/SjOQ=
github.com/Azure/azure-sdk-for-go v50.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
github.com/Azure/go-autorest/autorest v0.9.3 h1:OZEIaBbMdUE/Js+BQKlpO81XlISgipr6yDJ+PSwsgi4=
github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0=
github.com/Azure/go-autorest/autorest v0.11.15 h1:S5SDFpmgoVyvMEOcULyEDlYFrdPmu6Wl0Ic+shkEwzg=
github.com/Azure/go-autorest/autorest v0.11.15/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
github.com/Azure/go-autorest/autorest/adal v0.8.1 h1:pZdL8o72rK+avFWl+p9nE8RWi1JInZrWJYlnpfXJwHk=
github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
github.com/Azure/go-autorest/autorest/adal v0.9.10 h1:r6fZHMaHD8B6LDCn0o5vyBFHIHrM6Ywwx7mb49lPItI=
github.com/Azure/go-autorest/autorest/adal v0.9.10/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM=
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc=
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc=
github.com/Azure/go-autorest/autorest/to v0.3.0 h1:zebkZaadz7+wIQYgC7GXaz3Wb28yKYfVkkBKwc38VF8=
github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA=
github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8=
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/logger v0.2.0 h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8Pcx+3oqrE=
github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
@@ -88,16 +73,12 @@ github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb0
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OneOfOne/xxhash v1.2.7/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE=
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/PuerkitoBio/goquery v1.6.0 h1:j7taAbelrdcsOlGeMenZxc2AWXD5fieT1/znArdnx94=
github.com/PuerkitoBio/goquery v1.6.0/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91 h1:vX+gnvBc56EbWYrmlhYbFYRaeikAke1GL84N4BEYOFE=
github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91/go.mod h1:cDLGBht23g0XQdLjzn6xOGXDkLK182YfINAaZEQLCHQ=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM=
@@ -107,19 +88,16 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
github.com/alicebob/miniredis/v2 v2.14.1/go.mod h1:uS970Sw5Gs9/iK3yBg0l9Uj9s25wXxSpQUE9EaJ/Blg=
github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/andybalholm/cascadia v1.2.0 h1:vuRCkM5Ozh/BfmsaTm26kbjm0mIOM3yS5Ek/F5h18aE=
github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986 h1:2a30xLN2sUZcMXl50hg+PJCIDdJgIvIbVcKqLJ/ZrtM=
github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986/go.mod h1:NT+jyeCzXk6vXR5MTkdn4z64TgGfE5HMLC8qfj5unl8=
github.com/aquasecurity/fanal v0.0.0-20190819081512-f04452b627c6/go.mod h1:enEz4FFetw4XAbkffaYgyCVq1556R9Ry+noqT4rq9BE=
github.com/aquasecurity/fanal v0.0.0-20201129085323-d57dde147ddc/go.mod h1:f0nFZptUaL8ivi5soRDlYnJYFdY0anxlYe4K4z9EGxs=
github.com/aquasecurity/fanal v0.0.0-20201218050947-981a0510f9cb h1:T48y/j2wvl/xPX2IyV0ogFq+GeCLY+3548awySrUaJU=
github.com/aquasecurity/fanal v0.0.0-20201218050947-981a0510f9cb/go.mod h1:arUN1lJnuAWLL0PUQ/UYrkAomU/Mby+gCXJMU90GHlA=
github.com/aquasecurity/go-dep-parser v0.0.0-20190819075924-ea223f0ef24b h1:55Ulc/gvfWm4ylhVaR7MxOwujRjA6et7KhmUbSgUFf4=
github.com/aquasecurity/fanal v0.0.0-20210119051230-28c249da7cfd h1:meqa2AA+7K1r/nfNB19K2AP/v8+nemuWeQoTSqZ2R9s=
github.com/aquasecurity/fanal v0.0.0-20210119051230-28c249da7cfd/go.mod h1:kur6SaohYhsjQLzijAdtn+X8rkTtwxawE51WyVCXLKk=
github.com/aquasecurity/go-dep-parser v0.0.0-20190819075924-ea223f0ef24b/go.mod h1:BpNTD9vHfrejKsED9rx04ldM1WIbeyXGYxUrqTVwxVQ=
github.com/aquasecurity/go-dep-parser v0.0.0-20201028043324-889d4a92b8e0 h1:cLH3SebzhbJ+jU1GIad8A1N8p7m7OjHhtY6JePISiVc=
github.com/aquasecurity/go-dep-parser v0.0.0-20201028043324-889d4a92b8e0/go.mod h1:X42mTIRhgPalSm81Om2kD+3ydeunbC8TZtZj1bvgRo8=
@@ -131,13 +109,13 @@ github.com/aquasecurity/go-version v0.0.0-20201107203531-5e48ac5d022a/go.mod h1:
github.com/aquasecurity/go-version v0.0.0-20201115065329-578079e4ab05 h1:q0ZpFBjwzDk1ofey7gJ2kfA6ZNi2PeBWxNzmRPrfetA=
github.com/aquasecurity/go-version v0.0.0-20201115065329-578079e4ab05/go.mod h1:9Beu8XsUNNfzml7WBf3QmyPToP1wm1Gj/Vc5UJKqTzU=
github.com/aquasecurity/testdocker v0.0.0-20200426142840-5f05bce6f12a/go.mod h1:psfu0MVaiTDLpNxCoNsTeILSKY2EICBwv345f3M+Ffs=
github.com/aquasecurity/testdocker v0.0.0-20210106133225-0b17fe083674/go.mod h1:psfu0MVaiTDLpNxCoNsTeILSKY2EICBwv345f3M+Ffs=
github.com/aquasecurity/trivy v0.1.6/go.mod h1:5hobyhxLzDtxruHzPxpND2PUKOssvGUdE9BocpJUwo4=
github.com/aquasecurity/trivy v0.14.0 h1:6fbkcNos2d2fRZlM255JrrXbl2xrESLHEVZs4k0FqaA=
github.com/aquasecurity/trivy v0.14.0/go.mod h1:DZz+rdEPMIPiG/9Omip294RcqRwA7u6fdIQCC08Q7kk=
github.com/aquasecurity/trivy-db v0.0.0-20201117092632-b09c30858fc2 h1:AXA9aW464copH1GTKv35yCwztJsqDVZWKfCtBuMpI9U=
github.com/aquasecurity/trivy-db v0.0.0-20201117092632-b09c30858fc2/go.mod h1:+3+NEz0U0NCgO87Cyk0dy3SwH7CI6J4HUeCqqPj1fvQ=
github.com/aquasecurity/trivy-db v0.0.0-20201220084758-2d91316c83fa h1:S+565O3UOLifhjMO2ONtKmbSgWtQOxrbG2/vhnMF4k8=
github.com/aquasecurity/trivy-db v0.0.0-20201220084758-2d91316c83fa/go.mod h1:+3+NEz0U0NCgO87Cyk0dy3SwH7CI6J4HUeCqqPj1fvQ=
github.com/aquasecurity/trivy v0.15.0 h1:C70Sx3vEX17GLlpwE7hXAJM3Et8zgSRVJSbes+zj2So=
github.com/aquasecurity/trivy v0.15.0/go.mod h1:JPgyECgbdod5QI8pQ1aEwAS53h6KGZ5hpBuCotBJYiE=
github.com/aquasecurity/trivy-db v0.0.0-20201221070121-47d2cc0d7b58/go.mod h1:+3+NEz0U0NCgO87Cyk0dy3SwH7CI6J4HUeCqqPj1fvQ=
github.com/aquasecurity/trivy-db v0.0.0-20210121143430-2a5c54036a86 h1:KSJyJg4GnwPjhdxYlnewiqEX3OV901ivbl+XCN1ix+o=
github.com/aquasecurity/trivy-db v0.0.0-20210121143430-2a5c54036a86/go.mod h1:N7CWA/vjVw78GWAdCJGhFQVqNGEA4e47a6eIWm+C/Bc=
github.com/aquasecurity/vuln-list-update v0.0.0-20191016075347-3d158c2bf9a2/go.mod h1:6NhOP0CjZJL27bZZcaHECtzWdwDDm2g6yCY0QgXEGQQ=
github.com/araddon/dateparse v0.0.0-20190426192744-0d74ffceef83/go.mod h1:SLqhdZcd+dF3TEVL2RMoob5bBP5R1P1qkox+HtCBgGI=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
@@ -145,16 +123,14 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef h1:46PFijGLmAjMPwCCCo7Jf0W6f9slllCkkv7vyc1yOSg=
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/aws/aws-sdk-go v1.16.26/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.19.11/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.27.1 h1:MXnqY6SlWySaZAqNnXThOvjRFdiiOuKtC6i7baFdNdU=
github.com/aws/aws-sdk-go v1.27.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.36.12 h1:YJpKFEMbqEoo+incs5qMe61n1JH3o4O1IMkMexLzJG8=
github.com/aws/aws-sdk-go v1.36.12/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go v1.36.31 h1:BMVngapDGAfLBVEVzaSIw3fmJdWx7jOvhLCXgRXbXQI=
github.com/aws/aws-sdk-go v1.36.31/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
@@ -162,15 +138,12 @@ github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJm
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/briandowns/spinner v0.0.0-20190319032542-ac46072a5a91 h1:GMmnK0dvr0Sf0gx3DvTbln0c8DE07B7sPVD9dgHOqo4=
github.com/briandowns/spinner v0.0.0-20190319032542-ac46072a5a91/go.mod h1:hw/JEQBIE+c/BLI4aKM8UU8v+ZqrD3h7HC27kKt8JQU=
github.com/briandowns/spinner v1.11.1/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ=
github.com/briandowns/spinner v1.12.0 h1:72O0PzqGJb6G3KgrcIOtL/JAGGZ5ptOMCn9cUHmqsmw=
github.com/briandowns/spinner v1.12.0/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ=
github.com/caarlos0/env/v6 v6.0.0 h1:NZt6FAoB8ieKO5lEwRdwCzYxWFx7ZYF2R7UcoyaWtyc=
github.com/caarlos0/env/v6 v6.0.0/go.mod h1:+wdyOmtjoZIW2GJOc2OYa5NoOFuWD/bIpWqm30NgtRk=
github.com/caarlos0/env/v6 v6.4.0 h1:fUo2hQNR3O7Yb7E2sYy8cxY42BRvFxWa0G4XBMLJAQM=
github.com/caarlos0/env/v6 v6.4.0/go.mod h1:MX/8qQ2zCofGGkb7FxjmDLOOjUylO2b7dbsIpN30bnY=
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -178,7 +151,6 @@ github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cheggaaa/pb/v3 v3.0.3 h1:8WApbyUmgMOz7WIxJVNK0IRDcRfAmTxcEdi0TuxjdP4=
github.com/cheggaaa/pb/v3 v3.0.3/go.mod h1:Pp35CDuiEpHa/ZLGCtBbM6CBwMstv1bJlG884V+73Yc=
github.com/cheggaaa/pb/v3 v3.0.5 h1:lmZOti7CraK9RSjzExsY53+WWfub9Qv13B5m4ptEoPE=
github.com/cheggaaa/pb/v3 v3.0.5/go.mod h1:X1L61/+36nz9bjIsrDU52qHKOQukUQe2Ge+YvGuquCw=
@@ -189,7 +161,6 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.3.3 h1:LoIzb5y9x5l8VKAlyrbusNPXqBY0+kviRloxFUMFwKc=
github.com/containerd/containerd v1.3.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/continuity v0.0.0-20180921161001-7f53d412b9eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
@@ -232,22 +203,18 @@ github.com/docker/cli v0.0.0-20180920165730-54c19e67f69c/go.mod h1:JLrzqnKDaYBop
github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v0.0.0-20180920194744-16128bbac47f/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v0.0.0-20180924202107-a9c061deec0f/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v0.7.3-0.20190506211059-b20a14b54661/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7 h1:Cvj7S8I4Xpx78KAl6TwTmMHuHlZ/0SM60NUneGJQ7IE=
github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-ce v0.0.0-20180924210327-f53bd8bb8e43/go.mod h1:l1FUGRYBvbjnZ8MS6A2xOji4aZFlY/Qmgz7p4oXH7ac=
github.com/docker/docker-credential-helpers v0.6.1/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
github.com/docker/go-connections v0.0.0-20180821093606-97c2040d34df/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
@@ -258,13 +225,11 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/elazarl/goproxy v0.0.0-20190711103511-473e67f1d7d2 h1:aZtFdDNWY/yH86JPR2WX/PN63635VsE/f/nXNPAbYxY=
github.com/elazarl/goproxy v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/elazarl/goproxy v0.0.0-20200809112317-0581fc3aee2d/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/elazarl/goproxy v0.0.0-20201021153353-00ad82a08272 h1:Am81SElhR3XCQBunTisljzNkNese2T1FiV8jP79+dqg=
github.com/elazarl/goproxy v0.0.0-20201021153353-00ad82a08272/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/elazarl/goproxy v0.0.0-20210110162100-a92cc753f88e h1:/cwV7t2xezilMljIftb7WlFtzGANRCnoOhPjtl2ifcs=
github.com/elazarl/goproxy v0.0.0-20210110162100-a92cc753f88e/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy03t/bUadywsbyrQwCqZeNIEX6M1OtSZOM=
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
github.com/elazarl/goproxy/ext v0.0.0-20200809112317-0581fc3aee2d h1:st1tmvy+4duoRj+RaeeJoECWCWM015fBtf/4aR+hhqk=
github.com/elazarl/goproxy/ext v0.0.0-20200809112317-0581fc3aee2d/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
@@ -284,17 +249,12 @@ github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DP
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
github.com/etcd-io/bbolt v1.3.2/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fernet/fernet-go v0.0.0-20180830025343-9eac43b88a5e/go.mod h1:2H9hjfbpSMHwY503FclkV/lZTBh2YlOmLLSda12uL8c=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
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=
@@ -330,22 +290,19 @@ github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-redis/redis v6.15.7+incompatible h1:3skhDh95XQMpnqeqNftPkQD9jL9e5e36z/1SUm6dy1U=
github.com/go-redis/redis v6.15.7+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-redis/redis/v8 v8.4.0 h1:J5NCReIgh3QgUJu398hUncxDExN4gMOHI11NVbVicGQ=
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-redis/redis/v8 v8.4.0/go.mod h1:A1tbYoHSa1fXwN+//ljcCYYJeLmVrwL9hbQN45Jdy0M=
github.com/go-redis/redis/v8 v8.4.4 h1:fGqgxCTR1sydaKI00oQf3OmkU/DIe/I/fYXvGklCIuc=
github.com/go-redis/redis/v8 v8.4.4/go.mod h1:nA0bQuF0i5JFx4Ta9RZxGKXFrQ8cRWntra97f0196iY=
github.com/go-redis/redis/v8 v8.4.10 h1:fWdl0RBmVibUDOp8bqz1e2Yy9dShOeIeWsiAifYk06Y=
github.com/go-redis/redis/v8 v8.4.10/go.mod h1:d5yY/TlkQyYBSBHnXUmnf1OrHbyQere5JV4dLKwvXmo=
github.com/go-restruct/restruct v0.0.0-20191227155143-5734170a48a1/go.mod h1:KqrpKpn4M8OLznErihXTGLlsXFGeLxHUrLRRI/1YjGk=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
@@ -353,18 +310,15 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gocarina/gocsv v0.0.0-20190821091544-020a928c6f4e h1:dAuXgdjT2qGRDgU3BxtkRPv+QEjkJ6XVfG27u0Mydjk=
github.com/gocarina/gocsv v0.0.0-20190821091544-020a928c6f4e/go.mod h1:/oj50ZdPq/cUjA02lMZhijk5kR31SEydKyqah1OgBuo=
github.com/gocarina/gocsv v0.0.0-20201208093247-67c824bc04d4/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI=
github.com/goccy/go-yaml v1.8.1/go.mod h1:wS4gNoLalDSJxo/SpngzPQ2BN4uuZVLCmbM4S3vd4+Y=
github.com/goccy/go-yaml v1.8.2 h1:gDYrSN12XK/wQTFjxWIgcIqjNCV/Zb5V09M7cq+dbCs=
github.com/goccy/go-yaml v1.8.2/go.mod h1:wS4gNoLalDSJxo/SpngzPQ2BN4uuZVLCmbM4S3vd4+Y=
github.com/goccy/go-yaml v1.8.4 h1:AOEdR7aQgbgwHznGe3BLkDQVujxCPUpHOZZcQcp8Y3M=
github.com/goccy/go-yaml v1.8.4/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
@@ -387,7 +341,6 @@ github.com/golang/protobuf v0.0.0-20181025225059-d3de96c4c28e/go.mod h1:Qd/q+1AK
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
@@ -399,21 +352,17 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
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/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3 h1:x95R7cp+rSeeqAMI2knLtQ0DKlaBhv2NrtrOvafPHRo=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@@ -441,14 +390,11 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/wire v0.3.0 h1:imGQZGEVEHpje5056+K+cgdO72p0LQv2xIIFXNGUf60=
github.com/google/wire v0.3.0/go.mod h1:i1DMg/Lu8Sz5yYl25iOdmc5CT5qusaa+zmRWs16741s=
github.com/google/wire v0.4.0 h1:kXcsA/rIGzJImVqPdhfnr6q0xsS9gU0515q1EPpJ9fE=
github.com/google/wire v0.4.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3SQ2HBHWsJUfbNBiTXJDeW2QDxw9AQ0=
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
@@ -459,7 +405,6 @@ github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2z
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
@@ -468,8 +413,6 @@ github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grokify/html-strip-tags-go v0.0.0-20190424092004-025bd760b278 h1:DZo48DQFIDo/YWjUeFip1dfJztBhRuaxfUnPd+gAfcs=
github.com/grokify/html-strip-tags-go v0.0.0-20190424092004-025bd760b278/go.mod h1:Xk7G0nwBiIloTMbLddk4WWJOqi4i/JLhadLd0HUXO30=
github.com/grokify/html-strip-tags-go v0.0.0-20200923094847-079d207a09f1 h1:ETqBvCd8SQaNCb0TwQ5A+IlkecGuwjW1EUTxK9if+UE=
github.com/grokify/html-strip-tags-go v0.0.0-20200923094847-079d207a09f1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
@@ -506,15 +449,12 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c h1:aY2hhxLhjEAbfXOx2nRJxCXezC6CO2V/yN+OCr1srtk=
github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/htcat/htcat v1.0.2 h1:zro95dGwkKDeZOgq9ei+9szd5qurGxBGfHY8hRehA7k=
github.com/htcat/htcat v1.0.2/go.mod h1:i8ViQbjSi2+lJzM6Lx20FIxHENCz6mzJglK3HH06W3s=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/log15 v0.0.0-20180818164646-67afb5ed74ec h1:CGkYB1Q7DSsH/ku+to+foV4agt2F2miquaLUgF6L178=
github.com/inconshreveable/log15 v0.0.0-20180818164646-67afb5ed74ec/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
github.com/inconshreveable/log15 v0.0.0-20200109203555-b30bc20e4fd1 h1:KUDFlmBg2buRWNzIcwLlKvfcnujcHQRQ1As1LoaCLAM=
github.com/inconshreveable/log15 v0.0.0-20200109203555-b30bc20e4fd1/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
github.com/inconshreveable/log15 v0.0.0-20201112154412-8562bdadbbac h1:n1DqxAo4oWPMvH1+v+DLYlMCecgumhhgnxAPdqDIFHI=
github.com/inconshreveable/log15 v0.0.0-20201112154412-8562bdadbbac/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
@@ -524,9 +464,7 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i
github.com/jesseduffield/gocui v0.3.0 h1:l7wH8MKR2p+ozuZdtdhQiX7szILbv50vkMk1tg2+xow=
github.com/jesseduffield/gocui v0.3.0/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jinzhu/gorm v1.9.10 h1:HvrsqdhCW78xpJF67g1hMxS6eCToo9PZH4LDB8WKPac=
github.com/jinzhu/gorm v1.9.10/go.mod h1:Kh6hTsSGffh4ui079FHrR5Gg+5D0hgihqDcsDN2BBJY=
github.com/jinzhu/gorm v1.9.12 h1:Drgk1clyWT9t9ERbzHza6Mj/8FY/CqMyVzOiHviMo6Q=
github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs=
github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=
github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
@@ -534,7 +472,6 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
@@ -570,27 +507,24 @@ github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d/go.mod h1:
github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936 h1:HDjRqotkViMNcGMGicb7cgxklx8OwnjtCBmyWEqrRvM=
github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936/go.mod h1:i4sF0l1fFnY1aiw08QQSwVAFxHEm311Me3WsU/X7nL0=
github.com/knqyf263/go-rpmdb v0.0.0-20190501070121-10a1c42a10dc/go.mod h1:MrSSvdMpTSymaQWk1yFr9sxFSyQmKMj6jkbvGrchBV8=
github.com/knqyf263/go-rpmdb v0.0.0-20201028125045-8b9bad79f21b/go.mod h1:ovL3LB9TuA8LoLiEEMNm2fusIwoh+kM+lnhD1QlWAtA=
github.com/knqyf263/go-rpmdb v0.0.0-20201215100354-a9e3110d8ee1/go.mod h1:RDPNeIkU5NWXtt0OMEoILyxwUC/DyXeRtK295wpqSi0=
github.com/knqyf263/go-version v1.1.1 h1:+MpcBC9b7rk5ihag8Y/FLG8get1H2GjniwKQ+9DxI2o=
github.com/knqyf263/go-version v1.1.1/go.mod h1:0tBvHvOBSf5TqGNcY+/ih9o8qo3R16iZCpB9rP0D3VM=
github.com/knqyf263/gost v0.1.7 h1:mEbdwiIkEy3uU0wDBpr1y7dciAay7paxpRlGKfhEdr8=
github.com/knqyf263/gost v0.1.7/go.mod h1:rlf9JZR6qMyXtnz0bqyMIexDoYhFt+on0FK+OL7MhmU=
github.com/knqyf263/nested v0.0.1/go.mod h1:zwhsIhMkBg90DTOJQvxPkKIypEHPYkgWHs4gybdlUmk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kotakanbe/go-cve-dictionary v0.5.6 h1:xTq6AcWYkmdqHCwL5DiqH+/C0Ga4IHlZdQDWVLJeelo=
github.com/kotakanbe/go-cve-dictionary v0.5.6/go.mod h1:CtZPPDJUrU/+3TvUcD1xFHVWWlM9SSEZYRZ11pblmDQ=
github.com/kotakanbe/go-cve-dictionary v0.5.7 h1:HLJBp4fJ2tecJRSv/4eDmjXHKXshZ7AhSCxM1qqayPM=
github.com/kotakanbe/go-cve-dictionary v0.5.7/go.mod h1:CtZPPDJUrU/+3TvUcD1xFHVWWlM9SSEZYRZ11pblmDQ=
github.com/kotakanbe/go-pingscanner v0.1.0 h1:VG4/9l0i8WeToXclj7bIGoAZAu7a07Z3qmQiIfU0gT0=
github.com/kotakanbe/go-pingscanner v0.1.0/go.mod h1:/761QZzuZFcfN8h/1QuawUA+pKukp3qcNj5mxJCOiAk=
github.com/kotakanbe/goval-dictionary v0.2.16 h1:AmlzIWS5LiMnYyVDXxPqtzbGVD8LrPNGj4uSE8YrcW8=
github.com/kotakanbe/goval-dictionary v0.2.16/go.mod h1:sNRtiJrWJg92zrT9vucHI5C1R8rCBW32X1nGAOQ2sK8=
github.com/kotakanbe/goval-dictionary v0.3.1 h1:79gv75CqViYlzDAkmlPGS2tbEwJJICwKSn1sARtL6xY=
github.com/kotakanbe/goval-dictionary v0.3.1/go.mod h1:1FcXF2+KKTN+IwLbU+iUsufgUrlpurk8nFJUThksEiQ=
github.com/kotakanbe/logrus-prefixed-formatter v0.0.0-20180123152602-928f7356cb96 h1:xNVK0mQJdQjw+QYeaMM4G6fvucWr8rTGGIhlPakx1wU=
github.com/kotakanbe/logrus-prefixed-formatter v0.0.0-20180123152602-928f7356cb96/go.mod h1:ljq48H1V+0Vh0u7ucA3LjR4AfkAeCpxrf7LaaCk8Vmo=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
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/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@@ -599,9 +533,8 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
github.com/labstack/gommon v0.2.9/go.mod h1:E8ZTmW9vw5az5/ZyHWCp0Lw4OH2ecsaBP1C/NKavGG4=
github.com/labstack/echo/v4 v4.1.17/go.mod h1:Tn2yRQL/UclUalpb5rPdXDevbkJ+lp/2svdyFBg6CHQ=
github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
@@ -610,12 +543,9 @@ github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgx
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8=
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.4 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY=
github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@@ -623,9 +553,7 @@ github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw=
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
@@ -634,7 +562,6 @@ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx
github.com/mattn/go-isatty v0.0.5/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.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
@@ -644,16 +571,14 @@ github.com/mattn/go-runewidth v0.0.0-20181025052659-b20a3daf6a39/go.mod h1:LwmH8
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=
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q=
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
github.com/mattn/go-sqlite3 v1.14.5 h1:1IdxlwTNazvbKJQSxoJ5/9ECbEeaTTyeU7sEAZ5KKTQ=
github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
@@ -673,8 +598,6 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu
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.4.0 h1:7ks8ZkOP5/ujthUsT07rNv+nkLXCQWKNHuwzOAesEks=
github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
@@ -682,9 +605,6 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
github.com/mozqnet/go-exploitdb v0.1.2 h1:y6R+Lm64QtMl8Yxg7kHRY1f9XighjJwTOXy8BKKahI4=
github.com/mozqnet/go-exploitdb v0.1.2/go.mod h1:p1qSGf6+HcTTHxGBCV2q4/5HZd4WJFvdPncIGN2qrHU=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@@ -693,8 +613,8 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWb
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nlopes/slack v0.6.0 h1:jt0jxVQGhssx1Ib7naAOZEZcGdtIhTzkP0nopK0AsRA=
github.com/nlopes/slack v0.6.0/go.mod h1:JzQ9m3PMAqcpeCam7UaHSuBuupz7CmpjehYMayT6YOk=
github.com/nsf/termbox-go v0.0.0-20201124104050-ed494de23a00 h1:Rl8NelBe+n7SuLbJyw13ho7CGWUt2BjGGKIoreCWQ/c=
github.com/nsf/termbox-go v0.0.0-20201124104050-ed494de23a00/go.mod h1:T0cTdVuOwf7pHQNtfhnEbzHbcNyCEcVU4YPpouCbVxo=
github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1 h1:lh3PyZvY+B9nFliSGTn5uFuqQQJGuNrD0MLCokv09ag=
github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
@@ -707,7 +627,6 @@ github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M=
@@ -716,34 +635,25 @@ github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGV
github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
github.com/onsi/gomega v1.10.4 h1:NiTx7EEvBzu9sFOD1zORteLSt3o8gnlvZZwSE9TnY9U=
github.com/onsi/gomega v1.10.4/go.mod h1:g/HbgYopi++010VEqkFgJHKC09uJiW9UkXvMUuKHUCQ=
github.com/open-policy-agent/opa v0.21.1/go.mod h1:cZaTfhxsj7QdIiUI0U9aBtOLLTqVNe+XE60+9kZKLHw=
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6 h1:yN8BPXVwMBAm3Cuvh1L5XE8XpvYRMdsVLd82ILprhUU=
github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/parnurzeal/gorequest v0.2.15/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE=
github.com/parnurzeal/gorequest v0.2.16 h1:T/5x+/4BT+nj+3eSknXmCTnEVGSzFzPGdpqmUVVZXHQ=
github.com/parnurzeal/gorequest v0.2.16/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
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.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM=
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/peterh/liner v0.0.0-20170211195444-bf27d3ba8e1d/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
github.com/peterhellberg/link v1.0.0/go.mod h1:gtSlOT4jmkY8P47hbTc8PTgiDDWpdPbFYl75keYyBB8=
@@ -789,10 +699,9 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/saracen/walker v0.0.0-20191201085201-324a081bae7e/go.mod h1:G0Z6yVPru183i2MuRJx1DcR4dgIZtLcTdaaE/pC1BJU=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
@@ -802,7 +711,6 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/simplereach/timeutils v1.2.0/go.mod h1:VVbQDfN/FHRZa1LSqcwo4kNZ62OOyqLLGQKYB3pB0Q8=
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
@@ -812,7 +720,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.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
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.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs=
github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
@@ -829,19 +736,14 @@ github.com/spf13/afero v1.5.1 h1:VHu76Lk0LSP1x254maIu2bplkWpfBWI+B+6fdoZprcg=
github.com/spf13/afero v1.5.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
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=
github.com/spf13/cobra v0.0.0-20181021141114-fe5e611709b0/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
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/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4=
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
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 v0.0.0-20181024212040-082b515c9490/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
@@ -849,16 +751,13 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
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/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As=
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
@@ -866,7 +765,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.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@@ -874,9 +772,7 @@ github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/takuzoo3868/go-msfdb v0.1.3 h1:h2EYoxS2Y2fsn1yrKwc59Dw8xmEONJlhG6nQYh4bFOg=
github.com/takuzoo3868/go-msfdb v0.1.3/go.mod h1:VdYWAq8ikjJGOx0+srShozXsd1x0aHZ+yqAB59tJQsI=
github.com/tealeg/xlsx v1.0.3 h1:BXsDIQYBPq2HgbwUxrsVXIrnO0BDxmsdUfHSfvwfBuQ=
github.com/tealeg/xlsx v1.0.3/go.mod h1:uxu5UY2ovkuRPWKQ8Q7JG0JbSivrISjdPzZQKeo74mA=
github.com/testcontainers/testcontainers-go v0.3.1 h1:KZkEKNfnlsipJblzGCz6fmzd+0DzJ3djulYrislG3Zw=
github.com/testcontainers/testcontainers-go v0.3.1/go.mod h1:br7bkzIukhPSIjy07Ma3OuXjjFvl2jm7CDU0LQNsqLw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
@@ -887,18 +783,19 @@ github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljT
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/valyala/fasttemplate v1.1.0 h1:RZqt0yGBsps8NGvLSGW804QQqCUYYLsaOjTVHy1Ocw4=
github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/vdemeester/k8s-pkg-credentialprovider v1.17.4/go.mod h1:inCTmtUdr5KJbreVojo06krnTgaeAz/Z7lynpPk/Q2c=
github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
github.com/vulsio/go-exploitdb v0.1.4 h1:i9ce9xLa12VAKj9Oq5QfJK6bY9HVcxFsVIzBgIhPJ78=
github.com/vulsio/go-exploitdb v0.1.4/go.mod h1:CMNvEqjsVa2IMGsoyDV4QKXlOSWIaB6b6+29F6YmJWU=
github.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
@@ -909,7 +806,6 @@ github.com/ymomoi/goval-parser v0.0.0-20170813122243-0a0be1dd9d08/go.mod h1:ox1N
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
@@ -922,33 +818,25 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/otel v0.14.0 h1:YFBEfjCk9MTjaytCNSUkp9Q8lF7QJezA06T71FbQxLQ=
go.opentelemetry.io/otel v0.14.0/go.mod h1:vH5xEuwy7Rts0GNtsCW3HYQoZDY+OmBJ6t1bFGGlxgw=
go.opentelemetry.io/otel v0.15.0 h1:CZFy2lPhxd4HlhZnYK8gRyDotksO3Ip9rBweY1vVYJw=
go.opentelemetry.io/otel v0.15.0/go.mod h1:e4GKElweB8W2gWUqbghw0B8t5MCTccc9212eNHnOHwA=
go.opentelemetry.io/otel v0.16.0 h1:uIWEbdeb4vpKPGITLsRVUS44L5oDbDUCZxn8lkxhmgw=
go.opentelemetry.io/otel v0.16.0/go.mod h1:e4GKElweB8W2gWUqbghw0B8t5MCTccc9212eNHnOHwA=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.5.1 h1:rsqfU5vBkVknbhUGbAUwQKR2H4ItV8tjJ+6kJX4cxHM=
go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.4.0 h1:f3WCSC2KzAcBXGATIxAB1E2XuCpNU255wNKZ505qi3E=
go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM=
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -968,9 +856,11 @@ golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620 h1:3wPMTskHO3+O6jqTEXyFcsnuxMQOqYSaHsDxcbUXpqA=
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -996,7 +886,6 @@ golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHl
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=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
@@ -1004,7 +893,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=
@@ -1041,35 +929,32 @@ golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
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-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201216054612-986b41b23924 h1:QsnDpLLOKwHBBDa8nDws4DYNc/ryVW2vCpxCs09d4PY=
golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5 h1:Lm4OryKCca1vehdsWogr9N4t7NfZxLbJoc/H0w4K4S4=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210125201302-af13f521f196 h1:w0u30BeG/TALEc6xVf1Klaz2+etRR4K6jxhRkWCqt4g=
golang.org/x/oauth2 v0.0.0-20210125201302-af13f521f196/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1078,7 +963,6 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1102,7 +986,6 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1129,37 +1012,34 @@ golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775 h1:TC0v2RSO1u2kn1ZugjrFXkRZAEaqMN/RW+OTZkBzmLE=
golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201006155630-ac719f4daadf/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201218084310-7d0127a74742 h1:+CBz4km/0KPU3RGTwARGh/noP3bEwtHcq+0YcBQM2JQ=
golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 h1:nVuTkr9L6Bq62qpUqKo/RnZCFfzDBL0bYo6w9OJUqZY=
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
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/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
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/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1206,7 +1086,6 @@ golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapK
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/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=
@@ -1217,17 +1096,14 @@ golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWc
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200609164405-eb789aa7ce50/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d h1:W07d4xkoAUSNOkOzdzXCdFGxT7o2rW4q8M34tB2i//k=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58 h1:1Bs6RVeBFtLZ8Yi1Hk07DiOqzvwLD/4hln4iahvFlag=
golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
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=
@@ -1257,9 +1133,8 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180924164928-221a8d4f7494/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
@@ -1271,7 +1146,6 @@ google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRn
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
@@ -1306,7 +1180,6 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0 h1:bO/TA4OxCOummhSf10siHuG7vJOiwh7SpRpFZDkOgl4=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
@@ -1317,7 +1190,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/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
@@ -1328,7 +1200,6 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/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 h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -1336,21 +1207,18 @@ gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS
gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk=
gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
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/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
gopkg.in/go-playground/validator.v9 v9.30.0 h1:Wk0Z37oBmKj9/n+tPyBHZmeL19LaCoK3Qq48VwYENss=
gopkg.in/go-playground/validator.v9 v9.30.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
gopkg.in/go-playground/validator.v9 v9.31.0 h1:bmXmP2RSNtFES+bn4uYuHT7iJFJv7Vj+an+ZQdDaD1M=
gopkg.in/go-playground/validator.v9 v9.31.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
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.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
@@ -1369,9 +1237,7 @@ 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.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
@@ -1386,12 +1252,10 @@ 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=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.1.0 h1:AWNL1W1i7f0wNZ8VwOKNJ0sliKvOF/adn0EHenfUh+c=
honnef.co/go/tools v0.1.0/go.mod h1:XtegFAyX/PfluP4921rXU5IkjkqBCDnUq4W8VCIoKvM=
k8s.io/api v0.17.4/go.mod h1:5qxx6vjmwUVG2nHQTKGlLts8Tbok8PzHl4vHtVFuZCA=
k8s.io/apimachinery v0.17.4/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g=
k8s.io/apiserver v0.17.4/go.mod h1:5ZDQ6Xr5MNBxyi3iUZXS84QOhZl+W7Oq2us/29c0j9I=
@@ -1410,8 +1274,9 @@ k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKf
k8s.io/legacy-cloud-providers v0.17.4/go.mod h1:FikRNoD64ECjkxO36gkDgJeiQWwyZTuBkhu+yxOc1Js=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
k8s.io/utils v0.0.0-20201005171033-6301aaf42dc7/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw=
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20210111153108-fddb29f9d009 h1:0T5IaWHO3sJTEmCP6mUlBvMukxPKUQWqiI/YuiBNMiQ=
k8s.io/utils v0.0.0-20210111153108-fddb29f9d009/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=

View File

@@ -3,14 +3,8 @@
package gost
import (
"fmt"
"net/http"
cnf "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/knqyf263/gost/db"
"github.com/parnurzeal/gorequest"
"golang.org/x/xerrors"
)
// Base is a base struct
@@ -19,35 +13,5 @@ type Base struct {
// FillCVEsWithRedHat fills cve information that has in Gost
func (b Base) FillCVEsWithRedHat(driver db.DB, r *models.ScanResult) error {
return RedHat{}.fillFixed(driver, r)
}
// CheckHTTPHealth do health check
func (b Base) CheckHTTPHealth() error {
if !cnf.Conf.Gost.IsFetchViaHTTP() {
return nil
}
url := fmt.Sprintf("%s/health", cnf.Conf.Gost.URL)
var errs []error
var resp *http.Response
resp, _, errs = gorequest.New().Get(url).End()
// resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
// resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return xerrors.Errorf("Failed to connect to gost server. url: %s, errs: %w", url, errs)
}
return nil
}
// CheckIfGostFetched checks if oval entries are in DB by family, release.
func (b Base) CheckIfGostFetched(driver db.DB, osFamily string) (fetched bool, err error) {
//TODO
return true, nil
}
// CheckIfGostFresh checks if oval entries are fresh enough
func (b Base) CheckIfGostFresh(driver db.DB, osFamily string) (ok bool, err error) {
//TODO
return true, nil
return RedHat{}.fillCvesWithRedHatAPI(driver, r)
}

View File

@@ -12,12 +12,6 @@ import (
type Client interface {
DetectUnfixed(db.DB, *models.ScanResult, bool) (int, error)
FillCVEsWithRedHat(db.DB, *models.ScanResult) error
//TODO implement
// CheckHTTPHealth() error
// CheckIfGostFetched checks if Gost entries are fetched
// CheckIfGostFetched(db.DB, string, string) (bool, error)
// CheckIfGostFresh(db.DB, string, string) (bool, error)
}
// NewClient make Client by family

View File

@@ -21,10 +21,47 @@ type RedHat struct {
// DetectUnfixed fills cve information that has in Gost
func (red RedHat) DetectUnfixed(driver db.DB, r *models.ScanResult, ignoreWillNotFix bool) (nCVEs int, err error) {
return red.fillUnfixed(driver, r, ignoreWillNotFix)
return red.detectUnfixed(driver, r, ignoreWillNotFix)
}
func (red RedHat) fillFixed(driver db.DB, r *models.ScanResult) error {
func (red RedHat) detectUnfixed(driver db.DB, r *models.ScanResult, ignoreWillNotFix bool) (nCVEs int, err error) {
if config.Conf.Gost.IsFetchViaHTTP() {
prefix, _ := util.URLPathJoin(config.Conf.Gost.URL,
"redhat", major(r.Release), "pkgs")
responses, err := getAllUnfixedCvesViaHTTP(r, prefix)
if err != nil {
return 0, err
}
for _, res := range responses {
// CVE-ID: RedhatCVE
cves := map[string]gostmodels.RedhatCVE{}
if err := json.Unmarshal([]byte(res.json), &cves); err != nil {
return 0, err
}
for _, cve := range cves {
if newly := red.setUnfixedCveToScanResult(&cve, r); newly {
nCVEs++
}
}
}
} else {
if driver == nil {
return 0, nil
}
for _, pack := range r.Packages {
// CVE-ID: RedhatCVE
cves := driver.GetUnfixedCvesRedhat(major(r.Release), pack.Name, ignoreWillNotFix)
for _, cve := range cves {
if newly := red.setUnfixedCveToScanResult(&cve, r); newly {
nCVEs++
}
}
}
}
return nCVEs, nil
}
func (red RedHat) fillCvesWithRedHatAPI(driver db.DB, r *models.ScanResult) error {
cveIDs := []string{}
for cveID, vuln := range r.ScannedCves {
if _, ok := vuln.CveContents[models.RedHatAPI]; ok {
@@ -48,131 +85,68 @@ func (red RedHat) fillFixed(driver db.DB, r *models.ScanResult) error {
if redCve.ID == 0 {
continue
}
cveCont, mitigations := red.ConvertToModel(&redCve)
v, ok := r.ScannedCves[res.request.cveID]
if ok {
if v.CveContents == nil {
v.CveContents = models.NewCveContents(*cveCont)
} else {
v.CveContents[models.RedHatAPI] = *cveCont
}
} else {
v = models.VulnInfo{
CveID: cveCont.CveID,
CveContents: models.NewCveContents(*cveCont),
Confidences: models.Confidences{models.RedHatAPIMatch},
}
}
v.Mitigations = append(v.Mitigations, mitigations...)
r.ScannedCves[res.request.cveID] = v
red.setFixedCveToScanResult(&redCve, r)
}
} else {
if driver == nil {
return nil
}
for cveID, redCve := range driver.GetRedhatMulti(cveIDs) {
for _, redCve := range driver.GetRedhatMulti(cveIDs) {
if len(redCve.Name) == 0 {
continue
}
cveCont, mitigations := red.ConvertToModel(&redCve)
v, ok := r.ScannedCves[cveID]
if ok {
if v.CveContents == nil {
v.CveContents = models.NewCveContents(*cveCont)
} else {
v.CveContents[models.RedHatAPI] = *cveCont
}
} else {
v = models.VulnInfo{
CveID: cveCont.CveID,
CveContents: models.NewCveContents(*cveCont),
Confidences: models.Confidences{models.RedHatAPIMatch},
}
}
v.Mitigations = append(v.Mitigations, mitigations...)
r.ScannedCves[cveID] = v
red.setFixedCveToScanResult(&redCve, r)
}
}
return nil
}
func (red RedHat) fillUnfixed(driver db.DB, r *models.ScanResult, ignoreWillNotFix bool) (nCVEs int, err error) {
if config.Conf.Gost.IsFetchViaHTTP() {
prefix, _ := util.URLPathJoin(config.Conf.Gost.URL,
"redhat", major(r.Release), "pkgs")
responses, err := getAllUnfixedCvesViaHTTP(r, prefix)
if err != nil {
return 0, err
}
for _, res := range responses {
// CVE-ID: RedhatCVE
cves := map[string]gostmodels.RedhatCVE{}
if err := json.Unmarshal([]byte(res.json), &cves); err != nil {
return 0, err
}
for _, cve := range cves {
cveCont, mitigations := red.ConvertToModel(&cve)
v, ok := r.ScannedCves[cve.Name]
if ok {
if v.CveContents == nil {
v.CveContents = models.NewCveContents(*cveCont)
} else {
v.CveContents[models.RedHatAPI] = *cveCont
}
} else {
v = models.VulnInfo{
CveID: cveCont.CveID,
CveContents: models.NewCveContents(*cveCont),
Confidences: models.Confidences{models.RedHatAPIMatch},
}
nCVEs++
}
v.Mitigations = append(v.Mitigations, mitigations...)
pkgStats := red.mergePackageStates(v,
cve.PackageState, r.Packages, r.Release)
if 0 < len(pkgStats) {
v.AffectedPackages = pkgStats
r.ScannedCves[cve.Name] = v
}
}
func (red RedHat) setFixedCveToScanResult(cve *gostmodels.RedhatCVE, r *models.ScanResult) {
cveCont, mitigations := red.ConvertToModel(cve)
v, ok := r.ScannedCves[cveCont.CveID]
if ok {
if v.CveContents == nil {
v.CveContents = models.NewCveContents(*cveCont)
} else {
v.CveContents[models.RedHatAPI] = *cveCont
}
} else {
if driver == nil {
return 0, nil
}
for _, pack := range r.Packages {
// CVE-ID: RedhatCVE
cves := driver.GetUnfixedCvesRedhat(major(r.Release), pack.Name, ignoreWillNotFix)
for _, cve := range cves {
cveCont, mitigations := red.ConvertToModel(&cve)
v, ok := r.ScannedCves[cve.Name]
if ok {
if v.CveContents == nil {
v.CveContents = models.NewCveContents(*cveCont)
} else {
v.CveContents[models.RedHatAPI] = *cveCont
}
} else {
v = models.VulnInfo{
CveID: cveCont.CveID,
CveContents: models.NewCveContents(*cveCont),
Confidences: models.Confidences{models.RedHatAPIMatch},
}
nCVEs++
}
v.Mitigations = append(v.Mitigations, mitigations...)
pkgStats := red.mergePackageStates(v,
cve.PackageState, r.Packages, r.Release)
if 0 < len(pkgStats) {
v.AffectedPackages = pkgStats
r.ScannedCves[cve.Name] = v
}
}
v = models.VulnInfo{
CveID: cveCont.CveID,
CveContents: models.NewCveContents(*cveCont),
Confidences: models.Confidences{models.RedHatAPIMatch},
}
}
return nCVEs, nil
v.Mitigations = append(v.Mitigations, mitigations...)
r.ScannedCves[cveCont.CveID] = v
}
func (red RedHat) setUnfixedCveToScanResult(cve *gostmodels.RedhatCVE, r *models.ScanResult) (newly bool) {
cveCont, mitigations := red.ConvertToModel(cve)
v, ok := r.ScannedCves[cve.Name]
if ok {
if v.CveContents == nil {
v.CveContents = models.NewCveContents(*cveCont)
} else {
v.CveContents[models.RedHatAPI] = *cveCont
}
} else {
v = models.VulnInfo{
CveID: cveCont.CveID,
CveContents: models.NewCveContents(*cveCont),
Confidences: models.Confidences{models.RedHatAPIMatch},
}
newly = true
}
v.Mitigations = append(v.Mitigations, mitigations...)
pkgStats := red.mergePackageStates(v,
cve.PackageState, r.Packages, r.Release)
if 0 < len(pkgStats) {
v.AffectedPackages = pkgStats
r.ScannedCves[cve.Name] = v
}
return
}
func (red RedHat) mergePackageStates(v models.VulnInfo, ps []gostmodels.RedhatPackageState, installed models.Packages, release string) (pkgStats models.PackageFixStatuses) {

View File

@@ -154,13 +154,13 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er
count, retryMax := 0, 3
f := func() (err error) {
// resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
resp, body, errs = gorequest.New().Get(url).End()
resp, body, errs = gorequest.New().Timeout(10 * time.Second).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
count++
if count == retryMax {
return nil
}
return xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %w", url, resp, errs)
return xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %s", url, resp, errs)
}
return nil
}

View File

@@ -1,6 +1,7 @@
package models
import (
"strings"
"time"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
@@ -58,7 +59,7 @@ func (v CveContents) PrimarySrcURLs(lang, myFamily, cveID string) (values []CveC
}
}
order := CveContentTypes{Nvd, NvdXML, NewCveContentType(myFamily)}
order := CveContentTypes{Nvd, NewCveContentType(myFamily), GitHub}
for _, ctype := range order {
if cont, found := v[ctype]; found {
if cont.SourceLink == "" {
@@ -74,7 +75,7 @@ func (v CveContents) PrimarySrcURLs(lang, myFamily, cveID string) (values []CveC
}
}
if len(values) == 0 {
if len(values) == 0 && strings.HasPrefix(cveID, "CVE") {
return []CveContentStr{{
Type: Nvd,
Value: "https://nvd.nist.gov/vuln/detail/" + cveID,
@@ -83,7 +84,7 @@ func (v CveContents) PrimarySrcURLs(lang, myFamily, cveID string) (values []CveC
return values
}
// PrimarySrcURLs returns link of source
// PatchURLs returns link of patch
func (v CveContents) PatchURLs() (urls []string) {
cont, found := v[Nvd]
if !found {
@@ -228,8 +229,6 @@ type CveContentType string
// NewCveContentType create CveContentType
func NewCveContentType(name string) CveContentType {
switch name {
case "nvdxml":
return NvdXML
case "nvd":
return Nvd
case "jvn":
@@ -249,20 +248,19 @@ func NewCveContentType(name string) CveContentType {
case "microsoft":
return Microsoft
case "wordpress":
return WPVulnDB
return WpScan
case "amazon":
return Amazon
case "trivy":
return Trivy
case "GitHub":
return Trivy
default:
return Unknown
}
}
const (
// NvdXML is NvdXML
NvdXML CveContentType = "nvdxml"
// Nvd is Nvd JSON
Nvd CveContentType = "nvd"
@@ -296,12 +294,15 @@ const (
// Microsoft is Microsoft
Microsoft CveContentType = "microsoft"
// WPVulnDB is WordPress
WPVulnDB CveContentType = "wpvulndb"
// WpScan is WordPress
WpScan CveContentType = "wpscan"
// Trivy is Trivy
Trivy CveContentType = "trivy"
// GitHub is GitHub Security Alerts
GitHub CveContentType = "github"
// Unknown is Unknown
Unknown CveContentType = "unknown"
)
@@ -312,7 +313,6 @@ type CveContentTypes []CveContentType
// AllCveContetTypes has all of CveContentTypes
var AllCveContetTypes = CveContentTypes{
Nvd,
NvdXML,
Jvn,
RedHat,
RedHatAPI,
@@ -321,8 +321,9 @@ var AllCveContetTypes = CveContentTypes{
Amazon,
SUSE,
DebianSecurityTracker,
WPVulnDB,
WpScan,
Trivy,
GitHub,
}
// Except returns CveContentTypes except for given args

View File

@@ -52,10 +52,6 @@ func TestSourceLinks(t *testing.T) {
Type: RedHat,
SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
},
NvdXML: {
Type: NvdXML,
SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
},
Nvd: {
Type: Nvd,
References: []Reference{
@@ -85,10 +81,6 @@ func TestSourceLinks(t *testing.T) {
Type: Nvd,
Value: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
},
{
Type: NvdXML,
Value: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
},
{
Type: RedHat,
Value: "https://access.redhat.com/security/cve/CVE-2017-6074",
@@ -113,17 +105,9 @@ func TestSourceLinks(t *testing.T) {
Type: RedHat,
SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
},
NvdXML: {
Type: NvdXML,
SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
},
},
},
out: []CveContentStr{
{
Type: NvdXML,
Value: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
},
{
Type: RedHat,
Value: "https://access.redhat.com/security/cve/CVE-2017-6074",

View File

@@ -30,6 +30,14 @@ func (lss LibraryScanners) Find(path, name string) map[string]types.Library {
return filtered
}
// Total returns total count of pkgs
func (lss LibraryScanners) Total() (total int) {
for _, lib := range lss {
total += len(lib.Libs)
}
return
}
// LibraryScanner has libraries information
type LibraryScanner struct {
Path string

View File

@@ -63,13 +63,13 @@ func (ps Packages) FindOne(f func(Package) bool) (string, Package, bool) {
}
// FindByFQPN search a package by Fully-Qualified-Package-Name
func (ps Packages) FindByFQPN(nameVerRelArc string) (*Package, error) {
func (ps Packages) FindByFQPN(nameVerRel string) (*Package, error) {
for _, p := range ps {
if nameVerRelArc == p.FQPN() {
if nameVerRel == p.FQPN() {
return &p, nil
}
}
return nil, xerrors.Errorf("Failed to find the package: %s", nameVerRelArc)
return nil, xerrors.Errorf("Failed to find the package: %s", nameVerRel)
}
// Package has installed binary packages.
@@ -81,7 +81,7 @@ type Package struct {
NewRelease string `json:"newRelease"`
Arch string `json:"arch"`
Repository string `json:"repository"`
Changelog Changelog `json:"changelog"`
Changelog *Changelog `json:"changelog,omitempty"`
AffectedProcs []AffectedProcess `json:",omitempty"`
NeedRestartProcs []NeedRestartProcess `json:",omitempty"`
}
@@ -96,9 +96,6 @@ func (p Package) FQPN() string {
if p.Release != "" {
fqpn += fmt.Sprintf("-%s", p.Release)
}
if p.Arch != "" {
fqpn += fmt.Sprintf(".%s", p.Arch)
}
return fqpn
}

View File

@@ -287,7 +287,7 @@ func TestPackage_FormatVersionFromTo(t *testing.T) {
NewRelease: tt.fields.NewRelease,
Arch: tt.fields.Arch,
Repository: tt.fields.Repository,
Changelog: tt.fields.Changelog,
Changelog: &tt.fields.Changelog,
AffectedProcs: tt.fields.AffectedProcs,
NeedRestartProcs: tt.fields.NeedRestartProcs,
}

View File

@@ -48,7 +48,8 @@ type ScanResult struct {
RunningKernel Kernel `json:"runningKernel"`
Packages Packages `json:"packages"`
SrcPackages SrcPackages `json:",omitempty"`
WordPressPackages *WordPressPackages `json:",omitempty"`
EnabledDnfModules []string `json:"enabledDnfModules,omitempty"` // for dnf modules
WordPressPackages WordPressPackages `json:",omitempty"`
LibraryScanners LibraryScanners `json:"libraries,omitempty"`
CweDict CweDict `json:"cweDict,omitempty"`
Optional map[string]interface{} `json:",omitempty"`
@@ -127,13 +128,7 @@ type Kernel struct {
// FilterByCvssOver is filter function.
func (r ScanResult) FilterByCvssOver(over float64) ScanResult {
filtered := r.ScannedCves.Find(func(v VulnInfo) bool {
v2Max := v.MaxCvss2Score()
v3Max := v.MaxCvss3Score()
max := v2Max.Value.Score
if max < v3Max.Value.Score {
max = v3Max.Value.Score
}
if over <= max {
if over <= v.MaxCvssScore().Value.Score {
return true
}
return false
@@ -144,11 +139,12 @@ func (r ScanResult) FilterByCvssOver(over float64) ScanResult {
// FilterIgnoreCves is filter function.
func (r ScanResult) FilterIgnoreCves() ScanResult {
ignoreCves := []string{}
if len(r.Container.Name) == 0 {
//TODO pass by args
ignoreCves = config.Conf.Servers[r.ServerName].IgnoreCves
} else {
//TODO pass by args
if s, ok := config.Conf.Servers[r.ServerName]; ok {
if con, ok := s.Containers[r.Container.Name]; ok {
ignoreCves = con.IgnoreCves
@@ -175,8 +171,8 @@ func (r ScanResult) FilterIgnoreCves() ScanResult {
}
// FilterUnfixed is filter function.
func (r ScanResult) FilterUnfixed() ScanResult {
if !config.Conf.IgnoreUnfixed {
func (r ScanResult) FilterUnfixed(ignoreUnfixed bool) ScanResult {
if !ignoreUnfixed {
return r
}
filtered := r.ScannedCves.Find(func(v VulnInfo) bool {
@@ -198,6 +194,7 @@ func (r ScanResult) FilterUnfixed() ScanResult {
func (r ScanResult) FilterIgnorePkgs() ScanResult {
var ignorePkgsRegexps []string
if len(r.Container.Name) == 0 {
//TODO pass by args
ignorePkgsRegexps = config.Conf.Servers[r.ServerName].IgnorePkgsRegexp
} else {
if s, ok := config.Conf.Servers[r.ServerName]; ok {
@@ -250,8 +247,8 @@ func (r ScanResult) FilterIgnorePkgs() ScanResult {
}
// FilterInactiveWordPressLibs is filter function.
func (r ScanResult) FilterInactiveWordPressLibs() ScanResult {
if !config.Conf.Servers[r.ServerName].WordPress.IgnoreInactive {
func (r ScanResult) FilterInactiveWordPressLibs(detectInactive bool) ScanResult {
if detectInactive {
return r
}
@@ -347,16 +344,23 @@ func (r ScanResult) FormatTextReportHeader() string {
buf.WriteString("=")
}
return fmt.Sprintf("%s\n%s\n%s, %s, %s, %s, %s, %s\n",
pkgs := r.FormatUpdatablePacksSummary()
if 0 < len(r.WordPressPackages) {
pkgs = fmt.Sprintf("%s, %d WordPress pkgs", pkgs, len(r.WordPressPackages))
}
if 0 < len(r.LibraryScanners) {
pkgs = fmt.Sprintf("%s, %d libs", pkgs, r.LibraryScanners.Total())
}
return fmt.Sprintf("%s\n%s\n%s\n%s, %s, %s, %s\n%s\n",
r.ServerInfo(),
buf.String(),
r.ScannedCves.FormatCveSummary(),
r.ScannedCves.FormatFixedStatus(r.Packages),
r.FormatUpdatablePacksSummary(),
r.FormatExploitCveSummary(),
r.FormatMetasploitCveSummary(),
r.FormatAlertSummary(),
)
pkgs)
}
// FormatUpdatablePacksSummary returns a summary of updatable packages
@@ -387,7 +391,7 @@ func (r ScanResult) FormatExploitCveSummary() string {
nExploitCve++
}
}
return fmt.Sprintf("%d exploits", nExploitCve)
return fmt.Sprintf("%d poc", nExploitCve)
}
// FormatMetasploitCveSummary returns a summary of exploit cve
@@ -398,7 +402,7 @@ func (r ScanResult) FormatMetasploitCveSummary() string {
nMetasploitCve++
}
}
return fmt.Sprintf("%d modules", nMetasploitCve)
return fmt.Sprintf("%d exploits", nMetasploitCve)
}
// FormatAlertSummary returns a summary of CERT alerts
@@ -422,6 +426,7 @@ func (r ScanResult) isDisplayUpdatableNum() bool {
}
var mode config.ScanMode
//TODO pass by args
s, _ := config.Conf.Servers[r.ServerName]
mode = s.Mode
@@ -454,10 +459,8 @@ func (r ScanResult) IsContainer() bool {
// IsDeepScanMode checks if the scan mode is deep scan mode.
func (r ScanResult) IsDeepScanMode() bool {
for _, s := range r.Config.Scan.Servers {
for _, m := range s.ScanMode {
if m == "deep" {
return true
}
if ok := s.Mode.IsDeep(); ok {
return true
}
}
return false

View File

@@ -18,6 +18,7 @@ func TestFilterByCvssOver(t *testing.T) {
in in
out ScanResult
}{
//0
{
in: in{
over: 7.0,
@@ -27,7 +28,7 @@ func TestFilterByCvssOver(t *testing.T) {
CveID: "CVE-2017-0001",
CveContents: NewCveContents(
CveContent{
Type: NvdXML,
Type: Nvd,
CveID: "CVE-2017-0001",
Cvss2Score: 7.1,
LastModified: time.Time{},
@@ -38,7 +39,7 @@ func TestFilterByCvssOver(t *testing.T) {
CveID: "CVE-2017-0002",
CveContents: NewCveContents(
CveContent{
Type: NvdXML,
Type: Nvd,
CveID: "CVE-2017-0002",
Cvss2Score: 6.9,
LastModified: time.Time{},
@@ -49,7 +50,7 @@ func TestFilterByCvssOver(t *testing.T) {
CveID: "CVE-2017-0003",
CveContents: NewCveContents(
CveContent{
Type: NvdXML,
Type: Nvd,
CveID: "CVE-2017-0003",
Cvss2Score: 6.9,
LastModified: time.Time{},
@@ -71,7 +72,7 @@ func TestFilterByCvssOver(t *testing.T) {
CveID: "CVE-2017-0001",
CveContents: NewCveContents(
CveContent{
Type: NvdXML,
Type: Nvd,
CveID: "CVE-2017-0001",
Cvss2Score: 7.1,
LastModified: time.Time{},
@@ -82,7 +83,7 @@ func TestFilterByCvssOver(t *testing.T) {
CveID: "CVE-2017-0003",
CveContents: NewCveContents(
CveContent{
Type: NvdXML,
Type: Nvd,
CveID: "CVE-2017-0003",
Cvss2Score: 6.9,
LastModified: time.Time{},
@@ -98,7 +99,7 @@ func TestFilterByCvssOver(t *testing.T) {
},
},
},
// OVAL Severity
//1 OVAL Severity
{
in: in{
over: 7.0,
@@ -110,7 +111,7 @@ func TestFilterByCvssOver(t *testing.T) {
CveContent{
Type: Ubuntu,
CveID: "CVE-2017-0001",
Cvss2Severity: "HIGH",
Cvss3Severity: "HIGH",
LastModified: time.Time{},
},
),
@@ -119,9 +120,9 @@ func TestFilterByCvssOver(t *testing.T) {
CveID: "CVE-2017-0002",
CveContents: NewCveContents(
CveContent{
Type: RedHat,
Type: Debian,
CveID: "CVE-2017-0002",
Cvss2Severity: "CRITICAL",
Cvss3Severity: "CRITICAL",
LastModified: time.Time{},
},
),
@@ -130,9 +131,9 @@ func TestFilterByCvssOver(t *testing.T) {
CveID: "CVE-2017-0003",
CveContents: NewCveContents(
CveContent{
Type: Oracle,
Type: GitHub,
CveID: "CVE-2017-0003",
Cvss2Severity: "IMPORTANT",
Cvss3Severity: "IMPORTANT",
LastModified: time.Time{},
},
),
@@ -148,7 +149,7 @@ func TestFilterByCvssOver(t *testing.T) {
CveContent{
Type: Ubuntu,
CveID: "CVE-2017-0001",
Cvss2Severity: "HIGH",
Cvss3Severity: "HIGH",
LastModified: time.Time{},
},
),
@@ -157,9 +158,9 @@ func TestFilterByCvssOver(t *testing.T) {
CveID: "CVE-2017-0002",
CveContents: NewCveContents(
CveContent{
Type: RedHat,
Type: Debian,
CveID: "CVE-2017-0002",
Cvss2Severity: "CRITICAL",
Cvss3Severity: "CRITICAL",
LastModified: time.Time{},
},
),
@@ -168,9 +169,9 @@ func TestFilterByCvssOver(t *testing.T) {
CveID: "CVE-2017-0003",
CveContents: NewCveContents(
CveContent{
Type: Oracle,
Type: GitHub,
CveID: "CVE-2017-0003",
Cvss2Severity: "IMPORTANT",
Cvss3Severity: "IMPORTANT",
LastModified: time.Time{},
},
),
@@ -179,13 +180,14 @@ func TestFilterByCvssOver(t *testing.T) {
},
},
}
for _, tt := range tests {
pp.ColoringEnabled = false
for i, tt := range tests {
actual := tt.in.rs.FilterByCvssOver(tt.in.over)
for k := range tt.out.ScannedCves {
if !reflect.DeepEqual(tt.out.ScannedCves[k], actual.ScannedCves[k]) {
o := pp.Sprintf("%v", tt.out.ScannedCves[k])
a := pp.Sprintf("%v", actual.ScannedCves[k])
t.Errorf("[%s] expected: %v\n actual: %v\n", k, o, a)
t.Errorf("[%d: %s] expected: %v\n actual: %v\n", i, k, o, a)
}
}
}
@@ -392,8 +394,7 @@ func TestFilterUnfixed(t *testing.T) {
},
}
for i, tt := range tests {
config.Conf.IgnoreUnfixed = true
actual := tt.in.FilterUnfixed()
actual := tt.in.FilterUnfixed(true)
if !reflect.DeepEqual(tt.out.ScannedCves, actual.ScannedCves) {
o := pp.Sprintf("%v", tt.out.ScannedCves)
a := pp.Sprintf("%v", actual.ScannedCves)

View File

@@ -65,10 +65,14 @@ func ConvertNvdJSONToModel(cveID string, nvd *cvedict.NvdJSON) (*CveContent, []E
exploits := []Exploit{}
mitigations := []Mitigation{}
for _, r := range nvd.References {
var tags []string
if 0 < len(r.Tags) {
tags = strings.Split(r.Tags, ",")
}
refs = append(refs, Reference{
Link: r.Link,
Source: r.Source,
Tags: strings.Split(r.Tags, ","),
Tags: tags,
})
if strings.Contains(r.Tags, "Exploit") {
exploits = append(exploits, Exploit{

View File

@@ -8,7 +8,7 @@ import (
"time"
"github.com/future-architect/vuls/config"
exploitmodels "github.com/mozqnet/go-exploitdb/models"
exploitmodels "github.com/vulsio/go-exploitdb/models"
)
// VulnInfos has a map of VulnInfo
@@ -57,11 +57,13 @@ func (v VulnInfos) ToSortedSlice() (sorted []VulnInfo) {
func (v VulnInfos) CountGroupBySeverity() map[string]int {
m := map[string]int{}
for _, vInfo := range v {
score := vInfo.MaxCvss2Score().Value.Score
score := vInfo.MaxCvss3Score().Value.Score
if score < 0.1 {
score = vInfo.MaxCvss3Score().Value.Score
score = vInfo.MaxCvss2Score().Value.Score
}
switch {
case 9 <= score:
m["Critical"]++
case 7.0 <= score:
m["High"]++
case 4.0 <= score:
@@ -76,16 +78,22 @@ func (v VulnInfos) CountGroupBySeverity() map[string]int {
}
// FormatCveSummary summarize the number of CVEs group by CVSSv2 Severity
func (v VulnInfos) FormatCveSummary() string {
func (v VulnInfos) FormatCveSummary() (line string) {
m := v.CountGroupBySeverity()
if config.Conf.IgnoreUnscoredCves {
return fmt.Sprintf("Total: %d (High:%d Medium:%d Low:%d)",
m["High"]+m["Medium"]+m["Low"], m["High"], m["Medium"], m["Low"])
line = fmt.Sprintf("Total: %d (Critical:%d High:%d Medium:%d Low:%d)",
m["High"]+m["Medium"]+m["Low"], m["Critical"], m["High"], m["Medium"], m["Low"])
} else {
line = fmt.Sprintf("Total: %d (Critical:%d High:%d Medium:%d Low:%d ?:%d)",
m["High"]+m["Medium"]+m["Low"]+m["Unknown"],
m["Critical"], m["High"], m["Medium"], m["Low"], m["Unknown"])
}
return fmt.Sprintf("Total: %d (High:%d Medium:%d Low:%d ?:%d)",
m["High"]+m["Medium"]+m["Low"]+m["Unknown"],
m["High"], m["Medium"], m["Low"], m["Unknown"])
if config.Conf.DiffMinus || config.Conf.DiffPlus {
nPlus, nMinus := v.CountDiff()
line = fmt.Sprintf("%s +%d -%d", line, nPlus, nMinus)
}
return line
}
// FormatFixedStatus summarize the number of cves are fixed.
@@ -103,6 +111,18 @@ func (v VulnInfos) FormatFixedStatus(packs Packages) string {
return fmt.Sprintf("%d/%d Fixed", fixed, total)
}
// CountDiff counts the number of added/removed CVE-ID
func (v VulnInfos) CountDiff() (nPlus int, nMinus int) {
for _, vInfo := range v {
if vInfo.DiffStatus == DiffPlus {
nPlus++
} else if vInfo.DiffStatus == DiffMinus {
nMinus++
}
}
return
}
// PackageFixStatuses is a list of PackageStatus
type PackageFixStatuses []PackageFixStatus
@@ -157,8 +177,8 @@ type VulnInfo struct {
GitHubSecurityAlerts GitHubSecurityAlerts `json:"gitHubSecurityAlerts,omitempty"`
WpPackageFixStats WpPackageFixStats `json:"wpPackageFixStats,omitempty"`
LibraryFixedIns LibraryFixedIns `json:"libraryFixedIns,omitempty"`
VulnType string `json:"vulnType,omitempty"`
VulnType string `json:"vulnType,omitempty"`
DiffStatus DiffStatus `json:"diffStatus,omitempty"`
}
// Alert has CERT alert information
@@ -234,24 +254,47 @@ func (g WpPackages) Add(pkg WpPackage) WpPackages {
return append(g, pkg)
}
// DiffStatus keeps a comparison with the previous detection results for this CVE
type DiffStatus string
const (
// DiffPlus is newly detected CVE
DiffPlus = DiffStatus("+")
// DiffMinus is resolved CVE
DiffMinus = DiffStatus("-")
)
// CveIDDiffFormat format CVE-ID for diff mode
func (v VulnInfo) CveIDDiffFormat(isDiffMode bool) string {
if isDiffMode {
return fmt.Sprintf("%s %s", v.DiffStatus, v.CveID)
}
return fmt.Sprintf("%s", v.CveID)
}
// Titles returns title (TUI)
func (v VulnInfo) Titles(lang, myFamily string) (values []CveContentStr) {
if lang == "ja" {
if cont, found := v.CveContents[Jvn]; found && 0 < len(cont.Title) {
if cont, found := v.CveContents[Jvn]; found && cont.Title != "" {
values = append(values, CveContentStr{Jvn, cont.Title})
}
}
// RedHat API has one line title.
if cont, found := v.CveContents[RedHatAPI]; found && 0 < len(cont.Title) {
if cont, found := v.CveContents[RedHatAPI]; found && cont.Title != "" {
values = append(values, CveContentStr{RedHatAPI, cont.Title})
}
order := CveContentTypes{Trivy, Nvd, NvdXML, NewCveContentType(myFamily)}
// GitHub security alerts has a title.
if cont, found := v.CveContents[GitHub]; found && cont.Title != "" {
values = append(values, CveContentStr{GitHub, cont.Title})
}
order := CveContentTypes{Trivy, Nvd, NewCveContentType(myFamily)}
order = append(order, AllCveContetTypes.Except(append(order, Jvn)...)...)
for _, ctype := range order {
// Only JVN has meaningful title. so return first 100 char of summary
if cont, found := v.CveContents[ctype]; found && 0 < len(cont.Summary) {
if cont, found := v.CveContents[ctype]; found && cont.Summary != "" {
summary := strings.Replace(cont.Summary, "\n", " ", -1)
values = append(values, CveContentStr{
Type: ctype,
@@ -279,7 +322,7 @@ func (v VulnInfo) Titles(lang, myFamily string) (values []CveContentStr) {
// Summaries returns summaries
func (v VulnInfo) Summaries(lang, myFamily string) (values []CveContentStr) {
if lang == "ja" {
if cont, found := v.CveContents[Jvn]; found && 0 < len(cont.Summary) {
if cont, found := v.CveContents[Jvn]; found && cont.Summary != "" {
summary := cont.Title
summary += "\n" + strings.Replace(
strings.Replace(cont.Summary, "\n", " ", -1), "\r", " ", -1)
@@ -287,10 +330,10 @@ func (v VulnInfo) Summaries(lang, myFamily string) (values []CveContentStr) {
}
}
order := CveContentTypes{Trivy, NewCveContentType(myFamily), Nvd, NvdXML}
order := CveContentTypes{Trivy, NewCveContentType(myFamily), Nvd, GitHub}
order = append(order, AllCveContetTypes.Except(append(order, Jvn)...)...)
for _, ctype := range order {
if cont, found := v.CveContents[ctype]; found && 0 < len(cont.Summary) {
if cont, found := v.CveContents[ctype]; found && cont.Summary != "" {
summary := strings.Replace(cont.Summary, "\n", " ", -1)
values = append(values, CveContentStr{
Type: ctype,
@@ -306,9 +349,9 @@ func (v VulnInfo) Summaries(lang, myFamily string) (values []CveContentStr) {
})
}
if v, ok := v.CveContents[WPVulnDB]; ok {
if v, ok := v.CveContents[WpScan]; ok {
values = append(values, CveContentStr{
Type: "WPVDB",
Type: WpScan,
Value: v.Title,
})
}
@@ -324,14 +367,11 @@ func (v VulnInfo) Summaries(lang, myFamily string) (values []CveContentStr) {
}
// Cvss2Scores returns CVSS V2 Scores
func (v VulnInfo) Cvss2Scores(myFamily string) (values []CveContentCvss) {
order := []CveContentType{Nvd, NvdXML, RedHatAPI, RedHat, Jvn}
if myFamily != config.RedHat && myFamily != config.CentOS {
order = append(order, NewCveContentType(myFamily))
}
func (v VulnInfo) Cvss2Scores() (values []CveContentCvss) {
order := []CveContentType{RedHatAPI, RedHat, Nvd, Jvn}
for _, ctype := range order {
if cont, found := v.CveContents[ctype]; found {
if cont.Cvss2Score == 0 || cont.Cvss2Severity == "" {
if cont.Cvss2Score == 0 && cont.Cvss2Severity == "" {
continue
}
// https://nvd.nist.gov/vuln-metrics/cvss
@@ -346,52 +386,17 @@ func (v VulnInfo) Cvss2Scores(myFamily string) (values []CveContentCvss) {
})
}
}
for _, adv := range v.DistroAdvisories {
if adv.Severity != "" {
values = append(values, CveContentCvss{
Type: "Advisory",
Value: Cvss{
Type: CVSS2,
Score: severityToV2ScoreRoughly(adv.Severity),
CalculatedBySeverity: true,
Vector: "-",
Severity: strings.ToUpper(adv.Severity),
},
})
}
}
// An OVAL entry in Ubuntu and Debian has only severity (CVSS score isn't included).
// Show severity and dummy score calculated roughly.
order = append(order, AllCveContetTypes.Except(order...)...)
for _, ctype := range order {
if cont, found := v.CveContents[ctype]; found &&
cont.Cvss2Score == 0 &&
cont.Cvss3Score == 0 &&
cont.Cvss2Severity != "" {
values = append(values, CveContentCvss{
Type: cont.Type,
Value: Cvss{
Type: CVSS2,
Score: severityToV2ScoreRoughly(cont.Cvss2Severity),
CalculatedBySeverity: true,
Vector: "-",
Severity: strings.ToUpper(cont.Cvss2Severity),
},
})
}
}
return
}
// Cvss3Scores returns CVSS V3 Score
func (v VulnInfo) Cvss3Scores() (values []CveContentCvss) {
order := []CveContentType{Nvd, RedHatAPI, RedHat, Jvn}
order := []CveContentType{RedHatAPI, RedHat, Nvd, Jvn}
for _, ctype := range order {
if cont, found := v.CveContents[ctype]; found {
if cont.Cvss3Score == 0 && cont.Cvss3Severity == "" {
continue
}
// https://nvd.nist.gov/vuln-metrics/cvss
values = append(values, CveContentCvss{
Type: ctype,
@@ -405,131 +410,74 @@ func (v VulnInfo) Cvss3Scores() (values []CveContentCvss) {
}
}
if cont, found := v.CveContents[Trivy]; found && cont.Cvss3Severity != "" {
values = append(values, CveContentCvss{
Type: Trivy,
Value: Cvss{
Type: CVSS3,
Score: severityToV2ScoreRoughly(cont.Cvss3Severity),
Severity: strings.ToUpper(cont.Cvss3Severity),
},
})
}
return
}
// MaxCvss3Score returns Max CVSS V3 Score
func (v VulnInfo) MaxCvss3Score() CveContentCvss {
order := []CveContentType{Nvd, RedHat, RedHatAPI, Jvn}
max := 0.0
value := CveContentCvss{
Type: Unknown,
Value: Cvss{Type: CVSS3},
}
for _, ctype := range order {
if cont, found := v.CveContents[ctype]; found && max < cont.Cvss3Score {
// https://nvd.nist.gov/vuln-metrics/cvss
value = CveContentCvss{
for _, ctype := range []CveContentType{Debian, DebianSecurityTracker, Ubuntu, Amazon, Trivy, GitHub, WpScan} {
if cont, found := v.CveContents[ctype]; found && cont.Cvss3Severity != "" {
values = append(values, CveContentCvss{
Type: ctype,
Value: Cvss{
Type: CVSS3,
Score: cont.Cvss3Score,
Vector: cont.Cvss3Vector,
Severity: strings.ToUpper(cont.Cvss3Severity),
Type: CVSS3,
Score: severityToCvssScoreRoughly(cont.Cvss3Severity),
CalculatedBySeverity: true,
Severity: strings.ToUpper(cont.Cvss3Severity),
},
}
max = cont.Cvss3Score
})
}
}
return value
// Memo: Only RedHat, Oracle and Amazon has severity data in advisory.
for _, adv := range v.DistroAdvisories {
if adv.Severity != "" {
score := severityToCvssScoreRoughly(adv.Severity)
values = append(values, CveContentCvss{
Type: "Vendor",
Value: Cvss{
Type: CVSS3,
Score: score,
CalculatedBySeverity: true,
Severity: strings.ToUpper(adv.Severity),
},
})
}
}
return
}
// MaxCvssScore returns max CVSS Score
// If there is no CVSS Score, return Severity as a numerical value.
func (v VulnInfo) MaxCvssScore() CveContentCvss {
v3Max := v.MaxCvss3Score()
v2Max := v.MaxCvss2Score()
max := v3Max
if max.Type == Unknown {
return v2Max
if v3Max.Type != Unknown {
return v3Max
}
return v.MaxCvss2Score()
}
if max.Value.Score < v2Max.Value.Score && !v2Max.Value.CalculatedBySeverity {
max = v2Max
// MaxCvss3Score returns Max CVSS V3 Score
func (v VulnInfo) MaxCvss3Score() CveContentCvss {
max := CveContentCvss{
Type: Unknown,
Value: Cvss{Type: CVSS3},
}
for _, cvss := range v.Cvss3Scores() {
if max.Value.Score < cvss.Value.Score {
max = cvss
}
}
return max
}
// MaxCvss2Score returns Max CVSS V2 Score
func (v VulnInfo) MaxCvss2Score() CveContentCvss {
order := []CveContentType{Nvd, NvdXML, RedHat, RedHatAPI, Jvn}
max := 0.0
value := CveContentCvss{
max := CveContentCvss{
Type: Unknown,
Value: Cvss{Type: CVSS2},
}
for _, ctype := range order {
if cont, found := v.CveContents[ctype]; found && max < cont.Cvss2Score {
// https://nvd.nist.gov/vuln-metrics/cvss
value = CveContentCvss{
Type: ctype,
Value: Cvss{
Type: CVSS2,
Score: cont.Cvss2Score,
Vector: cont.Cvss2Vector,
Severity: strings.ToUpper(cont.Cvss2Severity),
},
}
max = cont.Cvss2Score
for _, cvss := range v.Cvss2Scores() {
if max.Value.Score < cvss.Value.Score {
max = cvss
}
}
if 0 < max {
return value
}
// If CVSS score isn't on NVD, RedHat and JVN, use OVAL and advisory Severity.
// Convert severity to cvss score roughly, then returns max severity.
// Only Ubuntu, RedHat and Oracle have severity data in OVAL.
order = []CveContentType{Ubuntu, RedHat, Oracle}
for _, ctype := range order {
if cont, found := v.CveContents[ctype]; found && 0 < len(cont.Cvss2Severity) {
score := severityToV2ScoreRoughly(cont.Cvss2Severity)
if max < score {
value = CveContentCvss{
Type: ctype,
Value: Cvss{
Type: CVSS2,
Score: score,
CalculatedBySeverity: true,
Vector: cont.Cvss2Vector,
Severity: strings.ToUpper(cont.Cvss2Severity),
},
}
}
max = score
}
}
// Only RedHat, Oracle and Amazon has severity data in advisory.
for _, adv := range v.DistroAdvisories {
if adv.Severity != "" {
score := severityToV2ScoreRoughly(adv.Severity)
if max < score {
value = CveContentCvss{
Type: "Vendor",
Value: Cvss{
Type: CVSS2,
Score: score,
CalculatedBySeverity: true,
Vector: "-",
Severity: adv.Severity,
},
}
}
}
}
return value
return max
}
// AttackVector returns attack vector string
@@ -613,16 +561,29 @@ type Cvss struct {
// Format CVSS Score and Vector
func (c Cvss) Format() string {
if c.Score == 0 || c.Vector == "" {
return c.Severity
if c.Vector == "" {
return fmt.Sprintf("%s %s", c.SeverityToCvssScoreRange(), c.Severity)
}
switch c.Type {
case CVSS2:
return fmt.Sprintf("%3.1f/%s %s", c.Score, c.Vector, c.Severity)
case CVSS3:
return fmt.Sprintf("%3.1f/%s %s", c.Score, c.Vector, c.Severity)
return fmt.Sprintf("%3.1f/%s %s", c.Score, c.Vector, c.Severity)
}
// SeverityToCvssScoreRange returns CVSS score range
func (c Cvss) SeverityToCvssScoreRange() string {
return severityToCvssScoreRange(c.Severity)
}
func severityToCvssScoreRange(severity string) string {
switch strings.ToUpper(severity) {
case "CRITICAL":
return "9.0-10.0"
case "IMPORTANT", "HIGH":
return "7.0-8.9"
case "MODERATE", "MEDIUM":
return "4.0-6.9"
case "LOW":
return "0.1-3.9"
}
return ""
return "None"
}
// Amazon Linux Security Advisory
@@ -637,7 +598,7 @@ func (c Cvss) Format() string {
// Critical, High, Medium, Low
// https://wiki.ubuntu.com/Bugs/Importance
// https://people.canonical.com/~ubuntu-security/cve/priority.html
func severityToV2ScoreRoughly(severity string) float64 {
func severityToCvssScoreRoughly(severity string) float64 {
switch strings.ToUpper(severity) {
case "CRITICAL":
return 10.0
@@ -810,8 +771,8 @@ const (
// GitHubMatchStr is a String representation of GitHubMatch
GitHubMatchStr = "GitHubMatch"
// WPVulnDBMatchStr is a String representation of WordPress VulnDB scanning
WPVulnDBMatchStr = "WPVulnDBMatch"
// WpScanMatchStr is a String representation of WordPress VulnDB scanning
WpScanMatchStr = "WpScanMatch"
// FailedToGetChangelog is a String representation of FailedToGetChangelog
FailedToGetChangelog = "FailedToGetChangelog"
@@ -851,6 +812,6 @@ var (
// GitHubMatch is a ranking how confident the CVE-ID was detected correctly
GitHubMatch = Confidence{97, GitHubMatchStr, 2}
// WPVulnDBMatch is a ranking how confident the CVE-ID was detected correctly
WPVulnDBMatch = Confidence{100, WPVulnDBMatchStr, 0}
// WpScanMatch is a ranking how confident the CVE-ID was detected correctly
WpScanMatch = Confidence{100, WpScanMatchStr, 0}
)

View File

@@ -28,8 +28,8 @@ func TestTitles(t *testing.T) {
Type: RedHat,
Summary: "Summary RedHat",
},
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Summary: "Summary NVD",
// Severity is NOT included in NVD
},
@@ -42,7 +42,7 @@ func TestTitles(t *testing.T) {
Value: "Title1",
},
{
Type: NvdXML,
Type: Nvd,
Value: "Summary NVD",
},
{
@@ -65,8 +65,8 @@ func TestTitles(t *testing.T) {
Type: RedHat,
Summary: "Summary RedHat",
},
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Summary: "Summary NVD",
// Severity is NOT included in NVD
},
@@ -75,7 +75,7 @@ func TestTitles(t *testing.T) {
},
out: []CveContentStr{
{
Type: NvdXML,
Type: Nvd,
Value: "Summary NVD",
},
{
@@ -98,10 +98,10 @@ func TestTitles(t *testing.T) {
},
},
}
for _, tt := range tests {
for i, tt := range tests {
actual := tt.in.cont.Titles(tt.in.lang, "redhat")
if !reflect.DeepEqual(tt.out, actual) {
t.Errorf("\nexpected: %v\n actual: %v\n", tt.out, actual)
t.Errorf("[%d]\nexpected: %v\n actual: %v\n", i, tt.out, actual)
}
}
}
@@ -130,8 +130,8 @@ func TestSummaries(t *testing.T) {
Type: RedHat,
Summary: "Summary RedHat",
},
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Summary: "Summary NVD",
// Severity is NOT included in NVD
},
@@ -148,7 +148,7 @@ func TestSummaries(t *testing.T) {
Value: "Summary RedHat",
},
{
Type: NvdXML,
Type: Nvd,
Value: "Summary NVD",
},
},
@@ -168,8 +168,8 @@ func TestSummaries(t *testing.T) {
Type: RedHat,
Summary: "Summary RedHat",
},
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Summary: "Summary NVD",
// Severity is NOT included in NVD
},
@@ -182,7 +182,7 @@ func TestSummaries(t *testing.T) {
Value: "Summary RedHat",
},
{
Type: NvdXML,
Type: Nvd,
Value: "Summary NVD",
},
},
@@ -219,21 +219,75 @@ func TestCountGroupBySeverity(t *testing.T) {
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: CveContents{
NvdXML: {
Type: NvdXML,
Cvss2Score: 6.0,
Nvd: {
Type: Nvd,
Cvss3Score: 6.0,
},
RedHat: {
Type: RedHat,
Cvss2Score: 7.0,
Cvss3Score: 7.0,
},
},
},
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
CveContents: CveContents{
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss3Score: 2.0,
},
},
},
"CVE-2017-0004": {
CveID: "CVE-2017-0004",
CveContents: CveContents{
Nvd: {
Type: Nvd,
Cvss3Score: 5.0,
},
},
},
"CVE-2017-0005": {
CveID: "CVE-2017-0005",
},
"CVE-2017-0006": {
CveID: "CVE-2017-0005",
CveContents: CveContents{
Nvd: {
Type: Nvd,
Cvss3Score: 10.0,
},
},
},
},
out: map[string]int{
"Critical": 1,
"High": 1,
"Medium": 1,
"Low": 1,
"Unknown": 1,
},
},
{
in: VulnInfos{
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: CveContents{
Nvd: {
Type: Nvd,
Cvss2Score: 1.0,
},
RedHat: {
Type: RedHat,
Cvss3Score: 7.0,
},
},
},
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
CveContents: CveContents{
Nvd: {
Type: Nvd,
Cvss2Score: 2.0,
},
},
@@ -241,8 +295,8 @@ func TestCountGroupBySeverity(t *testing.T) {
"CVE-2017-0004": {
CveID: "CVE-2017-0004",
CveContents: CveContents{
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 5.0,
},
},
@@ -250,21 +304,31 @@ func TestCountGroupBySeverity(t *testing.T) {
"CVE-2017-0005": {
CveID: "CVE-2017-0005",
},
"CVE-2017-0006": {
CveID: "CVE-2017-0005",
CveContents: CveContents{
Nvd: {
Type: Nvd,
Cvss2Score: 10.0,
},
},
},
},
out: map[string]int{
"High": 1,
"Medium": 1,
"Low": 1,
"Unknown": 1,
"Critical": 1,
"High": 1,
"Medium": 1,
"Low": 1,
"Unknown": 1,
},
},
}
for _, tt := range tests {
for i, tt := range tests {
actual := tt.in.CountGroupBySeverity()
for k := range tt.out {
if tt.out[k] != actual[k] {
t.Errorf("\nexpected %s: %d\n actual %d\n",
k, tt.out[k], actual[k])
t.Errorf("[%d]\nexpected %s: %d\n actual %d\n",
i, k, tt.out[k], actual[k])
}
}
}
@@ -275,13 +339,14 @@ func TestToSortedSlice(t *testing.T) {
in VulnInfos
out []VulnInfo
}{
//0
{
in: VulnInfos{
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: CveContents{
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 6.0,
},
RedHat: {
@@ -293,8 +358,8 @@ func TestToSortedSlice(t *testing.T) {
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
CveContents: CveContents{
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 7.0,
},
RedHat: {
@@ -308,8 +373,8 @@ func TestToSortedSlice(t *testing.T) {
{
CveID: "CVE-2017-0001",
CveContents: CveContents{
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 7.0,
},
RedHat: {
@@ -321,8 +386,8 @@ func TestToSortedSlice(t *testing.T) {
{
CveID: "CVE-2017-0002",
CveContents: CveContents{
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 6.0,
},
RedHat: {
@@ -333,14 +398,14 @@ func TestToSortedSlice(t *testing.T) {
},
},
},
// When max scores are the same, sort by CVE-ID
//[1] When max scores are the same, sort by CVE-ID
{
in: VulnInfos{
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: CveContents{
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 6.0,
},
RedHat: {
@@ -354,7 +419,7 @@ func TestToSortedSlice(t *testing.T) {
CveContents: CveContents{
RedHat: {
Type: RedHat,
Cvss2Score: 7.0,
Cvss3Score: 7.0,
},
},
},
@@ -365,15 +430,15 @@ func TestToSortedSlice(t *testing.T) {
CveContents: CveContents{
RedHat: {
Type: RedHat,
Cvss2Score: 7.0,
Cvss3Score: 7.0,
},
},
},
{
CveID: "CVE-2017-0002",
CveContents: CveContents{
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 6.0,
},
RedHat: {
@@ -384,7 +449,7 @@ func TestToSortedSlice(t *testing.T) {
},
},
},
// When there are no cvss scores, sort by severity
//[2] When there are no cvss scores, sort by severity
{
in: VulnInfos{
"CVE-2017-0002": {
@@ -392,7 +457,7 @@ func TestToSortedSlice(t *testing.T) {
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Cvss2Severity: "High",
Cvss3Severity: "High",
},
},
},
@@ -401,7 +466,7 @@ func TestToSortedSlice(t *testing.T) {
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Cvss2Severity: "Low",
Cvss3Severity: "Low",
},
},
},
@@ -412,7 +477,7 @@ func TestToSortedSlice(t *testing.T) {
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Cvss2Severity: "High",
Cvss3Severity: "High",
},
},
},
@@ -421,17 +486,17 @@ func TestToSortedSlice(t *testing.T) {
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Cvss2Severity: "Low",
Cvss3Severity: "Low",
},
},
},
},
},
}
for _, tt := range tests {
for i, tt := range tests {
actual := tt.in.ToSortedSlice()
if !reflect.DeepEqual(tt.out, actual) {
t.Errorf("\nexpected: %v\n actual: %v\n", tt.out, actual)
t.Errorf("[%d]\nexpected: %v\n actual: %v\n", i, tt.out, actual)
}
}
}
@@ -456,29 +521,36 @@ func TestCvss2Scores(t *testing.T) {
Cvss2Score: 8.0,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
},
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 8.1,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
Cvss2Severity: "HIGH",
},
//v3
RedHatAPI: {
Type: RedHatAPI,
Cvss3Score: 8.1,
Cvss3Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
Cvss3Severity: "HIGH",
},
},
},
out: []CveContentCvss{
{
Type: NvdXML,
Type: RedHat,
Value: Cvss{
Type: CVSS2,
Score: 8.1,
Score: 8.0,
Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
Severity: "HIGH",
},
},
{
Type: RedHat,
Type: Nvd,
Value: Cvss{
Type: CVSS2,
Score: 8.0,
Score: 8.1,
Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
Severity: "HIGH",
},
@@ -501,7 +573,7 @@ func TestCvss2Scores(t *testing.T) {
},
}
for i, tt := range tests {
actual := tt.in.Cvss2Scores("redhat")
actual := tt.in.Cvss2Scores()
if !reflect.DeepEqual(tt.out, actual) {
t.Errorf("[%d]\nexpected: %v\n actual: %v\n", i, tt.out, actual)
}
@@ -513,6 +585,7 @@ func TestMaxCvss2Scores(t *testing.T) {
in VulnInfo
out CveContentCvss
}{
// 0
{
in: VulnInfo{
CveContents: CveContents{
@@ -528,8 +601,8 @@ func TestMaxCvss2Scores(t *testing.T) {
Cvss2Score: 8.0,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
},
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 8.1,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
// Severity is NOT included in NVD
@@ -546,26 +619,6 @@ func TestMaxCvss2Scores(t *testing.T) {
},
},
},
// Severity in OVAL
{
in: VulnInfo{
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Cvss2Severity: "HIGH",
},
},
},
out: CveContentCvss{
Type: Ubuntu,
Value: Cvss{
Type: CVSS2,
Score: 8.9,
CalculatedBySeverity: true,
Severity: "HIGH",
},
},
},
// Empty
{
in: VulnInfo{},
@@ -602,8 +655,8 @@ func TestCvss3Scores(t *testing.T) {
Cvss3Score: 8.0,
Cvss3Vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
},
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 8.1,
Cvss2Vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
Cvss2Severity: "HIGH",
@@ -622,16 +675,38 @@ func TestCvss3Scores(t *testing.T) {
},
},
},
// [1] Severity in OVAL
{
in: VulnInfo{
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Cvss3Severity: "HIGH",
},
},
},
out: []CveContentCvss{
{
Type: Ubuntu,
Value: Cvss{
Type: CVSS3,
Score: 8.9,
CalculatedBySeverity: true,
Severity: "HIGH",
},
},
},
},
// Empty
{
in: VulnInfo{},
out: nil,
},
}
for _, tt := range tests {
for i, tt := range tests {
actual := tt.in.Cvss3Scores()
if !reflect.DeepEqual(tt.out, actual) {
t.Errorf("\nexpected: %v\n actual: %v\n", tt.out, actual)
t.Errorf("[%d]\nexpected: %v\n actual: %v\n", i, tt.out, actual)
}
}
}
@@ -692,8 +767,8 @@ func TestMaxCvssScores(t *testing.T) {
{
in: VulnInfo{
CveContents: CveContents{
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss3Score: 7.0,
},
RedHat: {
@@ -703,10 +778,10 @@ func TestMaxCvssScores(t *testing.T) {
},
},
out: CveContentCvss{
Type: RedHat,
Type: Nvd,
Value: Cvss{
Type: CVSS2,
Score: 8.0,
Type: CVSS3,
Score: 7.0,
},
},
},
@@ -733,14 +808,14 @@ func TestMaxCvssScores(t *testing.T) {
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Cvss2Severity: "HIGH",
Cvss3Severity: "HIGH",
},
},
},
out: CveContentCvss{
Type: Ubuntu,
Value: Cvss{
Type: CVSS2,
Type: CVSS3,
Score: 8.9,
CalculatedBySeverity: true,
Severity: "HIGH",
@@ -753,21 +828,22 @@ func TestMaxCvssScores(t *testing.T) {
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Cvss2Severity: "MEDIUM",
Cvss3Severity: "MEDIUM",
},
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 7.0,
Cvss2Severity: "HIGH",
},
},
},
out: CveContentCvss{
Type: NvdXML,
Type: Ubuntu,
Value: Cvss{
Type: CVSS2,
Score: 7.0,
Severity: "HIGH",
Type: CVSS3,
Score: 6.9,
Severity: "MEDIUM",
CalculatedBySeverity: true,
},
},
},
@@ -783,23 +859,23 @@ func TestMaxCvssScores(t *testing.T) {
out: CveContentCvss{
Type: "Vendor",
Value: Cvss{
Type: CVSS2,
Type: CVSS3,
Score: 8.9,
CalculatedBySeverity: true,
Vector: "-",
Severity: "HIGH",
},
},
},
//5
{
in: VulnInfo{
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Cvss2Severity: "MEDIUM",
Cvss3Severity: "MEDIUM",
},
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 4.0,
Cvss2Severity: "MEDIUM",
},
@@ -811,11 +887,12 @@ func TestMaxCvssScores(t *testing.T) {
},
},
out: CveContentCvss{
Type: NvdXML,
Type: "Vendor",
Value: Cvss{
Type: CVSS2,
Score: 4,
Severity: "MEDIUM",
Type: CVSS3,
Score: 8.9,
Severity: "HIGH",
CalculatedBySeverity: true,
},
},
},
@@ -854,17 +931,17 @@ func TestFormatMaxCvssScore(t *testing.T) {
},
RedHat: {
Type: RedHat,
Cvss2Severity: "HIGH",
Cvss3Severity: "HIGH",
Cvss3Score: 8.0,
},
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 8.1,
// Severity is NOT included in NVD
},
},
},
out: "8.3 HIGH (jvn)",
out: "8.0 HIGH (redhat)",
},
{
in: VulnInfo{
@@ -881,8 +958,8 @@ func TestFormatMaxCvssScore(t *testing.T) {
Cvss3Severity: "HIGH",
Cvss3Score: 9.9,
},
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 8.1,
},
},
@@ -890,10 +967,10 @@ func TestFormatMaxCvssScore(t *testing.T) {
out: "9.9 HIGH (redhat)",
},
}
for _, tt := range tests {
for i, tt := range tests {
actual := tt.in.FormatMaxCvssScore()
if !reflect.DeepEqual(tt.out, actual) {
t.Errorf("\nexpected: %v\n actual: %v\n", tt.out, actual)
t.Errorf("[%d]\nexpected: %v\n actual: %v\n", i, tt.out, actual)
}
}
}

View File

@@ -3,15 +3,9 @@
package msf
import (
"fmt"
"net/http"
cnf "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/parnurzeal/gorequest"
"github.com/takuzoo3868/go-msfdb/db"
metasploitmodels "github.com/takuzoo3868/go-msfdb/models"
"golang.org/x/xerrors"
)
// FillWithMetasploit fills metasploit module information that has in module
@@ -55,21 +49,3 @@ func ConvertToModels(ms []*metasploitmodels.Metasploit) (modules []models.Metasp
}
return modules
}
// CheckHTTPHealth do health check
func CheckHTTPHealth() error {
if !cnf.Conf.Metasploit.IsFetchViaHTTP() {
return nil
}
url := fmt.Sprintf("%s/health", cnf.Conf.Metasploit.URL)
var errs []error
var resp *http.Response
resp, _, errs = gorequest.New().Get(url).End()
// resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
// resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return xerrors.Errorf("Failed to connect to metasploit server. url: %s, errs: %w", url, errs)
}
return nil
}

View File

@@ -211,7 +211,7 @@ func NewUbuntu() Ubuntu {
// FillWithOval returns scan result after updating CVE info by OVAL
func (o Ubuntu) FillWithOval(driver db.DB, r *models.ScanResult) (nCVEs int, err error) {
switch major(r.Release) {
switch util.Major(r.Release) {
case "14":
kernelNamesInOval := []string{
"linux-aws",

View File

@@ -4,8 +4,6 @@ package oval
import (
"encoding/json"
"fmt"
"net/http"
"time"
cnf "github.com/future-architect/vuls/config"
@@ -18,7 +16,6 @@ import (
// Client is the interface of OVAL client.
type Client interface {
CheckHTTPHealth() error
FillWithOval(db.DB, *models.ScanResult) (int, error)
// CheckIfOvalFetched checks if oval entries are in DB by family, release.
@@ -31,25 +28,6 @@ type Base struct {
family string
}
// CheckHTTPHealth do health check
func (b Base) CheckHTTPHealth() error {
if !cnf.Conf.OvalDict.IsFetchViaHTTP() {
return nil
}
url := fmt.Sprintf("%s/health", cnf.Conf.OvalDict.URL)
var errs []error
var resp *http.Response
resp, _, errs = gorequest.New().Get(url).End()
// resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
// resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return xerrors.Errorf("Failed to request to OVAL server. url: %s, errs: %w",
url, errs)
}
return nil
}
// CheckIfOvalFetched checks if oval entries are in DB by family, release.
func (b Base) CheckIfOvalFetched(driver db.DB, osFamily, release string) (fetched bool, err error) {
if !cnf.Conf.OvalDict.IsFetchViaHTTP() {
@@ -61,9 +39,9 @@ func (b Base) CheckIfOvalFetched(driver db.DB, osFamily, release string) (fetche
}
url, _ := util.URLPathJoin(cnf.Conf.OvalDict.URL, "count", osFamily, release)
resp, body, errs := gorequest.New().Get(url).End()
resp, body, errs := gorequest.New().Timeout(10 * time.Second).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return false, xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %w", url, resp, errs)
return false, xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %s", url, resp, errs)
}
count := 0
if err := json.Unmarshal([]byte(body), &count); err != nil {
@@ -79,9 +57,9 @@ func (b Base) CheckIfOvalFresh(driver db.DB, osFamily, release string) (ok bool,
lastModified = driver.GetLastModified(osFamily, release)
} else {
url, _ := util.URLPathJoin(cnf.Conf.OvalDict.URL, "lastmodified", osFamily, release)
resp, body, errs := gorequest.New().Get(url).End()
resp, body, errs := gorequest.New().Timeout(10 * time.Second).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return false, xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %w", url, resp, errs)
return false, xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %s", url, resp, errs)
}
if err := json.Unmarshal([]byte(body), &lastModified); err != nil {

View File

@@ -173,17 +173,15 @@ func (o RedHatBase) convertToModel(cveID string, def *ovalmodels.Definition) *mo
score2, vec2 := o.parseCvss2(cve.Cvss2)
score3, vec3 := o.parseCvss3(cve.Cvss3)
severity := def.Advisory.Severity
sev2, sev3, severity := "", "", def.Advisory.Severity
if cve.Impact != "" {
severity = cve.Impact
}
sev2, sev3 := "", ""
if score2 == 0 {
sev2 = severity
}
if score3 == 0 {
if severity != "None" {
sev3 = severity
if score2 != 0 {
sev2 = severity
}
}
// CWE-ID in RedHat OVAL may have multiple cweIDs separated by space

View File

@@ -6,7 +6,6 @@ import (
"encoding/json"
"net/http"
"regexp"
"strings"
"time"
"github.com/cenkalti/backoff"
@@ -78,6 +77,7 @@ type request struct {
arch string
binaryPackNames []string
isSrcPack bool
modularityLabel string // RHEL 8 or later only
}
type response struct {
@@ -147,7 +147,7 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult) (
select {
case res := <-resChan:
for _, def := range res.defs {
affected, notFixedYet, fixedIn := isOvalDefAffected(def, res.request, r.Family, r.RunningKernel)
affected, notFixedYet, fixedIn := isOvalDefAffected(def, res.request, r.Family, r.RunningKernel, r.EnabledDnfModules)
if !affected {
continue
}
@@ -189,13 +189,13 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er
count, retryMax := 0, 3
f := func() (err error) {
// resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
resp, body, errs = gorequest.New().Get(url).End()
resp, body, errs = gorequest.New().Timeout(10 * time.Second).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
count++
if count == retryMax {
return nil
}
return xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %w", url, resp, errs)
return xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %s", url, resp, errs)
}
return nil
}
@@ -250,7 +250,7 @@ func getDefsByPackNameFromOvalDB(driver db.DB, r *models.ScanResult) (relatedDef
return relatedDefs, xerrors.Errorf("Failed to get %s OVAL info by package: %#v, err: %w", r.Family, req, err)
}
for _, def := range definitions {
affected, notFixedYet, fixedIn := isOvalDefAffected(def, req, r.Family, r.RunningKernel)
affected, notFixedYet, fixedIn := isOvalDefAffected(def, req, r.Family, r.RunningKernel, r.EnabledDnfModules)
if !affected {
continue
}
@@ -277,32 +277,33 @@ func getDefsByPackNameFromOvalDB(driver db.DB, r *models.ScanResult) (relatedDef
return
}
func major(version string) string {
if version == "" {
return ""
}
ss := strings.SplitN(version, ":", 2)
ver := ""
if len(ss) == 1 {
ver = ss[0]
} else {
ver = ss[1]
}
return ver[0:strings.Index(ver, ".")]
}
func isOvalDefAffected(def ovalmodels.Definition, req request, family string, running models.Kernel) (affected, notFixedYet bool, fixedIn string) {
func isOvalDefAffected(def ovalmodels.Definition, req request, family string, running models.Kernel, enabledMods []string) (affected, notFixedYet bool, fixedIn string) {
for _, ovalPack := range def.AffectedPacks {
if req.packName != ovalPack.Name {
continue
}
isModularityLabelEmptyOrSame := false
if ovalPack.ModularityLabel != "" {
for _, mod := range enabledMods {
if mod == ovalPack.ModularityLabel {
isModularityLabelEmptyOrSame = true
break
}
}
} else {
isModularityLabelEmptyOrSame = true
}
if !isModularityLabelEmptyOrSame {
continue
}
if running.Release != "" {
switch family {
case config.RedHat, config.CentOS:
// For kernel related packages, ignore OVAL information with different major versions
if _, ok := kernelRelatedPackNames[ovalPack.Name]; ok {
if major(ovalPack.Version) != major(running.Release) {
if util.Major(ovalPack.Version) != util.Major(running.Release) {
continue
}
}
@@ -362,9 +363,6 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family string, ru
return false, false, ""
}
var centosVerPattern = regexp.MustCompile(`\.[es]l(\d+)(?:_\d+)?(?:\.centos)?`)
var esVerPattern = regexp.MustCompile(`\.el(\d+)(?:_\d+)?`)
func lessThan(family, newVer string, packInOVAL ovalmodels.Package) (bool, error) {
switch family {
case config.Debian,
@@ -400,8 +398,8 @@ func lessThan(family, newVer string, packInOVAL ovalmodels.Package) (bool, error
case config.RedHat,
config.CentOS:
vera := rpmver.NewVersion(centosVerPattern.ReplaceAllString(newVer, ".el$1"))
verb := rpmver.NewVersion(esVerPattern.ReplaceAllString(packInOVAL.Version, ".el$1"))
vera := rpmver.NewVersion(centOSVersionToRHEL(newVer))
verb := rpmver.NewVersion(packInOVAL.Version)
return vera.LessThan(verb), nil
default:
@@ -409,3 +407,9 @@ func lessThan(family, newVer string, packInOVAL ovalmodels.Package) (bool, error
}
return false, xerrors.Errorf("Package version comparison not supported: %s", family)
}
var centosVerPattern = regexp.MustCompile(`\.[es]l(\d+)(?:_\d+)?(?:\.centos)?`)
func centOSVersionToRHEL(ver string) string {
return centosVerPattern.ReplaceAllString(ver, ".el$1")
}

View File

@@ -202,6 +202,7 @@ func TestIsOvalDefAffected(t *testing.T) {
req request
family string
kernel models.Kernel
mods []string
}
var tests = []struct {
in in
@@ -1076,9 +1077,85 @@ func TestIsOvalDefAffected(t *testing.T) {
notFixedYet: false,
fixedIn: "3.1.0",
},
// dnf module
{
in: in{
family: config.RedHat,
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "nginx",
Version: "1.16.1-1.module+el8.3.0+8844+e5e7039f.1",
NotFixedYet: false,
ModularityLabel: "nginx:1.16",
},
},
},
req: request{
packName: "nginx",
versionRelease: "1.16.0-1.module+el8.3.0+8844+e5e7039f.1",
},
mods: []string{
"nginx:1.16",
},
},
affected: true,
notFixedYet: false,
fixedIn: "1.16.1-1.module+el8.3.0+8844+e5e7039f.1",
},
// dnf module 2
{
in: in{
family: config.RedHat,
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "nginx",
Version: "1.16.1-1.module+el8.3.0+8844+e5e7039f.1",
NotFixedYet: false,
ModularityLabel: "nginx:1.16",
},
},
},
req: request{
packName: "nginx",
versionRelease: "1.16.2-1.module+el8.3.0+8844+e5e7039f.1",
},
mods: []string{
"nginx:1.16",
},
},
affected: false,
notFixedYet: false,
},
// dnf module 3
{
in: in{
family: config.RedHat,
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "nginx",
Version: "1.16.1-1.module+el8.3.0+8844+e5e7039f.1",
NotFixedYet: false,
ModularityLabel: "nginx:1.16",
},
},
},
req: request{
packName: "nginx",
versionRelease: "1.16.0-1.module+el8.3.0+8844+e5e7039f.1",
},
mods: []string{
"nginx:1.14",
},
},
affected: false,
notFixedYet: false,
},
}
for i, tt := range tests {
affected, notFixedYet, fixedIn := isOvalDefAffected(tt.in.def, tt.in.req, tt.in.family, tt.in.kernel)
affected, notFixedYet, fixedIn := isOvalDefAffected(tt.in.def, tt.in.req, tt.in.family, tt.in.kernel, tt.in.mods)
if tt.affected != affected {
t.Errorf("[%d] affected\nexpected: %v\n actual: %v\n", i, tt.affected, affected)
}
@@ -1091,28 +1168,35 @@ func TestIsOvalDefAffected(t *testing.T) {
}
}
func Test_major(t *testing.T) {
var tests = []struct {
in string
expected string
func Test_centOSVersionToRHEL(t *testing.T) {
type args struct {
ver string
}
tests := []struct {
name string
args args
want string
}{
{
in: "",
expected: "",
name: "remove centos.",
args: args{
ver: "grub2-tools-2.02-0.80.el7.centos.x86_64",
},
want: "grub2-tools-2.02-0.80.el7.x86_64",
},
{
in: "4.1",
expected: "4",
},
{
in: "0:4.1",
expected: "4",
name: "noop",
args: args{
ver: "grub2-tools-2.02-0.80.el7.x86_64",
},
want: "grub2-tools-2.02-0.80.el7.x86_64",
},
}
for i, tt := range tests {
a := major(tt.in)
if tt.expected != a {
t.Errorf("[%d]\nexpected: %s\n actual: %s\n", i, tt.expected, a)
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := centOSVersionToRHEL(tt.args.ver); got != tt.want {
t.Errorf("centOSVersionToRHEL() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -3,7 +3,6 @@ package report
import (
"bytes"
"encoding/json"
"encoding/xml"
"fmt"
"time"
@@ -66,18 +65,6 @@ func (w AzureBlobWriter) Write(rs ...models.ScanResult) (err error) {
return err
}
}
if c.Conf.FormatXML {
k := key + ".xml"
var b []byte
if b, err = xml.Marshal(r); err != nil {
return xerrors.Errorf("Failed to Marshal to XML: %w", err)
}
allBytes := bytes.Join([][]byte{[]byte(xml.Header + vulsOpenTag), b, []byte(vulsCloseTag)}, []byte{})
if err := createBlockBlob(cli, k, allBytes); err != nil {
return err
}
}
}
return
}

View File

@@ -1,14 +1,17 @@
package report
import (
"context"
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
"time"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
)
// ChatWorkWriter send report to ChatWork
@@ -48,26 +51,25 @@ func (w ChatWorkWriter) Write(rs ...models.ScanResult) (err error) {
func chatWorkpostMessage(room, token, message string) error {
uri := fmt.Sprintf("https://api.chatwork.com/v2/rooms/%s/messages=%s", room, token)
payload := url.Values{"body": {message}}
payload := url.Values{
"body": {message},
}
reqs, err := http.NewRequest("POST", uri, strings.NewReader(payload.Encode()))
reqs.Header.Add("X-ChatWorkToken", token)
reqs.Header.Add("Content-Type", "application/x-www-form-urlencoded")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, uri, strings.NewReader(payload.Encode()))
defer cancel()
if err != nil {
return err
}
client := &http.Client{}
req.Header.Add("X-ChatWorkToken", token)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(reqs)
client, err := util.GetHTTPClient(config.Conf.HTTPProxy)
if err != nil {
return err
}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}

View File

@@ -18,7 +18,7 @@ import (
cvemodels "github.com/kotakanbe/go-cve-dictionary/models"
)
// CveClient is api client of CVE disctionary service.
// CveClient is api client of CVE dictionary service.
var CveClient cvedictClient
type cvedictClient struct {
@@ -26,29 +26,6 @@ type cvedictClient struct {
baseURL string
}
func (api *cvedictClient) initialize() {
api.baseURL = config.Conf.CveDict.URL
}
func (api cvedictClient) CheckHealth() error {
if !config.Conf.CveDict.IsFetchViaHTTP() {
util.Log.Debugf("get cve-dictionary from %s", config.Conf.CveDict.Type)
return nil
}
api.initialize()
url := fmt.Sprintf("%s/health", api.baseURL)
var errs []error
var resp *http.Response
resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
// resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return xerrors.Errorf("Failed to request to CVE server. url: %s, errs: %w",
url, errs)
}
return nil
}
type response struct {
Key string
CveDetail cvemodels.CveDetail
@@ -137,9 +114,9 @@ func (api cvedictClient) httpGet(key, url string, resChan chan<- response, errCh
var resp *http.Response
f := func() (err error) {
// resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
resp, body, errs = gorequest.New().Get(url).End()
resp, body, errs = gorequest.New().Timeout(10 * time.Second).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return xerrors.Errorf("HTTP GET Error, url: %s, resp: %v, err: %w",
return xerrors.Errorf("HTTP GET Error, url: %s, resp: %v, err: %s",
url, resp, errs)
}
return nil
@@ -155,7 +132,7 @@ func (api cvedictClient) httpGet(key, url string, resChan chan<- response, errCh
}
cveDetail := cvemodels.CveDetail{}
if err := json.Unmarshal([]byte(body), &cveDetail); err != nil {
errChan <- xerrors.Errorf("Failed to Unmarshall. body: %s, err: %w", body, err)
errChan <- xerrors.Errorf("Failed to Unmarshal. body: %s, err: %w", body, err)
return
}
resChan <- response{
@@ -185,13 +162,13 @@ func (api cvedictClient) httpPost(key, url string, query map[string]string) ([]c
var resp *http.Response
f := func() (err error) {
// req := gorequest.New().SetDebug(config.Conf.Debug).Post(url)
req := gorequest.New().Post(url)
req := gorequest.New().Timeout(10 * time.Second).Post(url)
for key := range query {
req = req.Send(fmt.Sprintf("%s=%s", key, query[key])).Type("json")
}
resp, body, errs = req.End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return xerrors.Errorf("HTTP POST error. url: %s, resp: %v, err: %w", url, resp, errs)
return xerrors.Errorf("HTTP POST error. url: %s, resp: %v, err: %s", url, resp, errs)
}
return nil
}
@@ -206,7 +183,7 @@ func (api cvedictClient) httpPost(key, url string, query map[string]string) ([]c
cveDetails := []cvemodels.CveDetail{}
if err := json.Unmarshal([]byte(body), &cveDetails); err != nil {
return nil,
xerrors.Errorf("Failed to Unmarshall. body: %s, err: %w", body, err)
xerrors.Errorf("Failed to Unmarshal. body: %s, err: %w", body, err)
}
return cveDetails, nil
}

View File

@@ -10,8 +10,8 @@ import (
gostdb "github.com/knqyf263/gost/db"
cvedb "github.com/kotakanbe/go-cve-dictionary/db"
ovaldb "github.com/kotakanbe/goval-dictionary/db"
exploitdb "github.com/mozqnet/go-exploitdb/db"
metasploitdb "github.com/takuzoo3868/go-msfdb/db"
exploitdb "github.com/vulsio/go-exploitdb/db"
"golang.org/x/xerrors"
)
@@ -176,7 +176,7 @@ func NewExploitDB(cnf DBClientConf) (driver exploitdb.DB, locked bool, err error
path = cnf.ExploitCnf.SQLite3Path
if _, err := os.Stat(path); os.IsNotExist(err) {
util.Log.Warnf("--exploitdb-path=%s file not found. Fetch go-exploit-db before reporting if you want to display exploit codes of detected CVE-IDs. For details, see `https://github.com/mozqnet/go-exploitdb`", path)
util.Log.Warnf("--exploitdb-path=%s file not found. Fetch go-exploit-db before reporting if you want to display exploit codes of detected CVE-IDs. For details, see `https://github.com/vulsio/go-exploitdb`", path)
return nil, false, nil
}
}

View File

@@ -123,11 +123,11 @@ func (e *emailSender) sendMail(smtpServerAddr, message string) (err error) {
if ok, param := c.Extension("AUTH"); ok {
authList := strings.Split(param, " ")
auth = e.newSaslClient(authList)
if err = c.Auth(auth); err != nil {
return xerrors.Errorf("Failed to authenticate: %w", err)
}
}
if err = c.Auth(auth); err != nil {
return xerrors.Errorf("Failed to authenticate: %w", err)
}
if err = c.Mail(emailConf.From, nil); err != nil {
return xerrors.Errorf("Failed to send Mail command: %w", err)
}

View File

@@ -1,74 +0,0 @@
package report
import (
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
)
// HipChatWriter send report to HipChat
type HipChatWriter struct{}
func (w HipChatWriter) Write(rs ...models.ScanResult) (err error) {
conf := config.Conf.HipChat
for _, r := range rs {
serverInfo := fmt.Sprintf("%s", r.ServerInfo())
if err = postMessage(conf.Room, conf.AuthToken, serverInfo); err != nil {
return err
}
for _, vinfo := range r.ScannedCves {
maxCvss := vinfo.MaxCvssScore()
severity := strings.ToUpper(maxCvss.Value.Severity)
if severity == "" {
severity = "?"
}
message := fmt.Sprintf(`<a href="https://nvd.nist.gov/vuln/detail\%s"> %s </a> <br/>%s (%s)<br/>%s`,
vinfo.CveID,
vinfo.CveID,
strconv.FormatFloat(maxCvss.Value.Score, 'f', 1, 64),
severity,
vinfo.Summaries(config.Conf.Lang, r.Family)[0].Value,
)
if err = postMessage(conf.Room, conf.AuthToken, message); err != nil {
return err
}
}
}
return nil
}
func postMessage(room, token, message string) error {
uri := fmt.Sprintf("https://api.hipchat.com/v2/room/%s/notification?auth_token=%s", room, token)
payload := url.Values{
"color": {"purple"},
"message_format": {"html"},
"message": {message},
}
reqs, err := http.NewRequest("POST", uri, strings.NewReader(payload.Encode()))
if err != nil {
return err
}
reqs.Header.Add("Content-Type", "application/x-www-form-urlencoded")
client := &http.Client{}
resp, err := client.Do(reqs)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}

View File

@@ -37,7 +37,7 @@ type HTTPResponseWriter struct {
func (w HTTPResponseWriter) Write(rs ...models.ScanResult) (err error) {
res, err := json.Marshal(rs)
if err != nil {
return xerrors.Errorf("Failed to marshal scah results: %w", err)
return xerrors.Errorf("Failed to marshal scan results: %w", err)
}
w.Writer.Header().Set("Content-Type", "application/json")
_, err = w.Writer.Write(res)

View File

@@ -1,9 +1,7 @@
package report
import (
"bytes"
"encoding/json"
"encoding/xml"
"io/ioutil"
"os"
"path/filepath"
@@ -33,13 +31,10 @@ func (w LocalFileWriter) Write(rs ...models.ScanResult) (err error) {
path := filepath.Join(w.CurrentDir, r.ReportFileName())
if c.Conf.FormatJSON {
var p string
if c.Conf.Diff {
p := path + ".json"
if c.Conf.DiffPlus || c.Conf.DiffMinus {
p = path + "_diff.json"
} else {
p = path + ".json"
}
var b []byte
if b, err = json.MarshalIndent(r, "", " "); err != nil {
return xerrors.Errorf("Failed to Marshal to JSON: %w", err)
@@ -50,13 +45,10 @@ func (w LocalFileWriter) Write(rs ...models.ScanResult) (err error) {
}
if c.Conf.FormatList {
var p string
if c.Conf.Diff {
p := path + "_short.txt"
if c.Conf.DiffPlus || c.Conf.DiffMinus {
p = path + "_short_diff.txt"
} else {
p = path + "_short.txt"
}
if err := writeFile(
p, []byte(formatList(r)), 0600); err != nil {
return xerrors.Errorf(
@@ -65,11 +57,9 @@ func (w LocalFileWriter) Write(rs ...models.ScanResult) (err error) {
}
if c.Conf.FormatFullText {
var p string
if c.Conf.Diff {
p := path + "_full.txt"
if c.Conf.DiffPlus || c.Conf.DiffMinus {
p = path + "_full_diff.txt"
} else {
p = path + "_full.txt"
}
if err := writeFile(
@@ -79,28 +69,10 @@ func (w LocalFileWriter) Write(rs ...models.ScanResult) (err error) {
}
}
if c.Conf.FormatXML {
var p string
if c.Conf.Diff {
p = path + "_diff.xml"
} else {
p = path + ".xml"
}
var b []byte
if b, err = xml.Marshal(r); err != nil {
return xerrors.Errorf("Failed to Marshal to XML: %w", err)
}
allBytes := bytes.Join([][]byte{[]byte(xml.Header + vulsOpenTag), b, []byte(vulsCloseTag)}, []byte{})
if err := writeFile(p, allBytes, 0600); err != nil {
return xerrors.Errorf("Failed to write XML. path: %s, err: %w", p, err)
}
}
if c.Conf.FormatCsvList {
p := path + "_short.csv"
if c.Conf.Diff {
p = path + "_short_diff.csv"
p := path + ".csv"
if c.Conf.DiffPlus || c.Conf.DiffMinus {
p = path + "_diff.csv"
}
if err := formatCsvList(r, p); err != nil {
return xerrors.Errorf("Failed to write CSV: %s, %w", p, err)

View File

@@ -7,9 +7,6 @@ import (
"strings"
"time"
"github.com/future-architect/vuls/libmanager"
gostdb "github.com/knqyf263/gost/db"
"github.com/future-architect/vuls/config"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/contrib/owasp-dependency-check/parser"
@@ -17,16 +14,18 @@ import (
"github.com/future-architect/vuls/exploit"
"github.com/future-architect/vuls/github"
"github.com/future-architect/vuls/gost"
"github.com/future-architect/vuls/libmanager"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/msf"
"github.com/future-architect/vuls/oval"
"github.com/future-architect/vuls/util"
"github.com/future-architect/vuls/wordpress"
gostdb "github.com/knqyf263/gost/db"
cvedb "github.com/kotakanbe/go-cve-dictionary/db"
cvemodels "github.com/kotakanbe/go-cve-dictionary/models"
ovaldb "github.com/kotakanbe/goval-dictionary/db"
exploitdb "github.com/mozqnet/go-exploitdb/db"
metasploitdb "github.com/takuzoo3868/go-msfdb/db"
exploitdb "github.com/vulsio/go-exploitdb/db"
"golang.org/x/xerrors"
)
@@ -87,11 +86,12 @@ func FillCveInfos(dbclient DBClient, rs []models.ScanResult, dir string) ([]mode
return nil, xerrors.Errorf("Failed to detect CVE of `%s`: %w", cpeURIs, err)
}
if err := DetectGitHubCves(&r); err != nil {
repos := c.Conf.Servers[r.ServerName].GitHubRepos
if err := DetectGitHubCves(&r, repos); err != nil {
return nil, xerrors.Errorf("Failed to detect GitHub Cves: %w", err)
}
if err := DetectWordPressCves(&r); err != nil {
if err := DetectWordPressCves(&r, &config.Conf.WpScan); err != nil {
return nil, xerrors.Errorf("Failed to detect WordPress Cves: %w", err)
}
@@ -121,30 +121,20 @@ func FillCveInfos(dbclient DBClient, rs []models.ScanResult, dir string) ([]mode
}
}
if c.Conf.Diff {
if c.Conf.DiffPlus || c.Conf.DiffMinus {
prevs, err := loadPrevious(rs)
if err != nil {
return nil, err
}
diff, err := diff(rs, prevs)
if err != nil {
return nil, err
}
for i, r := range diff {
if err := fillCvesWithNvdJvn(dbclient.CveDB, &r); err != nil {
return nil, err
}
rs[i] = r
}
rs = diff(rs, prevs, c.Conf.DiffPlus, c.Conf.DiffMinus)
}
for i, r := range rs {
r = r.FilterByCvssOver(c.Conf.CvssScoreOver)
r = r.FilterIgnoreCves()
r = r.FilterUnfixed()
r = r.FilterUnfixed(c.Conf.IgnoreUnfixed)
r = r.FilterIgnorePkgs()
r = r.FilterInactiveWordPressLibs()
r = r.FilterInactiveWordPressLibs(c.Conf.WpScan.DetectInactive)
if c.Conf.IgnoreUnscoredCves {
r.ScannedCves = r.ScannedCves.FindScoredVulns()
}
@@ -153,7 +143,7 @@ func FillCveInfos(dbclient DBClient, rs []models.ScanResult, dir string) ([]mode
return rs, nil
}
// DetectPkgCVEs detects OS pkg cves
// DetectPkgCves detects OS pkg cves
func DetectPkgCves(dbclient DBClient, r *models.ScanResult) error {
// Pkg Scan
if r.Release != "" {
@@ -168,7 +158,7 @@ func DetectPkgCves(dbclient DBClient, r *models.ScanResult) error {
}
} else if reuseScannedCves(r) {
util.Log.Infof("r.Release is empty. Use CVEs as it as.")
} else if r.Family == config.ServerTypePseudo {
} else if r.Family == c.ServerTypePseudo {
util.Log.Infof("pseudo type. Skip OVAL and gost detection")
} else {
return xerrors.Errorf("Failed to fill CVEs. r.Release is empty")
@@ -205,44 +195,37 @@ func DetectPkgCves(dbclient DBClient, r *models.ScanResult) error {
}
// DetectGitHubCves fetches CVEs from GitHub Security Alerts
func DetectGitHubCves(r *models.ScanResult) error {
repos := c.Conf.Servers[r.ServerName].GitHubRepos
if len(repos) == 0 {
func DetectGitHubCves(r *models.ScanResult, githubConfs map[string]c.GitHubConf) error {
if len(githubConfs) == 0 {
return nil
}
githubInts := GithubSecurityAlerts(repos)
ints := &integrationResults{}
for _, o := range []Integration{githubInts} {
if err := o.apply(r, ints); err != nil {
return xerrors.Errorf("Failed to detect CVE with integration: %w", err)
for ownerRepo, setting := range githubConfs {
ss := strings.Split(ownerRepo, "/")
if len(ss) != 2 {
return xerrors.Errorf("Failed to parse GitHub owner/repo: %s", ownerRepo)
}
owner, repo := ss[0], ss[1]
n, err := github.DetectGitHubSecurityAlerts(r, owner, repo, setting.Token)
if err != nil {
return xerrors.Errorf("Failed to access GitHub Security Alerts: %w", err)
}
util.Log.Infof("%s: %d CVEs detected with GHSA %s/%s",
r.FormatServerName(), n, owner, repo)
}
util.Log.Infof("%s: %d CVEs are detected with GitHub Security Alerts",
r.FormatServerName(), ints.GithubAlertsCveCounts)
return nil
}
// DetectWordPressCves detects CVEs of WordPress
func DetectWordPressCves(r *models.ScanResult) error {
token := c.Conf.Servers[r.ServerName].WordPress.WPVulnDBToken
if token == "" {
func DetectWordPressCves(r *models.ScanResult, wpCnf *c.WpScanConf) error {
if len(r.WordPressPackages) == 0 {
return nil
}
wpVulnCaches := map[string]string{}
wpOpt := WordPressOption{
token,
&wpVulnCaches,
util.Log.Infof("Detect WordPress CVE. pkgs: %d ", len(r.WordPressPackages))
n, err := wordpress.DetectWordPressCves(r, wpCnf)
if err != nil {
return xerrors.Errorf("Failed to detect WordPress CVE: %w", err)
}
ints := &integrationResults{}
for _, o := range []Integration{wpOpt} {
if err := o.apply(r, ints); err != nil {
return xerrors.Errorf("Failed to detect CVE with integration: %w", err)
}
}
util.Log.Infof("%s: %d CVEs are detected with wpscan API",
r.FormatServerName(), ints.WordPressCveCounts)
util.Log.Infof("%s: found %d WordPress CVEs", r.FormatServerName(), n)
return nil
}
@@ -423,9 +406,9 @@ func detectPkgsCvesWithGost(driver gostdb.DB, r *models.ScanResult) error {
}
// fillWithExploitDB fills Exploits with exploit dataabase
// https://github.com/mozqnet/go-exploitdb
// https://github.com/vulsio/go-exploitdb
func fillWithExploitDB(driver exploitdb.DB, r *models.ScanResult) (nExploitCve int, err error) {
return exploit.FillWithExploit(driver, r)
return exploit.FillWithExploit(driver, r, &config.Conf.Exploit)
}
// fillWithMetasploit fills metasploit modules with metasploit database
@@ -437,7 +420,7 @@ func fillWithMetasploit(driver metasploitdb.DB, r *models.ScanResult) (nMetasplo
// DetectCpeURIsCves detects CVEs of given CPE-URIs
func DetectCpeURIsCves(driver cvedb.DB, r *models.ScanResult, cpeURIs []string) error {
nCVEs := 0
if len(cpeURIs) != 0 && driver == nil && !config.Conf.CveDict.IsFetchViaHTTP() {
if len(cpeURIs) != 0 && driver == nil && !c.Conf.CveDict.IsFetchViaHTTP() {
return xerrors.Errorf("cpeURIs %s specified, but cve-dictionary DB not found. Fetch cve-dictionary before reporting. For details, see `https://github.com/kotakanbe/go-cve-dictionary#deploy-go-cve-dictionary`",
cpeURIs)
}
@@ -469,62 +452,6 @@ func DetectCpeURIsCves(driver cvedb.DB, r *models.ScanResult, cpeURIs []string)
return nil
}
type integrationResults struct {
GithubAlertsCveCounts int
WordPressCveCounts int
}
// Integration is integration of vuls report
type Integration interface {
apply(*models.ScanResult, *integrationResults) error
}
// GithubSecurityAlerts :
func GithubSecurityAlerts(githubConfs map[string]config.GitHubConf) Integration {
return GithubSecurityAlertOption{
GithubConfs: githubConfs,
}
}
// GithubSecurityAlertOption :
type GithubSecurityAlertOption struct {
GithubConfs map[string]config.GitHubConf
}
// https://help.github.com/articles/about-security-alerts-for-vulnerable-dependencies/
func (g GithubSecurityAlertOption) apply(r *models.ScanResult, ints *integrationResults) (err error) {
var nCVEs int
for ownerRepo, setting := range g.GithubConfs {
ss := strings.Split(ownerRepo, "/")
owner, repo := ss[0], ss[1]
n, err := github.FillGitHubSecurityAlerts(r, owner, repo, setting.Token)
if err != nil {
return xerrors.Errorf("Failed to access GitHub Security Alerts: %w", err)
}
nCVEs += n
}
ints.GithubAlertsCveCounts = nCVEs
return nil
}
// WordPressOption :
type WordPressOption struct {
token string
wpVulnCaches *map[string]string
}
func (g WordPressOption) apply(r *models.ScanResult, ints *integrationResults) (err error) {
if g.token == "" {
return nil
}
n, err := wordpress.FillWordPress(r, g.token, g.wpVulnCaches)
if err != nil {
return xerrors.Errorf("Failed to fetch from WPVulnDB. Check the WPVulnDBToken in config.toml. err: %w", err)
}
ints.WordPressCveCounts = n
return nil
}
func fillCweDict(r *models.ScanResult) {
uniqCweIDMap := map[string]bool{}
for _, vinfo := range r.ScannedCves {

View File

@@ -3,7 +3,6 @@ package report
import (
"bytes"
"encoding/json"
"encoding/xml"
"fmt"
"path"
"time"
@@ -92,18 +91,6 @@ func (w S3Writer) Write(rs ...models.ScanResult) (err error) {
return err
}
}
if c.Conf.FormatXML {
k := key + ".xml"
var b []byte
if b, err = xml.Marshal(r); err != nil {
return xerrors.Errorf("Failed to Marshal to XML: %w", err)
}
allBytes := bytes.Join([][]byte{[]byte(xml.Header + vulsOpenTag), b, []byte(vulsCloseTag)}, []byte{})
if err := putObject(svc, k, allBytes); err != nil {
return err
}
}
}
return nil
}

View File

@@ -136,14 +136,14 @@ func send(msg message) error {
jsonBody := string(bytes)
f := func() (err error) {
resp, body, errs := gorequest.New().Proxy(config.Conf.HTTPProxy).Post(conf.HookURL).Send(string(jsonBody)).End()
resp, body, errs := gorequest.New().Timeout(10 * time.Second).Proxy(config.Conf.HTTPProxy).Post(conf.HookURL).Send(string(jsonBody)).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
count++
if count == retryMax {
return nil
}
return xerrors.Errorf(
"HTTP POST error. url: %s, resp: %v, body: %s, err: %w",
"HTTP POST error. url: %s, resp: %v, body: %s, err: %s",
conf.HookURL, resp, body, errs)
}
return nil
@@ -206,7 +206,7 @@ func toSlackAttachments(r models.ScanResult) (attaches []slack.Attachment) {
}
a := slack.Attachment{
Title: vinfo.CveID,
Title: vinfo.CveIDDiffFormat(config.Conf.DiffMinus || config.Conf.DiffPlus),
TitleLink: "https://nvd.nist.gov/vuln/detail/" + vinfo.CveID,
Text: attachmentText(vinfo, r.Family, r.CweDict, r.Packages),
MarkdownIn: []string{"text", "pretext"},
@@ -248,7 +248,7 @@ func attachmentText(vinfo models.VulnInfo, osFamily string, cweDict map[string]m
maxCvss := vinfo.MaxCvssScore()
vectors := []string{}
scores := append(vinfo.Cvss3Scores(), vinfo.Cvss2Scores(osFamily)...)
scores := append(vinfo.Cvss3Scores(), vinfo.Cvss2Scores()...)
for _, cvss := range scores {
if cvss.Value.Severity == "" {
continue

View File

@@ -1,81 +0,0 @@
package report
import (
"bytes"
"fmt"
"net/http"
"strconv"
"strings"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
)
// StrideWriter send report to Stride
type StrideWriter struct{}
type strideSender struct{}
func (w StrideWriter) Write(rs ...models.ScanResult) (err error) {
conf := config.Conf.Stride
for _, r := range rs {
w := strideSender{}
serverInfo := fmt.Sprintf("%s", r.ServerInfo())
message := fmt.Sprintf(`{"body":{"version":1,"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":" %s "}]}]}}`,
serverInfo,
)
if err = w.sendMessage(conf.HookURL, conf.AuthToken, message); err != nil {
return err
}
for _, vinfo := range r.ScannedCves {
maxCvss := vinfo.MaxCvssScore()
severity := strings.ToUpper(maxCvss.Value.Severity)
if severity == "" {
severity = "?"
}
message = fmt.Sprintf(`{"body":{"version":1,"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":" %s ","marks": [ { "type": "link", "attrs": { "href": "https://nvd.nist.gov/vuln/detail/%s", "title": "cve" } } ]}]}]}}`,
vinfo.CveID,
vinfo.CveID,
)
if err = w.sendMessage(conf.HookURL, conf.AuthToken, message); err != nil {
return err
}
message = fmt.Sprintf(`{"body":{"version":1,"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":" %s (%s) "}]}]}}`,
strconv.FormatFloat(maxCvss.Value.Score, 'f', 1, 64),
severity,
)
if err = w.sendMessage(conf.HookURL, conf.AuthToken, message); err != nil {
return err
}
message = fmt.Sprintf(`{"body":{"version":1,"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":" %s "}]}]}}`,
vinfo.Summaries(config.Conf.Lang, r.Family)[0].Value,
)
if err = w.sendMessage(conf.HookURL, conf.AuthToken, message); err != nil {
return err
}
}
}
return nil
}
func (w strideSender) sendMessage(uri, token, jsonStr string) error {
reqs, err := http.NewRequest("POST", uri, bytes.NewBuffer([]byte(jsonStr)))
if err != nil {
return err
}
reqs.Header.Add("Content-Type", "application/json")
reqs.Header.Add("Authorization", "Bearer "+token)
client := &http.Client{}
resp, err := client.Do(reqs)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}

View File

@@ -2,9 +2,9 @@ package report
import (
"fmt"
"log/syslog"
"strings"
syslog "github.com/RackSec/srslog"
"golang.org/x/xerrors"
"github.com/future-architect/vuls/config"
@@ -59,7 +59,7 @@ func (w SyslogWriter) encodeSyslog(result models.ScanResult) (messages []string)
kvPairs = append(kvPairs, fmt.Sprintf(`packages="%s"`, pkgs))
kvPairs = append(kvPairs, fmt.Sprintf(`cve_id="%s"`, cveID))
for _, cvss := range vinfo.Cvss2Scores(result.Family) {
for _, cvss := range vinfo.Cvss2Scores() {
kvPairs = append(kvPairs, fmt.Sprintf(`cvss_score_%s_v2="%.2f"`, cvss.Type, cvss.Value.Score))
kvPairs = append(kvPairs, fmt.Sprintf(`cvss_vector_%s_v2="%s"`, cvss.Type, cvss.Value.Vector))
}
@@ -69,7 +69,7 @@ func (w SyslogWriter) encodeSyslog(result models.ScanResult) (messages []string)
kvPairs = append(kvPairs, fmt.Sprintf(`cvss_vector_%s_v3="%s"`, cvss.Type, cvss.Value.Vector))
}
if content, ok := vinfo.CveContents[models.NvdXML]; ok {
if content, ok := vinfo.CveContents[models.Nvd]; ok {
cwes := strings.Join(content.CweIDs, ",")
kvPairs = append(kvPairs, fmt.Sprintf(`cwe_ids="%s"`, cwes))
if config.Conf.Syslog.Verbose {

View File

@@ -33,11 +33,14 @@ func TestSyslogWriterEncodeSyslog(t *testing.T) {
models.PackageFixStatus{Name: "pkg4"},
},
CveContents: models.CveContents{
models.NvdXML: models.CveContent{
models.Nvd: models.CveContent{
Cvss2Score: 5.0,
Cvss2Vector: "AV:L/AC:L/Au:N/C:N/I:N/A:C",
Cvss2Severity: "MEDIUM",
CweIDs: []string{"CWE-20"},
Cvss3Score: 9.8,
Cvss3Vector: "AV:L/AC:L/Au:N/C:N/I:N/A:C",
Cvss3Severity: "HIGH",
},
},
},
@@ -45,9 +48,10 @@ func TestSyslogWriterEncodeSyslog(t *testing.T) {
},
expectedMessages: []string{
`scanned_at="2018-06-13 16:10:00 +0000 UTC" server_name="teste01" os_family="ubuntu" os_release="16.04" ipv4_addr="192.168.0.1,10.0.2.15" ipv6_addr="" packages="pkg1,pkg2" cve_id="CVE-2017-0001"`,
`scanned_at="2018-06-13 16:10:00 +0000 UTC" server_name="teste01" os_family="ubuntu" os_release="16.04" ipv4_addr="192.168.0.1,10.0.2.15" ipv6_addr="" packages="pkg3,pkg4" cve_id="CVE-2017-0002" cvss_score_nvdxml_v2="5.00" cvss_vector_nvdxml_v2="AV:L/AC:L/Au:N/C:N/I:N/A:C" cwe_ids="CWE-20"`,
`scanned_at="2018-06-13 16:10:00 +0000 UTC" server_name="teste01" os_family="ubuntu" os_release="16.04" ipv4_addr="192.168.0.1,10.0.2.15" ipv6_addr="" packages="pkg3,pkg4" cve_id="CVE-2017-0002" cvss_score_nvd_v2="5.00" cvss_vector_nvd_v2="AV:L/AC:L/Au:N/C:N/I:N/A:C" cvss_score_nvd_v3="9.80" cvss_vector_nvd_v3="AV:L/AC:L/Au:N/C:N/I:N/A:C" cwe_ids="CWE-20"`,
},
},
// 1
{
result: models.ScanResult{
ScannedAt: time.Date(2018, 6, 13, 17, 10, 0, 0, time.UTC),
@@ -62,10 +66,11 @@ func TestSyslogWriterEncodeSyslog(t *testing.T) {
},
CveContents: models.CveContents{
models.RedHat: models.CveContent{
Cvss3Score: 5.0,
Cvss3Vector: "AV:L/AC:L/Au:N/C:N/I:N/A:C",
CweIDs: []string{"CWE-284"},
Title: "RHSA-2017:0001: pkg5 security update (Important)",
Cvss3Score: 5.0,
Cvss3Severity: "Medium",
Cvss3Vector: "AV:L/AC:L/Au:N/C:N/I:N/A:C",
CweIDs: []string{"CWE-284"},
Title: "RHSA-2017:0001: pkg5 security update (Important)",
},
},
},

View File

@@ -2,13 +2,16 @@ package report
import (
"bytes"
"context"
"fmt"
"net/http"
"strconv"
"strings"
"time"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"golang.org/x/xerrors"
)
@@ -55,15 +58,21 @@ func (w TelegramWriter) Write(rs ...models.ScanResult) (err error) {
func sendMessage(chatID, token, message string) error {
uri := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", token)
payload := `{"text": "` + strings.Replace(message, `"`, `\"`, -1) + `", "chat_id": "` + chatID + `", "parse_mode": "Markdown" }`
req, err := http.NewRequest("POST", uri, bytes.NewBuffer([]byte(payload)))
req.Header.Add("Content-Type", "application/json")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, uri, bytes.NewBuffer([]byte(payload)))
defer cancel()
if err != nil {
return err
}
req.Header.Add("Content-Type", "application/json")
client, err := util.GetHTTPClient(config.Conf.HTTPProxy)
if err != nil {
return err
}
client := &http.Client{}
resp, err := client.Do(req)
if checkResponse(resp) != nil && err != nil {
fmt.Println(err)
return err
}
defer resp.Body.Close()

View File

@@ -150,8 +150,6 @@ func keybindings(g *gocui.Gui) (err error) {
// errs = append(errs, g.SetKeybinding("msg", gocui.KeyEnter, gocui.ModNone, delMsg))
// errs = append(errs, g.SetKeybinding("detail", gocui.KeyEnter, gocui.ModNone, showMsg))
//TODO Help Ctrl-h
errs = append(errs, g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit))
// errs = append(errs, g.SetKeybinding("side", gocui.KeyEnter, gocui.ModNone, getLine))
// errs = append(errs, g.SetKeybinding("msg", gocui.KeyEnter, gocui.ModNone, delMsg))
@@ -635,6 +633,7 @@ func summaryLines(r models.ScanResult) string {
var cols []string
cols = []string{
fmt.Sprintf(indexFormat, i+1),
string(vinfo.DiffStatus),
vinfo.CveID,
cvssScore + " |",
fmt.Sprintf("%-6s |", av),
@@ -937,20 +936,11 @@ func detailLines() (string, error) {
table := uitable.New()
table.MaxColWidth = maxColWidth
table.Wrap = true
scores := append(vinfo.Cvss3Scores(), vinfo.Cvss2Scores(r.Family)...)
scores := append(vinfo.Cvss3Scores(), vinfo.Cvss2Scores()...)
var cols []interface{}
for _, score := range scores {
if score.Value.Score == 0 && score.Value.Severity == "" {
continue
}
scoreStr := "-"
if 0 < score.Value.Score {
scoreStr = fmt.Sprintf("%3.1f", score.Value.Score)
}
scoreVec := fmt.Sprintf("%s/%s", scoreStr, score.Value.Vector)
cols = []interface{}{
scoreVec,
score.Value.Severity,
score.Value.Format(),
score.Type,
}
table.AddRow(cols...)

View File

@@ -42,6 +42,12 @@ func formatScanSummary(rs ...models.ScanResult) string {
fmt.Sprintf("%s%s", r.Family, r.Release),
r.FormatUpdatablePacksSummary(),
}
if 0 < len(r.WordPressPackages) {
cols = append(cols, fmt.Sprintf("%d WordPress pkgs", len(r.WordPressPackages)))
}
if 0 < len(r.LibraryScanners) {
cols = append(cols, fmt.Sprintf("%d libs", r.LibraryScanners.Total()))
}
} else {
cols = []interface{}{
r.FormatServerName(),
@@ -53,8 +59,7 @@ func formatScanSummary(rs ...models.ScanResult) string {
table.AddRow(cols...)
if len(r.Warnings) != 0 {
warnMsgs = append(warnMsgs, fmt.Sprintf("Warning for %s: %s",
r.FormatServerName(), r.Warnings))
warnMsgs = append(warnMsgs, fmt.Sprintf("Warning: %s", r.Warnings))
}
}
return fmt.Sprintf("%s\n\n%s", table, strings.Join(
@@ -144,7 +149,7 @@ No CVE-IDs are found in updatable packages.
}
data = append(data, []string{
vinfo.CveID,
vinfo.CveIDDiffFormat(config.Conf.DiffMinus || config.Conf.DiffPlus),
fmt.Sprintf("%4.1f", max),
fmt.Sprintf("%5s", vinfo.AttackVector()),
// fmt.Sprintf("%4.1f", v2max),
@@ -208,7 +213,7 @@ No CVE-IDs are found in updatable packages.
}
}
for _, cvss := range vuln.Cvss2Scores(r.Family) {
for _, cvss := range vuln.Cvss2Scores() {
if cvssstr := cvss.Value.Format(); cvssstr != "" {
data = append(data, []string{string(cvss.Type), cvssstr})
}
@@ -368,7 +373,7 @@ No CVE-IDs are found in updatable packages.
table.SetColWidth(80)
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
table.SetHeader([]string{
vuln.CveID,
vuln.CveIDDiffFormat(config.Conf.DiffMinus || config.Conf.DiffPlus),
vuln.PatchStatus(r.Packages),
})
table.SetBorder(true)
@@ -472,15 +477,18 @@ func needToRefreshCve(r models.ScanResult) bool {
func overwriteJSONFile(dir string, r models.ScanResult) error {
before := config.Conf.FormatJSON
beforeDiff := config.Conf.Diff
beforePlusDiff := config.Conf.DiffPlus
beforeMinusDiff := config.Conf.DiffMinus
config.Conf.FormatJSON = true
config.Conf.Diff = false
config.Conf.DiffPlus = false
config.Conf.DiffMinus = false
w := LocalFileWriter{CurrentDir: dir}
if err := w.Write(r); err != nil {
return xerrors.Errorf("Failed to write summary report: %w", err)
}
config.Conf.FormatJSON = before
config.Conf.Diff = beforeDiff
config.Conf.DiffPlus = beforePlusDiff
config.Conf.DiffMinus = beforeMinusDiff
return nil
}
@@ -499,7 +507,7 @@ func loadPrevious(currs models.ScanResults) (prevs models.ScanResults, err error
path := filepath.Join(dir, filename)
r, err := loadOneServerScanResult(path)
if err != nil {
util.Log.Errorf("%+v", err)
util.Log.Debugf("%+v", err)
continue
}
if r.Family == result.Family && r.Release == result.Release {
@@ -515,7 +523,7 @@ func loadPrevious(currs models.ScanResults) (prevs models.ScanResults, err error
return prevs, nil
}
func diff(curResults, preResults models.ScanResults) (diffed models.ScanResults, err error) {
func diff(curResults, preResults models.ScanResults, isPlus, isMinus bool) (diffed models.ScanResults) {
for _, current := range curResults {
found := false
var previous models.ScanResult
@@ -527,24 +535,46 @@ func diff(curResults, preResults models.ScanResults) (diffed models.ScanResults,
}
}
if found {
current.ScannedCves = getDiffCves(previous, current)
packages := models.Packages{}
for _, s := range current.ScannedCves {
for _, affected := range s.AffectedPackages {
p := current.Packages[affected.Name]
packages[affected.Name] = p
}
}
current.Packages = packages
if !found {
diffed = append(diffed, current)
continue
}
cves := models.VulnInfos{}
if isPlus {
cves = getPlusDiffCves(previous, current)
}
if isMinus {
minus := getMinusDiffCves(previous, current)
if len(cves) == 0 {
cves = minus
} else {
for k, v := range minus {
cves[k] = v
}
}
}
packages := models.Packages{}
for _, s := range cves {
for _, affected := range s.AffectedPackages {
var p models.Package
if s.DiffStatus == models.DiffPlus {
p = current.Packages[affected.Name]
} else {
p = previous.Packages[affected.Name]
}
packages[affected.Name] = p
}
}
current.ScannedCves = cves
current.Packages = packages
diffed = append(diffed, current)
}
return diffed, err
return
}
func getDiffCves(previous, current models.ScanResult) models.VulnInfos {
func getPlusDiffCves(previous, current models.ScanResult) models.VulnInfos {
previousCveIDsSet := map[string]bool{}
for _, previousVulnInfo := range previous.ScannedCves {
previousCveIDsSet[previousVulnInfo.CveID] = true
@@ -555,6 +585,7 @@ func getDiffCves(previous, current models.ScanResult) models.VulnInfos {
for _, v := range current.ScannedCves {
if previousCveIDsSet[v.CveID] {
if isCveInfoUpdated(v.CveID, previous, current) {
v.DiffStatus = models.DiffPlus
updated[v.CveID] = v
util.Log.Debugf("updated: %s", v.CveID)
@@ -570,11 +601,12 @@ func getDiffCves(previous, current models.ScanResult) models.VulnInfos {
}
} else {
util.Log.Debugf("new: %s", v.CveID)
v.DiffStatus = models.DiffPlus
new[v.CveID] = v
}
}
if len(updated) == 0 {
if len(updated) == 0 && len(new) == 0 {
util.Log.Infof("%s: There are %d vulnerabilities, but no difference between current result and previous one.", current.FormatServerName(), len(current.ScannedCves))
}
@@ -584,6 +616,27 @@ func getDiffCves(previous, current models.ScanResult) models.VulnInfos {
return updated
}
func getMinusDiffCves(previous, current models.ScanResult) models.VulnInfos {
currentCveIDsSet := map[string]bool{}
for _, currentVulnInfo := range current.ScannedCves {
currentCveIDsSet[currentVulnInfo.CveID] = true
}
clear := models.VulnInfos{}
for _, v := range previous.ScannedCves {
if !currentCveIDsSet[v.CveID] {
v.DiffStatus = models.DiffMinus
clear[v.CveID] = v
util.Log.Debugf("clear: %s", v.CveID)
}
}
if len(clear) == 0 {
util.Log.Infof("%s: There are %d vulnerabilities, but no difference between current result and previous one.", current.FormatServerName(), len(current.ScannedCves))
}
return clear
}
func isCveFixed(current models.VulnInfo, previous models.ScanResult) bool {
preVinfo, _ := previous.ScannedCves[current.CveID]
pre := map[string]bool{}
@@ -601,7 +654,7 @@ func isCveFixed(current models.VulnInfo, previous models.ScanResult) bool {
func isCveInfoUpdated(cveID string, previous, current models.ScanResult) bool {
cTypes := []models.CveContentType{
models.NvdXML,
models.Nvd,
models.Jvn,
models.NewCveContentType(current.Family),
}

View File

@@ -14,6 +14,7 @@ import (
func TestMain(m *testing.M) {
util.Log = util.NewCustomLogger(config.ServerInfo{})
pp.ColoringEnabled = false
code := m.Run()
os.Exit(code)
}
@@ -42,7 +43,7 @@ func TestIsCveInfoUpdated(t *testing.T) {
CveID: "CVE-2017-0001",
CveContents: models.NewCveContents(
models.CveContent{
Type: models.NvdXML,
Type: models.Nvd,
CveID: "CVE-2017-0001",
LastModified: time.Time{},
},
@@ -56,7 +57,7 @@ func TestIsCveInfoUpdated(t *testing.T) {
CveID: "CVE-2017-0001",
CveContents: models.NewCveContents(
models.CveContent{
Type: models.NvdXML,
Type: models.Nvd,
CveID: "CVE-2017-0001",
LastModified: time.Time{},
},
@@ -113,7 +114,7 @@ func TestIsCveInfoUpdated(t *testing.T) {
CveID: "CVE-2017-0003",
CveContents: models.NewCveContents(
models.CveContent{
Type: models.NvdXML,
Type: models.Nvd,
CveID: "CVE-2017-0002",
LastModified: new,
},
@@ -128,7 +129,7 @@ func TestIsCveInfoUpdated(t *testing.T) {
CveID: "CVE-2017-0003",
CveContents: models.NewCveContents(
models.CveContent{
Type: models.NvdXML,
Type: models.Nvd,
CveID: "CVE-2017-0002",
LastModified: old,
},
@@ -150,7 +151,7 @@ func TestIsCveInfoUpdated(t *testing.T) {
CveID: "CVE-2017-0004",
CveContents: models.NewCveContents(
models.CveContent{
Type: models.NvdXML,
Type: models.Nvd,
CveID: "CVE-2017-0002",
LastModified: old,
},
@@ -174,7 +175,7 @@ func TestIsCveInfoUpdated(t *testing.T) {
}
}
func TestDiff(t *testing.T) {
func TestPlusMinusDiff(t *testing.T) {
atCurrent, _ := time.Parse("2006-01-02", "2014-12-31")
atPrevious, _ := time.Parse("2006-01-02", "2014-11-31")
var tests = []struct {
@@ -182,81 +183,56 @@ func TestDiff(t *testing.T) {
inPrevious models.ScanResults
out models.ScanResult
}{
//same
{
inCurrent: models.ScanResults{
{
ScannedAt: atCurrent,
ServerName: "u16",
Family: "ubuntu",
Release: "16.04",
ScannedCves: models.VulnInfos{
"CVE-2012-6702": {
CveID: "CVE-2012-6702",
AffectedPackages: models.PackageFixStatuses{{Name: "libexpat1"}},
DistroAdvisories: []models.DistroAdvisory{},
CpeURIs: []string{},
},
"CVE-2014-9761": {
CveID: "CVE-2014-9761",
AffectedPackages: models.PackageFixStatuses{{Name: "libc-bin"}},
DistroAdvisories: []models.DistroAdvisory{},
CpeURIs: []string{},
},
},
Packages: models.Packages{},
Errors: []string{},
Optional: map[string]interface{}{},
},
},
inPrevious: models.ScanResults{
{
ScannedAt: atPrevious,
ServerName: "u16",
Family: "ubuntu",
Release: "16.04",
ScannedCves: models.VulnInfos{
"CVE-2012-6702": {
CveID: "CVE-2012-6702",
AffectedPackages: models.PackageFixStatuses{{Name: "libexpat1"}},
DistroAdvisories: []models.DistroAdvisory{},
CpeURIs: []string{},
},
"CVE-2014-9761": {
CveID: "CVE-2014-9761",
AffectedPackages: models.PackageFixStatuses{{Name: "libc-bin"}},
DistroAdvisories: []models.DistroAdvisory{},
CpeURIs: []string{},
},
},
Packages: models.Packages{},
Errors: []string{},
Optional: map[string]interface{}{},
},
},
out: models.ScanResult{
ScannedAt: atCurrent,
ServerName: "u16",
Family: "ubuntu",
Release: "16.04",
Packages: models.Packages{},
ScannedCves: models.VulnInfos{},
Errors: []string{},
Optional: map[string]interface{}{},
},
},
//plus, minus
{
inCurrent: models.ScanResults{
{
ScannedAt: atCurrent,
ServerName: "u16",
Family: "ubuntu",
Release: "16.04",
ScannedCves: models.VulnInfos{
"CVE-2016-6662": {
CveID: "CVE-2016-6662",
AffectedPackages: models.PackageFixStatuses{{Name: "mysql-libs"}},
DistroAdvisories: []models.DistroAdvisory{},
CpeURIs: []string{},
},
},
Packages: models.Packages{
@@ -267,34 +243,45 @@ func TestDiff(t *testing.T) {
NewVersion: "5.1.73",
NewRelease: "8.el6_8",
Repository: "",
Changelog: models.Changelog{
Contents: "",
Method: "",
},
},
},
},
},
inPrevious: models.ScanResults{
{
ScannedAt: atPrevious,
ServerName: "u16",
Family: "ubuntu",
Release: "16.04",
ScannedCves: models.VulnInfos{},
ScannedAt: atPrevious,
ServerName: "u16",
ScannedCves: models.VulnInfos{
"CVE-2020-6662": {
CveID: "CVE-2020-6662",
AffectedPackages: models.PackageFixStatuses{{Name: "bind"}},
},
},
Packages: models.Packages{
"bind": {
Name: "bind",
Version: "5.1.73",
Release: "7.el6",
NewVersion: "5.1.73",
NewRelease: "8.el6_8",
Repository: "",
},
},
},
},
out: models.ScanResult{
ScannedAt: atCurrent,
ServerName: "u16",
Family: "ubuntu",
Release: "16.04",
ScannedCves: models.VulnInfos{
"CVE-2016-6662": {
CveID: "CVE-2016-6662",
AffectedPackages: models.PackageFixStatuses{{Name: "mysql-libs"}},
DistroAdvisories: []models.DistroAdvisory{},
CpeURIs: []string{},
DiffStatus: "+",
},
"CVE-2020-6662": {
CveID: "CVE-2020-6662",
AffectedPackages: models.PackageFixStatuses{{Name: "bind"}},
DiffStatus: "-",
},
},
Packages: models.Packages{
@@ -305,10 +292,14 @@ func TestDiff(t *testing.T) {
NewVersion: "5.1.73",
NewRelease: "8.el6_8",
Repository: "",
Changelog: models.Changelog{
Contents: "",
Method: "",
},
},
"bind": {
Name: "bind",
Version: "5.1.73",
Release: "7.el6",
NewVersion: "5.1.73",
NewRelease: "8.el6_8",
Repository: "",
},
},
},
@@ -316,7 +307,312 @@ func TestDiff(t *testing.T) {
}
for i, tt := range tests {
diff, _ := diff(tt.inCurrent, tt.inPrevious)
diff := diff(tt.inCurrent, tt.inPrevious, true, true)
for _, actual := range diff {
if !reflect.DeepEqual(actual.ScannedCves, tt.out.ScannedCves) {
h := pp.Sprint(actual.ScannedCves)
x := pp.Sprint(tt.out.ScannedCves)
t.Errorf("[%d] cves actual: \n %s \n expected: \n %s", i, h, x)
}
for j := range tt.out.Packages {
if !reflect.DeepEqual(tt.out.Packages[j], actual.Packages[j]) {
h := pp.Sprint(tt.out.Packages[j])
x := pp.Sprint(actual.Packages[j])
t.Errorf("[%d] packages actual: \n %s \n expected: \n %s", i, x, h)
}
}
}
}
}
func TestPlusDiff(t *testing.T) {
atCurrent, _ := time.Parse("2006-01-02", "2014-12-31")
atPrevious, _ := time.Parse("2006-01-02", "2014-11-31")
var tests = []struct {
inCurrent models.ScanResults
inPrevious models.ScanResults
out models.ScanResult
}{
{
// same
inCurrent: models.ScanResults{
{
ScannedAt: atCurrent,
ServerName: "u16",
ScannedCves: models.VulnInfos{
"CVE-2012-6702": {
CveID: "CVE-2012-6702",
AffectedPackages: models.PackageFixStatuses{{Name: "libexpat1"}},
},
"CVE-2014-9761": {
CveID: "CVE-2014-9761",
AffectedPackages: models.PackageFixStatuses{{Name: "libc-bin"}},
},
},
},
},
inPrevious: models.ScanResults{
{
ScannedAt: atPrevious,
ServerName: "u16",
ScannedCves: models.VulnInfos{
"CVE-2012-6702": {
CveID: "CVE-2012-6702",
AffectedPackages: models.PackageFixStatuses{{Name: "libexpat1"}},
},
"CVE-2014-9761": {
CveID: "CVE-2014-9761",
AffectedPackages: models.PackageFixStatuses{{Name: "libc-bin"}},
},
},
},
},
out: models.ScanResult{
ScannedAt: atCurrent,
ServerName: "u16",
ScannedCves: models.VulnInfos{},
},
},
// plus
{
inCurrent: models.ScanResults{
{
ScannedAt: atCurrent,
ServerName: "u16",
ScannedCves: models.VulnInfos{
"CVE-2016-6662": {
CveID: "CVE-2016-6662",
AffectedPackages: models.PackageFixStatuses{{Name: "mysql-libs"}},
},
},
Packages: models.Packages{
"mysql-libs": {
Name: "mysql-libs",
Version: "5.1.73",
Release: "7.el6",
NewVersion: "5.1.73",
NewRelease: "8.el6_8",
Repository: "",
},
},
},
},
inPrevious: models.ScanResults{
{
ScannedAt: atPrevious,
ServerName: "u16",
},
},
out: models.ScanResult{
ScannedAt: atCurrent,
ServerName: "u16",
ScannedCves: models.VulnInfos{
"CVE-2016-6662": {
CveID: "CVE-2016-6662",
AffectedPackages: models.PackageFixStatuses{{Name: "mysql-libs"}},
DiffStatus: "+",
},
},
Packages: models.Packages{
"mysql-libs": {
Name: "mysql-libs",
Version: "5.1.73",
Release: "7.el6",
NewVersion: "5.1.73",
NewRelease: "8.el6_8",
Repository: "",
},
},
},
},
// minus
{
inCurrent: models.ScanResults{
{
ScannedAt: atCurrent,
ServerName: "u16",
ScannedCves: models.VulnInfos{
"CVE-2012-6702": {
CveID: "CVE-2012-6702",
},
},
},
},
inPrevious: models.ScanResults{
{
ScannedAt: atPrevious,
ServerName: "u16",
ScannedCves: models.VulnInfos{
"CVE-2012-6702": {
CveID: "CVE-2012-6702",
},
"CVE-2014-9761": {
CveID: "CVE-2014-9761",
},
},
},
},
out: models.ScanResult{
ScannedAt: atCurrent,
ServerName: "u16",
ScannedCves: models.VulnInfos{},
},
},
}
for i, tt := range tests {
diff := diff(tt.inCurrent, tt.inPrevious, true, false)
for _, actual := range diff {
if !reflect.DeepEqual(actual.ScannedCves, tt.out.ScannedCves) {
h := pp.Sprint(actual.ScannedCves)
x := pp.Sprint(tt.out.ScannedCves)
t.Errorf("[%d] cves actual: \n %s \n expected: \n %s", i, h, x)
}
for j := range tt.out.Packages {
if !reflect.DeepEqual(tt.out.Packages[j], actual.Packages[j]) {
h := pp.Sprint(tt.out.Packages[j])
x := pp.Sprint(actual.Packages[j])
t.Errorf("[%d] packages actual: \n %s \n expected: \n %s", i, x, h)
}
}
}
}
}
func TestMinusDiff(t *testing.T) {
atCurrent, _ := time.Parse("2006-01-02", "2014-12-31")
atPrevious, _ := time.Parse("2006-01-02", "2014-11-31")
var tests = []struct {
inCurrent models.ScanResults
inPrevious models.ScanResults
out models.ScanResult
}{
// same
{
inCurrent: models.ScanResults{
{
ScannedAt: atCurrent,
ServerName: "u16",
ScannedCves: models.VulnInfos{
"CVE-2012-6702": {
CveID: "CVE-2012-6702",
AffectedPackages: models.PackageFixStatuses{{Name: "libexpat1"}},
},
"CVE-2014-9761": {
CveID: "CVE-2014-9761",
AffectedPackages: models.PackageFixStatuses{{Name: "libc-bin"}},
},
},
},
},
inPrevious: models.ScanResults{
{
ScannedAt: atPrevious,
ServerName: "u16",
ScannedCves: models.VulnInfos{
"CVE-2012-6702": {
CveID: "CVE-2012-6702",
AffectedPackages: models.PackageFixStatuses{{Name: "libexpat1"}},
},
"CVE-2014-9761": {
CveID: "CVE-2014-9761",
AffectedPackages: models.PackageFixStatuses{{Name: "libc-bin"}},
},
},
},
},
out: models.ScanResult{
ScannedAt: atCurrent,
ServerName: "u16",
ScannedCves: models.VulnInfos{},
},
},
// minus
{
inCurrent: models.ScanResults{
{
ScannedAt: atPrevious,
ServerName: "u16",
Packages: models.Packages{},
},
},
inPrevious: models.ScanResults{
{
ScannedAt: atCurrent,
ServerName: "u16",
ScannedCves: models.VulnInfos{
"CVE-2016-6662": {
CveID: "CVE-2016-6662",
AffectedPackages: models.PackageFixStatuses{{Name: "mysql-libs"}},
},
},
Packages: models.Packages{
"mysql-libs": {
Name: "mysql-libs",
Version: "5.1.73",
Release: "7.el6",
NewVersion: "5.1.73",
NewRelease: "8.el6_8",
Repository: "",
},
},
},
},
out: models.ScanResult{
ScannedAt: atCurrent,
ServerName: "u16",
ScannedCves: models.VulnInfos{
"CVE-2016-6662": {
CveID: "CVE-2016-6662",
AffectedPackages: models.PackageFixStatuses{{Name: "mysql-libs"}},
DiffStatus: "-",
},
},
Packages: models.Packages{
"mysql-libs": {
Name: "mysql-libs",
Version: "5.1.73",
Release: "7.el6",
NewVersion: "5.1.73",
NewRelease: "8.el6_8",
Repository: "",
},
},
},
},
// plus
{
inCurrent: models.ScanResults{
{
ScannedAt: atPrevious,
ServerName: "u16",
ScannedCves: models.VulnInfos{
"CVE-2016-6662": {
CveID: "CVE-2016-6662",
AffectedPackages: models.PackageFixStatuses{{Name: "mysql-libs"}},
},
},
},
},
inPrevious: models.ScanResults{
{
ScannedAt: atCurrent,
ServerName: "u16",
ScannedCves: models.VulnInfos{},
},
},
out: models.ScanResult{
ScannedAt: atCurrent,
ServerName: "u16",
ScannedCves: models.VulnInfos{},
},
},
}
for i, tt := range tests {
diff := diff(tt.inCurrent, tt.inPrevious, false, true)
for _, actual := range diff {
if !reflect.DeepEqual(actual.ScannedCves, tt.out.ScannedCves) {
h := pp.Sprint(actual.ScannedCves)
@@ -356,7 +652,7 @@ func TestIsCveFixed(t *testing.T) {
},
CveContents: models.NewCveContents(
models.CveContent{
Type: models.NvdXML,
Type: models.Nvd,
CveID: "CVE-2016-6662",
LastModified: time.Time{},
},
@@ -374,7 +670,7 @@ func TestIsCveFixed(t *testing.T) {
},
CveContents: models.NewCveContents(
models.CveContent{
Type: models.NvdXML,
Type: models.Nvd,
CveID: "CVE-2016-6662",
LastModified: time.Time{},
},
@@ -397,7 +693,7 @@ func TestIsCveFixed(t *testing.T) {
},
CveContents: models.NewCveContents(
models.CveContent{
Type: models.NvdXML,
Type: models.Nvd,
CveID: "CVE-2016-6662",
LastModified: time.Time{},
},
@@ -415,7 +711,7 @@ func TestIsCveFixed(t *testing.T) {
},
CveContents: models.NewCveContents(
models.CveContent{
Type: models.NvdXML,
Type: models.Nvd,
CveID: "CVE-2016-6662",
LastModified: time.Time{},
},

View File

@@ -2,19 +2,22 @@ package saas
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
"path"
"strings"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/service/sts"
"github.com/future-architect/vuls/config"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
@@ -40,8 +43,7 @@ type payload struct {
}
// UploadSaas : UploadSaas
func (w Writer) Write(rs ...models.ScanResult) (err error) {
// dir string, configPath string, config *c.Config
func (w Writer) Write(rs ...models.ScanResult) error {
if len(rs) == 0 {
return nil
}
@@ -59,34 +61,25 @@ func (w Writer) Write(rs ...models.ScanResult) (err error) {
ScannedIPv4s: strings.Join(ipv4s, ", "),
ScannedIPv6s: strings.Join(ipv6s, ", "),
}
var body []byte
if body, err = json.Marshal(payload); err != nil {
body, err := json.Marshal(payload)
if err != nil {
return xerrors.Errorf("Failed to Marshal to JSON: %w", err)
}
var req *http.Request
if req, err = http.NewRequest("POST", c.Conf.Saas.URL, bytes.NewBuffer(body)); err != nil {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.Conf.Saas.URL, bytes.NewBuffer(body))
defer cancel()
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")
proxy := c.Conf.HTTPProxy
var client http.Client
if proxy != "" {
proxyURL, _ := url.Parse(proxy)
client = http.Client{
Transport: &http.Transport{
Proxy: http.ProxyURL(proxyURL),
},
}
} else {
client = http.Client{}
client, err := util.GetHTTPClient(config.Conf.HTTPProxy)
if err != nil {
return err
}
var resp *http.Response
if resp, err = client.Do(req); err != nil {
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
@@ -94,44 +87,40 @@ func (w Writer) Write(rs ...models.ScanResult) (err error) {
return xerrors.Errorf("Failed to get Credential. Request JSON : %s,", string(body))
}
var t []byte
if t, err = ioutil.ReadAll(resp.Body); err != nil {
t, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
var tempCredential TempCredential
if err = json.Unmarshal(t, &tempCredential); err != nil {
if err := json.Unmarshal(t, &tempCredential); err != nil {
return xerrors.Errorf("Failed to unmarshal saas credential file. err : %s", err)
}
credential := credentials.NewStaticCredentialsFromCreds(credentials.Value{
AccessKeyID: *tempCredential.Credential.AccessKeyId,
SecretAccessKey: *tempCredential.Credential.SecretAccessKey,
SessionToken: *tempCredential.Credential.SessionToken,
sess, err := session.NewSession(&aws.Config{
Credentials: credentials.NewStaticCredentialsFromCreds(credentials.Value{
AccessKeyID: *tempCredential.Credential.AccessKeyId,
SecretAccessKey: *tempCredential.Credential.SecretAccessKey,
SessionToken: *tempCredential.Credential.SessionToken,
}),
Region: aws.String("ap-northeast-1"),
})
var sess *session.Session
if sess, err = session.NewSession(&aws.Config{
Credentials: credential,
Region: aws.String("ap-northeast-1"),
}); err != nil {
if err != nil {
return xerrors.Errorf("Failed to new aws session. err: %w", err)
}
svc := s3.New(sess)
for _, r := range rs {
s3Key := renameKeyNameUTC(r.ScannedAt, r.ServerUUID, r.Container)
var b []byte
if b, err = json.Marshal(r); err != nil {
b, err := json.Marshal(r)
if err != nil {
return xerrors.Errorf("Failed to Marshal to JSON: %w", err)
}
util.Log.Infof("Uploading...: ServerName: %s, ", r.ServerName)
s3Key := renameKeyName(r.ServerUUID, r.Container)
putObjectInput := &s3.PutObjectInput{
Bucket: aws.String(tempCredential.S3Bucket),
Key: aws.String(path.Join(tempCredential.S3ResultsDir, s3Key)),
Body: bytes.NewReader(b),
}
if _, err := svc.PutObject(putObjectInput); err != nil {
return xerrors.Errorf("Failed to upload data to %s/%s, err: %w",
tempCredential.S3Bucket, s3Key, err)
@@ -140,3 +129,10 @@ func (w Writer) Write(rs ...models.ScanResult) (err error) {
util.Log.Infof("done")
return nil
}
func renameKeyName(uuid string, container models.Container) string {
if len(container.ContainerID) == 0 {
return fmt.Sprintf("%s.json", uuid)
}
return fmt.Sprintf("%s@%s.json", container.UUID, uuid)
}

View File

@@ -9,7 +9,6 @@ import (
"regexp"
"sort"
"strings"
"time"
"github.com/BurntSushi/toml"
c "github.com/future-architect/vuls/config"
@@ -19,14 +18,6 @@ import (
"golang.org/x/xerrors"
)
func renameKeyNameUTC(scannedAt time.Time, uuid string, container models.Container) string {
timestr := scannedAt.UTC().Format(time.RFC3339)
if len(container.ContainerID) == 0 {
return fmt.Sprintf("%s/%s.json", timestr, uuid)
}
return fmt.Sprintf("%s/%s@%s.json", timestr, container.UUID, uuid)
}
const reUUID = "[\\da-f]{8}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{12}"
// Scanning with the -containers-only flag at scan time, the UUID of Container Host may not be generated,
@@ -101,7 +92,6 @@ func EnsureUUIDs(configPath string, results models.ScanResults) (err error) {
return err
}
server.UUIDs[name] = serverUUID
server = cleanForTOMLEncoding(server, c.Conf.Default)
c.Conf.Servers[r.ServerName] = server
if r.IsContainer() {
@@ -116,98 +106,16 @@ func EnsureUUIDs(configPath string, results models.ScanResults) (err error) {
server = cleanForTOMLEncoding(server, c.Conf.Default)
c.Conf.Servers[name] = server
}
email := &c.Conf.EMail
if email.SMTPAddr == "" {
email = nil
}
slack := &c.Conf.Slack
if slack.HookURL == "" {
slack = nil
}
cveDict := &c.Conf.CveDict
ovalDict := &c.Conf.OvalDict
gost := &c.Conf.Gost
exploit := &c.Conf.Exploit
metasploit := &c.Conf.Metasploit
http := &c.Conf.HTTP
if http.URL == "" {
http = nil
}
syslog := &c.Conf.Syslog
if syslog.Host == "" {
syslog = nil
}
aws := &c.Conf.AWS
if aws.S3Bucket == "" {
aws = nil
}
azure := &c.Conf.Azure
if azure.AccountName == "" {
azure = nil
}
stride := &c.Conf.Stride
if stride.HookURL == "" {
stride = nil
}
hipChat := &c.Conf.HipChat
if hipChat.AuthToken == "" {
hipChat = nil
}
chatWork := &c.Conf.ChatWork
if chatWork.APIToken == "" {
chatWork = nil
}
saas := &c.Conf.Saas
if saas.GroupID == 0 {
saas = nil
if c.Conf.Default.WordPress != nil && c.Conf.Default.WordPress.IsZero() {
c.Conf.Default.WordPress = nil
}
c := struct {
CveDict *c.GoCveDictConf `toml:"cveDict"`
OvalDict *c.GovalDictConf `toml:"ovalDict"`
Gost *c.GostConf `toml:"gost"`
Exploit *c.ExploitConf `toml:"exploit"`
Metasploit *c.MetasploitConf `toml:"metasploit"`
Slack *c.SlackConf `toml:"slack"`
Email *c.SMTPConf `toml:"email"`
HTTP *c.HTTPConf `toml:"http"`
Syslog *c.SyslogConf `toml:"syslog"`
AWS *c.AWS `toml:"aws"`
Azure *c.Azure `toml:"azure"`
Stride *c.StrideConf `toml:"stride"`
HipChat *c.HipChatConf `toml:"hipChat"`
ChatWork *c.ChatWorkConf `toml:"chatWork"`
Saas *c.SaasConf `toml:"saas"`
Saas *c.SaasConf `toml:"saas"`
Default c.ServerInfo `toml:"default"`
Servers map[string]c.ServerInfo `toml:"servers"`
}{
CveDict: cveDict,
OvalDict: ovalDict,
Gost: gost,
Exploit: exploit,
Metasploit: metasploit,
Slack: slack,
Email: email,
HTTP: http,
Syslog: syslog,
AWS: aws,
Azure: azure,
Stride: stride,
HipChat: hipChat,
ChatWork: chatWork,
Saas: saas,
Saas: &c.Conf.Saas,
Default: c.Conf.Default,
Servers: c.Conf.Servers,
}
@@ -290,5 +198,11 @@ func cleanForTOMLEncoding(server c.ServerInfo, def c.ServerInfo) c.ServerInfo {
}
}
if server.WordPress != nil {
if server.WordPress.IsZero() || reflect.DeepEqual(server.WordPress, def.WordPress) {
server.WordPress = nil
}
}
return server
}

View File

@@ -32,19 +32,16 @@ func newAlpine(c config.ServerInfo) *alpine {
// Alpine
// https://github.com/mizzy/specinfra/blob/master/lib/specinfra/helper/detect_os/alpine.rb
func detectAlpine(c config.ServerInfo) (itsMe bool, os osTypeInterface) {
os = newAlpine(c)
func detectAlpine(c config.ServerInfo) (bool, osTypeInterface) {
if r := exec(c, "ls /etc/alpine-release", noSudo); !r.isSuccess() {
return false, os
return false, nil
}
if r := exec(c, "cat /etc/alpine-release", noSudo); r.isSuccess() {
os := newAlpine(c)
os.setDistro(config.Alpine, strings.TrimSpace(r.Stdout))
return true, os
}
return false, os
return false, nil
}
func (o *alpine) checkScanMode() error {
@@ -73,7 +70,6 @@ func (o *alpine) apkUpdate() error {
}
func (o *alpine) preCure() error {
o.log.Infof("Scanning in %s", o.getServerInfo().Mode)
if err := o.detectIPAddr(); err != nil {
o.log.Warnf("Failed to detect IP addresses: %s", err)
o.warns = append(o.warns, err)
@@ -92,6 +88,7 @@ func (o *alpine) detectIPAddr() (err error) {
}
func (o *alpine) scanPackages() error {
o.log.Infof("Scanning OS pkg in %s", o.getServerInfo().Mode)
if err := o.apkUpdate(); err != nil {
return err
}

View File

@@ -35,7 +35,7 @@ type base struct {
Platform models.Platform
osPackages
LibraryScanners []models.LibraryScanner
WordPress *models.WordPressPackages
WordPress models.WordPressPackages
log *logrus.Entry
errs []error
@@ -322,7 +322,7 @@ func (l *base) detectPlatform() {
var dsFingerPrintPrefix = "AgentStatus.agentCertHash: "
func (l *base) detectDeepSecurity() (fingerprint string, err error) {
func (l *base) detectDeepSecurity() (string, error) {
// only work root user
if l.getServerInfo().Mode.IsFastRoot() {
if r := l.exec("test -f /opt/ds_agent/dsa_query", sudo); r.isSuccess() {
@@ -449,6 +449,7 @@ func (l *base) convertToModel() models.ScanResult {
RunningKernel: l.Kernel,
Packages: l.Packages,
SrcPackages: l.SrcPackages,
EnabledDnfModules: l.EnabledDnfModules,
WordPressPackages: l.WordPress,
LibraryScanners: l.LibraryScanners,
Optional: l.ServerInfo.Optional,
@@ -525,7 +526,6 @@ func (l *base) parseSystemctlStatus(stdout string) string {
}
func (l *base) scanLibraries() (err error) {
// image already detected libraries
if len(l.LibraryScanners) != 0 {
return nil
}
@@ -535,6 +535,8 @@ func (l *base) scanLibraries() (err error) {
return nil
}
l.log.Info("Scanning Lockfile...")
libFilemap := map[string][]byte{}
detectFiles := l.ServerInfo.Lockfiles
@@ -619,35 +621,15 @@ func (d *DummyFileInfo) IsDir() bool { return false }
//Sys is
func (d *DummyFileInfo) Sys() interface{} { return nil }
func (l *base) scanWordPress() (err error) {
wpOpts := []string{l.ServerInfo.WordPress.OSUser,
l.ServerInfo.WordPress.DocRoot,
l.ServerInfo.WordPress.CmdPath,
l.ServerInfo.WordPress.WPVulnDBToken,
}
var isScanWp, hasEmptyOpt bool
for _, opt := range wpOpts {
if opt != "" {
isScanWp = true
break
} else {
hasEmptyOpt = true
}
}
if !isScanWp {
func (l *base) scanWordPress() error {
if l.ServerInfo.WordPress.IsZero() || l.ServerInfo.Type == config.ServerTypePseudo {
return nil
}
if hasEmptyOpt {
return xerrors.Errorf("%s has empty WordPress opts: %s",
l.getServerInfo().GetServerName(), wpOpts)
}
l.log.Info("Scanning WordPress...")
cmd := fmt.Sprintf("sudo -u %s -i -- %s cli version --allow-root",
l.ServerInfo.WordPress.OSUser,
l.ServerInfo.WordPress.CmdPath)
if r := exec(l.ServerInfo, cmd, noSudo); !r.isSuccess() {
l.ServerInfo.WordPress.WPVulnDBToken = "secret"
return xerrors.Errorf("Failed to exec `%s`. Check the OS user, command path of wp-cli, DocRoot and permission: %#v", cmd, l.ServerInfo.WordPress)
}
@@ -655,7 +637,7 @@ func (l *base) scanWordPress() (err error) {
if err != nil {
return xerrors.Errorf("Failed to scan wordpress: %w", err)
}
l.WordPress = wp
l.WordPress = *wp
return nil
}
@@ -742,6 +724,7 @@ func (l *base) detectWpPlugins() ([]models.WpPackage, error) {
}
func (l *base) scanPorts() (err error) {
l.log.Info("Scanning listen port...")
dest := l.detectScanDest()
open, err := l.execPortsScan(dest)
if err != nil {
@@ -837,7 +820,7 @@ func (l *base) findPortTestSuccessOn(listenIPPorts []string, searchListenPort mo
for _, ipPort := range listenIPPorts {
ipPort, err := models.NewPortStat(ipPort)
if err != nil {
util.Log.Warnf("Failed to find: %+v", err)
l.log.Warnf("Failed to find: %+v", err)
continue
}
if searchListenPort.BindAddress == "*" {
@@ -852,7 +835,7 @@ func (l *base) findPortTestSuccessOn(listenIPPorts []string, searchListenPort mo
return addrs
}
func (l *base) ps() (stdout string, err error) {
func (l *base) ps() (string, error) {
cmd := `LANGUAGE=en_US.UTF-8 ps --no-headers --ppid 2 -p 2 --deselect -o pid,comm`
r := l.exec(util.PrependProxyEnv(cmd), noSudo)
if !r.isSuccess() {
@@ -875,7 +858,7 @@ func (l *base) parsePs(stdout string) map[string]string {
return pidNames
}
func (l *base) lsProcExe(pid string) (stdout string, err error) {
func (l *base) lsProcExe(pid string) (string, error) {
cmd := fmt.Sprintf("ls -l /proc/%s/exe", pid)
r := l.exec(util.PrependProxyEnv(cmd), sudo)
if !r.isSuccess() {
@@ -892,7 +875,7 @@ func (l *base) parseLsProcExe(stdout string) (string, error) {
return ss[10], nil
}
func (l *base) grepProcMap(pid string) (stdout string, err error) {
func (l *base) grepProcMap(pid string) (string, error) {
cmd := fmt.Sprintf(`cat /proc/%s/maps 2>/dev/null | grep -v " 00:00 " | awk '{print $6}' | sort -n | uniq`, pid)
r := l.exec(util.PrependProxyEnv(cmd), sudo)
if !r.isSuccess() {
@@ -905,15 +888,16 @@ func (l *base) parseGrepProcMap(stdout string) (soPaths []string) {
scanner := bufio.NewScanner(strings.NewReader(stdout))
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
line = strings.Split(line, ";")[0]
soPaths = append(soPaths, line)
}
return soPaths
}
func (l *base) lsOfListen() (stdout string, err error) {
cmd := `lsof -i -P -n | grep LISTEN`
func (l *base) lsOfListen() (string, error) {
cmd := `lsof -i -P -n`
r := l.exec(util.PrependProxyEnv(cmd), sudo)
if !r.isSuccess(0, 1) {
if !r.isSuccess() {
return "", xerrors.Errorf("Failed to lsof: %s", r)
}
return r.Stdout, nil
@@ -923,7 +907,11 @@ func (l *base) parseLsOf(stdout string) map[string][]string {
portPids := map[string][]string{}
scanner := bufio.NewScanner(strings.NewReader(stdout))
for scanner.Scan() {
ss := strings.Fields(scanner.Text())
line := scanner.Text()
if !strings.Contains(line, "LISTEN") {
continue
}
ss := strings.Fields(line)
if len(ss) < 10 {
continue
}

View File

@@ -218,12 +218,14 @@ func Test_base_parseGrepProcMap(t *testing.T) {
args: args{
`/etc/selinux/targeted/contexts/files/file_contexts.bin
/etc/selinux/targeted/contexts/files/file_contexts.homedirs.bin
/usr/lib64/libdl-2.28.so`,
/usr/lib64/libdl-2.28.so
/usr/lib64/libnss_files-2.17.so;601ccbf3`,
},
wantSoPaths: []string{
"/etc/selinux/targeted/contexts/files/file_contexts.bin",
"/etc/selinux/targeted/contexts/files/file_contexts.homedirs.bin",
"/usr/lib64/libdl-2.28.so",
`/usr/lib64/libnss_files-2.17.so`,
},
},
}
@@ -255,6 +257,7 @@ sshd 644 root 4u IPv6 16716 0t0 TCP *:22 (LISTEN)
squid 959 proxy 11u IPv6 16351 0t0 TCP *:3128 (LISTEN)
node 1498 ubuntu 21u IPv6 20132 0t0 TCP *:35401 (LISTEN)
node 1498 ubuntu 22u IPv6 20133 0t0 TCP *:44801 (LISTEN)
rpcbind 568 rpc 7u IPv6 15149 0t0 UDP *:111
docker-pr 9135 root 4u IPv6 297133 0t0 TCP *:6379 (LISTEN)`,
},
wantPortPid: map[string][]string{

View File

@@ -40,18 +40,16 @@ func newDebian(c config.ServerInfo) *debian {
// Ubuntu, Debian, Raspbian
// https://github.com/serverspec/specinfra/blob/master/lib/specinfra/helper/detect_os/debian.rb
func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err error) {
deb = newDebian(c)
func detectDebian(c config.ServerInfo) (bool, osTypeInterface, error) {
if r := exec(c, "ls /etc/debian_version", noSudo); !r.isSuccess() {
if r.Error != nil {
return false, deb, nil
return false, nil, nil
}
if r.ExitStatus == 255 {
return false, deb, xerrors.Errorf("Unable to connect via SSH. Scan with -vvv option to print SSH debugging messages and check SSH settings. If you have never SSH to the host to be scanned, SSH to the host before scanning in order to add the HostKey. %s@%s port: %s\n%s", c.User, c.Host, c.Port, r)
return false, nil, xerrors.Errorf("Unable to connect via SSH. Scan with -vvv option to print SSH debugging messages and check SSH settings. If you have never SSH to the host to be scanned, SSH to the host before scanning in order to add the HostKey. %s@%s port: %s\n%s", c.User, c.Host, c.Port, r)
}
util.Log.Debugf("Not Debian like Linux. %s", r)
return false, deb, nil
return false, nil, nil
}
// Raspbian
@@ -62,8 +60,8 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
// Raspbian GNU/Linux 7 \n \l
result := strings.Fields(r.Stdout)
if len(result) > 2 && result[0] == config.Raspbian {
distro := strings.ToLower(trim(result[0]))
deb.setDistro(distro, trim(result[2]))
deb := newDebian(c)
deb.setDistro(strings.ToLower(trim(result[0])), trim(result[2]))
return true, deb, nil
}
}
@@ -76,10 +74,10 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
re := regexp.MustCompile(`(?s)^Distributor ID:\s*(.+?)\n*Release:\s*(.+?)$`)
result := re.FindStringSubmatch(trim(r.Stdout))
deb := newDebian(c)
if len(result) == 0 {
deb.setDistro("debian/ubuntu", "unknown")
util.Log.Warnf(
"Unknown Debian/Ubuntu version. lsb_release -ir: %s", r)
util.Log.Warnf("Unknown Debian/Ubuntu version. lsb_release -ir: %s", r)
} else {
distro := strings.ToLower(trim(result[1]))
deb.setDistro(distro, trim(result[2]))
@@ -95,6 +93,7 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
// DISTRIB_DESCRIPTION="Ubuntu 14.04.2 LTS"
re := regexp.MustCompile(`(?s)^DISTRIB_ID=(.+?)\n*DISTRIB_RELEASE=(.+?)\n.*$`)
result := re.FindStringSubmatch(trim(r.Stdout))
deb := newDebian(c)
if len(result) == 0 {
util.Log.Warnf(
"Unknown Debian/Ubuntu. cat /etc/lsb-release: %s", r)
@@ -109,12 +108,13 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
// Debian
cmd := "cat /etc/debian_version"
if r := exec(c, cmd, noSudo); r.isSuccess() {
deb := newDebian(c)
deb.setDistro(config.Debian, trim(r.Stdout))
return true, deb, nil
}
util.Log.Debugf("Not Debian like Linux: %s", c.ServerName)
return false, deb, nil
return false, nil, nil
}
func trim(str string) string {
@@ -241,7 +241,6 @@ func (o *debian) checkDeps() error {
}
func (o *debian) preCure() error {
o.log.Infof("Scanning in %s", o.getServerInfo().Mode)
if err := o.detectIPAddr(); err != nil {
o.log.Warnf("Failed to detect IP addresses: %s", err)
o.warns = append(o.warns, err)
@@ -277,6 +276,7 @@ func (o *debian) detectIPAddr() (err error) {
}
func (o *debian) scanPackages() error {
o.log.Infof("Scanning OS pkg in %s", o.getServerInfo().Mode)
// collect the running kernel information
release, version, err := o.runningKernel()
if err != nil {
@@ -968,7 +968,7 @@ func (o *debian) getCveIDsFromChangelog(
// If the version is not in changelog, return entire changelog to put into cache
pack := o.Packages[name]
pack.Changelog = models.Changelog{
pack.Changelog = &models.Changelog{
Contents: changelog,
Method: models.FailedToFindVersionInChangelog,
}
@@ -1018,7 +1018,7 @@ func (o *debian) parseChangelog(changelog, name, ver string, confidence models.C
if !found {
if o.Distro.Family == config.Raspbian {
pack := o.Packages[name]
pack.Changelog = models.Changelog{
pack.Changelog = &models.Changelog{
Contents: strings.Join(buf, "\n"),
Method: models.ChangelogLenientMatchStr,
}
@@ -1032,7 +1032,7 @@ func (o *debian) parseChangelog(changelog, name, ver string, confidence models.C
}
pack := o.Packages[name]
pack.Changelog = models.Changelog{
pack.Changelog = &models.Changelog{
Contents: "",
Method: models.FailedToFindVersionInChangelog,
}
@@ -1046,7 +1046,7 @@ func (o *debian) parseChangelog(changelog, name, ver string, confidence models.C
Method: confidence.DetectionMethod,
}
pack := o.Packages[name]
pack.Changelog = clog
pack.Changelog = &clog
cves := []DetectedCveID{}
for _, id := range cveIDs {
@@ -1297,7 +1297,8 @@ func (o *debian) dpkgPs() error {
pidListenPorts := map[string][]models.PortStat{}
stdout, err = o.lsOfListen()
if err != nil {
return xerrors.Errorf("Failed to ls of: %w", err)
// warning only, continue scanning
o.log.Warnf("Failed to lsof: %+v", err)
}
portPids := o.parseLsOf(stdout)
for ipPort, pids := range portPids {
@@ -1332,7 +1333,8 @@ func (o *debian) dpkgPs() error {
for _, n := range pkgNames {
p, ok := o.Packages[n]
if !ok {
return xerrors.Errorf("pkg not found %s", n)
o.log.Warnf("Failed to FindByFQPN: %+v", err)
continue
}
p.AffectedProcs = append(p.AffectedProcs, proc)
o.Packages[p.Name] = p

View File

@@ -225,7 +225,7 @@ systemd (228-5) unstable; urgency=medium`,
for i, tt := range tests {
aCveIDs, aPack := d.getCveIDsFromChangelog(tt.in[2], tt.in[0], tt.in[1])
if len(aCveIDs) != len(tt.cveIDs) {
t.Errorf("[%d] Len of return array are'nt same. expected %#v, actual %#v", i, tt.cveIDs, aCveIDs)
t.Errorf("[%d] Len of return array aren't same. expected %#v, actual %#v", i, tt.cveIDs, aCveIDs)
t.Errorf(pp.Sprintf("%s", tt.in))
continue
}
@@ -433,7 +433,7 @@ func TestGetChangelogCache(t *testing.T) {
d := newDebian(config.ServerInfo{})
actual := d.getChangelogCache(&meta, pack)
if actual != "" {
t.Errorf("Failed to get empty stirng from cache:")
t.Errorf("Failed to get empty string from cache:")
}
clog := "changelog-text"
@@ -794,7 +794,7 @@ vlc (3.0.10-0+deb10u1) buster-security; urgency=medium`,
},
expect: expect{
cveIDs: []DetectedCveID{{"CVE-2020-13428", models.ChangelogExactMatch}},
pack: models.Package{Changelog: models.Changelog{
pack: models.Package{Changelog: &models.Changelog{
Contents: `vlc (3.0.11-0+deb10u1+rpt2) buster; urgency=medium
* Add MMAL patch 19
@@ -837,7 +837,7 @@ vlc (3.0.11-0+deb10u1) buster-security; urgency=high
},
expect: expect{
cveIDs: []DetectedCveID{},
pack: models.Package{Changelog: models.Changelog{
pack: models.Package{Changelog: &models.Changelog{
Contents: `realvnc-vnc (6.7.2.42622) stable; urgency=low
* Debian package for VNC Server

View File

@@ -31,15 +31,14 @@ func newBsd(c config.ServerInfo) *bsd {
}
//https://github.com/mizzy/specinfra/blob/master/lib/specinfra/helper/detect_os/freebsd.rb
func detectFreebsd(c config.ServerInfo) (itsMe bool, bsd osTypeInterface) {
bsd = newBsd(c)
func detectFreebsd(c config.ServerInfo) (bool, osTypeInterface) {
// Prevent from adding `set -o pipefail` option
c.Distro = config.Distro{Family: config.FreeBSD}
if r := exec(c, "uname", noSudo); r.isSuccess() {
if strings.Contains(strings.ToLower(r.Stdout), config.FreeBSD) == true {
if b := exec(c, "freebsd-version", noSudo); b.isSuccess() {
bsd := newBsd(c)
rel := strings.TrimSpace(b.Stdout)
bsd.setDistro(config.FreeBSD, rel)
return true, bsd
@@ -47,7 +46,7 @@ func detectFreebsd(c config.ServerInfo) (itsMe bool, bsd osTypeInterface) {
}
}
util.Log.Debugf("Not FreeBSD. servernam: %s", c.ServerName)
return false, bsd
return false, nil
}
func (o *bsd) checkScanMode() error {
@@ -69,9 +68,9 @@ func (o *bsd) checkDeps() error {
}
func (o *bsd) preCure() error {
o.log.Infof("Scanning in %s", o.getServerInfo().Mode)
if err := o.detectIPAddr(); err != nil {
o.log.Debugf("Failed to detect IP addresses: %s", err)
o.log.Warnf("Failed to detect IP addresses: %s", err)
o.warns = append(o.warns, err)
}
// Ignore this error as it just failed to detect the IP addresses
return nil
@@ -115,6 +114,7 @@ func (l *base) parseIfconfig(stdout string) (ipv4Addrs []string, ipv6Addrs []str
}
func (o *bsd) scanPackages() error {
o.log.Infof("Scanning OS pkg in %s", o.getServerInfo().Mode)
// collect the running kernel information
release, version, err := o.runningKernel()
if err != nil {

View File

@@ -12,9 +12,12 @@ type pseudo struct {
}
func detectPseudo(c config.ServerInfo) (itsMe bool, pseudo osTypeInterface, err error) {
p := newPseudo(c)
p.setDistro(config.ServerTypePseudo, "")
return c.Type == config.ServerTypePseudo, p, nil
if c.Type == config.ServerTypePseudo {
p := newPseudo(c)
p.setDistro(config.ServerTypePseudo, "")
return true, p, nil
}
return false, nil, nil
}
func newPseudo(c config.ServerInfo) *pseudo {

View File

@@ -52,7 +52,7 @@ func detectRedhat(c config.ServerInfo) (bool, osTypeInterface) {
release := result[2]
switch strings.ToLower(result[1]) {
case "centos", "centos linux":
case "centos", "centos linux", "centos stream":
cent := newCentOS(c)
cent.setDistro(config.CentOS, release)
return true, cent
@@ -193,27 +193,27 @@ func (o *redhatBase) postScan() error {
}
func (o *redhatBase) detectIPAddr() (err error) {
o.log.Infof("Scanning in %s", o.getServerInfo().Mode)
o.ServerInfo.IPv4Addrs, o.ServerInfo.IPv6Addrs, err = o.ip()
return err
}
func (o *redhatBase) scanPackages() error {
installed, err := o.scanInstalledPackages()
func (o *redhatBase) scanPackages() (err error) {
o.log.Infof("Scanning OS pkg in %s", o.getServerInfo().Mode)
o.Packages, err = o.scanInstalledPackages()
if err != nil {
o.log.Errorf("Failed to scan installed packages: %s", err)
return err
return xerrors.Errorf("Failed to scan installed packages: %w", err)
}
o.Packages = installed
rebootRequired, err := o.rebootRequired()
if o.EnabledDnfModules, err = o.detectEnabledDnfModules(); err != nil {
return xerrors.Errorf("Failed to detect installed dnf modules: %w", err)
}
o.Kernel.RebootRequired, err = o.rebootRequired()
if err != nil {
err = xerrors.Errorf("Failed to detect the kernel reboot required: %w", err)
o.log.Warnf("err: %+v", err)
o.warns = append(o.warns, err)
// Only warning this error
} else {
o.Kernel.RebootRequired = rebootRequired
}
if o.getServerInfo().Mode.IsOffline() {
@@ -231,8 +231,7 @@ func (o *redhatBase) scanPackages() error {
o.warns = append(o.warns, err)
// Only warning this error
} else {
installed.MergeNewVersion(updatable)
o.Packages = installed
o.Packages.MergeNewVersion(updatable)
}
return nil
}
@@ -261,7 +260,7 @@ func (o *redhatBase) scanInstalledPackages() (models.Packages, error) {
Version: version,
}
r := o.exec(o.rpmQa(o.Distro), noSudo)
r := o.exec(o.rpmQa(), noSudo)
if !r.isSuccess() {
return nil, xerrors.Errorf("Scan packages failed: %s", r)
}
@@ -312,11 +311,22 @@ func (o *redhatBase) parseInstalledPackages(stdout string) (models.Packages, mod
}
func (o *redhatBase) parseInstalledPackagesLine(line string) (models.Package, error) {
for _, suffix := range []string{
"Permission denied",
"is not owned by any package",
"No such file or directory",
} {
if strings.HasSuffix(line, suffix) {
return models.Package{},
xerrors.Errorf("Failed to parse package line: %s", line)
}
}
fields := strings.Fields(line)
if len(fields) != 5 {
return models.Package{},
xerrors.Errorf("Failed to parse package line: %s", line)
}
ver := ""
epoch := fields[1]
if epoch == "0" || epoch == "(none)" {
@@ -370,12 +380,6 @@ func (o *redhatBase) parseUpdatablePacksLines(stdout string) (models.Packages, e
updatable := models.Packages{}
lines := strings.Split(stdout, "\n")
for _, line := range lines {
// TODO remove
// if strings.HasPrefix(line, "Obsoleting") ||
// strings.HasPrefix(line, "Security:") {
// // see https://github.com/future-architect/vuls/issues/165
// continue
// }
if len(strings.TrimSpace(line)) == 0 {
continue
} else if strings.HasPrefix(line, "Loading") {
@@ -494,7 +498,8 @@ func (o *redhatBase) yumPs() error {
pidListenPorts := map[string][]models.PortStat{}
stdout, err = o.lsOfListen()
if err != nil {
return xerrors.Errorf("Failed to ls of: %w", err)
// warning only, continue scanning
o.log.Warnf("Failed to lsof: %+v", err)
}
portPids := o.parseLsOf(stdout)
for ipPort, pids := range portPids {
@@ -509,15 +514,14 @@ func (o *redhatBase) yumPs() error {
}
for pid, loadedFiles := range pidLoadedFiles {
o.log.Debugf("rpm -qf: %#v", loadedFiles)
pkgNames, err := o.getPkgName(loadedFiles)
pkgNameVerRels, err := o.getPkgNameVerRels(loadedFiles)
if err != nil {
o.log.Debugf("Failed to get package name by file path: %s, err: %s", pkgNames, err)
o.log.Debugf("Failed to get package name by file path: %s, err: %s", pkgNameVerRels, err)
continue
}
uniq := map[string]struct{}{}
for _, name := range pkgNames {
for _, name := range pkgNameVerRels {
uniq[name] = struct{}{}
}
@@ -531,10 +535,11 @@ func (o *redhatBase) yumPs() error {
ListenPortStats: pidListenPorts[pid],
}
for fqpn := range uniq {
p, err := o.Packages.FindByFQPN(fqpn)
for pkgNameVerRel := range uniq {
p, err := o.Packages.FindByFQPN(pkgNameVerRel)
if err != nil {
return err
o.log.Warnf("Failed to FindByFQPN: %+v", err)
continue
}
p.AffectedProcs = append(p.AffectedProcs, proc)
o.Packages[p.Name] = *p
@@ -606,7 +611,7 @@ func (o *redhatBase) parseNeedsRestarting(stdout string) (procs []models.NeedRes
cmd := fmt.Sprintf("LANGUAGE=en_US.UTF-8 which %s", path)
r := o.exec(cmd, sudo)
if !r.isSuccess() {
o.log.Warnf("Failed to exec which %s: %s", path, r)
o.log.Debugf("Failed to exec which %s: %s", path, r)
continue
}
path = strings.TrimSpace(r.Stdout)
@@ -625,7 +630,7 @@ func (o *redhatBase) parseNeedsRestarting(stdout string) (procs []models.NeedRes
func (o *redhatBase) procPathToFQPN(execCommand string) (string, error) {
execCommand = strings.Replace(execCommand, "\x00", " ", -1) // for CentOS6.9
path := strings.Fields(execCommand)[0]
cmd := `LANGUAGE=en_US.UTF-8 rpm -qf --queryformat "%{NAME}-%{EPOCH}:%{VERSION}-%{RELEASE}.%{ARCH}\n" ` + path
cmd := `LANGUAGE=en_US.UTF-8 rpm -qf --queryformat "%{NAME}-%{EPOCH}:%{VERSION}-%{RELEASE}\n" ` + path
r := o.exec(cmd, noSudo)
if !r.isSuccess() {
return "", xerrors.Errorf("Failed to SSH: %s", r)
@@ -634,56 +639,99 @@ func (o *redhatBase) procPathToFQPN(execCommand string) (string, error) {
return strings.Replace(fqpn, "-(none):", "-", -1), nil
}
func (o *redhatBase) getPkgName(paths []string) (pkgNames []string, err error) {
cmd := o.rpmQf(o.Distro) + strings.Join(paths, " ")
func (o *redhatBase) getPkgNameVerRels(paths []string) (pkgNameVerRels []string, err error) {
cmd := o.rpmQf() + strings.Join(paths, " ")
r := o.exec(util.PrependProxyEnv(cmd), noSudo)
if !r.isSuccess(0, 2, 4, 8) {
return nil, xerrors.Errorf("Failed to rpm -qf: %s, cmd: %s", r, cmd)
}
// rpm exit code means `the number` of errors.
// https://listman.redhat.com/archives/rpm-list/2005-July/msg00071.html
// If we treat non-zero exit codes of `rpm` as errors,
// we will be missing a partial package list we can get.
scanner := bufio.NewScanner(strings.NewReader(r.Stdout))
for scanner.Scan() {
line := scanner.Text()
pack, err := o.parseInstalledPackagesLine(line)
if err != nil {
o.log.Debugf("Failed to parse rpm -qf line: %s, r: %s. continue", line, r)
o.log.Debugf("Failed to parse rpm -qf line: %s, r: %s", line, r)
continue
}
pkgNames = append(pkgNames, pack.FQPN())
if _, ok := o.Packages[pack.Name]; !ok {
o.log.Debugf("Failed to rpm -qf. pkg: %+v not found, r: %s, line: %s", pack, r, line)
continue
}
pkgNameVerRels = append(pkgNameVerRels, pack.FQPN())
}
return pkgNames, nil
return pkgNameVerRels, nil
}
func (o *redhatBase) rpmQa(distro config.Distro) string {
func (o *redhatBase) rpmQa() string {
const old = `rpm -qa --queryformat "%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH}\n"`
const new = `rpm -qa --queryformat "%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH}\n"`
switch distro.Family {
switch o.Distro.Family {
case config.SUSEEnterpriseServer:
if v, _ := distro.MajorVersion(); v < 12 {
if v, _ := o.Distro.MajorVersion(); v < 12 {
return old
}
return new
default:
if v, _ := distro.MajorVersion(); v < 6 {
if v, _ := o.Distro.MajorVersion(); v < 6 {
return old
}
return new
}
}
func (o *redhatBase) rpmQf(distro config.Distro) string {
func (o *redhatBase) rpmQf() string {
const old = `rpm -qf --queryformat "%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH}\n" `
const new = `rpm -qf --queryformat "%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH}\n" `
switch distro.Family {
switch o.Distro.Family {
case config.SUSEEnterpriseServer:
if v, _ := distro.MajorVersion(); v < 12 {
if v, _ := o.Distro.MajorVersion(); v < 12 {
return old
}
return new
default:
if v, _ := distro.MajorVersion(); v < 6 {
if v, _ := o.Distro.MajorVersion(); v < 6 {
return old
}
return new
}
}
func (o *redhatBase) detectEnabledDnfModules() ([]string, error) {
switch o.Distro.Family {
case config.RedHat, config.CentOS:
//TODO OracleLinux
default:
return nil, nil
}
if v, _ := o.Distro.MajorVersion(); v < 8 {
return nil, nil
}
cmd := `dnf --nogpgcheck --cacheonly --color=never --quiet module list --enabled`
r := o.exec(util.PrependProxyEnv(cmd), noSudo)
if !r.isSuccess() {
if strings.Contains(r.Stdout, "Cache-only enabled but no cache") {
return nil, xerrors.Errorf("sudo yum check-update to make local cache before scanning: %s", r)
}
return nil, xerrors.Errorf("Failed to dnf module list: %s", r)
}
return o.parseDnfModuleList(r.Stdout)
}
func (o *redhatBase) parseDnfModuleList(stdout string) (labels []string, err error) {
scanner := bufio.NewScanner(strings.NewReader(stdout))
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "Hint:") || !strings.Contains(line, "[i]") {
continue
}
ss := strings.Fields(line)
if len(ss) < 2 {
continue
}
labels = append(labels, fmt.Sprintf("%s:%s", ss[0], ss[1]))
}
return
}

View File

@@ -137,12 +137,13 @@ kernel-devel 0 2.6.32 695.20.3.el6 x86_64`,
}
}
func TestParseScanedPackagesLineRedhat(t *testing.T) {
func TestParseInstalledPackagesLine(t *testing.T) {
r := newRHEL(config.ServerInfo{})
var packagetests = []struct {
in string
pack models.Package
err bool
}{
{
"openssl 0 1.0.1e 30.el6.11 x86_64",
@@ -151,6 +152,7 @@ func TestParseScanedPackagesLineRedhat(t *testing.T) {
Version: "1.0.1e",
Release: "30.el6.11",
},
false,
},
{
"Percona-Server-shared-56 1 5.6.19 rel67.0.el6 x84_64",
@@ -159,11 +161,23 @@ func TestParseScanedPackagesLineRedhat(t *testing.T) {
Version: "1:5.6.19",
Release: "rel67.0.el6",
},
false,
},
{
"error: file /run/log/journal/346a500b7fb944199748954baca56086/system.journal: Permission denied",
models.Package{},
true,
},
}
for _, tt := range packagetests {
p, _ := r.parseInstalledPackagesLine(tt.in)
for i, tt := range packagetests {
p, err := r.parseInstalledPackagesLine(tt.in)
if err == nil && tt.err {
t.Errorf("Expected err not occurred: %d", i)
}
if err != nil && !tt.err {
t.Errorf("UnExpected err not occurred: %d", i)
}
if p.Name != tt.pack.Name {
t.Errorf("name: expected %s, actual %s", tt.pack.Name, p.Name)
}
@@ -383,3 +397,44 @@ func TestParseNeedsRestarting(t *testing.T) {
}
}
}
func Test_redhatBase_parseDnfModuleList(t *testing.T) {
type args struct {
stdout string
}
tests := []struct {
name string
args args
wantLabels []string
wantErr bool
}{
{
name: "Success",
args: args{
stdout: `Red Hat Enterprise Linux 8 for x86_64 - AppStream from RHUI (RPMs)
Name Stream Profiles Summary
virt rhel [d][e] common [d] Virtualization module
nginx 1.14 [d][e] common [d] [i] nginx webserver
Hint: [d]efault, [e]nabled, [x]disabled, [i]nstalled`,
},
wantLabels: []string{
"nginx:1.14",
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
o := &redhatBase{}
gotLabels, err := o.parseDnfModuleList(tt.args.stdout)
if (err != nil) != tt.wantErr {
t.Errorf("redhatBase.parseDnfModuleList() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(gotLabels, tt.wantLabels) {
t.Errorf("redhatBase.parseDnfModuleList() = %v, want %v", gotLabels, tt.wantLabels)
}
})
}
}

View File

@@ -70,6 +70,9 @@ type osPackages struct {
// installed source packages (Debian based only)
SrcPackages models.SrcPackages
// enabled dnf modules or packages
EnabledDnfModules []string
// unsecure packages
VulnInfos models.VulnInfos
@@ -170,25 +173,20 @@ func PrintSSHableServerNames() bool {
// InitServers detect the kind of OS distribution of target servers
func InitServers(timeoutSec int) error {
// use global servers, errServers when scan containers
servers, errServers = detectServerOSes(timeoutSec)
if len(servers) == 0 {
return xerrors.New("No scannable base servers")
hosts, errHosts := detectServerOSes(timeoutSec)
if len(hosts) == 0 {
return xerrors.New("No scannable host OS")
}
containers, errContainers := detectContainerOSes(hosts, timeoutSec)
// scan additional servers
var actives, inactives []osTypeInterface
oks, errs := detectContainerOSes(timeoutSec)
actives = append(actives, oks...)
inactives = append(inactives, errs...)
if config.Conf.ContainersOnly {
servers = actives
errServers = inactives
} else {
servers = append(servers, actives...)
errServers = append(errServers, inactives...)
// set to pkg global variable
for _, host := range hosts {
if !host.getServerInfo().ContainersOnly {
servers = append(servers, host)
}
}
servers = append(servers, containers...)
errServers = append(errHosts, errContainers...)
if len(servers) == 0 {
return xerrors.New("No scannable servers")
@@ -257,11 +255,11 @@ func detectServerOSes(timeoutSec int) (servers, errServers []osTypeInterface) {
return
}
func detectContainerOSes(timeoutSec int) (actives, inactives []osTypeInterface) {
func detectContainerOSes(hosts []osTypeInterface, timeoutSec int) (actives, inactives []osTypeInterface) {
util.Log.Info("Detecting OS of containers... ")
osTypesChan := make(chan []osTypeInterface, len(servers))
osTypesChan := make(chan []osTypeInterface, len(hosts))
defer close(osTypesChan)
for _, s := range servers {
for _, s := range hosts {
go func(s osTypeInterface) {
defer func() {
if p := recover(); p != nil {
@@ -274,7 +272,7 @@ func detectContainerOSes(timeoutSec int) (actives, inactives []osTypeInterface)
}
timeout := time.After(time.Duration(timeoutSec) * time.Second)
for i := 0; i < len(servers); i++ {
for i := 0; i < len(hosts); i++ {
select {
case res := <-osTypesChan:
for _, osi := range res {
@@ -492,7 +490,6 @@ func Scan(timeoutSec int) error {
}
}()
util.Log.Info("Scanning vulnerable OS packages...")
scannedAt := time.Now()
dir, err := EnsureResultDir(scannedAt)
if err != nil {
@@ -628,7 +625,7 @@ func setupChangelogCache() error {
// GetScanResults returns ScanResults from
func GetScanResults(scannedAt time.Time, timeoutSec int) (results models.ScanResults, err error) {
parallelExec(func(o osTypeInterface) (err error) {
if !(config.Conf.LibsOnly || config.Conf.WordPressOnly) {
if o.getServerInfo().Module.IsScanOSPkg() {
if err = o.preCure(); err != nil {
return err
}
@@ -639,14 +636,20 @@ func GetScanResults(scannedAt time.Time, timeoutSec int) (results models.ScanRes
return err
}
}
if err = o.scanWordPress(); err != nil {
return xerrors.Errorf("Failed to scan WordPress: %w", err)
if o.getServerInfo().Module.IsScanPort() {
if err = o.scanPorts(); err != nil {
return xerrors.Errorf("Failed to scan Ports: %w", err)
}
}
if err = o.scanLibraries(); err != nil {
return xerrors.Errorf("Failed to scan Library: %w", err)
if o.getServerInfo().Module.IsScanWordPress() {
if err = o.scanWordPress(); err != nil {
return xerrors.Errorf("Failed to scan WordPress: %w", err)
}
}
if err = o.scanPorts(); err != nil {
return xerrors.Errorf("Failed to scan Ports: %w", err)
if o.getServerInfo().Module.IsScanLockFile() {
if err = o.scanLibraries(); err != nil {
return xerrors.Errorf("Failed to scan Library: %w", err)
}
}
return nil
}, timeoutSec)
@@ -659,6 +662,7 @@ func GetScanResults(scannedAt time.Time, timeoutSec int) (results models.ScanRes
for _, s := range append(servers, errServers...) {
r := s.convertToModel()
checkEOL(&r)
r.ScannedAt = scannedAt
r.ScannedVersion = config.Version
r.ScannedRevision = config.Revision
@@ -676,6 +680,42 @@ func GetScanResults(scannedAt time.Time, timeoutSec int) (results models.ScanRes
return results, nil
}
func checkEOL(r *models.ScanResult) {
switch r.Family {
case config.ServerTypePseudo, config.Raspbian:
return
}
eol, found := config.GetEOL(r.Family, r.Release)
if !found {
r.Warnings = append(r.Warnings,
fmt.Sprintf("Failed to check EOL. Register the issue to https://github.com/future-architect/vuls/issues with the information in `Family: %s Release: %s`",
r.Family, r.Release))
return
}
now := time.Now()
if eol.IsStandardSupportEnded(now) {
r.Warnings = append(r.Warnings, "Standard OS support is EOL(End-of-Life). Purchase extended support if available or Upgrading your OS is strongly recommended.")
if eol.ExtendedSupportUntil.IsZero() {
return
}
if !eol.IsExtendedSuppportEnded(now) {
r.Warnings = append(r.Warnings,
fmt.Sprintf("Extended support available until %s. Check the vendor site.",
eol.ExtendedSupportUntil.Format("2006-01-02")))
} else {
r.Warnings = append(r.Warnings,
"Extended support is also EOL. There are many Vulnerabilities that are not detected, Upgrading your OS strongly recommended.")
}
} else if !eol.StandardSupportUntil.IsZero() &&
now.AddDate(0, 3, 0).After(eol.StandardSupportUntil) {
r.Warnings = append(r.Warnings,
fmt.Sprintf("Standard OS support will be end in 3 months. EOL date: %s",
eol.StandardSupportUntil.Format("2006-01-02")))
}
}
func writeScanResults(jsonDir string, results models.ScanResults) error {
config.Conf.FormatJSON = true
ws := []report.ResultWriter{
@@ -683,7 +723,7 @@ func writeScanResults(jsonDir string, results models.ScanResults) error {
}
for _, w := range ws {
if err := w.Write(results...); err != nil {
return xerrors.Errorf("Failed to write summary report: %s", err)
return xerrors.Errorf("Failed to write summary: %s", err)
}
}

View File

@@ -36,10 +36,10 @@ func newSUSE(c config.ServerInfo) *suse {
// https://github.com/mizzy/specinfra/blob/master/lib/specinfra/helper/detect_os/suse.rb
func detectSUSE(c config.ServerInfo) (bool, osTypeInterface) {
s := newSUSE(c)
if r := exec(c, "ls /etc/os-release", noSudo); r.isSuccess() {
if r := exec(c, "zypper -V", noSudo); r.isSuccess() {
if r := exec(c, "cat /etc/os-release", noSudo); r.isSuccess() {
s := newSUSE(c)
name, ver := s.parseOSRelease(r.Stdout)
s.setDistro(name, ver)
return true, s
@@ -52,6 +52,7 @@ func detectSUSE(c config.ServerInfo) (bool, osTypeInterface) {
result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) == 2 {
//TODO check opensuse or opensuse.leap
s := newSUSE(c)
s.setDistro(config.OpenSUSE, result[1])
return true, s
}
@@ -63,18 +64,19 @@ func detectSUSE(c config.ServerInfo) (bool, osTypeInterface) {
re = regexp.MustCompile(`PATCHLEVEL = (\d+)`)
result = re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) == 2 {
s := newSUSE(c)
s.setDistro(config.SUSEEnterpriseServer,
fmt.Sprintf("%s.%s", version, result[1]))
return true, s
}
}
util.Log.Warnf("Failed to parse SUSE Linux version: %s", r)
return true, s
return true, newSUSE(c)
}
}
}
util.Log.Debugf("Not SUSE Linux. servername: %s", c.ServerName)
return false, s
return false, nil
}
func (o *suse) parseOSRelease(content string) (name string, ver string) {
@@ -115,6 +117,7 @@ func (o *suse) checkIfSudoNoPasswd() error {
}
func (o *suse) scanPackages() error {
o.log.Infof("Scanning OS pkg in %s", o.getServerInfo().Mode)
installed, err := o.scanInstalledPackages()
if err != nil {
o.log.Errorf("Failed to scan installed packages: %s", err)

View File

@@ -36,7 +36,6 @@ func (*ConfigtestCmd) Usage() string {
[-log-dir=/path/to/log]
[-ask-key-password]
[-timeout=300]
[-ssh-config]
[-containers-only]
[-http-proxy=http://192.168.0.1:8080]
[-debug]
@@ -68,12 +67,6 @@ func (p *ConfigtestCmd) SetFlags(f *flag.FlagSet) {
f.BoolVar(&c.Conf.SSHNative, "ssh-native-insecure", false,
"Use Native Go implementation of SSH. Default: Use the external command")
f.BoolVar(&c.Conf.SSHConfig, "ssh-config", false,
"[Deprecated] Use SSH options specified in ssh_config preferentially")
f.BoolVar(&c.Conf.ContainersOnly, "containers-only", false,
"Test containers only. Default: Test both of hosts and containers")
f.BoolVar(&c.Conf.Vvv, "vvv", false, "ssh -vvv")
}
@@ -107,16 +100,6 @@ func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfa
return subcommands.ExitUsageError
}
if c.Conf.SSHConfig {
msg := []string{
"-ssh-config is deprecated",
"If you update Vuls and get this error, there may be incompatible changes in config.toml",
"Please check config.toml template : https://vuls.io/docs/en/usage-settings.html",
}
util.Log.Errorf("%s", strings.Join(msg, "\n"))
return subcommands.ExitUsageError
}
var servernames []string
if 0 < len(f.Args()) {
servernames = f.Args()

View File

@@ -74,33 +74,33 @@ func (p *DiscoverCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface
func printConfigToml(ips []string) (err error) {
const tomlTemplate = `
# https://vuls.io/docs/en/usage-settings.html
# https://vuls.io/docs/en/config.toml.html#database-section
[cveDict]
type = "sqlite3"
sqlite3Path = "/path/to/cve.sqlite3"
#type = ["sqlite3", "mysql", "postgres", "redis", "http" ]
#sqlite3Path = "/path/to/cve.sqlite3"
#url = ""
[ovalDict]
type = "sqlite3"
sqlite3Path = "/path/to/oval.sqlite3"
#type = ["sqlite3", "mysql", "postgres", "redis", "http" ]
#sqlite3Path = "/path/to/oval.sqlite3"
#url = ""
[gost]
type = "sqlite3"
sqlite3Path = "/path/to/gost.sqlite3"
#type = ["sqlite3", "mysql", "postgres", "redis", "http" ]
#sqlite3Path = "/path/to/gost.sqlite3"
#url = ""
[exploit]
type = "sqlite3"
sqlite3Path = "/path/to/go-exploitdb.sqlite3"
#type = ["sqlite3", "mysql", "postgres", "redis", "http" ]
#sqlite3Path = "/path/to/go-exploitdb.sqlite3"
#url = ""
[metasploit]
type = "sqlite3"
sqlite3Path = "/path/to/go-msfdb.sqlite3"
#type = ["sqlite3", "mysql", "postgres", "redis", "http" ]
#sqlite3Path = "/path/to/go-msfdb.sqlite3"
#url = ""
# https://vuls.io/docs/en/usage-settings.html#slack-section
# https://vuls.io/docs/en/config.toml.html#slack-section
#[slack]
#hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
##legacyToken = "xoxp-11111111111-222222222222-3333333333"
@@ -110,7 +110,7 @@ sqlite3Path = "/path/to/go-msfdb.sqlite3"
#authUser = "username"
#notifyUsers = ["@username"]
# https://vuls.io/docs/en/usage-settings.html#email-section
# https://vuls.io/docs/en/config.toml.html#email-section
#[email]
#smtpAddr = "smtp.example.com"
#smtpPort = "587"
@@ -121,11 +121,11 @@ sqlite3Path = "/path/to/go-msfdb.sqlite3"
#cc = ["cc@example.com"]
#subjectPrefix = "[vuls]"
# https://vuls.io/docs/en/usage-settings.html#http-section
# https://vuls.io/docs/en/config.toml.html#http-section
#[http]
#url = "http://localhost:11234"
# https://vuls.io/docs/en/usage-settings.html#syslog-section
# https://vuls.io/docs/en/config.toml.html#syslog-section
#[syslog]
#protocol = "tcp"
#host = "localhost"
@@ -149,42 +149,38 @@ sqlite3Path = "/path/to/go-msfdb.sqlite3"
#accountKey = "xxxxxxxxxxxxxx"
#containerName = "vuls"
# https://vuls.io/docs/en/usage-settings.html#stride-section
#[stride]
#hookURL = "xxxxxxxxxxxxxxx"
#authToken = "xxxxxxxxxxxxxx"
# https://vuls.io/docs/en/usage-settings.html#hipchat-section
#[hipchat]
#room = "vuls"
#authToken = "xxxxxxxxxxxxxx"
# https://vuls.io/docs/en/usage-settings.html#chatwork-section
# https://vuls.io/docs/en/config.toml.html#chatwork-section
#[chatwork]
#room = "xxxxxxxxxxx"
#apiToken = "xxxxxxxxxxxxxxxxxx"
# https://vuls.io/docs/en/usage-settings.html#telegram-section
# https://vuls.io/docs/en/config.toml.html#telegram-section
#[telegram]
#chatID = "xxxxxxxxxxx"
#token = "xxxxxxxxxxxxxxxxxx"
# https://vuls.io/docs/en/usage-settings.html#default-section
#[wpscan]
#token = "xxxxxxxxxxx"
#detectInactive = false
# https://vuls.io/docs/en/config.toml.html#default-section
[default]
#port = "22"
#user = "username"
#keyPath = "/home/username/.ssh/id_rsa"
#scanMode = ["fast", "fast-root", "deep", "offline"]
#scanModules = ["ospkg", "wordpress", "lockfile", "port"]
#cpeNames = [
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]
#owaspDCXMLPath = "/tmp/dependency-check-report.xml"
#ignoreCves = ["CVE-2014-6271"]
#containersOnly = false
#containerType = "docker" #or "lxd" or "lxc" default: docker
#containersIncluded = ["${running}"]
#containersExcluded = ["container_name_a"]
# https://vuls.io/docs/en/usage-settings.html#servers-section
# https://vuls.io/docs/en/config.toml.html#servers-section
[servers]
{{- $names:= .Names}}
{{range $i, $ip := .IPs}}
@@ -195,11 +191,13 @@ host = "{{$ip}}"
#sshConfigPath = "/home/username/.ssh/config"
#keyPath = "/home/username/.ssh/id_rsa"
#scanMode = ["fast", "fast-root", "deep", "offline"]
#scanModules = ["ospkg", "wordpress", "lockfile", "port"]
#type = "pseudo"
#memo = "DB Server"
#cpeNames = [ "cpe:/a:rubyonrails:ruby_on_rails:4.2.1" ]
#owaspDCXMLPath = "/path/to/dependency-check-report.xml"
#ignoreCves = ["CVE-2014-0160"]
#containersOnly = false
#containerType = "docker" #or "lxd" or "lxc" default: docker
#containersIncluded = ["${running}"]
#containersExcluded = ["container_name_a"]
@@ -216,8 +214,6 @@ host = "{{$ip}}"
#cmdPath = "/usr/local/bin/wp"
#osUser = "wordpress"
#docRoot = "/path/to/DocumentRoot/"
#wpVulnDBToken = "xxxxTokenxxxx"
#ignoreInactive = true
#[servers.{{index $names $i}}.optional]
#key = "value1"

View File

@@ -9,12 +9,9 @@ import (
"path/filepath"
"github.com/aquasecurity/trivy/pkg/utils"
"github.com/future-architect/vuls/config"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/exploit"
"github.com/future-architect/vuls/gost"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/msf"
"github.com/future-architect/vuls/oval"
"github.com/future-architect/vuls/report"
"github.com/future-architect/vuls/util"
"github.com/google/subcommands"
@@ -44,28 +41,25 @@ func (*ReportCmd) Usage() string {
[-refresh-cve]
[-cvss-over=7]
[-diff]
[-wp-ignore-inactive]
[-diff-minus]
[-diff-plus]
[-ignore-unscored-cves]
[-ignore-unfixed]
[-ignore-github-dismissed]
[-to-email]
[-to-http]
[-to-slack]
[-to-stride]
[-to-hipchat]
[-to-chatwork]
[-to-telegram]
[-to-localfile]
[-to-s3]
[-to-azure-blob]
[-format-json]
[-format-xml]
[-format-one-email]
[-format-one-line-text]
[-format-list]
[-format-full-text]
[-gzip]
[-uuid]
[-http-proxy=http://192.168.0.1:8080]
[-debug]
[-debug-sql]
@@ -103,11 +97,14 @@ func (p *ReportCmd) SetFlags(f *flag.FlagSet) {
f.Float64Var(&c.Conf.CvssScoreOver, "cvss-over", 0,
"-cvss-over=6.5 means reporting CVSS Score 6.5 and over (default: 0 (means report all))")
f.BoolVar(&c.Conf.Diff, "diff", false,
"Difference between previous result and current result ")
f.BoolVar(&c.Conf.DiffMinus, "diff-minus", false,
"Minus Difference between previous result and current result")
f.BoolVar(&c.Conf.WpIgnoreInactive, "wp-ignore-inactive", false,
"ignore inactive on wordpress's plugin and theme")
f.BoolVar(&c.Conf.DiffPlus, "diff-plus", false,
"Plus Difference between previous result and current result")
f.BoolVar(&c.Conf.Diff, "diff", false,
"Plus & Minus Difference between previous result and current result")
f.BoolVar(&c.Conf.IgnoreUnscoredCves, "ignore-unscored-cves", false,
"Don't report the unscored CVEs")
@@ -123,7 +120,6 @@ func (p *ReportCmd) SetFlags(f *flag.FlagSet) {
"http://proxy-url:port (default: empty)")
f.BoolVar(&c.Conf.FormatJSON, "format-json", false, "JSON format")
f.BoolVar(&c.Conf.FormatXML, "format-xml", false, "XML format")
f.BoolVar(&c.Conf.FormatCsvList, "format-csv", false, "CSV format")
f.BoolVar(&c.Conf.FormatOneEMail, "format-one-email", false,
"Send all the host report via only one EMail (Specify with -to-email)")
@@ -134,18 +130,16 @@ func (p *ReportCmd) SetFlags(f *flag.FlagSet) {
"Detail report in plain text")
f.BoolVar(&c.Conf.ToSlack, "to-slack", false, "Send report via Slack")
f.BoolVar(&c.Conf.ToStride, "to-stride", false, "Send report via Stride")
f.BoolVar(&c.Conf.ToHipChat, "to-hipchat", false, "Send report via hipchat")
f.BoolVar(&c.Conf.ToChatWork, "to-chatwork", false, "Send report via chatwork")
f.BoolVar(&c.Conf.ToTelegram, "to-telegram", false, "Send report via Telegram")
f.BoolVar(&c.Conf.ToEmail, "to-email", false, "Send report via Email")
f.BoolVar(&c.Conf.ToSyslog, "to-syslog", false, "Send report via Syslog")
f.BoolVar(&c.Conf.ToLocalFile, "to-localfile", false, "Write report to localfile")
f.BoolVar(&c.Conf.ToS3, "to-s3", false,
"Write report to S3 (bucket/yyyyMMdd_HHmm/servername.json/xml/txt)")
"Write report to S3 (bucket/yyyyMMdd_HHmm/servername.json/txt)")
f.BoolVar(&c.Conf.ToHTTP, "to-http", false, "Send report via HTTP POST")
f.BoolVar(&c.Conf.ToAzureBlob, "to-azure-blob", false,
"Write report to Azure Storage blob (container/yyyyMMdd_HHmm/servername.json/xml/txt)")
"Write report to Azure Storage blob (container/yyyyMMdd_HHmm/servername.json/txt)")
f.BoolVar(&c.Conf.GZIP, "gzip", false, "gzip compression")
f.BoolVar(&c.Conf.Pipe, "pipe", false, "Use args passed via PIPE")
@@ -163,11 +157,16 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
util.Log.Errorf("Error loading %s, %+v", p.configPath, err)
return subcommands.ExitUsageError
}
c.Conf.HTTP.Overwrite(p.httpConf)
c.Conf.HTTP.Init(p.httpConf)
if c.Conf.Diff {
c.Conf.DiffPlus = true
c.Conf.DiffMinus = true
}
var dir string
var err error
if c.Conf.Diff {
if c.Conf.DiffPlus || c.Conf.DiffMinus {
dir, err = report.JSONDir([]string{})
} else {
dir, err = report.JSONDir(f.Args())
@@ -183,7 +182,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
}
if !(c.Conf.FormatJSON || c.Conf.FormatOneLineText ||
c.Conf.FormatList || c.Conf.FormatFullText || c.Conf.FormatXML || c.Conf.FormatCsvList) {
c.Conf.FormatList || c.Conf.FormatFullText || c.Conf.FormatCsvList) {
c.Conf.FormatList = true
}
@@ -221,50 +220,19 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
return subcommands.ExitUsageError
}
if c.Conf.CveDict.URL != "" {
if err := report.CveClient.CheckHealth(); err != nil {
util.Log.Errorf("CVE HTTP server is not running. err: %+v", err)
util.Log.Errorf("Run go-cve-dictionary as server mode before reporting or run with `-cvedb-type=sqlite3 -cvedb-sqlite3-path` option instead of -cvedb-url")
for _, cnf := range []config.VulnSrcConf{
&c.Conf.CveDict,
&c.Conf.OvalDict,
&c.Conf.Gost,
&c.Conf.Exploit,
&c.Conf.Metasploit,
} {
if err := cnf.CheckHTTPHealth(); err != nil {
util.Log.Errorf("Run as server mode before reporting: %+v", err)
return subcommands.ExitFailure
}
}
if c.Conf.OvalDict.URL != "" {
err := oval.Base{}.CheckHTTPHealth()
if err != nil {
util.Log.Errorf("OVAL HTTP server is not running. err: %+v", err)
util.Log.Errorf("Run goval-dictionary as server mode before reporting or run with `-ovaldb-type=sqlite3 -ovaldb-sqlite3-path` option instead of -ovaldb-url")
return subcommands.ExitFailure
}
}
if c.Conf.Gost.URL != "" {
util.Log.Infof("gost: %s", c.Conf.Gost.URL)
err := gost.Base{}.CheckHTTPHealth()
if err != nil {
util.Log.Errorf("gost HTTP server is not running. err: %+v", err)
util.Log.Errorf("Run gost as server mode before reporting or run with `-gostdb-type=sqlite3 -gostdb-sqlite3-path` option instead of -gostdb-url")
return subcommands.ExitFailure
}
}
if c.Conf.Exploit.URL != "" {
err := exploit.CheckHTTPHealth()
if err != nil {
util.Log.Errorf("exploit HTTP server is not running. err: %+v", err)
util.Log.Errorf("Run go-exploitdb as server mode before reporting")
return subcommands.ExitFailure
}
}
if c.Conf.Metasploit.URL != "" {
err := msf.CheckHTTPHealth()
if err != nil {
util.Log.Errorf("metasploit HTTP server is not running. err: %+v", err)
util.Log.Errorf("Run go-msfdb as server mode before reporting")
return subcommands.ExitFailure
}
}
dbclient, locked, err := report.NewDBClient(report.DBClientConf{
CveDictCnf: c.Conf.CveDict,
OvalDictCnf: c.Conf.OvalDict,
@@ -297,14 +265,6 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
reports = append(reports, report.SlackWriter{})
}
if c.Conf.ToStride {
reports = append(reports, report.StrideWriter{})
}
if c.Conf.ToHipChat {
reports = append(reports, report.HipChatWriter{})
}
if c.Conf.ToChatWork {
reports = append(reports, report.ChatWorkWriter{})
}

View File

@@ -79,7 +79,7 @@ func (p *SaaSCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
}
util.Log.Info("Validating config...")
if !c.Conf.ValidateOnReport() {
if !c.Conf.ValidateOnSaaS() {
return subcommands.ExitUsageError
}

View File

@@ -39,11 +39,6 @@ func (*ScanCmd) Usage() string {
[-log-dir=/path/to/log]
[-cachedb-path=/path/to/cache.db]
[-ssh-native-insecure]
[-ssh-config]
[-containers-only]
[-libs-only]
[-wordpress-only]
[-skip-broken]
[-http-proxy=http://192.168.0.1:8080]
[-ask-key-password]
[-timeout=300]
@@ -81,21 +76,6 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
f.BoolVar(&c.Conf.SSHNative, "ssh-native-insecure", false,
"Use Native Go implementation of SSH. Default: Use the external command")
f.BoolVar(&c.Conf.SSHConfig, "ssh-config", false,
"[Deprecated] Use SSH options specified in ssh_config preferentially")
f.BoolVar(&c.Conf.ContainersOnly, "containers-only", false,
"Scan running containers only. Default: Scan both of hosts and running containers")
f.BoolVar(&c.Conf.LibsOnly, "libs-only", false,
"Scan libraries (lock files) specified in config.toml only.")
f.BoolVar(&c.Conf.WordPressOnly, "wordpress-only", false,
"Scan WordPress only.")
f.BoolVar(&c.Conf.SkipBroken, "skip-broken", false,
"[For CentOS] yum update changelog with --skip-broken option")
f.StringVar(&c.Conf.HTTPProxy, "http-proxy", "",
"http://proxy-url:port (default: empty)")
@@ -148,16 +128,6 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
return subcommands.ExitUsageError
}
if c.Conf.SSHConfig {
msg := []string{
"-ssh-config is deprecated",
"If you update Vuls and get this error, there may be incompatible changes in config.toml",
"Please check config.toml template : https://vuls.io/docs/en/usage-settings.html",
}
util.Log.Errorf("%s", strings.Join(msg, "\n"))
return subcommands.ExitUsageError
}
util.Log.Info("Start scanning")
util.Log.Infof("config: %s", p.configPath)
@@ -218,7 +188,6 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
util.Log.Info("Detecting IPS identifiers... ")
scan.DetectIPSs(p.timeoutSec)
util.Log.Info("Scanning vulnerabilities... ")
if err := scan.Scan(p.scanTimeoutSec); err != nil {
util.Log.Errorf("Failed to scan. err: %+v", err)
return subcommands.ExitFailure

View File

@@ -12,11 +12,8 @@ import (
// "github.com/future-architect/vuls/Server"
"github.com/future-architect/vuls/config"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/exploit"
"github.com/future-architect/vuls/gost"
"github.com/future-architect/vuls/msf"
"github.com/future-architect/vuls/oval"
"github.com/future-architect/vuls/report"
"github.com/future-architect/vuls/server"
"github.com/future-architect/vuls/util"
@@ -109,50 +106,19 @@ func (p *ServerCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
return subcommands.ExitUsageError
}
if c.Conf.CveDict.URL != "" {
if err := report.CveClient.CheckHealth(); err != nil {
util.Log.Errorf("CVE HTTP server is not running. err: %+v", err)
util.Log.Errorf("Run go-cve-dictionary as server mode before reporting or run with `-cvedb-type=sqlite3 -cvedb-sqlite3-path` option instead of -cvedb-url")
for _, cnf := range []config.VulnSrcConf{
&c.Conf.CveDict,
&c.Conf.OvalDict,
&c.Conf.Gost,
&c.Conf.Exploit,
&c.Conf.Metasploit,
} {
if err := cnf.CheckHTTPHealth(); err != nil {
util.Log.Errorf("Run as server mode before reporting: %+v", err)
return subcommands.ExitFailure
}
}
if c.Conf.OvalDict.URL != "" {
err := oval.Base{}.CheckHTTPHealth()
if err != nil {
util.Log.Errorf("OVAL HTTP server is not running. err: %s", err)
util.Log.Errorf("Run goval-dictionary as server mode before reporting or run with `-ovaldb-type=sqlite3 -ovaldb-sqlite3-path` option instead of -ovaldb-url")
return subcommands.ExitFailure
}
}
if c.Conf.Gost.URL != "" {
util.Log.Infof("gost: %s", c.Conf.Gost.URL)
err := gost.Base{}.CheckHTTPHealth()
if err != nil {
util.Log.Errorf("gost HTTP server is not running. err: %+v", err)
util.Log.Errorf("Run gost as server mode before reporting or run with `-gostdb-type=sqlite3 -gostdb-sqlite3-path` option instead of -gostdb-url")
return subcommands.ExitFailure
}
}
if c.Conf.Exploit.URL != "" {
err := exploit.CheckHTTPHealth()
if err != nil {
util.Log.Errorf("exploit HTTP server is not running. err: %+v", err)
util.Log.Errorf("Run go-exploitdb as server mode before reporting")
return subcommands.ExitFailure
}
}
if c.Conf.Metasploit.URL != "" {
err := msf.CheckHTTPHealth()
if err != nil {
util.Log.Errorf("metasploit HTTP server is not running. err: %+v", err)
util.Log.Errorf("Run go-msfdb as server mode before reporting")
return subcommands.ExitFailure
}
}
dbclient, locked, err := report.NewDBClient(report.DBClientConf{
CveDictCnf: c.Conf.CveDict,
OvalDictCnf: c.Conf.OvalDict,

View File

@@ -9,12 +9,9 @@ import (
"path/filepath"
"github.com/aquasecurity/trivy/pkg/utils"
"github.com/future-architect/vuls/config"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/exploit"
"github.com/future-architect/vuls/gost"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/msf"
"github.com/future-architect/vuls/oval"
"github.com/future-architect/vuls/report"
"github.com/future-architect/vuls/util"
"github.com/google/subcommands"
@@ -39,6 +36,8 @@ func (*TuiCmd) Usage() string {
[-config=/path/to/config.toml]
[-cvss-over=7]
[-diff]
[-diff-minus]
[-diff-plus]
[-ignore-unscored-cves]
[-ignore-unfixed]
[-results-dir=/path/to/results]
@@ -78,7 +77,13 @@ func (p *TuiCmd) SetFlags(f *flag.FlagSet) {
"-cvss-over=6.5 means reporting CVSS Score 6.5 and over (default: 0 (means report all))")
f.BoolVar(&c.Conf.Diff, "diff", false,
"Difference between previous result and current result ")
"Plus Difference between previous result and current result")
f.BoolVar(&c.Conf.DiffPlus, "diff-plus", false,
"Plus Difference between previous result and current result")
f.BoolVar(&c.Conf.DiffMinus, "diff-minus", false,
"Minus Difference between previous result and current result")
f.BoolVar(
&c.Conf.IgnoreUnscoredCves, "ignore-unscored-cves", false,
@@ -103,9 +108,13 @@ func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) s
c.Conf.Lang = "en"
if c.Conf.Diff {
c.Conf.DiffPlus = true
c.Conf.DiffMinus = true
}
var dir string
var err error
if c.Conf.Diff {
if c.Conf.DiffPlus || c.Conf.DiffMinus {
dir, err = report.JSONDir([]string{})
} else {
dir, err = report.JSONDir(f.Args())
@@ -132,50 +141,19 @@ func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) s
return subcommands.ExitUsageError
}
if c.Conf.CveDict.URL != "" {
if err := report.CveClient.CheckHealth(); err != nil {
util.Log.Errorf("CVE HTTP server is not running. err: %+v", err)
util.Log.Errorf("Run go-cve-dictionary as server mode before reporting or run with `-cvedb-type=sqlite3 -cvedb-sqlite3-path` option instead of -cvedb-url")
for _, cnf := range []config.VulnSrcConf{
&c.Conf.CveDict,
&c.Conf.OvalDict,
&c.Conf.Gost,
&c.Conf.Exploit,
&c.Conf.Metasploit,
} {
if err := cnf.CheckHTTPHealth(); err != nil {
util.Log.Errorf("Run as server mode before reporting: %+v", err)
return subcommands.ExitFailure
}
}
if c.Conf.OvalDict.URL != "" {
err := oval.Base{}.CheckHTTPHealth()
if err != nil {
util.Log.Errorf("OVAL HTTP server is not running. err: %+v", err)
util.Log.Errorf("Run goval-dictionary as server mode before reporting or run with `-ovaldb-type=sqlite3 -ovaldb-sqlite3-path` option instead of -ovaldb-url")
return subcommands.ExitFailure
}
}
if c.Conf.Gost.URL != "" {
util.Log.Infof("gost: %s", c.Conf.Gost.URL)
err := gost.Base{}.CheckHTTPHealth()
if err != nil {
util.Log.Errorf("gost HTTP server is not running. err: %+v", err)
util.Log.Errorf("Run gost as server mode before reporting or run with `-gostdb-type=sqlite3 -gostdb-sqlite3-path` option instead of -gostdb-url")
return subcommands.ExitFailure
}
}
if c.Conf.Exploit.URL != "" {
err := exploit.CheckHTTPHealth()
if err != nil {
util.Log.Errorf("exploit HTTP server is not running. err: %+v", err)
util.Log.Errorf("Run go-exploitdb as server mode before reporting")
return subcommands.ExitFailure
}
}
if c.Conf.Metasploit.URL != "" {
err := msf.CheckHTTPHealth()
if err != nil {
util.Log.Errorf("metasploit HTTP server is not running. err: %+v", err)
util.Log.Errorf("Run go-msfdb as server mode before reporting")
return subcommands.ExitFailure
}
}
dbclient, locked, err := report.NewDBClient(report.DBClientConf{
CveDictCnf: c.Conf.CveDict,
OvalDictCnf: c.Conf.OvalDict,

View File

@@ -8,6 +8,7 @@ import (
"path/filepath"
"runtime"
"github.com/k0kubun/pp"
"github.com/rifflock/lfshook"
"github.com/sirupsen/logrus"
@@ -26,12 +27,13 @@ func init() {
}
// NewCustomLogger creates logrus
func NewCustomLogger(c config.ServerInfo) *logrus.Entry {
func NewCustomLogger(server config.ServerInfo) *logrus.Entry {
log := logrus.New()
log.Formatter = &formatter.TextFormatter{MsgAnsiColor: c.LogMsgAnsiColor}
log.Formatter = &formatter.TextFormatter{MsgAnsiColor: server.LogMsgAnsiColor}
log.Level = logrus.InfoLevel
if config.Conf.Debug {
log.Level = logrus.DebugLevel
pp.ColoringEnabled = false
}
if flag.Lookup("test.v") != nil {
@@ -64,8 +66,8 @@ func NewCustomLogger(c config.ServerInfo) *logrus.Entry {
}
whereami := "localhost"
if 0 < len(c.ServerName) {
whereami = c.GetServerName()
if 0 < len(server.ServerName) {
whereami = server.GetServerName()
}
if _, err := os.Stat(logDir); err == nil {
@@ -84,8 +86,9 @@ func NewCustomLogger(c config.ServerInfo) *logrus.Entry {
}
}
fields := logrus.Fields{"prefix": whereami}
return log.WithFields(fields)
entry := log.WithFields(logrus.Fields{"prefix": whereami})
entry.Infof("vuls-%s-%s", config.Version, config.Revision)
return entry
}
// GetDefaultLogDir returns default log directory

View File

@@ -3,6 +3,7 @@ package util
import (
"fmt"
"net"
"net/http"
"net/url"
"strings"
@@ -127,7 +128,7 @@ func ProxyEnv() string {
// PrependProxyEnv prepends proxy environment variable
func PrependProxyEnv(cmd string) string {
if len(config.Conf.HTTPProxy) == 0 {
if config.Conf.HTTPProxy == "" {
return cmd
}
return fmt.Sprintf("%s %s", ProxyEnv(), cmd)
@@ -163,3 +164,34 @@ func Distinct(ss []string) (distincted []string) {
}
return
}
// Major return the major version of the given semantic version
func Major(version string) string {
if version == "" {
return ""
}
ss := strings.SplitN(version, ":", 2)
ver := ""
if len(ss) == 1 {
ver = ss[0]
} else {
ver = ss[1]
}
return ver[0:strings.Index(ver, ".")]
}
// GetHTTPClient return http.Client
func GetHTTPClient(proxy string) (*http.Client, error) {
if proxy != "" {
proxyURL, err := url.Parse(proxy)
if err != nil {
return nil, err
}
return &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyURL(proxyURL),
},
}, nil
}
return &http.Client{}, nil
}

View File

@@ -154,3 +154,29 @@ func TestTruncate(t *testing.T) {
}
}
}
func Test_major(t *testing.T) {
var tests = []struct {
in string
expected string
}{
{
in: "",
expected: "",
},
{
in: "4.1",
expected: "4",
},
{
in: "0:4.1",
expected: "4",
},
}
for i, tt := range tests {
a := Major(tt.in)
if tt.expected != a {
t.Errorf("[%d]\nexpected: %s\n actual: %s\n", i, tt.expected, a)
}
}
}

View File

@@ -1,6 +1,7 @@
package wordpress
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
@@ -8,14 +9,16 @@ import (
"strings"
"time"
"github.com/future-architect/vuls/config"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/errof"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
version "github.com/hashicorp/go-version"
"golang.org/x/xerrors"
)
//WpCveInfos is for wpvulndb's json
//WpCveInfos is for wpscan json
type WpCveInfos struct {
ReleaseDate string `json:"release_date"`
ChangelogURL string `json:"changelog_url"`
@@ -27,153 +30,76 @@ type WpCveInfos struct {
Error string `json:"error"`
}
//WpCveInfo is for wpvulndb's json
//WpCveInfo is for wpscan json
type WpCveInfo struct {
ID int `json:"id"`
Title string `json:"title"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
// PublishedDate string `json:"published_date"`
ID string `json:"id"`
Title string `json:"title"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
VulnType string `json:"vuln_type"`
References References `json:"references"`
FixedIn string `json:"fixed_in"`
}
//References is for wpvulndb's json
//References is for wpscan json
type References struct {
URL []string `json:"url"`
Cve []string `json:"cve"`
Secunia []string `json:"secunia"`
}
// FillWordPress access to wpvulndb and fetch scurity alerts and then set to the given ScanResult.
// DetectWordPressCves access to wpscan and fetch scurity alerts and then set to the given ScanResult.
// https://wpscan.com/
func FillWordPress(r *models.ScanResult, token string, wpVulnCaches *map[string]string) (int, error) {
// TODO move to report
func DetectWordPressCves(r *models.ScanResult, cnf *c.WpScanConf) (int, error) {
if len(r.WordPressPackages) == 0 {
return 0, nil
}
// Core
ver := strings.Replace(r.WordPressPackages.CoreVersion(), ".", "", -1)
if ver == "" {
return 0, xerrors.New("Failed to get WordPress core version")
return 0, errof.New(errof.ErrFailedToAccessWpScan,
fmt.Sprintf("Failed to get WordPress core version."))
}
body, ok := searchCache(ver, wpVulnCaches)
if !ok {
url := fmt.Sprintf("https://wpscan.com/api/v3/wordpresses/%s", ver)
var err error
body, err = httpRequest(url, token)
if err != nil {
return 0, err
}
if body == "" {
util.Log.Warnf("A result of REST access is empty: %s", url)
}
(*wpVulnCaches)[ver] = body
}
wpVinfos, err := convertToVinfos(models.WPCore, body)
url := fmt.Sprintf("https://wpscan.com/api/v3/wordpresses/%s", ver)
wpVinfos, err := wpscan(url, ver, cnf.Token)
if err != nil {
return 0, err
}
themes := r.WordPressPackages.Themes()
plugins := r.WordPressPackages.Plugins()
if c.Conf.WpIgnoreInactive {
themes = removeInactives(themes)
plugins = removeInactives(plugins)
}
// Themes
themes := r.WordPressPackages.Themes()
if !cnf.DetectInactive {
themes = removeInactives(themes)
}
for _, p := range themes {
body, ok := searchCache(p.Name, wpVulnCaches)
if !ok {
url := fmt.Sprintf("https://wpscan.com/api/v3/themes/%s", p.Name)
var err error
body, err = httpRequest(url, token)
if err != nil {
return 0, err
}
(*wpVulnCaches)[p.Name] = body
}
if body == "" {
continue
}
templateVinfos, err := convertToVinfos(p.Name, body)
url := fmt.Sprintf("https://wpscan.com/api/v3/themes/%s", p.Name)
candidates, err := wpscan(url, p.Name, cnf.Token)
if err != nil {
return 0, err
}
for _, v := range templateVinfos {
for _, fixstat := range v.WpPackageFixStats {
pkg, ok := r.WordPressPackages.Find(fixstat.Name)
if !ok {
continue
}
ok, err := match(pkg.Version, fixstat.FixedIn)
if err != nil {
util.Log.Infof("[poor] %s installed: %s, fixedIn: %s", pkg.Name, pkg.Version, fixstat.FixedIn)
continue
}
if ok {
wpVinfos = append(wpVinfos, v)
util.Log.Infof("[match] %s installed: %s, fixedIn: %s", pkg.Name, pkg.Version, fixstat.FixedIn)
} else {
util.Log.Debugf("[miss] %s installed: %s, fixedIn: %s", pkg.Name, pkg.Version, fixstat.FixedIn)
}
}
}
vulns := detect(p, candidates)
wpVinfos = append(wpVinfos, vulns...)
}
// Plugins
plugins := r.WordPressPackages.Plugins()
if !cnf.DetectInactive {
plugins = removeInactives(plugins)
}
for _, p := range plugins {
body, ok := searchCache(p.Name, wpVulnCaches)
if !ok {
url := fmt.Sprintf("https://wpscan.com/api/v3/plugins/%s", p.Name)
var err error
body, err = httpRequest(url, token)
if err != nil {
return 0, err
}
(*wpVulnCaches)[p.Name] = body
}
if body == "" {
continue
}
pluginVinfos, err := convertToVinfos(p.Name, body)
url := fmt.Sprintf("https://wpscan.com/api/v3/plugins/%s", p.Name)
candidates, err := wpscan(url, p.Name, cnf.Token)
if err != nil {
return 0, err
}
for _, v := range pluginVinfos {
for _, fixstat := range v.WpPackageFixStats {
pkg, ok := r.WordPressPackages.Find(fixstat.Name)
if !ok {
continue
}
ok, err := match(pkg.Version, fixstat.FixedIn)
if err != nil {
util.Log.Infof("[poor] %s installed: %s, fixedIn: %s", pkg.Name, pkg.Version, fixstat.FixedIn)
continue
}
if ok {
wpVinfos = append(wpVinfos, v)
//TODO Debugf
util.Log.Infof("[match] %s installed: %s, fixedIn: %s", pkg.Name, pkg.Version, fixstat.FixedIn)
} else {
//TODO Debugf
util.Log.Infof("[miss] %s installed: %s, fixedIn: %s", pkg.Name, pkg.Version, fixstat.FixedIn)
}
}
}
vulns := detect(p, candidates)
wpVinfos = append(wpVinfos, vulns...)
}
for _, wpVinfo := range wpVinfos {
if vinfo, ok := r.ScannedCves[wpVinfo.CveID]; ok {
vinfo.CveContents[models.WPVulnDB] = wpVinfo.CveContents[models.WPVulnDB]
vinfo.CveContents[models.WpScan] = wpVinfo.CveContents[models.WpScan]
vinfo.VulnType = wpVinfo.VulnType
vinfo.Confidences = append(vinfo.Confidences, wpVinfo.Confidences...)
vinfo.WpPackageFixStats = append(vinfo.WpPackageFixStats, wpVinfo.WpPackageFixStats...)
@@ -185,6 +111,40 @@ func FillWordPress(r *models.ScanResult, token string, wpVulnCaches *map[string]
return len(wpVinfos), nil
}
func wpscan(url, name, token string) (vinfos []models.VulnInfo, err error) {
body, err := httpRequest(url, token)
if err != nil {
return nil, err
}
if body == "" {
util.Log.Debugf("wpscan.com response body is empty. URL: %s", url)
}
return convertToVinfos(name, body)
}
func detect(installed models.WpPackage, candidates []models.VulnInfo) (vulns []models.VulnInfo) {
for _, v := range candidates {
for _, fixstat := range v.WpPackageFixStats {
ok, err := match(installed.Version, fixstat.FixedIn)
if err != nil {
util.Log.Errorf("Failed to compare versions %s installed: %s, fixedIn: %s, v: %+v",
installed.Name, installed.Version, fixstat.FixedIn, v)
// continue scanning
continue
}
if ok {
vulns = append(vulns, v)
util.Log.Debugf("Affected: %s installed: %s, fixedIn: %s",
installed.Name, installed.Version, fixstat.FixedIn)
} else {
util.Log.Debugf("Not affected: %s : %s, fixedIn: %s",
installed.Name, installed.Version, fixstat.FixedIn)
}
}
}
return
}
func match(installedVer, fixedIn string) (bool, error) {
v1, err := version.NewVersion(installedVer)
if err != nil {
@@ -219,7 +179,7 @@ func extractToVulnInfos(pkgName string, cves []WpCveInfo) (vinfos []models.VulnI
var cveIDs []string
if len(vulnerability.References.Cve) == 0 {
cveIDs = append(cveIDs, fmt.Sprintf("WPVDBID-%d", vulnerability.ID))
cveIDs = append(cveIDs, fmt.Sprintf("WPVDBID-%s", vulnerability.ID))
}
for _, cveNumber := range vulnerability.References.Cve {
cveIDs = append(cveIDs, "CVE-"+cveNumber)
@@ -237,15 +197,17 @@ func extractToVulnInfos(pkgName string, cves []WpCveInfo) (vinfos []models.VulnI
CveID: cveID,
CveContents: models.NewCveContents(
models.CveContent{
Type: models.WPVulnDB,
CveID: cveID,
Title: vulnerability.Title,
References: refs,
Type: models.WpScan,
CveID: cveID,
Title: vulnerability.Title,
References: refs,
Published: vulnerability.CreatedAt,
LastModified: vulnerability.UpdatedAt,
},
),
VulnType: vulnerability.VulnType,
Confidences: []models.Confidence{
models.WPVulnDBMatch,
models.WpScanMatch,
},
WpPackageFixStats: []models.WpPackageFixStatus{{
Name: pkgName,
@@ -258,36 +220,41 @@ func extractToVulnInfos(pkgName string, cves []WpCveInfo) (vinfos []models.VulnI
}
func httpRequest(url, token string) (string, error) {
retry := 1
util.Log.Debugf("%s", url)
req, err := http.NewRequest("GET", url, nil)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
defer cancel()
if err != nil {
return "", errof.New(errof.ErrFailedToAccessWpScan,
fmt.Sprintf("Failed to access to wpscan.com. err: %s", err))
}
req.Header.Set("Authorization", fmt.Sprintf("Token token=%s", token))
client, err := util.GetHTTPClient(config.Conf.HTTPProxy)
if err != nil {
return "", err
}
req.Header.Set("Authorization", fmt.Sprintf("Token token=%s", token))
loop:
resp, err := new(http.Client).Do(req)
resp, err := client.Do(req)
if err != nil {
return "", err
return "", errof.New(errof.ErrFailedToAccessWpScan,
fmt.Sprintf("Failed to access to wpscan.com. err: %s", err))
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
return "", errof.New(errof.ErrFailedToAccessWpScan,
fmt.Sprintf("Failed to access to wpscan.com. err: %s", err))
}
defer resp.Body.Close()
if resp.StatusCode == 200 {
return string(body), nil
} else if resp.StatusCode == 404 {
// This package is not in WPVulnDB
// This package is not in wpscan
return "", nil
} else if resp.StatusCode == 429 {
return "", errof.New(errof.ErrWpScanAPILimitExceeded,
fmt.Sprintf("wpscan.com API limit exceeded: %+v", resp.Status))
} else {
util.Log.Warnf("wpscan.com unknown status code: %+v", resp.Status)
return "", nil
} else if resp.StatusCode == 429 && retry <= 3 {
// 429 Too Many Requests
util.Log.Debugf("sleep %d min(s): %s", retry, resp.Status)
time.Sleep(time.Duration(retry) * time.Minute)
retry++
goto loop
}
return "", err
}
func removeInactives(pkgs models.WordPressPackages) (removed models.WordPressPackages) {
@@ -299,11 +266,3 @@ func removeInactives(pkgs models.WordPressPackages) (removed models.WordPressPac
}
return removed
}
func searchCache(name string, wpVulnCaches *map[string]string) (string, bool) {
value, ok := (*wpVulnCaches)[name]
if ok {
return value, true
}
return "", false
}

View File

@@ -79,52 +79,3 @@ func TestRemoveInactive(t *testing.T) {
}
}
}
func TestSearchCache(t *testing.T) {
var tests = []struct {
name string
wpVulnCache map[string]string
value string
ok bool
}{
{
name: "akismet",
wpVulnCache: map[string]string{
"akismet": "body",
},
value: "body",
ok: true,
},
{
name: "akismet",
wpVulnCache: map[string]string{
"BackWPup": "body",
"akismet": "body",
},
value: "body",
ok: true,
},
{
name: "akismet",
wpVulnCache: map[string]string{
"BackWPup": "body",
},
value: "",
ok: false,
},
{
name: "akismet",
wpVulnCache: nil,
value: "",
ok: false,
},
}
for i, tt := range tests {
value, ok := searchCache(tt.name, &tt.wpVulnCache)
if value != tt.value || ok != tt.ok {
t.Errorf("[%d] searchCache error ", i)
}
}
}