feat(scan): WordPress Vulnerability Scan (core, plugin, theme) (#769)

https://github.com/future-architect/vuls/pull/769
This commit is contained in:
kazuminn
2019-04-08 17:27:44 +09:00
committed by Kota Kanbe
parent 91df593566
commit 99c65eff48
59 changed files with 1284 additions and 602 deletions

View File

@@ -1,7 +1,7 @@
language: go
go:
- "1.11.x"
- "1.12.x"
after_success:
- test -n "$TRAVIS_TAG" && curl -sL https://git.io/goreleaser | bash

View File

@@ -5,7 +5,7 @@
install \
all \
vendor \
lint \
lint \
vet \
fmt \
fmtcheck \
@@ -33,6 +33,9 @@ depup:
dep ensure -update -v
build: main.go dep pretest
go build -a -ldflags "$(LDFLAGS)" -o vuls $<
b: main.go dep pretest
go build -ldflags "$(LDFLAGS)" -o vuls $<
install: main.go dep pretest

90
Gopkg.lock generated
View File

@@ -2,23 +2,23 @@
[[projects]]
digest = "1:bfd9c4a3ff18fa3715d243c6dff6465eaf3c9c8cd0a61d81ce74ca0f6eb239f9"
digest = "1:ac06a4a43f8618111e6f37d22280a4d58d6d09ca621437cce93af7623c2117d3"
name = "contrib.go.opencensus.io/exporter/ocagent"
packages = ["."]
pruneopts = "UT"
revision = "902c0ccba68df93f7fefbe7e7c6f16be33108b40"
version = "v0.4.9"
revision = "bc69a60230000ba2fe80ce3aba578b8cc6ec7587"
version = "v0.4.11"
[[projects]]
digest = "1:6d0c80d0c0ca37ad7b3f46671a586b319090ac573dcb6229a567f0ac56bd943b"
digest = "1:e5385be33ddb613a47d6648aa2cde03750b1d80a5e6800b809cab755ebfcc188"
name = "github.com/Azure/azure-sdk-for-go"
packages = [
"storage",
"version",
]
pruneopts = "UT"
revision = "f0ff339f1297d3374fc36bcbfc7d1bbba045d992"
version = "v26.7.0"
revision = "d659f2a91175cac99aa5627d09b83026eacc978d"
version = "v27.0.0"
[[projects]]
digest = "1:b34c165560597b272b6ccf89dd3175eb7717e6247ce5ece215fa1905a7e19c22"
@@ -60,7 +60,7 @@
version = "v9"
[[projects]]
digest = "1:7f1dc60a57b23f6656f28d46513e13e75d24c52e256864d332ff44ca2e39d751"
digest = "1:ced9b678d6fdca06376ad5a1203bf7ab164a500268714fedaebdb3b9e90e6530"
name = "github.com/aws/aws-sdk-go"
packages = [
"aws",
@@ -99,8 +99,8 @@
"service/sts",
]
pruneopts = "UT"
revision = "bae168494294b48b3fc9d6722a711246a8543034"
version = "v1.19.2"
revision = "56c1def75689cceec1fa6f14c2eedb4b798827f9"
version = "v1.19.11"
[[projects]]
digest = "1:0f98f59e9a2f4070d66f0c9c39561f68fcd1dc837b22a852d28d0003aebd1b1e"
@@ -150,12 +150,12 @@
version = "v3.2.0"
[[projects]]
digest = "1:865079840386857c809b72ce300be7580cb50d3d3129ce11bf9aa6ca2bc1934a"
digest = "1:938a2672d6ebbb7f7bc63eee3e4b9464c16ffcf77ec8913d3edbf32b4e3984dd"
name = "github.com/fatih/color"
packages = ["."]
pruneopts = "UT"
revision = "5b77d2a35fb0ede96d138fc9a99f5c9b6aef11b4"
version = "v1.7.0"
revision = "570b54cabe6b8eb0bc2dfce68d964677d63b5260"
version = "v1.5.0"
[[projects]]
digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd"
@@ -362,12 +362,12 @@
version = "v0.4.0"
[[projects]]
digest = "1:3569ef9147aa8c258a306facf2f62eaff98ee7a1acd52579b4bf9632f001386b"
digest = "1:86c75d55fde56a814f69515d092963634d6f5f6bc1c3e9091b01fbe373a9546e"
name = "github.com/k0kubun/pp"
packages = ["."]
pruneopts = "UT"
revision = "1fdb803f401223aa9a9b7df66b7444d304f50bb0"
version = "v3.0.0"
revision = "3d73dea227e0711e38b911ffa6fbafc8ff6b2991"
version = "v3.0.1"
[[projects]]
branch = "master"
@@ -500,12 +500,12 @@
version = "v1.1.0"
[[projects]]
digest = "1:c658e84ad3916da105a761660dcaeb01e63416c8ec7bc62256a9b411a05fcd67"
digest = "1:2fa7b0155cd54479a755c629de26f888a918e13f8857a2c442205d825368e084"
name = "github.com/mattn/go-colorable"
packages = ["."]
pruneopts = "UT"
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
version = "v0.0.9"
revision = "3a70a971f94a22f2fa562ffcc7a0eb45f5daf045"
version = "v0.1.1"
[[projects]]
digest = "1:e150b5fafbd7607e2d638e4e5cf43aa4100124e5593385147b0a74e2733d8b0d"
@@ -611,12 +611,12 @@
version = "v0.2.15"
[[projects]]
digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e"
digest = "1:e0f50a07c0def90588d69f77178712c6fdc67eb6576365f551cce98b44b501bf"
name = "github.com/pelletier/go-toml"
packages = ["."]
pruneopts = "UT"
revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194"
version = "v1.2.0"
revision = "63909f0a90ab0f36909e8e044e46ace10cf13ba2"
version = "v1.3.0"
[[projects]]
digest = "1:cf31692c14422fa27c83a05292eb5cbe0fb2775972e8f1f8446a71549bd8980b"
@@ -644,11 +644,11 @@
[[projects]]
branch = "master"
digest = "1:35e32faf05a9a694fd831ac0ca6db9bc785db2863cf9352965a06e91e40260b7"
digest = "1:cabe9ec8289704e71e45138c18ad90275bce25a8d61145c4130b55d9d9a6d6dc"
name = "github.com/sirupsen/logrus"
packages = ["."]
pruneopts = "UT"
revision = "1115b87d621f90f39ac3d2bd230797e35c1d478d"
revision = "9b3cdde74fbe9443d704467498a7dcb61a79de9b"
[[projects]]
digest = "1:bb495ec276ab82d3dd08504bbc0594a65de8c3b22c6f2aaa92d05b73fbf3a82e"
@@ -718,13 +718,15 @@
revision = "0a0be1dd9d0855b50be0be5a10ad3085382b6d59"
[[projects]]
digest = "1:006e728c96e02ac83ee246183a4b87b44b21e67e004d3a06edd891583a4bf197"
digest = "1:4c93890bbbb5016505e856cb06b5c5a2ff5b7217584d33f2a9071ebef4b5d473"
name = "go.opencensus.io"
packages = [
".",
"exemplar",
"internal",
"internal/tagencoding",
"metric/metricdata",
"metric/metricproducer",
"plugin/ocgrpc",
"plugin/ochttp",
"plugin/ochttp/propagation/b3",
"plugin/ochttp/propagation/tracecontext",
@@ -739,8 +741,8 @@
"trace/tracestate",
]
pruneopts = "UT"
revision = "3b8e2721f2c3c01fa1bf4a2e455874e7b8319cd7"
version = "v0.19.2"
revision = "43463a80402d8447b7fce0d2c58edf1687ff0b58"
version = "v0.19.3"
[[projects]]
branch = "master"
@@ -758,11 +760,11 @@
"ssh/terminal",
]
pruneopts = "UT"
revision = "a5d413f7728c81fb97d96a2b722368945f651e78"
revision = "38d8ce5564a5b71b2e3a00553993f1b9a7ae852f"
[[projects]]
branch = "master"
digest = "1:8b5b5fb6f7a8670347ad0c21555752ef43b7e8bc95e7b60de621e88636a3c2a8"
digest = "1:df69c1d5940da38566e9d90689ae308d78d5d510a72466384a6d8eca40bca80f"
name = "golang.org/x/net"
packages = [
"context",
@@ -776,7 +778,7 @@
"trace",
]
pruneopts = "UT"
revision = "710a502c58a2a4b2d7417a4e24ccb96d8dc13fa3"
revision = "eb5bcb51f2a31c7d5141d810b70815c05d9c9146"
[[projects]]
branch = "master"
@@ -787,7 +789,7 @@
"internal",
]
pruneopts = "UT"
revision = "c85d3e98c914e3a33234ad863dcbff5dbc425bb8"
revision = "9f3314589c9a9136388751d9adae6b0ed400978a"
[[projects]]
branch = "master"
@@ -799,7 +801,7 @@
[[projects]]
branch = "master"
digest = "1:78ed3d0c6d5b4996ef2867e280f39ea7b44438558c6331721740ab9061db532c"
digest = "1:580bd6f5fcc95d5d3dfa5f6916ccfaac7abb1722b1bfc7f042603e167428a802"
name = "golang.org/x/sys"
packages = [
"cpu",
@@ -807,7 +809,7 @@
"windows",
]
pruneopts = "UT"
revision = "f49334f85ddcf0f08d7fb6dd7363e9e6d6b777eb"
revision = "4b34438f7a67ee5f45cc6132e2bad873a20324e9"
[[projects]]
digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
@@ -832,6 +834,17 @@
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
version = "v0.3.0"
[[projects]]
branch = "master"
digest = "1:363686890941480b8d1a0421b9e538cf55feaf9c7967bf4c809150b36138fe1c"
name = "golang.org/x/xerrors"
packages = [
".",
"internal",
]
pruneopts = "UT"
revision = "d61658bd2e18010be0e21349cc92b1b706e35146"
[[projects]]
digest = "1:5f003878aabe31d7f6b842d4de32b41c46c214bb629bb485387dbcce1edf5643"
name = "google.golang.org/api"
@@ -867,7 +880,7 @@
"protobuf/field_mask",
]
pruneopts = "UT"
revision = "d831d65fe17df2e52bcc4316d4a9f7a418701f43"
revision = "64821d5d210748c883cd2b809589555ae4654203"
[[projects]]
digest = "1:c00eb80d7b152379c3e94c38d82b29deca98b1d0f53e4e20362589b7fcbffa07"
@@ -934,12 +947,12 @@
version = "v2.0.6"
[[projects]]
digest = "1:865079840386857c809b72ce300be7580cb50d3d3129ce11bf9aa6ca2bc1934a"
digest = "1:938a2672d6ebbb7f7bc63eee3e4b9464c16ffcf77ec8913d3edbf32b4e3984dd"
name = "gopkg.in/fatih/color.v1"
packages = ["."]
pruneopts = "UT"
revision = "5b77d2a35fb0ede96d138fc9a99f5c9b6aef11b4"
version = "v1.7.0"
revision = "570b54cabe6b8eb0bc2dfce68d964677d63b5260"
version = "v1.5.0"
[[projects]]
digest = "1:c658e84ad3916da105a761660dcaeb01e63416c8ec7bc62256a9b411a05fcd67"
@@ -992,6 +1005,7 @@
"github.com/cenkalti/backoff",
"github.com/google/subcommands",
"github.com/gosuri/uitable",
"github.com/hashicorp/go-version",
"github.com/hashicorp/uuid",
"github.com/howeyc/gopass",
"github.com/jroimartin/gocui",
@@ -1014,12 +1028,12 @@
"github.com/nlopes/slack",
"github.com/olekukonko/tablewriter",
"github.com/parnurzeal/gorequest",
"github.com/pkg/errors",
"github.com/rifflock/lfshook",
"github.com/sirupsen/logrus",
"golang.org/x/crypto/ssh",
"golang.org/x/crypto/ssh/agent",
"golang.org/x/oauth2",
"golang.org/x/xerrors",
]
solver-name = "gps-cdcl"
solver-version = 1

View File

@@ -41,3 +41,7 @@
[prune]
go-tests = true
unused-packages = true
[[constraint]]
branch = "master"
name = "golang.org/x/xerrors"

View File

@@ -75,6 +75,7 @@ Vuls uses multiple vulnerability databases
- [Exploit Database](https://www.exploit-db.com/)
- [US-CERT](https://www.us-cert.gov/ncas/alerts)
- [JPCERT](http://www.jpcert.or.jp/at/2019.html)
- [WPVulnDB](https://wpvulndb.com/api)
- Changelog
## Fast scan and Deep scan
@@ -102,16 +103,23 @@ Vuls uses multiple vulnerability databases
it's possible to create a list of all vulnerabilities that need to be fixed.
- Sometimes load on the scan target server
## [Remote scan and Local scan](https://vuls.io/docs/en/architecture-remote-local.html)
## [Remote scan, Local scan mode, Server mode](https://vuls.io/docs/en/architecture-remote-local.html)
[Remote Scan](https://vuls.io/docs/en/architecture-remote-scan.html)
[Remote scan mode](https://vuls.io/docs/en/architecture-remote-scan.html)
- User is required to only set up one machine that is connected to other target servers via SSH
[Local Scan](https://vuls.io/docs/en/architecture-local-scan.html)
[Local scan mode](https://vuls.io/docs/en/architecture-local-scan.html)
- If you don't want the central Vuls server to connect to each server by SSH, you can use Vuls in the Local Scan mode.
[Server mode](https://vuls.io/docs/en/usage-server.html)
- First, start Vuls in server mode and listen as an HTTP server.
- Start Vuls in server mode and listen as an HTTP server.
- Next, issue a command on the scan target server to collect software information. Then send the result to Vuls Server via HTTP. You receive the scan results as JSON format.
- No SSH needed, No Scanner needed. Only issuing Linux commands directory on the scan tareget server.
## **Dynamic** Analysis
- It is possible to acquire the state of the server by connecting via SSH and executing the command.
@@ -120,9 +128,14 @@ Vuls uses multiple vulnerability databases
## Scan vulnerabilities of non-OS packages
- [Common Platform Enumeration (CPE) based Scan](https://vuls.io/docs/en/usage-scan-non-os-packages.html#how-to-search-cpe-name-by-software-name)
- NW equipment, middleware, programming language libraries and framework for vulnerability
- Integrate with [GitHub Security Alerts](https://vuls.io/docs/en/usage-scan-non-os-packages.html#usage-integrate-with-github-security-alerts)
- Integrate with [OWASP Dependency Check](https://vuls.io/docs/en/usage-scan-non-os-packages.html#usage-integrate-with-owasp-dependency-check-to-automatic-update-when-the-libraries-are-updated-experimental)
- Scan middleware, programming language libraries and framework for vulnerability
- Support software registered in CPE
# Integration
- [GitHub Security Alerts](https://vuls.io/docs/en/usage-scan-non-os-packages.html#usage-integrate-with-github-security-alerts)
- [OWASP Dependency Check](https://vuls.io/docs/en/usage-scan-non-os-packages.html#usage-integrate-with-owasp-dependency-check-to-automatic-update-when-the-libraries-are-updated-experimental)
- [WordPress](https://vuls.io/docs/en/usage-scan-wordpress.html)
## MISC

12
cache/bolt.go vendored
View File

@@ -19,12 +19,12 @@ package cache
import (
"encoding/json"
"fmt"
"time"
"github.com/boltdb/bolt"
"github.com/future-architect/vuls/util"
"github.com/sirupsen/logrus"
"golang.org/x/xerrors"
)
// Bolt holds a pointer of bolt.DB
@@ -69,7 +69,7 @@ func (b *Bolt) createBucketIfNotExists(name string) error {
return b.db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucketIfNotExists([]byte(name))
if err != nil {
return fmt.Errorf("Failed to create bucket: %s", err)
return xerrors.Errorf("Failed to create bucket: %w", err)
}
return nil
})
@@ -98,7 +98,7 @@ func (b Bolt) RefreshMeta(meta Meta) error {
meta.CreatedAt = time.Now()
jsonBytes, err := json.Marshal(meta)
if err != nil {
return fmt.Errorf("Failed to marshal to JSON: %s", err)
return xerrors.Errorf("Failed to marshal to JSON: %w", err)
}
return b.db.Update(func(tx *bolt.Tx) error {
bkt := tx.Bucket([]byte(metabucket))
@@ -114,7 +114,7 @@ func (b Bolt) RefreshMeta(meta Meta) error {
func (b Bolt) EnsureBuckets(meta Meta) error {
jsonBytes, err := json.Marshal(meta)
if err != nil {
return fmt.Errorf("Failed to marshal to JSON: %s", err)
return xerrors.Errorf("Failed to marshal to JSON: %w", err)
}
return b.db.Update(func(tx *bolt.Tx) error {
b.Log.Debugf("Put to meta: %s", meta.Name)
@@ -163,7 +163,7 @@ func (b Bolt) GetChangelog(servername, packName string) (changelog string, err e
err = b.db.View(func(tx *bolt.Tx) error {
bkt := tx.Bucket([]byte(servername))
if bkt == nil {
return fmt.Errorf("Failed to get Bucket: %s", servername)
return xerrors.Errorf("Failed to get Bucket: %s", servername)
}
v := bkt.Get([]byte(packName))
if v == nil {
@@ -181,7 +181,7 @@ func (b Bolt) PutChangelog(servername, packName, changelog string) error {
return b.db.Update(func(tx *bolt.Tx) error {
bkt := tx.Bucket([]byte(servername))
if bkt == nil {
return fmt.Errorf("Failed to get Bucket: %s", servername)
return xerrors.Errorf("Failed to get Bucket: %s", servername)
}
return bkt.Put([]byte(packName), []byte(changelog))
})

View File

@@ -223,6 +223,13 @@ host = "{{$ip}}"
#[servers.{{index $names $i}}.githubs."owner/repo"]
#token = "yourToken"
#[servers.{{index $names $i}}.wordpress]
#cmdPath = "/usr/local/bin/wp"
#osUser = "wordpress"
#docRoot = "/path/to/DocumentRoot/"
#wpVulnDBToken = "xxxxTokenxxxx"
#ignoreInactive = true
#[servers.{{index $names $i}}.optional]
#key = "value1"

View File

@@ -24,6 +24,7 @@ import (
"github.com/howeyc/gopass"
homedir "github.com/mitchellh/go-homedir"
"golang.org/x/xerrors"
)
func getPasswd(prompt string) (string, error) {
@@ -31,7 +32,7 @@ func getPasswd(prompt string) (string, error) {
fmt.Print(prompt)
pass, err := gopass.GetPasswdMasked()
if err != nil {
return "", fmt.Errorf("Failed to read password")
return "", xerrors.New("Failed to read a password")
}
if 0 < len(pass) {
return string(pass), nil

View File

@@ -27,6 +27,7 @@ import (
"strings"
syslog "github.com/RackSec/srslog"
"golang.org/x/xerrors"
valid "github.com/asaskevich/govalidator"
log "github.com/sirupsen/logrus"
@@ -167,7 +168,7 @@ func (c Config) ValidateOnConfigtest() bool {
errs := []error{}
if runtime.GOOS == "windows" && !c.SSHNative {
errs = append(errs, fmt.Errorf("-ssh-native-insecure is needed on windows"))
errs = append(errs, xerrors.New("-ssh-native-insecure is needed on windows"))
}
_, err := valid.ValidateStruct(c)
@@ -188,25 +189,25 @@ func (c Config) ValidateOnScan() bool {
if len(c.ResultsDir) != 0 {
if ok, _ := valid.IsFilePath(c.ResultsDir); !ok {
errs = append(errs, fmt.Errorf(
errs = append(errs, xerrors.Errorf(
"JSON base directory must be a *Absolute* file path. -results-dir: %s", c.ResultsDir))
}
}
if runtime.GOOS == "windows" && !c.SSHNative {
errs = append(errs, fmt.Errorf("-ssh-native-insecure is needed on windows"))
errs = append(errs, xerrors.New("-ssh-native-insecure is needed on windows"))
}
if len(c.ResultsDir) != 0 {
if ok, _ := valid.IsFilePath(c.ResultsDir); !ok {
errs = append(errs, fmt.Errorf(
errs = append(errs, xerrors.Errorf(
"JSON base directory must be a *Absolute* file path. -results-dir: %s", c.ResultsDir))
}
}
if len(c.CacheDBPath) != 0 {
if ok, _ := valid.IsFilePath(c.CacheDBPath); !ok {
errs = append(errs, fmt.Errorf(
errs = append(errs, xerrors.Errorf(
"Cache DB path must be a *Absolute* file path. -cache-dbpath: %s",
c.CacheDBPath))
}
@@ -233,7 +234,7 @@ func (c Config) ValidateOnReportDB() bool {
}
if c.CveDict.Type == "sqlite3" {
if _, err := os.Stat(c.CveDict.SQLite3Path); os.IsNotExist(err) {
errs = append(errs, fmt.Errorf("SQLite3 DB path (%s) is not exist: %s", "cvedb", c.CveDict.SQLite3Path))
errs = append(errs, xerrors.Errorf("SQLite3 DB path (%s) is not exist: %s", "cvedb", c.CveDict.SQLite3Path))
}
}
@@ -262,7 +263,7 @@ func (c Config) ValidateOnReport() bool {
if len(c.ResultsDir) != 0 {
if ok, _ := valid.IsFilePath(c.ResultsDir); !ok {
errs = append(errs, fmt.Errorf(
errs = append(errs, xerrors.Errorf(
"JSON base directory must be a *Absolute* file path. -results-dir: %s", c.ResultsDir))
}
}
@@ -321,7 +322,7 @@ func (c Config) ValidateOnTui() bool {
if len(c.ResultsDir) != 0 {
if ok, _ := valid.IsFilePath(c.ResultsDir); !ok {
errs = append(errs, fmt.Errorf(
errs = append(errs, xerrors.Errorf(
"JSON base directory must be a *Absolute* file path. -results-dir: %s", c.ResultsDir))
}
}
@@ -331,7 +332,7 @@ func (c Config) ValidateOnTui() bool {
}
if c.CveDict.Type == "sqlite3" {
if _, err := os.Stat(c.CveDict.SQLite3Path); os.IsNotExist(err) {
errs = append(errs, fmt.Errorf("SQLite3 DB path (%s) is not exist: %s", "cvedb", c.CveDict.SQLite3Path))
errs = append(errs, xerrors.Errorf("SQLite3 DB path (%s) is not exist: %s", "cvedb", c.CveDict.SQLite3Path))
}
}
@@ -351,35 +352,35 @@ func validateDB(dictionaryDBName, dbType, dbPath, dbURL string) error {
switch dbType {
case "sqlite3":
if dbURL != "" {
return fmt.Errorf("To use SQLite3, specify -%s-type=sqlite3 and -%s-path. To use as http server mode, specify -%s-type=http and -%s-url",
return xerrors.Errorf("To use SQLite3, specify -%s-type=sqlite3 and -%s-path. To use as http server mode, specify -%s-type=http and -%s-url",
dictionaryDBName, dictionaryDBName, dictionaryDBName, dictionaryDBName)
}
if ok, _ := valid.IsFilePath(dbPath); !ok {
return fmt.Errorf("SQLite3 path must be a *Absolute* file path. -%s-path: %s",
return xerrors.Errorf("SQLite3 path must be a *Absolute* file path. -%s-path: %s",
dictionaryDBName, dbPath)
}
case "mysql":
if dbURL == "" {
return fmt.Errorf(`MySQL connection string is needed. -%s-url="user:pass@tcp(localhost:3306)/dbname"`,
return xerrors.Errorf(`MySQL connection string is needed. -%s-url="user:pass@tcp(localhost:3306)/dbname"`,
dictionaryDBName)
}
case "postgres":
if dbURL == "" {
return fmt.Errorf(`PostgreSQL connection string is needed. -%s-url="host=myhost user=user dbname=dbname sslmode=disable password=password"`,
return xerrors.Errorf(`PostgreSQL connection string is needed. -%s-url="host=myhost user=user dbname=dbname sslmode=disable password=password"`,
dictionaryDBName)
}
case "redis":
if dbURL == "" {
return fmt.Errorf(`Redis connection string is needed. -%s-url="redis://localhost/0"`,
return xerrors.Errorf(`Redis connection string is needed. -%s-url="redis://localhost/0"`,
dictionaryDBName)
}
case "http":
if dbURL == "" {
return fmt.Errorf(`URL is needed. -%s-url="http://localhost:1323"`,
return xerrors.Errorf(`URL is needed. -%s-url="http://localhost:1323"`,
dictionaryDBName)
}
default:
return fmt.Errorf("%s type must be either 'sqlite3', 'mysql', 'postgres', 'redis' or 'http'. -%s-type: %s",
return xerrors.Errorf("%s type must be either 'sqlite3', 'mysql', 'postgres', 'redis' or 'http'. -%s-type: %s",
dictionaryDBName, dictionaryDBName, dbType)
}
return nil
@@ -403,7 +404,7 @@ func checkEmails(emails []string) (errs []error) {
return
}
if ok := valid.IsEmail(addr); !ok {
errs = append(errs, fmt.Errorf("Invalid email address. email: %s", addr))
errs = append(errs, xerrors.Errorf("Invalid email address. email: %s", addr))
}
}
return
@@ -425,16 +426,16 @@ func (c *SMTPConf) Validate() (errs []error) {
}
if len(c.SMTPAddr) == 0 {
errs = append(errs, fmt.Errorf("email.smtpAddr must not be empty"))
errs = append(errs, xerrors.New("email.smtpAddr must not be empty"))
}
if len(c.SMTPPort) == 0 {
errs = append(errs, fmt.Errorf("email.smtpPort must not be empty"))
errs = append(errs, xerrors.New("email.smtpPort must not be empty"))
}
if len(c.To) == 0 {
errs = append(errs, fmt.Errorf("email.To required at least one address"))
errs = append(errs, xerrors.New("email.To required at least one address"))
}
if len(c.From) == 0 {
errs = append(errs, fmt.Errorf("email.From required at least one address"))
errs = append(errs, xerrors.New("email.From required at least one address"))
}
_, err := valid.ValidateStruct(c)
@@ -457,11 +458,11 @@ func (c *StrideConf) Validate() (errs []error) {
}
if len(c.HookURL) == 0 {
errs = append(errs, fmt.Errorf("stride.HookURL must not be empty"))
errs = append(errs, xerrors.New("stride.HookURL must not be empty"))
}
if len(c.AuthToken) == 0 {
errs = append(errs, fmt.Errorf("stride.AuthToken must not be empty"))
errs = append(errs, xerrors.New("stride.AuthToken must not be empty"))
}
_, err := valid.ValidateStruct(c)
@@ -489,21 +490,21 @@ func (c *SlackConf) Validate() (errs []error) {
}
if len(c.HookURL) == 0 && len(c.LegacyToken) == 0 {
errs = append(errs, fmt.Errorf("slack.hookURL or slack.LegacyToken must not be empty"))
errs = append(errs, xerrors.New("slack.hookURL or slack.LegacyToken must not be empty"))
}
if len(c.Channel) == 0 {
errs = append(errs, fmt.Errorf("slack.channel must not be empty"))
errs = append(errs, xerrors.New("slack.channel must not be empty"))
} else {
if !(strings.HasPrefix(c.Channel, "#") ||
c.Channel == "${servername}") {
errs = append(errs, fmt.Errorf(
errs = append(errs, xerrors.Errorf(
"channel's prefix must be '#', channel: %s", c.Channel))
}
}
if len(c.AuthUser) == 0 {
errs = append(errs, fmt.Errorf("slack.authUser must not be empty"))
errs = append(errs, xerrors.New("slack.authUser must not be empty"))
}
_, err := valid.ValidateStruct(c)
@@ -526,11 +527,11 @@ func (c *HipChatConf) Validate() (errs []error) {
return
}
if len(c.Room) == 0 {
errs = append(errs, fmt.Errorf("hipcaht.room must not be empty"))
errs = append(errs, xerrors.New("hipcaht.room must not be empty"))
}
if len(c.AuthToken) == 0 {
errs = append(errs, fmt.Errorf("hipcaht.AuthToken must not be empty"))
errs = append(errs, xerrors.New("hipcaht.AuthToken must not be empty"))
}
_, err := valid.ValidateStruct(c)
@@ -552,11 +553,11 @@ func (c *ChatWorkConf) Validate() (errs []error) {
return
}
if len(c.Room) == 0 {
errs = append(errs, fmt.Errorf("chatworkcaht.room must not be empty"))
errs = append(errs, xerrors.New("chatworkcaht.room must not be empty"))
}
if len(c.APIToken) == 0 {
errs = append(errs, fmt.Errorf("chatworkcaht.ApiToken must not be empty"))
errs = append(errs, xerrors.New("chatworkcaht.ApiToken must not be empty"))
}
_, err := valid.ValidateStruct(c)
@@ -578,11 +579,11 @@ func (c *TelegramConf) Validate() (errs []error) {
return
}
if len(c.ChatID) == 0 {
errs = append(errs, fmt.Errorf("TelegramConf.ChatID must not be empty"))
errs = append(errs, xerrors.New("TelegramConf.ChatID must not be empty"))
}
if len(c.Token) == 0 {
errs = append(errs, fmt.Errorf("TelegramConf.Token must not be empty"))
errs = append(errs, xerrors.New("TelegramConf.Token must not be empty"))
}
_, err := valid.ValidateStruct(c)
@@ -606,15 +607,15 @@ func (c *SaasConf) Validate() (errs []error) {
}
if c.GroupID == 0 {
errs = append(errs, fmt.Errorf("saas.GroupID must not be empty"))
errs = append(errs, xerrors.New("saas.GroupID must not be empty"))
}
if len(c.Token) == 0 {
errs = append(errs, fmt.Errorf("saas.Token must not be empty"))
errs = append(errs, xerrors.New("saas.Token must not be empty"))
}
if len(c.URL) == 0 {
errs = append(errs, fmt.Errorf("saas.URL must not be empty"))
errs = append(errs, xerrors.New("saas.URL must not be empty"))
}
_, err := valid.ValidateStruct(c)
@@ -688,7 +689,7 @@ func (c *SyslogConf) GetSeverity() (syslog.Priority, error) {
case "debug":
return syslog.LOG_DEBUG, nil
default:
return -1, fmt.Errorf("Invalid severity: %s", c.Severity)
return -1, xerrors.Errorf("Invalid severity: %s", c.Severity)
}
}
@@ -740,7 +741,7 @@ func (c *SyslogConf) GetFacility() (syslog.Priority, error) {
case "local7":
return syslog.LOG_LOCAL7, nil
default:
return -1, fmt.Errorf("Invalid facility: %s", c.Facility)
return -1, xerrors.Errorf("Invalid facility: %s", c.Facility)
}
}
@@ -1041,16 +1042,16 @@ type Azure struct {
// ServerInfo has SSH Info, additional CPE packages to scan.
type ServerInfo struct {
ServerName string `toml:"-" json:"serverName"`
User string `toml:"user,omitempty" json:"user"`
Host string `toml:"host,omitempty" json:"host"`
Port string `toml:"port,omitempty" json:"port"`
KeyPath string `toml:"keyPath,omitempty" json:"keyPath"`
KeyPassword string `json:"-" toml:"-"`
ServerName string `toml:"-" json:"serverName,omitempty"`
User string `toml:"user,omitempty" json:"user,omitempty"`
Host string `toml:"host,omitempty" json:"host,omitempty"`
Port string `toml:"port,omitempty" json:"port,omitempty"`
KeyPath string `toml:"keyPath,omitempty" json:"keyPath,omitempty"`
KeyPassword string `json:"-,omitempty" toml:"-"`
CpeNames []string `toml:"cpeNames,omitempty" json:"cpeNames,omitempty"`
ScanMode []string `toml:"scanMode,omitempty" json:"scanMode,omitempty"`
DependencyCheckXMLPath string `toml:"dependencyCheckXMLPath,omitempty" json:"-"` // TODO Deprecated remove in near future
OwaspDCXMLPath string `toml:"owaspDCXMLPath,omitempty" json:"owaspDCXMLPath"`
OwaspDCXMLPath string `toml:"owaspDCXMLPath,omitempty" json:"owaspDCXMLPath,omitempty"`
ContainersIncluded []string `toml:"containersIncluded,omitempty" json:"containersIncluded,omitempty"`
ContainersExcluded []string `toml:"containersExcluded,omitempty" json:"containersExcluded,omitempty"`
ContainerType string `toml:"containerType,omitempty" json:"containerType,omitempty"`
@@ -1059,14 +1060,18 @@ type ServerInfo struct {
IgnorePkgsRegexp []string `toml:"ignorePkgsRegexp,omitempty" json:"ignorePkgsRegexp,omitempty"`
GitHubRepos map[string]GitHubConf `toml:"githubs" json:"githubs,omitempty"` // key: owner/repo
UUIDs map[string]string `toml:"uuids,omitempty" json:"uuids,omitempty"`
Memo string `toml:"memo,omitempty" json:"memo"`
Memo string `toml:"memo,omitempty" json:"memo,omitempty"`
Enablerepo []string `toml:"enablerepo,omitempty" json:"enablerepo,omitempty"` // For CentOS, RHEL, Amazon
Optional map[string]interface{} `toml:"optional,omitempty" json:"optional,omitempty"` // Optional key-value set that will be outputted to JSON
Type string `toml:"type,omitempty" json:"type"` // "pseudo" or ""
IPv4Addrs []string `toml:"-" json:"ipv4Addrs,omitempty"`
IPv6Addrs []string `toml:"-" json:"ipv6Addrs,omitempty"`
Type string `toml:"type,omitempty" json:"type,omitempty"` // "pseudo" or ""
WordPress WordPressConf `toml:"wordpress,omitempty" json:"wordpress,omitempty"`
// used internal
IPv4Addrs []string `toml:"-" json:"ipv4Addrs,omitempty"`
IPv6Addrs []string `toml:"-" json:"ipv6Addrs,omitempty"`
LogMsgAnsiColor string `toml:"-" json:"-"` // DebugLog Color
Container Container `toml:"-" json:"-"`
Distro Distro `toml:"-" json:"-"`
@@ -1081,21 +1086,18 @@ type ContainerSetting struct {
IgnoreCves []string `json:"ignoreCves,omitempty"`
}
// IntegrationConf is used for integration configuration
type IntegrationConf struct {
GitHubConf map[string]GitHubConf
}
// New creates IntegrationConf and initialize fields
func (c IntegrationConf) New() IntegrationConf {
return IntegrationConf{
GitHubConf: map[string]GitHubConf{},
}
// WordPressConf used for WordPress Scanning
type WordPressConf struct {
OSUser string `toml:"osUser" json:"osUser,omitempty"`
DocRoot string `toml:"docRoot" json:"docRoot,omitempty"`
CmdPath string `toml:"cmdPath" json:"cmdPath,omitempty"`
WPVulnDBToken string `toml:"wpVulnDBToken" json:"-,omitempty"`
IgnoreInactive bool `json:"ignoreInactive,omitempty"`
}
// GitHubConf is used for GitHub integration
type GitHubConf struct {
Token string `json:"token"`
Token string `json:"-"`
}
// ScanMode has a type of scan mode. fast, fast-root, deep and offline
@@ -1138,9 +1140,9 @@ func (s ScanMode) validate() error {
if numTrue == 0 {
s.Set(Fast)
} else if s.IsDeep() && s.IsOffline() {
return fmt.Errorf("Don't specify both of -deep and offline")
return xerrors.New("Don't specify both of -deep and offline")
} else if numTrue != 1 {
return fmt.Errorf("Specify only one of -fast, -fast-root or -deep")
return xerrors.New("Specify only one of -fast, -fast-root or -deep")
}
return nil
}
@@ -1203,7 +1205,7 @@ func (l Distro) MajorVersion() (ver int, err error) {
if 0 < len(l.Release) {
ver, err = strconv.Atoi(strings.Split(l.Release, ".")[0])
} else {
err = fmt.Errorf("Release is empty")
err = xerrors.New("Release is empty")
}
return
}

View File

@@ -17,7 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package config
import "fmt"
import "golang.org/x/xerrors"
// JSONLoader loads configuration
type JSONLoader struct {
@@ -25,5 +25,5 @@ type JSONLoader struct {
// Load load the configuration JSON file specified by path arg.
func (c JSONLoader) Load(path, sudoPass, keyPass string) (err error) {
return fmt.Errorf("Not implement yet")
return xerrors.New("Not implement yet")
}

View File

@@ -18,13 +18,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package config
import (
"fmt"
"os"
"regexp"
"strings"
"github.com/BurntSushi/toml"
"github.com/knqyf263/go-cpe/naming"
"golang.org/x/xerrors"
)
// TOMLLoader loads config
@@ -65,14 +65,14 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
i := 0
for serverName, v := range conf.Servers {
if 0 < len(v.KeyPassword) {
return fmt.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)
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)
}
s := ServerInfo{ServerName: serverName}
if v.Type != ServerTypePseudo {
s.Host = v.Host
if len(s.Host) == 0 {
return fmt.Errorf("%s is invalid. host is empty", serverName)
return xerrors.Errorf("%s is invalid. host is empty", serverName)
}
switch {
@@ -91,7 +91,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
s.User = d.User
default:
if s.Port != "local" {
return fmt.Errorf("%s is invalid. User is empty", serverName)
return xerrors.Errorf("%s is invalid. User is empty", serverName)
}
}
@@ -101,7 +101,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
}
if s.KeyPath != "" {
if _, err := os.Stat(s.KeyPath); err != nil {
return fmt.Errorf(
return xerrors.Errorf(
"%s is invalid. keypath: %s not exists", serverName, s.KeyPath)
}
}
@@ -130,11 +130,11 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
case "offline":
s.Mode.Set(Offline)
default:
return fmt.Errorf("scanMode: %s of %s is invalie. Specify -fast, -fast-root, -deep or offline", m, serverName)
return xerrors.Errorf("scanMode: %s of %s is invalie. Specify -fast, -fast-root, -deep or offline", m, serverName)
}
}
if err := s.Mode.validate(); err != nil {
return fmt.Errorf("%s in %s", err, serverName)
return xerrors.Errorf("%s in %s", err, serverName)
}
s.CpeNames = v.CpeNames
@@ -145,7 +145,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
for i, n := range s.CpeNames {
uri, err := toCpeURI(n)
if err != nil {
return fmt.Errorf("Failed to parse CPENames %s in %s: %s", n, serverName, err)
return xerrors.Errorf("Failed to parse CPENames %s in %s, err: %w", n, serverName, err)
}
s.CpeNames[i] = uri
}
@@ -172,7 +172,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
}
if len(v.DependencyCheckXMLPath) != 0 || len(d.DependencyCheckXMLPath) != 0 {
return fmt.Errorf("[DEPRECATED] dependencyCheckXMLPath IS DEPRECATED. USE owaspDCXMLPath INSTEAD: %s", serverName)
return xerrors.Errorf("[DEPRECATED] dependencyCheckXMLPath IS DEPRECATED. USE owaspDCXMLPath INSTEAD: %s", serverName)
}
s.OwaspDCXMLPath = v.OwaspDCXMLPath
@@ -215,14 +215,14 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
for _, reg := range s.IgnorePkgsRegexp {
_, err := regexp.Compile(reg)
if err != nil {
return fmt.Errorf("Faild to parse %s in %s. err: %s", reg, serverName, err)
return xerrors.Errorf("Faild to parse %s in %s. err: %w", reg, serverName, err)
}
}
for contName, cont := range s.Containers {
for _, reg := range cont.IgnorePkgsRegexp {
_, err := regexp.Compile(reg)
if err != nil {
return fmt.Errorf("Faild to parse %s in %s@%s. err: %s",
return xerrors.Errorf("Faild to parse %s in %s@%s. err: %w",
reg, contName, serverName, err)
}
}
@@ -247,7 +247,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
case "base", "updates":
// nop
default:
return fmt.Errorf(
return xerrors.Errorf(
"For now, enablerepo have to be base or updates: %s, servername: %s",
s.Enablerepo, serverName)
}
@@ -257,11 +257,11 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
s.GitHubRepos = v.GitHubRepos
for ownerRepo, githubSetting := range s.GitHubRepos {
if ss := strings.Split(ownerRepo, "/"); len(ss) != 2 {
return fmt.Errorf("Failed to parse GitHub owner/repo: %s in %s",
return xerrors.Errorf("Failed to parse GitHub owner/repo: %s in %s",
ownerRepo, serverName)
}
if githubSetting.Token == "" {
return fmt.Errorf("GitHub owner/repo: %s in %s token is empty",
return xerrors.Errorf("GitHub owner/repo: %s in %s token is empty",
ownerRepo, serverName)
}
}
@@ -269,6 +269,12 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
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.LogMsgAnsiColor = Colors[i%len(Colors)]
i++
@@ -292,5 +298,5 @@ func toCpeURI(cpename string) (string, error) {
}
return naming.BindToURI(wfn), nil
}
return "", fmt.Errorf("Unknow CPE format: %s", cpename)
return "", xerrors.Errorf("Unknow CPE format: %s", cpename)
}

View File

@@ -2,12 +2,12 @@ package parser
import (
"encoding/xml"
"fmt"
"io/ioutil"
"os"
"strings"
log "github.com/sirupsen/logrus"
"golang.org/x/xerrors"
)
type analysis struct {
@@ -49,7 +49,7 @@ func Parse(path string) ([]string, error) {
var anal analysis
if err := xml.Unmarshal(b, &anal); err != nil {
return nil, fmt.Errorf("Failed to unmarshal: %s", err)
return nil, xerrors.Errorf("Failed to unmarshal: %s", err)
}
cpes := []string{}

View File

@@ -28,6 +28,7 @@ import (
"github.com/mozqnet/go-exploitdb/db"
exploitmodels "github.com/mozqnet/go-exploitdb/models"
"github.com/parnurzeal/gorequest"
"golang.org/x/xerrors"
)
// FillWithExploit fills exploit information that has in Exploit
@@ -112,8 +113,7 @@ func CheckHTTPHealth() error {
// 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 fmt.Errorf("Failed to connect to exploit server. url: %s, errs: %v",
url, errs)
return xerrors.Errorf("Failed to connect to exploit server. url: %s, errs: %w", url, errs)
}
return nil
}

View File

@@ -18,13 +18,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package exploit
import (
"fmt"
"net/http"
"time"
"github.com/cenkalti/backoff"
"github.com/future-architect/vuls/util"
"github.com/parnurzeal/gorequest"
"golang.org/x/xerrors"
)
type response struct {
@@ -79,11 +79,11 @@ func getCvesViaHTTP(cveIDs []string, urlPrefix string) (
case err := <-errChan:
errs = append(errs, err)
case <-timeout:
return nil, fmt.Errorf("Timeout Fetching OVAL")
return nil, xerrors.New("Timeout Fetching OVAL")
}
}
if len(errs) != 0 {
return nil, fmt.Errorf("Failed to fetch OVAL. err: %v", errs)
return nil, xerrors.Errorf("Failed to fetch OVAL. err: %w", errs)
}
return
}
@@ -108,8 +108,7 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er
if count == retryMax {
return nil
}
return fmt.Errorf("HTTP GET error: %v, url: %s, resp: %v",
errs, url, resp)
return xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %w", url, resp, errs)
}
return nil
}
@@ -118,11 +117,11 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er
}
err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
if err != nil {
errChan <- fmt.Errorf("HTTP Error %s", err)
errChan <- xerrors.Errorf("HTTP Error %w", err)
return
}
if count == retryMax {
errChan <- fmt.Errorf("HRetry count exceeded")
errChan <- xerrors.New("Retry count exceeded")
return
}

View File

@@ -147,7 +147,7 @@ func (deb Debian) FillWithGost(driver db.DB, r *models.ScanResult) (nCVEs int, e
}
for _, name := range names {
v.AffectedPackages = v.AffectedPackages.Store(models.PackageStatus{
v.AffectedPackages = v.AffectedPackages.Store(models.PackageFixStatus{
Name: name,
FixState: "open",
NotFixedYet: true,

View File

@@ -26,6 +26,7 @@ import (
"github.com/future-architect/vuls/models"
"github.com/knqyf263/gost/db"
"github.com/parnurzeal/gorequest"
"golang.org/x/xerrors"
)
// Client is the interface of OVAL client.
@@ -71,8 +72,7 @@ func (b Base) CheckHTTPHealth() error {
// 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 fmt.Errorf("Failed to connect to gost server. url: %s, errs: %v",
url, errs)
return xerrors.Errorf("Failed to connect to gost server. url: %s, errs: %w", url, errs)
}
return nil
}

View File

@@ -14,7 +14,7 @@ func TestSetPackageStates(t *testing.T) {
installed models.Packages
release string
in models.VulnInfo
out models.PackageStatuses
out models.PackageFixStatuses
}{
//0 one
@@ -31,7 +31,7 @@ func TestSetPackageStates(t *testing.T) {
},
release: "7",
in: models.VulnInfo{},
out: []models.PackageStatus{
out: []models.PackageFixStatus{
{
Name: "bouncycastle",
FixState: "Will not fix",
@@ -66,7 +66,7 @@ func TestSetPackageStates(t *testing.T) {
},
release: "7",
in: models.VulnInfo{},
out: []models.PackageStatus{
out: []models.PackageFixStatus{
{
Name: "bouncycastle",
FixState: "Will not fix",
@@ -94,9 +94,9 @@ func TestSetPackageStates(t *testing.T) {
},
release: "7",
in: models.VulnInfo{
AffectedPackages: models.PackageStatuses{},
AffectedPackages: models.PackageFixStatuses{},
},
out: models.PackageStatuses{},
out: models.PackageFixStatuses{},
},
//3 look only the same os release.
@@ -113,9 +113,9 @@ func TestSetPackageStates(t *testing.T) {
},
release: "7",
in: models.VulnInfo{
AffectedPackages: models.PackageStatuses{},
AffectedPackages: models.PackageFixStatuses{},
},
out: models.PackageStatuses{},
out: models.PackageFixStatuses{},
},
}

View File

@@ -191,7 +191,7 @@ func (red RedHat) fillUnfixed(driver db.DB, r *models.ScanResult) (nCVEs int, er
return nCVEs, nil
}
func (red RedHat) mergePackageStates(v models.VulnInfo, ps []gostmodels.RedhatPackageState, installed models.Packages, release string) (pkgStats models.PackageStatuses) {
func (red RedHat) mergePackageStates(v models.VulnInfo, ps []gostmodels.RedhatPackageState, installed models.Packages, release string) (pkgStats models.PackageFixStatuses) {
pkgStats = v.AffectedPackages
for _, pstate := range ps {
if pstate.Cpe !=
@@ -214,7 +214,7 @@ func (red RedHat) mergePackageStates(v models.VulnInfo, ps []gostmodels.RedhatPa
notFixedYet = true
}
pkgStats = pkgStats.Store(models.PackageStatus{
pkgStats = pkgStats.Store(models.PackageFixStatus{
Name: pstate.PackageName,
FixState: pstate.FixState,
NotFixedYet: notFixedYet,

View File

@@ -18,7 +18,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package gost
import (
"fmt"
"net/http"
"time"
@@ -26,6 +25,7 @@ import (
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"github.com/parnurzeal/gorequest"
"golang.org/x/xerrors"
)
type response struct {
@@ -80,11 +80,11 @@ func getCvesViaHTTP(cveIDs []string, urlPrefix string) (
case err := <-errChan:
errs = append(errs, err)
case <-timeout:
return nil, fmt.Errorf("Timeout Fetching OVAL")
return nil, xerrors.New("Timeout Fetching OVAL")
}
}
if len(errs) != 0 {
return nil, fmt.Errorf("Failed to fetch OVAL. err: %v", errs)
return nil, xerrors.Errorf("Failed to fetch OVAL. err: %w", errs)
}
return
}
@@ -154,11 +154,11 @@ func getAllUnfixedCvesViaHTTP(r *models.ScanResult, urlPrefix string) (
case err := <-errChan:
errs = append(errs, err)
case <-timeout:
return nil, fmt.Errorf("Timeout Fetching OVAL")
return nil, xerrors.New("Timeout Fetching OVAL")
}
}
if len(errs) != 0 {
return nil, fmt.Errorf("Failed to fetch OVAL. err: %v", errs)
return nil, xerrors.Errorf("Failed to fetch OVAL. err: %w", errs)
}
return
}
@@ -176,8 +176,7 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er
if count == retryMax {
return nil
}
return fmt.Errorf("HTTP GET error: %v, url: %s, resp: %v",
errs, url, resp)
return xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %w", url, resp, errs)
}
return nil
}
@@ -186,11 +185,11 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er
}
err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
if err != nil {
errChan <- fmt.Errorf("HTTP Error %s", err)
errChan <- xerrors.Errorf("HTTP Error %w", err)
return
}
if count == retryMax {
errChan <- fmt.Errorf("HRetry count exceeded")
errChan <- xerrors.New("Retry count exceeded")
return
}

View File

@@ -140,6 +140,7 @@ func (v CveContents) References(myFamily string) (values []CveContentRefs) {
})
}
}
return
}
@@ -230,6 +231,8 @@ func NewCveContentType(name string) CveContentType {
return DebianSecurityTracker
case "microsoft":
return Microsoft
case "wordpress":
return WPVulnDB
default:
return Unknown
}
@@ -269,6 +272,9 @@ const (
// Microsoft is Microsoft
Microsoft CveContentType = "microsoft"
// WPVulnDB is WordPress
WPVulnDB CveContentType = "wpvulndb"
// Unknown is Unknown
Unknown CveContentType = "unknown"
)
@@ -286,6 +292,7 @@ var AllCveContetTypes = CveContentTypes{
Ubuntu,
RedHatAPI,
DebianSecurityTracker,
WPVulnDB,
}
// Except returns CveContentTypes except for given args

View File

@@ -21,6 +21,8 @@ import (
"bytes"
"fmt"
"strings"
"golang.org/x/xerrors"
)
// Packages is Map of Package
@@ -83,7 +85,7 @@ func (ps Packages) FindByFQPN(nameVerRelArc string) (*Package, error) {
return &p, nil
}
}
return nil, fmt.Errorf("Failed to find the package: %s", nameVerRelArc)
return nil, xerrors.Errorf("Failed to find the package: %s", nameVerRelArc)
}
// Package has installed binary packages.

View File

@@ -36,35 +36,37 @@ type ScanResults []ScanResult
// ScanResult has the result of scanned CVE information.
type ScanResult struct {
JSONVersion int `json:"jsonVersion"`
Lang string `json:"lang"`
ServerUUID string `json:"serverUUID"`
ServerName string `json:"serverName"` // TOML Section key
Family string `json:"family"`
Release string `json:"release"`
Container Container `json:"container"`
Platform Platform `json:"platform"`
IPv4Addrs []string `json:"ipv4Addrs,omitempty"` // only global unicast address (https://golang.org/pkg/net/#IP.IsGlobalUnicast)
IPv6Addrs []string `json:"ipv6Addrs,omitempty"` // only global unicast address (https://golang.org/pkg/net/#IP.IsGlobalUnicast)
ScannedAt time.Time `json:"scannedAt"`
ScanMode string `json:"scanMode"`
ScannedVersion string `json:"scannedVersion"`
ScannedRevision string `json:"scannedRevision"`
ScannedBy string `json:"scannedBy"`
ScannedIPv4Addrs []string `json:"scannedIpv4Addrs"`
ScannedIPv6Addrs []string `json:"scannedIpv6Addrs"`
ReportedAt time.Time `json:"reportedAt"`
ReportedVersion string `json:"reportedVersion"`
ReportedRevision string `json:"reportedRevision"`
ReportedBy string `json:"reportedBy"`
ScannedCves VulnInfos `json:"scannedCves"`
RunningKernel Kernel `json:"runningKernel"`
Packages Packages `json:"packages"`
CweDict CweDict `json:"cweDict"`
Optional map[string]interface{} `json:",omitempty"`
SrcPackages SrcPackages `json:",omitempty"`
Errors []string `json:"errors"`
Config struct {
JSONVersion int `json:"jsonVersion"`
Lang string `json:"lang"`
ServerUUID string `json:"serverUUID"`
ServerName string `json:"serverName"` // TOML Section key
Family string `json:"family"`
Release string `json:"release"`
Container Container `json:"container"`
Platform Platform `json:"platform"`
IPv4Addrs []string `json:"ipv4Addrs,omitempty"` // only global unicast address (https://golang.org/pkg/net/#IP.IsGlobalUnicast)
IPv6Addrs []string `json:"ipv6Addrs,omitempty"` // only global unicast address (https://golang.org/pkg/net/#IP.IsGlobalUnicast)
ScannedAt time.Time `json:"scannedAt"`
ScanMode string `json:"scanMode"`
ScannedVersion string `json:"scannedVersion"`
ScannedRevision string `json:"scannedRevision"`
ScannedBy string `json:"scannedBy"`
ScannedIPv4Addrs []string `json:"scannedIpv4Addrs,omitempty"`
ScannedIPv6Addrs []string `json:"scannedIpv6Addrs,omitempty"`
ReportedAt time.Time `json:"reportedAt"`
ReportedVersion string `json:"reportedVersion"`
ReportedRevision string `json:"reportedRevision"`
ReportedBy string `json:"reportedBy"`
Errors []string `json:"errors"`
ScannedCves VulnInfos `json:"scannedCves"`
RunningKernel Kernel `json:"runningKernel"`
Packages Packages `json:"packages"`
SrcPackages SrcPackages `json:",omitempty"`
WordPressPackages *WordPressPackages `json:",omitempty"`
CweDict CweDict `json:"cweDict,omitempty"`
Optional map[string]interface{} `json:",omitempty"`
Config struct {
Scan config.Config `json:"scan"`
Report config.Config `json:"report"`
} `json:"config"`
@@ -252,6 +254,30 @@ func (r ScanResult) FilterIgnorePkgs() ScanResult {
return r
}
// FilterInactiveWordPressLibs is filter function.
func (r ScanResult) FilterInactiveWordPressLibs() ScanResult {
if !config.Conf.Servers[r.ServerName].WordPress.IgnoreInactive {
return r
}
filtered := r.ScannedCves.Find(func(v VulnInfo) bool {
if len(v.WpPackageFixStats) == 0 {
return true
}
// Ignore if all libs in this vulnInfo inactive
for _, wp := range v.WpPackageFixStats {
if p, ok := r.WordPressPackages.Find(wp.Name); ok {
if p.Status != Inactive {
return true
}
}
}
return false
})
r.ScannedCves = filtered
return r
}
// ReportFileName returns the filename on localhost without extention
func (r ScanResult) ReportFileName() (name string) {
if len(r.Container.ContainerID) == 0 {

View File

@@ -348,7 +348,7 @@ func TestFilterUnfixed(t *testing.T) {
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
AffectedPackages: PackageStatuses{
AffectedPackages: PackageFixStatuses{
{
Name: "a",
NotFixedYet: true,
@@ -357,7 +357,7 @@ func TestFilterUnfixed(t *testing.T) {
},
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
AffectedPackages: PackageStatuses{
AffectedPackages: PackageFixStatuses{
{
Name: "b",
NotFixedYet: false,
@@ -366,7 +366,7 @@ func TestFilterUnfixed(t *testing.T) {
},
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
AffectedPackages: PackageStatuses{
AffectedPackages: PackageFixStatuses{
{
Name: "c",
NotFixedYet: true,
@@ -383,7 +383,7 @@ func TestFilterUnfixed(t *testing.T) {
ScannedCves: VulnInfos{
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
AffectedPackages: PackageStatuses{
AffectedPackages: PackageFixStatuses{
{
Name: "b",
NotFixedYet: false,
@@ -392,7 +392,7 @@ func TestFilterUnfixed(t *testing.T) {
},
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
AffectedPackages: PackageStatuses{
AffectedPackages: PackageFixStatuses{
{
Name: "c",
NotFixedYet: true,
@@ -435,7 +435,7 @@ func TestFilterIgnorePkgs(t *testing.T) {
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
AffectedPackages: PackageStatuses{
AffectedPackages: PackageFixStatuses{
{Name: "kernel"},
},
},
@@ -462,7 +462,7 @@ func TestFilterIgnorePkgs(t *testing.T) {
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
AffectedPackages: PackageStatuses{
AffectedPackages: PackageFixStatuses{
{Name: "kernel"},
{Name: "vim"},
},
@@ -475,7 +475,7 @@ func TestFilterIgnorePkgs(t *testing.T) {
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
AffectedPackages: PackageStatuses{
AffectedPackages: PackageFixStatuses{
{Name: "kernel"},
{Name: "vim"},
},
@@ -491,7 +491,7 @@ func TestFilterIgnorePkgs(t *testing.T) {
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
AffectedPackages: PackageStatuses{
AffectedPackages: PackageFixStatuses{
{Name: "kernel"},
{Name: "vim"},
},
@@ -545,7 +545,7 @@ func TestFilterIgnorePkgsContainer(t *testing.T) {
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
AffectedPackages: PackageStatuses{
AffectedPackages: PackageFixStatuses{
{Name: "kernel"},
},
},
@@ -574,7 +574,7 @@ func TestFilterIgnorePkgsContainer(t *testing.T) {
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
AffectedPackages: PackageStatuses{
AffectedPackages: PackageFixStatuses{
{Name: "kernel"},
{Name: "vim"},
},
@@ -588,7 +588,7 @@ func TestFilterIgnorePkgsContainer(t *testing.T) {
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
AffectedPackages: PackageStatuses{
AffectedPackages: PackageFixStatuses{
{Name: "kernel"},
{Name: "vim"},
},
@@ -605,7 +605,7 @@ func TestFilterIgnorePkgsContainer(t *testing.T) {
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
AffectedPackages: PackageStatuses{
AffectedPackages: PackageFixStatuses{
{Name: "kernel"},
{Name: "vim"},
},

View File

@@ -122,20 +122,19 @@ func (v VulnInfos) FormatFixedStatus(packs Packages) string {
return fmt.Sprintf("%d/%d Fixed", fixed, total)
}
// PackageStatuses is a list of PackageStatus
type PackageStatuses []PackageStatus
// PackageFixStatuses is a list of PackageStatus
type PackageFixStatuses []PackageFixStatus
// FormatTuiSummary format packname to show TUI summary
func (ps PackageStatuses) FormatTuiSummary() string {
names := []string{}
// Names return a slice of package names
func (ps PackageFixStatuses) Names() (names []string) {
for _, p := range ps {
names = append(names, p.Name)
}
return strings.Join(names, ", ")
return names
}
// Store insert given pkg if missing, update pkg if exists
func (ps PackageStatuses) Store(pkg PackageStatus) PackageStatuses {
func (ps PackageFixStatuses) Store(pkg PackageFixStatus) PackageFixStatuses {
for i, p := range ps {
if p.Name == pkg.Name {
ps[i] = pkg
@@ -147,15 +146,15 @@ func (ps PackageStatuses) Store(pkg PackageStatus) PackageStatuses {
}
// Sort by Name
func (ps PackageStatuses) Sort() {
func (ps PackageFixStatuses) Sort() {
sort.Slice(ps, func(i, j int) bool {
return ps[i].Name < ps[j].Name
})
return
}
// PackageStatus has name and other status abount the package
type PackageStatus struct {
// PackageFixStatus has name and other status abount the package
type PackageFixStatus struct {
Name string `json:"name"`
NotFixedYet bool `json:"notFixedYet"`
FixState string `json:"fixState"`
@@ -163,22 +162,24 @@ type PackageStatus struct {
// VulnInfo has a vulnerability information and unsecure packages
type VulnInfo struct {
CveID string `json:"cveID,omitempty"`
Confidences Confidences `json:"confidences,omitempty"`
AffectedPackages PackageStatuses `json:"affectedPackages,omitempty"`
DistroAdvisories []DistroAdvisory `json:"distroAdvisories,omitempty"` // for Aamazon, RHEL, FreeBSD
CveContents CveContents `json:"cveContents,omitempty"`
Exploits []Exploit `json:"exploits,omitempty"`
AlertDict AlertDict `json:"alertDict,omitempty"`
CveID string `json:"cveID,omitempty"`
Confidences Confidences `json:"confidences,omitempty"`
AffectedPackages PackageFixStatuses `json:"affectedPackages,omitempty"`
DistroAdvisories []DistroAdvisory `json:"distroAdvisories,omitempty"` // for Aamazon, RHEL, FreeBSD
CveContents CveContents `json:"cveContents,omitempty"`
Exploits []Exploit `json:"exploits,omitempty"`
AlertDict AlertDict `json:"alertDict,omitempty"`
CpeURIs []string `json:"cpeURIs,omitempty"` // CpeURIs related to this CVE defined in config.toml
GitHubSecurityAlerts GitHubSecurityAlerts `json:"gitHubSecurityAlerts,omitempty"`
WpPackageFixStats WpPackageFixStats `json:"wpPackageFixStats,omitempty"`
VulnType string `json:"vulnType,omitempty"`
}
// GitHubSecurityAlerts is a list of GitHubSecurityAlert
type GitHubSecurityAlerts []GitHubSecurityAlert
// Add adds given arg to the slice and return the slice (imutable)
// Add adds given arg to the slice and return the slice (immutable)
func (g GitHubSecurityAlerts) Add(alert GitHubSecurityAlert) GitHubSecurityAlerts {
for _, a := range g {
if a.PackageName == alert.PackageName {
@@ -188,12 +189,12 @@ func (g GitHubSecurityAlerts) Add(alert GitHubSecurityAlert) GitHubSecurityAlert
return append(g, alert)
}
func (g GitHubSecurityAlerts) String() string {
ss := []string{}
// Names return a slice of lib names
func (g GitHubSecurityAlerts) Names() (names []string) {
for _, a := range g {
ss = append(ss, a.PackageName)
names = append(names, a.PackageName)
}
return strings.Join(ss, ", ")
return names
}
// GitHubSecurityAlert has detected CVE-ID, PackageName, Status fetched via GitHub API
@@ -206,6 +207,30 @@ type GitHubSecurityAlert struct {
DismissReason string `json:"dismissReason"`
}
// WpPackageFixStats is a list of WpPackageFixStatus
type WpPackageFixStats []WpPackageFixStatus
// Names return a slice of names
func (ws WpPackageFixStats) Names() (names []string) {
for _, w := range ws {
names = append(names, w.Name)
}
return names
}
// WpPackages has a list of WpPackage
type WpPackages []WpPackage
// Add adds given arg to the slice and return the slice (immutable)
func (g WpPackages) Add(pkg WpPackage) WpPackages {
for _, a := range g {
if a.Name == pkg.Name {
return g
}
}
return append(g, pkg)
}
// Titles returns tilte (TUI)
func (v VulnInfo) Titles(lang, myFamily string) (values []CveContentStr) {
if lang == "ja" {
@@ -278,6 +303,13 @@ func (v VulnInfo) Summaries(lang, myFamily string) (values []CveContentStr) {
})
}
if v, ok := v.CveContents[WPVulnDB]; ok {
values = append(values, CveContentStr{
Type: "WPVDB",
Value: v.Title,
})
}
if len(values) == 0 {
return []CveContentStr{{
Type: Unknown,
@@ -511,15 +543,15 @@ func (v VulnInfo) AttackVector() string {
for _, cnt := range v.CveContents {
if strings.HasPrefix(cnt.Cvss2Vector, "AV:N") ||
strings.HasPrefix(cnt.Cvss3Vector, "CVSS:3.0/AV:N") {
return "Network"
return "N"
} else if strings.HasPrefix(cnt.Cvss2Vector, "AV:A") ||
strings.HasPrefix(cnt.Cvss3Vector, "CVSS:3.0/AV:A") {
return "Adjacent"
return "A"
} else if strings.HasPrefix(cnt.Cvss2Vector, "AV:L") ||
strings.HasPrefix(cnt.Cvss3Vector, "CVSS:3.0/AV:L") {
return "Local"
return "L"
} else if strings.HasPrefix(cnt.Cvss3Vector, "CVSS:3.0/AV:P") {
return "Physical"
return "P"
}
}
if cont, found := v.CveContents[DebianSecurityTracker]; found {
@@ -538,17 +570,17 @@ func (v VulnInfo) PatchStatus(packs Packages) string {
}
for _, p := range v.AffectedPackages {
if p.NotFixedYet {
return "Unfixed"
return "unfixed"
}
// fast, offline mode doesn't have new version
if pack, ok := packs[p.Name]; ok {
if pack.NewVersion == "" {
return "Unknown"
return "unknown"
}
}
}
return "Fixed"
return "fixed"
}
// CveContentCvss has CVSS information
@@ -648,6 +680,12 @@ func (v VulnInfo) Cvss3CalcURL() string {
// VendorLinks returns links of vendor support's URL
func (v VulnInfo) VendorLinks(family string) map[string]string {
links := map[string]string{}
if strings.HasPrefix(v.CveID, "WPVDBID") {
links["WPVulnDB"] = fmt.Sprintf("https://wpvulndb.com/vulnerabilities/%s",
strings.TrimPrefix(v.CveID, "WPVDBID-"))
return links
}
switch family {
case config.RedHat, config.CentOS:
links["RHEL-CVE"] = "https://access.redhat.com/security/cve/" + v.CveID
@@ -810,6 +848,9 @@ const (
// GitHubMatchStr is a String representation of GitHubMatch
GitHubMatchStr = "GitHubMatch"
// WPVulnDBMatchStr is a String representation of WordPress VulnDB scanning
WPVulnDBMatchStr = "WPVulnDBMatch"
// FailedToGetChangelog is a String representation of FailedToGetChangelog
FailedToGetChangelog = "FailedToGetChangelog"
@@ -844,4 +885,7 @@ var (
// GitHubMatch is a ranking how confident the CVE-ID was deteted correctly
GitHubMatch = Confidence{97, GitHubMatchStr, 2}
// WPVulnDBMatch is a ranking how confident the CVE-ID was deteted correctly
WPVulnDBMatch = Confidence{100, WPVulnDBMatchStr, 0}
)

View File

@@ -916,15 +916,15 @@ func TestFormatMaxCvssScore(t *testing.T) {
func TestSortPackageStatues(t *testing.T) {
var tests = []struct {
in PackageStatuses
out PackageStatuses
in PackageFixStatuses
out PackageFixStatuses
}{
{
in: PackageStatuses{
in: PackageFixStatuses{
{Name: "b"},
{Name: "a"},
},
out: PackageStatuses{
out: PackageFixStatuses{
{Name: "a"},
{Name: "b"},
},
@@ -940,19 +940,19 @@ func TestSortPackageStatues(t *testing.T) {
func TestStorePackageStatueses(t *testing.T) {
var tests = []struct {
pkgstats PackageStatuses
in PackageStatus
out PackageStatuses
pkgstats PackageFixStatuses
in PackageFixStatus
out PackageFixStatuses
}{
{
pkgstats: PackageStatuses{
pkgstats: PackageFixStatuses{
{Name: "a"},
{Name: "b"},
},
in: PackageStatus{
in: PackageFixStatus{
Name: "c",
},
out: PackageStatuses{
out: PackageFixStatuses{
{Name: "a"},
{Name: "b"},
{Name: "c"},

88
models/wordpress.go Normal file
View File

@@ -0,0 +1,88 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package models
// WordPressPackages has Core version, plugins and themes.
type WordPressPackages []WpPackage
// CoreVersion returns the core version of the installed WordPress
func (w WordPressPackages) CoreVersion() string {
for _, p := range w {
if p.Type == WPCore {
return p.Version
}
}
return ""
}
// Plugins returns a slice of plugins of the installed WordPress
func (w WordPressPackages) Plugins() (ps []WpPackage) {
for _, p := range w {
if p.Type == WPPlugin {
ps = append(ps, p)
}
}
return
}
// Themes returns a slice of themes of the installed WordPress
func (w WordPressPackages) Themes() (ps []WpPackage) {
for _, p := range w {
if p.Type == WPTheme {
ps = append(ps, p)
}
}
return
}
// Find searches by specified name
func (w WordPressPackages) Find(name string) (ps *WpPackage, found bool) {
for _, p := range w {
if p.Name == name {
return &p, true
}
}
return nil, false
}
const (
// WPCore is a type `core` in WPPackage struct
WPCore = "core"
// WPPlugin is a type `plugin` in WPPackage struct
WPPlugin = "plugin"
// WPTheme is a type `theme` in WPPackage struct
WPTheme = "theme"
// Inactive is a inactive status in WPPackage struct
Inactive = "inactive"
)
// WpPackage has a details of plugin and theme
type WpPackage struct {
Name string `json:"name,omitempty"`
Status string `json:"status,omitempty"` // active, inactive or must-use
Update string `json:"update,omitempty"` // available or none
Version string `json:"version,omitempty"`
Type string `json:"type,omitempty"` // core, plugin, theme
}
// WpPackageFixStatus is used in Vulninfo.WordPress
type WpPackageFixStatus struct {
Name string `json:"name,omitempty"`
FixedIn string `json:"fixedIn,omitempty"`
}

View File

@@ -36,7 +36,7 @@ func TestPackNamesOfUpdateDebian(t *testing.T) {
in: models.ScanResult{
ScannedCves: models.VulnInfos{
"CVE-2000-1000": models.VulnInfo{
AffectedPackages: models.PackageStatuses{
AffectedPackages: models.PackageFixStatuses{
{Name: "packA"},
{Name: "packC"},
},
@@ -56,7 +56,7 @@ func TestPackNamesOfUpdateDebian(t *testing.T) {
out: models.ScanResult{
ScannedCves: models.VulnInfos{
"CVE-2000-1000": models.VulnInfo{
AffectedPackages: models.PackageStatuses{
AffectedPackages: models.PackageFixStatuses{
{Name: "packA"},
{Name: "packB", NotFixedYet: true},
{Name: "packC"},

View File

@@ -28,6 +28,7 @@ import (
"github.com/future-architect/vuls/util"
"github.com/kotakanbe/goval-dictionary/db"
"github.com/parnurzeal/gorequest"
"golang.org/x/xerrors"
)
// Client is the interface of OVAL client.
@@ -58,7 +59,7 @@ func (b Base) CheckHTTPHealth() error {
// 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 fmt.Errorf("Failed to request to OVAL server. url: %s, errs: %v",
return xerrors.Errorf("Failed to request to OVAL server. url: %s, errs: %w",
url, errs)
}
return nil
@@ -69,8 +70,7 @@ func (b Base) CheckIfOvalFetched(driver db.DB, osFamily, release string) (fetche
if !cnf.Conf.OvalDict.IsFetchViaHTTP() {
count, err := driver.CountDefs(osFamily, release)
if err != nil {
return false, fmt.Errorf("Failed to count OVAL defs: %s, %s, %v",
osFamily, release, err)
return false, xerrors.Errorf("Failed to count OVAL defs: %s, %s, %w", osFamily, release, err)
}
return 0 < count, nil
}
@@ -78,13 +78,11 @@ 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()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return false, fmt.Errorf("HTTP GET error: %v, url: %s, resp: %v",
errs, url, resp)
return false, xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %w", url, resp, errs)
}
count := 0
if err := json.Unmarshal([]byte(body), &count); err != nil {
return false, fmt.Errorf("Failed to Unmarshall. body: %s, err: %s",
body, err)
return false, xerrors.Errorf("Failed to Unmarshall. body: %s, err: %w", body, err)
}
return 0 < count, nil
}
@@ -98,13 +96,11 @@ func (b Base) CheckIfOvalFresh(driver db.DB, osFamily, release string) (ok bool,
url, _ := util.URLPathJoin(cnf.Conf.OvalDict.URL, "lastmodified", osFamily, release)
resp, body, errs := gorequest.New().Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return false, fmt.Errorf("HTTP GET error: %v, url: %s, resp: %v",
errs, url, resp)
return false, xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %w", url, resp, errs)
}
if err := json.Unmarshal([]byte(body), &lastModified); err != nil {
return false, fmt.Errorf("Failed to Unmarshall. body: %s, err: %s",
body, err)
return false, xerrors.Errorf("Failed to Unmarshall. body: %s, err: %w", body, err)
}
}

View File

@@ -102,7 +102,7 @@ func TestPackNamesOfUpdate(t *testing.T) {
in: models.ScanResult{
ScannedCves: models.VulnInfos{
"CVE-2000-1000": models.VulnInfo{
AffectedPackages: models.PackageStatuses{
AffectedPackages: models.PackageFixStatuses{
{Name: "packA"},
{Name: "packB", NotFixedYet: false},
},
@@ -126,7 +126,7 @@ func TestPackNamesOfUpdate(t *testing.T) {
out: models.ScanResult{
ScannedCves: models.VulnInfos{
"CVE-2000-1000": models.VulnInfo{
AffectedPackages: models.PackageStatuses{
AffectedPackages: models.PackageFixStatuses{
{Name: "packA"},
{Name: "packB", NotFixedYet: true},
},

View File

@@ -19,7 +19,6 @@ package oval
import (
"encoding/json"
"fmt"
"net/http"
"regexp"
"strings"
@@ -34,6 +33,7 @@ import (
"github.com/kotakanbe/goval-dictionary/db"
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
"github.com/parnurzeal/gorequest"
"golang.org/x/xerrors"
)
type ovalResult struct {
@@ -47,9 +47,9 @@ type defPacks struct {
actuallyAffectedPackNames map[string]bool
}
func (e defPacks) toPackStatuses() (ps models.PackageStatuses) {
func (e defPacks) toPackStatuses() (ps models.PackageFixStatuses) {
for name, notFixedYet := range e.actuallyAffectedPackNames {
ps = append(ps, models.PackageStatus{
ps = append(ps, models.PackageFixStatus{
Name: name,
NotFixedYet: notFixedYet,
})
@@ -164,11 +164,11 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult) (
case err := <-errChan:
errs = append(errs, err)
case <-timeout:
return relatedDefs, fmt.Errorf("Timeout Fetching OVAL")
return relatedDefs, xerrors.New("Timeout Fetching OVAL")
}
}
if len(errs) != 0 {
return relatedDefs, fmt.Errorf("Failed to fetch OVAL. err: %v", errs)
return relatedDefs, xerrors.Errorf("Failed to fetch OVAL. err: %w", errs)
}
return
}
@@ -186,8 +186,7 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er
if count == retryMax {
return nil
}
return fmt.Errorf("HTTP GET error: %v, url: %s, resp: %v",
errs, url, resp)
return xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %w", url, resp, errs)
}
return nil
}
@@ -196,18 +195,17 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er
}
err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
if err != nil {
errChan <- fmt.Errorf("HTTP Error %s", err)
errChan <- xerrors.Errorf("HTTP Error %w", err)
return
}
if count == retryMax {
errChan <- fmt.Errorf("HRetry count exceeded")
errChan <- xerrors.New("HRetry count exceeded")
return
}
defs := []ovalmodels.Definition{}
if err := json.Unmarshal([]byte(body), &defs); err != nil {
errChan <- fmt.Errorf("Failed to Unmarshall. body: %s, err: %s",
body, err)
errChan <- xerrors.Errorf("Failed to Unmarshall. body: %s, err: %w", body, err)
return
}
resChan <- response{
@@ -238,7 +236,7 @@ func getDefsByPackNameFromOvalDB(driver db.DB, r *models.ScanResult) (relatedDef
for _, req := range requests {
definitions, err := driver.GetByPackName(r.Release, req.packName)
if err != nil {
return relatedDefs, fmt.Errorf("Failed to get %s OVAL info by package: %#v, err: %s", r.Family, req, err)
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 := isOvalDefAffected(def, req, r.Family, r.RunningKernel)
@@ -349,5 +347,5 @@ func lessThan(family, versionRelease string, packB ovalmodels.Package) (bool, er
default:
util.Log.Errorf("Not implemented yet: %s", family)
}
return false, fmt.Errorf("Package version comparison not supported: %s", family)
return false, xerrors.Errorf("Package version comparison not supported: %s", family)
}

View File

@@ -110,7 +110,7 @@ func TestDefpacksToPackStatuses(t *testing.T) {
}
var tests = []struct {
in in
out models.PackageStatuses
out models.PackageFixStatuses
}{
// Ubuntu
{
@@ -135,7 +135,7 @@ func TestDefpacksToPackStatuses(t *testing.T) {
},
},
},
out: models.PackageStatuses{
out: models.PackageFixStatuses{
{
Name: "a",
NotFixedYet: true,

View File

@@ -25,6 +25,7 @@ import (
"time"
storage "github.com/Azure/azure-sdk-for-go/storage"
"golang.org/x/xerrors"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
@@ -60,7 +61,7 @@ func (w AzureBlobWriter) Write(rs ...models.ScanResult) (err error) {
k := key + ".json"
var b []byte
if b, err = json.Marshal(r); err != nil {
return fmt.Errorf("Failed to Marshal to JSON: %s", err)
return xerrors.Errorf("Failed to Marshal to JSON: %w", err)
}
if err := createBlockBlob(cli, k, b); err != nil {
return err
@@ -87,7 +88,7 @@ func (w AzureBlobWriter) Write(rs ...models.ScanResult) (err error) {
k := key + ".xml"
var b []byte
if b, err = xml.Marshal(r); err != nil {
return fmt.Errorf("Failed to Marshal to XML: %s", err)
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 {
@@ -117,7 +118,7 @@ func CheckIfAzureContainerExists() error {
}
}
if !found {
return fmt.Errorf("Container not found. Container: %s", c.Conf.Azure.ContainerName)
return xerrors.Errorf("Container not found. Container: %s", c.Conf.Azure.ContainerName)
}
return nil
}
@@ -142,7 +143,7 @@ func createBlockBlob(cli storage.BlobStorageClient, k string, b []byte) error {
ref := cli.GetContainerReference(c.Conf.Azure.ContainerName)
blob := ref.GetBlobReference(k)
if err := blob.CreateBlockBlobFromReader(bytes.NewReader(b), nil); err != nil {
return fmt.Errorf("Failed to upload data to %s/%s, %s",
return xerrors.Errorf("Failed to upload data to %s/%s, err: %w",
c.Conf.Azure.ContainerName, k, err)
}
return nil

View File

@@ -25,6 +25,7 @@ import (
"github.com/cenkalti/backoff"
"github.com/parnurzeal/gorequest"
"golang.org/x/xerrors"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/util"
@@ -57,7 +58,7 @@ func (api cvedictClient) CheckHealth() error {
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 fmt.Errorf("Failed to request to CVE server. url: %s, errs: %v",
return xerrors.Errorf("Failed to request to CVE server. url: %s, errs: %w",
url, errs)
}
return nil
@@ -73,7 +74,7 @@ func (api cvedictClient) FetchCveDetails(driver cvedb.DB, cveIDs []string) (cveD
for _, cveID := range cveIDs {
cveDetail, err := driver.Get(cveID)
if err != nil {
return nil, fmt.Errorf("Failed to fetch CVE. err: %s", err)
return nil, xerrors.Errorf("Failed to fetch CVE. err: %w", err)
}
if len(cveDetail.CveID) == 0 {
cveDetails = append(cveDetails, cve.CveDetail{
@@ -132,12 +133,12 @@ func (api cvedictClient) FetchCveDetails(driver cvedb.DB, cveIDs []string) (cveD
case err := <-errChan:
errs = append(errs, err)
case <-timeout:
return nil, fmt.Errorf("Timeout Fetching CVE")
return nil, xerrors.New("Timeout Fetching CVE")
}
}
if len(errs) != 0 {
return nil,
fmt.Errorf("Failed to fetch CVE. err: %v", errs)
xerrors.Errorf("Failed to fetch CVE. err: %w", errs)
}
return
}
@@ -150,8 +151,8 @@ func (api cvedictClient) httpGet(key, url string, resChan chan<- response, errCh
// resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
resp, body, errs = gorequest.New().Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return fmt.Errorf("HTTP GET error: %v, url: %s, resp: %v",
errs, url, resp)
return xerrors.Errorf("HTTP GET Error, url: %s, resp: %v, err: %w",
url, resp, errs)
}
return nil
}
@@ -161,13 +162,12 @@ func (api cvedictClient) httpGet(key, url string, resChan chan<- response, errCh
}
err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
if err != nil {
errChan <- fmt.Errorf("HTTP Error %s", err)
errChan <- xerrors.Errorf("HTTP Error: %w", err)
return
}
cveDetail := cve.CveDetail{}
if err := json.Unmarshal([]byte(body), &cveDetail); err != nil {
errChan <- fmt.Errorf("Failed to Unmarshall. body: %s, err: %s",
body, err)
errChan <- xerrors.Errorf("Failed to Unmarshall. body: %s, err: %w", body, err)
return
}
resChan <- response{
@@ -203,7 +203,7 @@ func (api cvedictClient) httpPost(key, url string, query map[string]string) ([]c
}
resp, body, errs = req.End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return fmt.Errorf("HTTP POST error: %v, url: %s, resp: %v", errs, url, resp)
return xerrors.Errorf("HTTP POST error. url: %s, resp: %v, err: %w", url, resp, errs)
}
return nil
}
@@ -212,13 +212,13 @@ func (api cvedictClient) httpPost(key, url string, query map[string]string) ([]c
}
err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
if err != nil {
return nil, fmt.Errorf("HTTP Error %s", err)
return nil, xerrors.Errorf("HTTP Error: %w", err)
}
cveDetails := []cve.CveDetail{}
if err := json.Unmarshal([]byte(body), &cveDetails); err != nil {
return nil,
fmt.Errorf("Failed to Unmarshall. body: %s, err: %s", body, err)
xerrors.Errorf("Failed to Unmarshall. body: %s, err: %w", body, err)
}
return cveDetails, nil
}

View File

@@ -1,7 +1,6 @@
package report
import (
"fmt"
"os"
"github.com/future-architect/vuls/config"
@@ -10,6 +9,7 @@ import (
cvedb "github.com/kotakanbe/go-cve-dictionary/db"
ovaldb "github.com/kotakanbe/goval-dictionary/db"
exploitdb "github.com/mozqnet/go-exploitdb/db"
"golang.org/x/xerrors"
)
// DBClient is a dictionarie's db client for reporting
@@ -33,7 +33,7 @@ type DBClientConf struct {
func NewDBClient(cnf DBClientConf) (dbclient *DBClient, locked bool, err error) {
cveDriver, locked, err := NewCveDB(cnf)
if locked {
return nil, true, fmt.Errorf("CveDB is locked: %s",
return nil, true, xerrors.Errorf("CveDB is locked: %s",
cnf.OvalDictCnf.SQLite3Path)
} else if err != nil {
return nil, locked, err
@@ -41,7 +41,7 @@ func NewDBClient(cnf DBClientConf) (dbclient *DBClient, locked bool, err error)
ovaldb, locked, err := NewOvalDB(cnf)
if locked {
return nil, true, fmt.Errorf("OvalDB is locked: %s",
return nil, true, xerrors.Errorf("OvalDB is locked: %s",
cnf.OvalDictCnf.SQLite3Path)
} else if err != nil {
util.Log.Warnf("Unable to use OvalDB: %s, err: %s",
@@ -50,7 +50,7 @@ func NewDBClient(cnf DBClientConf) (dbclient *DBClient, locked bool, err error)
gostdb, locked, err := NewGostDB(cnf)
if locked {
return nil, true, fmt.Errorf("gostDB is locked: %s",
return nil, true, xerrors.Errorf("gostDB is locked: %s",
cnf.GostCnf.SQLite3Path)
} else if err != nil {
util.Log.Warnf("Unable to use gostDB: %s, err: %s",
@@ -59,7 +59,7 @@ func NewDBClient(cnf DBClientConf) (dbclient *DBClient, locked bool, err error)
exploitdb, locked, err := NewExploitDB(cnf)
if locked {
return nil, true, fmt.Errorf("exploitDB is locked: %s",
return nil, true, xerrors.Errorf("exploitDB is locked: %s",
cnf.ExploitCnf.SQLite3Path)
} else if err != nil {
util.Log.Warnf("Unable to use exploitDB: %s, err: %s",
@@ -88,7 +88,7 @@ func NewCveDB(cnf DBClientConf) (driver cvedb.DB, locked bool, err error) {
util.Log.Debugf("Open cve-dictionary db (%s): %s", cnf.CveDictCnf.Type, path)
driver, locked, err = cvedb.NewDB(cnf.CveDictCnf.Type, path, cnf.DebugSQL)
if err != nil {
err = fmt.Errorf("Failed to init CVE DB. err: %s, path: %s", err, path)
err = xerrors.Errorf("Failed to init CVE DB. err: %w, path: %s", err, path)
return nil, locked, err
}
return driver, false, nil
@@ -112,7 +112,7 @@ func NewOvalDB(cnf DBClientConf) (driver ovaldb.DB, locked bool, err error) {
util.Log.Debugf("Open oval-dictionary db (%s): %s", cnf.OvalDictCnf.Type, path)
driver, locked, err = ovaldb.NewDB("", cnf.OvalDictCnf.Type, path, cnf.DebugSQL)
if err != nil {
err = fmt.Errorf("Failed to new OVAL DB. err: %s", err)
err = xerrors.Errorf("Failed to new OVAL DB. err: %w", err)
if locked {
return nil, true, err
}

View File

@@ -27,6 +27,7 @@ import (
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"golang.org/x/xerrors"
)
// EMailWriter send mail
@@ -108,7 +109,7 @@ func (e *emailSender) Send(subject, body string) (err error) {
cc := strings.Join(emailConf.Cc[:], ", ")
mailAddresses := append(emailConf.To, emailConf.Cc...)
if _, err := mail.ParseAddressList(strings.Join(mailAddresses[:], ", ")); err != nil {
return fmt.Errorf("Failed to parse email addresses: %s", err)
return xerrors.Errorf("Failed to parse email addresses: %w", err)
}
headers := make(map[string]string)
@@ -141,7 +142,7 @@ func (e *emailSender) Send(subject, body string) (err error) {
[]byte(message),
)
if err != nil {
return fmt.Errorf("Failed to send emails: %s", err)
return xerrors.Errorf("Failed to send emails: %w", err)
}
return nil
}
@@ -153,7 +154,7 @@ func (e *emailSender) Send(subject, body string) (err error) {
[]byte(message),
)
if err != nil {
return fmt.Errorf("Failed to send emails: %s", err)
return xerrors.Errorf("Failed to send emails: %w", err)
}
return nil
}

View File

@@ -22,10 +22,9 @@ import (
"encoding/json"
"net/http"
"github.com/pkg/errors"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"golang.org/x/xerrors"
)
// HTTPRequestWriter writes results to HTTP request
@@ -53,7 +52,7 @@ type HTTPResponseWriter struct {
func (w HTTPResponseWriter) Write(rs ...models.ScanResult) (err error) {
res, err := json.Marshal(rs)
if err != nil {
return errors.Wrap(err, "Failed to marshal scah results")
return xerrors.Errorf("Failed to marshal scah results: %w", err)
}
w.Writer.Header().Set("Content-Type", "application/json")
_, err = w.Writer.Write(res)

View File

@@ -21,13 +21,13 @@ import (
"bytes"
"encoding/json"
"encoding/xml"
"fmt"
"io/ioutil"
"os"
"path/filepath"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"golang.org/x/xerrors"
)
// LocalFileWriter writes results to a local file.
@@ -40,8 +40,8 @@ func (w LocalFileWriter) Write(rs ...models.ScanResult) (err error) {
path := filepath.Join(w.CurrentDir, "summary.txt")
text := formatOneLineSummary(rs...)
if err := writeFile(path, []byte(text), 0600); err != nil {
return fmt.Errorf(
"Failed to write to file. path: %s, err: %s",
return xerrors.Errorf(
"Failed to write to file. path: %s, err: %w",
path, err)
}
}
@@ -60,15 +60,15 @@ func (w LocalFileWriter) Write(rs ...models.ScanResult) (err error) {
var b []byte
if c.Conf.Debug {
if b, err = json.MarshalIndent(r, "", " "); err != nil {
return fmt.Errorf("Failed to Marshal to JSON: %s", err)
return xerrors.Errorf("Failed to Marshal to JSON: %w", err)
}
} else {
if b, err = json.Marshal(r); err != nil {
return fmt.Errorf("Failed to Marshal to JSON: %s", err)
return xerrors.Errorf("Failed to Marshal to JSON: %w", err)
}
}
if err := writeFile(p, b, 0600); err != nil {
return fmt.Errorf("Failed to write JSON. path: %s, err: %s", p, err)
return xerrors.Errorf("Failed to write JSON. path: %s, err: %w", p, err)
}
}
@@ -82,8 +82,8 @@ func (w LocalFileWriter) Write(rs ...models.ScanResult) (err error) {
if err := writeFile(
p, []byte(formatList(r)), 0600); err != nil {
return fmt.Errorf(
"Failed to write text files. path: %s, err: %s", p, err)
return xerrors.Errorf(
"Failed to write text files. path: %s, err: %w", p, err)
}
}
@@ -97,8 +97,8 @@ func (w LocalFileWriter) Write(rs ...models.ScanResult) (err error) {
if err := writeFile(
p, []byte(formatFullPlainText(r)), 0600); err != nil {
return fmt.Errorf(
"Failed to write text files. path: %s, err: %s", p, err)
return xerrors.Errorf(
"Failed to write text files. path: %s, err: %w", p, err)
}
}
@@ -112,11 +112,11 @@ func (w LocalFileWriter) Write(rs ...models.ScanResult) (err error) {
var b []byte
if b, err = xml.Marshal(r); err != nil {
return fmt.Errorf("Failed to Marshal to XML: %s", err)
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 fmt.Errorf("Failed to write XML. path: %s, err: %s", p, err)
return xerrors.Errorf("Failed to write XML. path: %s, err: %w", p, err)
}
}
}

View File

@@ -39,12 +39,13 @@ import (
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/oval"
"github.com/future-architect/vuls/util"
"github.com/future-architect/vuls/wordpress"
"github.com/hashicorp/uuid"
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"
"github.com/pkg/errors"
"golang.org/x/xerrors"
)
const (
@@ -69,7 +70,7 @@ func FillCveInfos(dbclient DBClient, rs []models.ScanResult, dir string) ([]mode
if owaspDCXMLPath != "" {
cpes, err := parser.Parse(owaspDCXMLPath)
if err != nil {
return nil, fmt.Errorf("Failed to read OWASP Dependency Check XML: %s, %s, %s",
return nil, xerrors.Errorf("Failed to read OWASP Dependency Check XML on %s, `%s`, err: %w",
r.ServerName, owaspDCXMLPath, err)
}
cpeURIs = append(cpeURIs, cpes...)
@@ -82,7 +83,7 @@ func FillCveInfos(dbclient DBClient, rs []models.ScanResult, dir string) ([]mode
if owaspDCXMLPath != "" {
cpes, err := parser.Parse(owaspDCXMLPath)
if err != nil {
return nil, fmt.Errorf("Failed to read OWASP Dependency Check XML: %s, %s, %s",
return nil, xerrors.Errorf("Failed to read OWASP Dependency Check XML on %s, `%s`, err: %w",
r.ServerInfo(), owaspDCXMLPath, err)
}
cpeURIs = append(cpeURIs, cpes...)
@@ -91,8 +92,16 @@ func FillCveInfos(dbclient DBClient, rs []models.ScanResult, dir string) ([]mode
}
}
// Integrations
githubInts := GithubSecurityAlerts(c.Conf.Servers[r.ServerName].GitHubRepos)
if err := FillCveInfo(dbclient, &r, cpeURIs, githubInts); err != nil {
wpOpt := WordPressOption{c.Conf.Servers[r.ServerName].WordPress.WPVulnDBToken}
if err := FillCveInfo(dbclient,
&r,
cpeURIs,
githubInts,
wpOpt); err != nil {
return nil, err
}
r.Lang = c.Conf.Lang
@@ -105,7 +114,7 @@ func FillCveInfos(dbclient DBClient, rs []models.ScanResult, dir string) ([]mode
r.ServerName: c.Conf.Servers[r.ServerName],
}
if err := overwriteJSONFile(dir, r); err != nil {
return nil, fmt.Errorf("Failed to write JSON: %s", err)
return nil, xerrors.Errorf("Failed to write JSON: %w", err)
}
filledResults = append(filledResults, r)
} else {
@@ -139,6 +148,7 @@ func FillCveInfos(dbclient DBClient, rs []models.ScanResult, dir string) ([]mode
r = r.FilterIgnoreCves()
r = r.FilterUnfixed()
r = r.FilterIgnorePkgs()
r = r.FilterInactiveWordPressLibs()
if c.Conf.IgnoreUnscoredCves {
r.ScannedCves = r.ScannedCves.FindScoredVulns()
}
@@ -153,7 +163,7 @@ func FillCveInfo(dbclient DBClient, r *models.ScanResult, cpeURIs []string, inte
nCVEs, err := FillWithOval(dbclient.OvalDB, r)
if err != nil {
return fmt.Errorf("Failed to fill with OVAL: %s", err)
return xerrors.Errorf("Failed to fill with OVAL: %w", err)
}
util.Log.Infof("%s: %d CVEs are detected with OVAL",
r.FormatServerName(), nCVEs)
@@ -169,11 +179,11 @@ func FillCveInfo(dbclient DBClient, r *models.ScanResult, cpeURIs []string, inte
nCVEs, err = fillVulnByCpeURIs(dbclient.CveDB, r, cpeURIs)
if err != nil {
return fmt.Errorf("Failed to detect vulns of %s: %s", cpeURIs, err)
return xerrors.Errorf("Failed to detect vulns of `%s`: %w", cpeURIs, err)
}
util.Log.Infof("%s: %d CVEs are detected with CPE", r.FormatServerName(), nCVEs)
ints := &ints{}
ints := &integrationResults{}
for _, o := range integrations {
if err = o.apply(r, ints); err != nil {
return err
@@ -183,20 +193,20 @@ func FillCveInfo(dbclient DBClient, r *models.ScanResult, cpeURIs []string, inte
nCVEs, err = FillWithGost(dbclient.GostDB, r)
if err != nil {
return fmt.Errorf("Failed to fill with gost: %s", err)
return xerrors.Errorf("Failed to fill with gost: %w", err)
}
util.Log.Infof("%s: %d unfixed CVEs are detected with gost",
r.FormatServerName(), nCVEs)
util.Log.Infof("Fill CVE detailed information with CVE-DB")
if err := fillCveDetail(dbclient.CveDB, r); err != nil {
return fmt.Errorf("Failed to fill with CVE: %s", err)
return xerrors.Errorf("Failed to fill with CVE: %w", err)
}
util.Log.Infof("Fill exploit information with Exploit-DB")
nExploitCve, err := FillWithExploit(dbclient.ExploitDB, r)
if err != nil {
return fmt.Errorf("Failed to fill with exploit: %s", err)
return xerrors.Errorf("Failed to fill with exploit: %w", err)
}
util.Log.Infof("%s: %d exploits are detected",
r.FormatServerName(), nExploitCve)
@@ -280,9 +290,9 @@ func FillWithOval(driver ovaldb.DB, r *models.ScanResult) (nCVEs int, err error)
return 0, nil
default:
if r.Family == "" {
return 0, fmt.Errorf("Probably an error occurred during scanning. Check the error message")
return 0, xerrors.New("Probably an error occurred during scanning. Check the error message")
}
return 0, fmt.Errorf("OVAL for %s is not implemented yet", r.Family)
return 0, xerrors.Errorf("OVAL for %s is not implemented yet", r.Family)
}
if !c.Conf.OvalDict.IsFetchViaHTTP() {
@@ -290,7 +300,7 @@ func FillWithOval(driver ovaldb.DB, r *models.ScanResult) (nCVEs int, err error)
return 0, nil
}
if err = driver.NewOvalDB(ovalFamily); err != nil {
return 0, fmt.Errorf("Failed to New Oval DB. err: %s", err)
return 0, xerrors.Errorf("Failed to New Oval DB. err: %w", err)
}
}
@@ -356,13 +366,14 @@ func fillVulnByCpeURIs(driver cvedb.DB, r *models.ScanResult, cpeURIs []string)
return nCVEs, nil
}
type ints struct {
type integrationResults struct {
GithubAlertsCveCounts int
WordPressCveCounts int
}
// Integration is integration of vuls report
type Integration interface {
apply(*models.ScanResult, *ints) error
apply(*models.ScanResult, *integrationResults) error
}
// GithubSecurityAlerts :
@@ -377,14 +388,15 @@ type GithubSecurityAlertOption struct {
GithubConfs map[string]config.GitHubConf
}
func (g GithubSecurityAlertOption) apply(r *models.ScanResult, ints *ints) (err error) {
// 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 errors.Wrap(err, "Failed to access GitHub Security Alerts")
return xerrors.Errorf("Failed to access GitHub Security Alerts: %w", err)
}
nCVEs += n
}
@@ -392,19 +404,21 @@ func (g GithubSecurityAlertOption) apply(r *models.ScanResult, ints *ints) (err
return nil
}
// https://help.github.com/articles/about-security-alerts-for-vulnerable-dependencies/
func fillGitHubSecurityAlerts(r *models.ScanResult) (nCVEs int, err error) {
repos := c.Conf.Servers[r.ServerName].GitHubRepos
for ownerRepo, setting := range repos {
ss := strings.Split(ownerRepo, "/")
owner, repo := ss[0], ss[1]
n, err := github.FillGitHubSecurityAlerts(r, owner, repo, setting.Token)
if err != nil {
return 0, err
}
nCVEs += n
// WordPressOption :
type WordPressOption struct {
token string
}
func (g WordPressOption) apply(r *models.ScanResult, ints *integrationResults) (err error) {
if g.token == "" {
return nil
}
return nCVEs, nil
n, err := wordpress.FillWordPress(r, g.token)
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) {
@@ -420,9 +434,6 @@ func fillCweDict(r *models.ScanResult) {
}
}
// TODO check the format of CWEID, clean CWEID
// JVN, NVD XML, JSON, OVALs
dict := map[string]models.CweDictEntry{}
for id := range uniqCweIDMap {
entry := models.CweDictEntry{}
@@ -638,21 +649,21 @@ func EnsureUUIDs(configPath string, results models.ScanResults) error {
// rename the current config.toml to config.toml.bak
info, err := os.Lstat(configPath)
if err != nil {
return fmt.Errorf("Failed to lstat %s: %s", configPath, err)
return xerrors.Errorf("Failed to lstat %s: %w", configPath, err)
}
realPath := configPath
if info.Mode()&os.ModeSymlink == os.ModeSymlink {
if realPath, err = os.Readlink(configPath); err != nil {
return fmt.Errorf("Failed to Read link %s: %s", configPath, err)
return xerrors.Errorf("Failed to Read link %s: %w", configPath, err)
}
}
if err := os.Rename(realPath, realPath+".bak"); err != nil {
return fmt.Errorf("Failed to rename %s: %s", configPath, err)
return xerrors.Errorf("Failed to rename %s: %w", configPath, err)
}
var buf bytes.Buffer
if err := toml.NewEncoder(&buf).Encode(c); err != nil {
return fmt.Errorf("Failed to encode to toml: %s", err)
return xerrors.Errorf("Failed to encode to toml: %w", err)
}
str := strings.Replace(buf.String(), "\n [", "\n\n [", -1)
str = fmt.Sprintf("%s\n\n%s",

View File

@@ -31,6 +31,7 @@ import (
"github.com/aws/aws-sdk-go/aws/ec2metadata"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"golang.org/x/xerrors"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
@@ -75,7 +76,7 @@ func (w S3Writer) Write(rs ...models.ScanResult) (err error) {
k := key + ".json"
var b []byte
if b, err = json.Marshal(r); err != nil {
return fmt.Errorf("Failed to Marshal to JSON: %s", err)
return xerrors.Errorf("Failed to Marshal to JSON: %w", err)
}
if err := putObject(svc, k, b); err != nil {
return err
@@ -102,7 +103,7 @@ func (w S3Writer) Write(rs ...models.ScanResult) (err error) {
k := key + ".xml"
var b []byte
if b, err = xml.Marshal(r); err != nil {
return fmt.Errorf("Failed to Marshal to XML: %s", err)
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 {
@@ -118,8 +119,8 @@ func CheckIfBucketExists() error {
svc := getS3()
result, err := svc.ListBuckets(&s3.ListBucketsInput{})
if err != nil {
return fmt.Errorf(
"Failed to list buckets. err: %s, profile: %s, region: %s",
return xerrors.Errorf(
"Failed to list buckets. err: %w, profile: %s, region: %s",
err, c.Conf.AWS.Profile, c.Conf.AWS.Region)
}
@@ -131,7 +132,7 @@ func CheckIfBucketExists() error {
}
}
if !found {
return fmt.Errorf(
return xerrors.Errorf(
"Failed to find the buckets. profile: %s, region: %s, bucket: %s",
c.Conf.AWS.Profile, c.Conf.AWS.Region, c.Conf.AWS.S3Bucket)
}
@@ -158,7 +159,7 @@ func putObject(svc *s3.S3, k string, b []byte) error {
}
if _, err := svc.PutObject(putObjectInput); err != nil {
return fmt.Errorf("Failed to upload data to %s/%s, %s",
return xerrors.Errorf("Failed to upload data to %s/%s, err: %w",
c.Conf.AWS.S3Bucket, k, err)
}
return nil

View File

@@ -37,6 +37,7 @@ import (
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"golang.org/x/xerrors"
)
// SaasWriter writes results to SaaS
@@ -80,7 +81,7 @@ func (w SaasWriter) Write(rs ...models.ScanResult) (err error) {
var body []byte
if body, err = json.Marshal(payload); err != nil {
return fmt.Errorf("Failed to Marshal to JSON: %s", err)
return xerrors.Errorf("Failed to Marshal to JSON: %w", err)
}
var req *http.Request
@@ -109,7 +110,7 @@ func (w SaasWriter) Write(rs ...models.ScanResult) (err error) {
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return fmt.Errorf("Failed to get Credential. Request JSON : %s,", string(body))
return xerrors.Errorf("Failed to get Credential. Request JSON : %s,", string(body))
}
var t []byte
@@ -119,7 +120,7 @@ func (w SaasWriter) Write(rs ...models.ScanResult) (err error) {
var tempCredential TempCredential
if err = json.Unmarshal(t, &tempCredential); err != nil {
return fmt.Errorf("Failed to unmarshal saas credential file. err : %s", err)
return xerrors.Errorf("Failed to unmarshal saas credential file. err : %s", err)
}
credential := credentials.NewStaticCredentialsFromCreds(credentials.Value{
@@ -133,7 +134,7 @@ func (w SaasWriter) Write(rs ...models.ScanResult) (err error) {
Credentials: credential,
Region: aws.String("ap-northeast-1"),
}); err != nil {
return fmt.Errorf("Failed to new aws session. err : %s", err)
return xerrors.Errorf("Failed to new aws session. err: %w", err)
}
svc := s3.New(sess)
@@ -141,7 +142,7 @@ func (w SaasWriter) Write(rs ...models.ScanResult) (err error) {
s3Key := renameKeyNameUTC(r.ScannedAt, r.ServerUUID, r.Container)
var b []byte
if b, err = json.Marshal(r); err != nil {
return fmt.Errorf("Failed to Marshal to JSON: %s", err)
return xerrors.Errorf("Failed to Marshal to JSON: %w", err)
}
util.Log.Infof("Uploading...: ServerName: %s, ", r.ServerName)
putObjectInput := &s3.PutObjectInput{
@@ -151,7 +152,7 @@ func (w SaasWriter) Write(rs ...models.ScanResult) (err error) {
}
if _, err := svc.PutObject(putObjectInput); err != nil {
return fmt.Errorf("Failed to upload data to %s/%s, %s",
return xerrors.Errorf("Failed to upload data to %s/%s, err: %w",
tempCredential.S3Bucket, s3Key, err)
}
}

View File

@@ -30,6 +30,7 @@ import (
"github.com/nlopes/slack"
"github.com/parnurzeal/gorequest"
log "github.com/sirupsen/logrus"
"golang.org/x/xerrors"
)
type field struct {
@@ -155,9 +156,9 @@ func send(msg message) error {
if count == retryMax {
return nil
}
return fmt.Errorf(
"HTTP POST error: %v, url: %s, resp: %v, body: %s",
errs, conf.HookURL, resp, body)
return xerrors.Errorf(
"HTTP POST error. url: %s, resp: %v, body: %s, err: %w",
conf.HookURL, resp, body, errs)
}
return nil
}
@@ -167,10 +168,10 @@ func send(msg message) error {
}
boff := backoff.NewExponentialBackOff()
if err := backoff.RetryNotify(f, boff, notify); err != nil {
return fmt.Errorf("HTTP error: %s", err)
return xerrors.Errorf("HTTP error: %w", err)
}
if count == retryMax {
return fmt.Errorf("Retry count exceeded")
return xerrors.New("Retry count exceeded")
}
return nil
}
@@ -202,39 +203,44 @@ func msgText(r models.ScanResult) string {
func toSlackAttachments(r models.ScanResult) (attaches []slack.Attachment) {
vinfos := r.ScannedCves.ToSortedSlice()
for _, vinfo := range vinfos {
curent := []string{}
installed, candidate := []string{}, []string{}
for _, affected := range vinfo.AffectedPackages {
if p, ok := r.Packages[affected.Name]; ok {
curent = append(curent,
installed = append(installed,
fmt.Sprintf("%s-%s", p.Name, p.FormatVer()))
} else {
curent = append(curent, affected.Name)
installed = append(installed, affected.Name)
}
}
for _, n := range vinfo.CpeURIs {
curent = append(curent, n)
}
for _, n := range vinfo.GitHubSecurityAlerts {
curent = append(curent, n.PackageName)
}
new := []string{}
for _, affected := range vinfo.AffectedPackages {
if p, ok := r.Packages[affected.Name]; ok {
if affected.NotFixedYet {
new = append(new, "Not Fixed Yet")
candidate = append(candidate, "Not Fixed Yet")
} else {
new = append(new, p.FormatNewVer())
candidate = append(candidate, p.FormatNewVer())
}
} else {
new = append(new, "?")
candidate = append(candidate, "?")
}
}
for range vinfo.CpeURIs {
new = append(new, "?")
for _, n := range vinfo.CpeURIs {
installed = append(installed, n)
candidate = append(candidate, "?")
}
for range vinfo.GitHubSecurityAlerts {
new = append(new, "?")
for _, n := range vinfo.GitHubSecurityAlerts {
installed = append(installed, n.PackageName)
candidate = append(candidate, "?")
}
for _, wp := range vinfo.WpPackageFixStats {
if p, ok := r.WordPressPackages.Find(wp.Name); ok {
installed = append(installed, fmt.Sprintf("%s-%s", wp.Name, p.Version))
candidate = append(candidate, wp.FixedIn)
} else {
installed = append(installed, wp.Name)
candidate = append(candidate, "?")
}
}
a := slack.Attachment{
@@ -246,12 +252,12 @@ func toSlackAttachments(r models.ScanResult) (attaches []slack.Attachment) {
{
// Title: "Current Package/CPE",
Title: "Installed",
Value: strings.Join(curent, "\n"),
Value: strings.Join(installed, "\n"),
Short: true,
},
{
Title: "Candidate",
Value: strings.Join(new, "\n"),
Value: strings.Join(candidate, "\n"),
Short: true,
},
},
@@ -269,7 +275,7 @@ func cvssColor(cvssScore float64) string {
return "danger"
case 4 <= cvssScore && cvssScore < 7:
return "warning"
case cvssScore < 0:
case cvssScore == 0:
return "#C0C0C0"
default:
return "good"

View File

@@ -22,8 +22,7 @@ import (
"strings"
syslog "github.com/RackSec/srslog"
"github.com/pkg/errors"
"golang.org/x/xerrors"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
@@ -40,7 +39,7 @@ func (w SyslogWriter) Write(rs ...models.ScanResult) (err error) {
sysLog, err := syslog.Dial(conf.Protocol, raddr, severity|facility, conf.Tag)
if err != nil {
return errors.Wrap(err, "Failed to initialize syslog client")
return xerrors.Errorf("Failed to initialize syslog client: %w", err)
}
for _, r := range rs {

View File

@@ -22,15 +22,15 @@ func TestSyslogWriterEncodeSyslog(t *testing.T) {
IPv4Addrs: []string{"192.168.0.1", "10.0.2.15"},
ScannedCves: models.VulnInfos{
"CVE-2017-0001": models.VulnInfo{
AffectedPackages: models.PackageStatuses{
models.PackageStatus{Name: "pkg1"},
models.PackageStatus{Name: "pkg2"},
AffectedPackages: models.PackageFixStatuses{
models.PackageFixStatus{Name: "pkg1"},
models.PackageFixStatus{Name: "pkg2"},
},
},
"CVE-2017-0002": models.VulnInfo{
AffectedPackages: models.PackageStatuses{
models.PackageStatus{Name: "pkg3"},
models.PackageStatus{Name: "pkg4"},
AffectedPackages: models.PackageFixStatuses{
models.PackageFixStatus{Name: "pkg3"},
models.PackageFixStatus{Name: "pkg4"},
},
CveContents: models.CveContents{
models.NvdXML: models.CveContent{
@@ -57,8 +57,8 @@ func TestSyslogWriterEncodeSyslog(t *testing.T) {
IPv6Addrs: []string{"2001:0DB8::1"},
ScannedCves: models.VulnInfos{
"CVE-2017-0003": models.VulnInfo{
AffectedPackages: models.PackageStatuses{
models.PackageStatus{Name: "pkg5"},
AffectedPackages: models.PackageFixStatuses{
models.PackageFixStatus{Name: "pkg5"},
},
CveContents: models.CveContents{
models.RedHat: models.CveContent{

View File

@@ -9,6 +9,7 @@ import (
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"golang.org/x/xerrors"
)
// TelegramWriter sends report to Telegram
@@ -73,5 +74,5 @@ func checkResponse(r *http.Response) error {
if c := r.StatusCode; 200 <= c && c <= 299 {
return nil
}
return fmt.Errorf("API call to %s failed: %s", r.Request.URL.String(), r.Status)
return xerrors.Errorf("API call to %s failed: %s", r.Request.URL.String(), r.Status)
}

View File

@@ -27,6 +27,7 @@ import (
"time"
"github.com/future-architect/vuls/alert"
"golang.org/x/xerrors"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
@@ -581,7 +582,7 @@ func setSideLayout(g *gocui.Gui) error {
fmt.Fprintln(v, result.ServerInfoTui())
}
if len(scanResults) == 0 {
return fmt.Errorf("No scan results")
return xerrors.New("No scan results")
}
currentScanResult = scanResults[0]
vinfos = scanResults[0].ScannedCves.ToSortedSlice()
@@ -634,9 +635,10 @@ func summaryLines(r models.ScanResult) string {
cvssScore = fmt.Sprintf("| %4.1f", max)
}
packname := vinfo.AffectedPackages.FormatTuiSummary()
packname += strings.Join(vinfo.CpeURIs, ", ")
packname += vinfo.GitHubSecurityAlerts.String()
pkgNames := vinfo.AffectedPackages.Names()
pkgNames = append(pkgNames, vinfo.CpeURIs...)
pkgNames = append(pkgNames, vinfo.GitHubSecurityAlerts.Names()...)
pkgNames = append(pkgNames, vinfo.WpPackageFixStats.Names()...)
alert := " "
if vinfo.AlertDict.HasAlert() {
@@ -648,9 +650,9 @@ func summaryLines(r models.ScanResult) string {
fmt.Sprintf(indexFormat, i+1),
alert + vinfo.CveID,
cvssScore + " |",
fmt.Sprintf("%8s |", vinfo.AttackVector()),
fmt.Sprintf("%1s |", vinfo.AttackVector()),
fmt.Sprintf("%7s |", vinfo.PatchStatus(r.Packages)),
packname,
strings.Join(pkgNames, ", "),
}
icols := make([]interface{}, len(cols))
for j := range cols {
@@ -747,6 +749,22 @@ func setChangelogLayout(g *gocui.Gui) error {
lines = append(lines, "* "+alert.PackageName)
}
r := currentScanResult
for _, wp := range vinfo.WpPackageFixStats {
if p, ok := r.WordPressPackages.Find(wp.Name); ok {
if p.Type == models.WPCore {
lines = append(lines, fmt.Sprintf("* %s-%s, FixedIn: %s",
wp.Name, p.Version, wp.FixedIn))
} else {
lines = append(lines,
fmt.Sprintf("* %s-%s, Update: %s, FixedIn: %s, %s",
wp.Name, p.Version, p.Update, wp.FixedIn, p.Status))
}
} else {
lines = append(lines, fmt.Sprintf("* %s", wp.Name))
}
}
for _, adv := range vinfo.DistroAdvisories {
lines = append(lines, "\n",
"Advisories",
@@ -846,10 +864,13 @@ func detailLines() (string, error) {
}
vinfo := vinfos[currentVinfo]
links := []string{vinfo.CveContents.SourceLinks(
config.Conf.Lang, r.Family, vinfo.CveID)[0].Value,
vinfo.Cvss2CalcURL(),
vinfo.Cvss3CalcURL()}
links := []string{}
if strings.HasPrefix(vinfo.CveID, "CVE-") {
links = append(links, vinfo.CveContents.SourceLinks(
config.Conf.Lang, r.Family, vinfo.CveID)[0].Value,
vinfo.Cvss2CalcURL(),
vinfo.Cvss3CalcURL())
}
for _, url := range vinfo.VendorLinks(r.Family) {
links = append(links, url)
}

View File

@@ -35,6 +35,7 @@ import (
"github.com/future-architect/vuls/util"
"github.com/gosuri/uitable"
"github.com/olekukonko/tablewriter"
"golang.org/x/xerrors"
)
const maxColWidth = 100
@@ -121,17 +122,23 @@ No CVE-IDs are found in updatable packages.
exploits = " Y"
}
link := ""
if strings.HasPrefix(vinfo.CveID, "CVE-") {
link = fmt.Sprintf("https://nvd.nist.gov/vuln/detail/%s", vinfo.CveID)
} else if strings.HasPrefix(vinfo.CveID, "WPVDBID-") {
link = fmt.Sprintf("https://wpvulndb.com/vulnerabilities/%s", strings.TrimPrefix(vinfo.CveID, "WPVDBID-"))
}
data = append(data, []string{
vinfo.CveID,
fmt.Sprintf("%7s", vinfo.PatchStatus(r.Packages)),
vinfo.AlertDict.FormatSource(),
fmt.Sprintf("%4.1f", max),
// fmt.Sprintf("%4.1f", v2max),
// fmt.Sprintf("%4.1f", v3max),
fmt.Sprintf("%8s", vinfo.AttackVector()),
fmt.Sprintf("%7s", vinfo.PatchStatus(r.Packages)),
// packname,
fmt.Sprintf("https://nvd.nist.gov/vuln/detail/%s", vinfo.CveID),
fmt.Sprintf("%2s", vinfo.AttackVector()),
exploits,
link,
})
}
@@ -139,15 +146,14 @@ No CVE-IDs are found in updatable packages.
table := tablewriter.NewWriter(&b)
table.SetHeader([]string{
"CVE-ID",
"Fixed",
"CERT",
"CVSS",
// "v3",
// "v2",
"Attack",
"Fixed",
// "Pkg",
"AV",
"PoC",
"NVD",
"Exploit",
})
table.SetBorder(true)
table.AppendBulk(data)
@@ -243,19 +249,37 @@ No CVE-IDs are found in updatable packages.
data = append(data, []string{"GitHub", alert.PackageName})
}
for _, wp := range vuln.WpPackageFixStats {
if p, ok := r.WordPressPackages.Find(wp.Name); ok {
if p.Type == models.WPCore {
data = append(data, []string{"WordPress",
fmt.Sprintf("%s-%s, FixedIn: %s", wp.Name, p.Version, wp.FixedIn)})
} else {
data = append(data, []string{"WordPress",
fmt.Sprintf("%s-%s, Update: %s, FixedIn: %s, %s",
wp.Name, p.Version, p.Update, wp.FixedIn, p.Status)})
}
} else {
data = append(data, []string{"WordPress",
fmt.Sprintf("%s", wp.Name)})
}
}
for _, confidence := range vuln.Confidences {
data = append(data, []string{"Confidence", confidence.String()})
}
links := vuln.CveContents.SourceLinks(
config.Conf.Lang, r.Family, vuln.CveID)
data = append(data, []string{"Source", links[0].Value})
if strings.HasPrefix(vuln.CveID, "CVE-") {
links := vuln.CveContents.SourceLinks(
config.Conf.Lang, r.Family, vuln.CveID)
data = append(data, []string{"Source", links[0].Value})
if 0 < len(vuln.Cvss2Scores(r.Family)) {
data = append(data, []string{"CVSSv2 Calc", vuln.Cvss2CalcURL()})
}
if 0 < len(vuln.Cvss3Scores()) {
data = append(data, []string{"CVSSv3 Calc", vuln.Cvss3CalcURL()})
if 0 < len(vuln.Cvss2Scores(r.Family)) {
data = append(data, []string{"CVSSv2 Calc", vuln.Cvss2CalcURL()})
}
if 0 < len(vuln.Cvss3Scores()) {
data = append(data, []string{"CVSSv3 Calc", vuln.Cvss3CalcURL()})
}
}
vlinks := vuln.VendorLinks(r.Family)
@@ -292,7 +316,7 @@ No CVE-IDs are found in updatable packages.
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
table.SetHeader([]string{
vuln.CveID,
"",
vuln.PatchStatus(r.Packages),
})
table.SetBorder(true)
table.AppendBulk(data)
@@ -353,7 +377,7 @@ func overwriteJSONFile(dir string, r models.ScanResult) error {
config.Conf.Diff = false
w := LocalFileWriter{CurrentDir: dir}
if err := w.Write(r); err != nil {
return fmt.Errorf("Failed to write summary report: %s", err)
return xerrors.Errorf("Failed to write summary report: %w", err)
}
config.Conf.FormatJSON = before
config.Conf.Diff = beforeDiff
@@ -521,7 +545,7 @@ var jsonDirPattern = regexp.MustCompile(
func ListValidJSONDirs() (dirs []string, err error) {
var dirInfo []os.FileInfo
if dirInfo, err = ioutil.ReadDir(config.Conf.ResultsDir); err != nil {
err = fmt.Errorf("Failed to read %s: %s",
err = xerrors.Errorf("Failed to read %s: %w",
config.Conf.ResultsDir, err)
return
}
@@ -559,20 +583,20 @@ func JSONDir(args []string) (string, error) {
}
}
return "", fmt.Errorf("Invalid path: %s", path)
return "", xerrors.Errorf("Invalid path: %s", path)
}
// PIPE
if config.Conf.Pipe {
bytes, err := ioutil.ReadAll(os.Stdin)
if err != nil {
return "", fmt.Errorf("Failed to read stdin: %s", err)
return "", xerrors.Errorf("Failed to read stdin: %w", err)
}
fields := strings.Fields(string(bytes))
if 0 < len(fields) {
return filepath.Join(config.Conf.ResultsDir, fields[0]), nil
}
return "", fmt.Errorf("Stdin is invalid: %s", string(bytes))
return "", xerrors.Errorf("Stdin is invalid: %s", string(bytes))
}
// returns latest dir when no args or no PIPE
@@ -580,7 +604,7 @@ func JSONDir(args []string) (string, error) {
return "", err
}
if len(dirs) == 0 {
return "", fmt.Errorf("No results under %s",
return "", xerrors.Errorf("No results under %s",
config.Conf.ResultsDir)
}
return dirs[0], nil
@@ -590,7 +614,7 @@ func JSONDir(args []string) (string, error) {
func LoadScanResults(jsonDir string) (results models.ScanResults, err error) {
var files []os.FileInfo
if files, err = ioutil.ReadDir(jsonDir); err != nil {
return nil, fmt.Errorf("Failed to read %s: %s", jsonDir, err)
return nil, xerrors.Errorf("Failed to read %s: %w", jsonDir, err)
}
for _, f := range files {
if filepath.Ext(f.Name()) != ".json" || strings.HasSuffix(f.Name(), "_diff.json") {
@@ -605,7 +629,7 @@ func LoadScanResults(jsonDir string) (results models.ScanResults, err error) {
results = append(results, *r)
}
if len(results) == 0 {
return nil, fmt.Errorf("There is no json file under %s", jsonDir)
return nil, xerrors.Errorf("There is no json file under %s", jsonDir)
}
return
}
@@ -617,11 +641,11 @@ func loadOneServerScanResult(jsonFile string) (*models.ScanResult, error) {
err error
)
if data, err = ioutil.ReadFile(jsonFile); err != nil {
return nil, fmt.Errorf("Failed to read %s: %s", jsonFile, err)
return nil, xerrors.Errorf("Failed to read %s: %w", jsonFile, err)
}
result := &models.ScanResult{}
if err := json.Unmarshal(data, result); err != nil {
return nil, fmt.Errorf("Failed to parse %s: %s", jsonFile, err)
return nil, xerrors.Errorf("Failed to parse %s: %w", jsonFile, err)
}
return result, nil
}

View File

@@ -192,13 +192,13 @@ func TestDiff(t *testing.T) {
ScannedCves: models.VulnInfos{
"CVE-2012-6702": {
CveID: "CVE-2012-6702",
AffectedPackages: models.PackageStatuses{{Name: "libexpat1"}},
AffectedPackages: models.PackageFixStatuses{{Name: "libexpat1"}},
DistroAdvisories: []models.DistroAdvisory{},
CpeURIs: []string{},
},
"CVE-2014-9761": {
CveID: "CVE-2014-9761",
AffectedPackages: models.PackageStatuses{{Name: "libc-bin"}},
AffectedPackages: models.PackageFixStatuses{{Name: "libc-bin"}},
DistroAdvisories: []models.DistroAdvisory{},
CpeURIs: []string{},
},
@@ -217,13 +217,13 @@ func TestDiff(t *testing.T) {
ScannedCves: models.VulnInfos{
"CVE-2012-6702": {
CveID: "CVE-2012-6702",
AffectedPackages: models.PackageStatuses{{Name: "libexpat1"}},
AffectedPackages: models.PackageFixStatuses{{Name: "libexpat1"}},
DistroAdvisories: []models.DistroAdvisory{},
CpeURIs: []string{},
},
"CVE-2014-9761": {
CveID: "CVE-2014-9761",
AffectedPackages: models.PackageStatuses{{Name: "libc-bin"}},
AffectedPackages: models.PackageFixStatuses{{Name: "libc-bin"}},
DistroAdvisories: []models.DistroAdvisory{},
CpeURIs: []string{},
},
@@ -254,7 +254,7 @@ func TestDiff(t *testing.T) {
ScannedCves: models.VulnInfos{
"CVE-2016-6662": {
CveID: "CVE-2016-6662",
AffectedPackages: models.PackageStatuses{{Name: "mysql-libs"}},
AffectedPackages: models.PackageFixStatuses{{Name: "mysql-libs"}},
DistroAdvisories: []models.DistroAdvisory{},
CpeURIs: []string{},
},
@@ -292,7 +292,7 @@ func TestDiff(t *testing.T) {
ScannedCves: models.VulnInfos{
"CVE-2016-6662": {
CveID: "CVE-2016-6662",
AffectedPackages: models.PackageStatuses{{Name: "mysql-libs"}},
AffectedPackages: models.PackageFixStatuses{{Name: "mysql-libs"}},
DistroAdvisories: []models.DistroAdvisory{},
CpeURIs: []string{},
},
@@ -348,7 +348,7 @@ func TestIsCveFixed(t *testing.T) {
in: In{
v: models.VulnInfo{
CveID: "CVE-2016-6662",
AffectedPackages: models.PackageStatuses{
AffectedPackages: models.PackageFixStatuses{
{
Name: "mysql-libs",
NotFixedYet: false,
@@ -366,7 +366,7 @@ func TestIsCveFixed(t *testing.T) {
ScannedCves: models.VulnInfos{
"CVE-2016-6662": {
CveID: "CVE-2016-6662",
AffectedPackages: models.PackageStatuses{
AffectedPackages: models.PackageFixStatuses{
{
Name: "mysql-libs",
NotFixedYet: true,
@@ -389,7 +389,7 @@ func TestIsCveFixed(t *testing.T) {
in: In{
v: models.VulnInfo{
CveID: "CVE-2016-6662",
AffectedPackages: models.PackageStatuses{
AffectedPackages: models.PackageFixStatuses{
{
Name: "mysql-libs",
NotFixedYet: true,
@@ -407,7 +407,7 @@ func TestIsCveFixed(t *testing.T) {
ScannedCves: models.VulnInfos{
"CVE-2016-6662": {
CveID: "CVE-2016-6662",
AffectedPackages: models.PackageStatuses{
AffectedPackages: models.PackageFixStatuses{
{
Name: "mysql-libs",
NotFixedYet: true,

View File

@@ -19,12 +19,12 @@ package scan
import (
"bufio"
"fmt"
"strings"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"golang.org/x/xerrors"
)
// inherit OsTypeInterface
@@ -66,7 +66,7 @@ func detectAlpine(c config.ServerInfo) (itsMe bool, os osTypeInterface) {
func (o *alpine) checkScanMode() error {
if o.getServerInfo().Mode.IsOffline() {
return fmt.Errorf("Remove offline scan mode, Alpine needs internet connection")
return xerrors.New("Remove offline scan mode, Alpine needs internet connection")
}
return nil
}
@@ -84,7 +84,7 @@ func (o *alpine) checkIfSudoNoPasswd() error {
func (o *alpine) apkUpdate() error {
r := o.exec("apk update", noSudo)
if !r.isSuccess() {
return fmt.Errorf("Failed to SSH: %s", r)
return xerrors.Errorf("Failed to SSH: %s", r)
}
return nil
}
@@ -143,7 +143,7 @@ func (o *alpine) scanInstalledPackages() (models.Packages, error) {
cmd := util.PrependProxyEnv("apk info -v")
r := o.exec(cmd, noSudo)
if !r.isSuccess() {
return nil, fmt.Errorf("Failed to SSH: %s", r)
return nil, xerrors.Errorf("Failed to SSH: %s", r)
}
return o.parseApkInfo(r.Stdout)
}
@@ -160,7 +160,7 @@ func (o *alpine) parseApkInfo(stdout string) (models.Packages, error) {
line := scanner.Text()
ss := strings.Split(line, "-")
if len(ss) < 3 {
return nil, fmt.Errorf("Failed to parse apk info -v: %s", line)
return nil, xerrors.Errorf("Failed to parse apk info -v: %s", line)
}
name := strings.Join(ss[:len(ss)-2], "-")
packs[name] = models.Package{
@@ -175,7 +175,7 @@ func (o *alpine) scanUpdatablePackages() (models.Packages, error) {
cmd := util.PrependProxyEnv("apk version")
r := o.exec(cmd, noSudo)
if !r.isSuccess() {
return nil, fmt.Errorf("Failed to SSH: %s", r)
return nil, xerrors.Errorf("Failed to SSH: %s", r)
}
return o.parseApkVersion(r.Stdout)
}

View File

@@ -1,11 +1,10 @@
package scan
import (
"fmt"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"golang.org/x/xerrors"
)
// inherit OsTypeInterface
@@ -33,7 +32,7 @@ func newAmazon(c config.ServerInfo) *amazon {
func (o *amazon) checkScanMode() error {
if o.getServerInfo().Mode.IsOffline() {
return fmt.Errorf("Remove offline scan mode, Amazon needs internet connection")
return xerrors.New("Remove offline scan mode, Amazon needs internet connection")
}
return nil
}
@@ -46,7 +45,7 @@ func (o *amazon) checkDeps() error {
} else if o.getServerInfo().Mode.IsDeep() {
return o.execCheckDeps(o.depsDeep())
}
return fmt.Errorf("Unknown scan mode")
return xerrors.New("Unknown scan mode")
}
func (o *amazon) depsFast() []string {

View File

@@ -19,6 +19,7 @@ package scan
import (
"bufio"
"encoding/json"
"fmt"
"net"
"regexp"
@@ -28,6 +29,7 @@ import (
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/sirupsen/logrus"
"golang.org/x/xerrors"
)
type base struct {
@@ -35,6 +37,7 @@ type base struct {
Distro config.Distro
Platform models.Platform
osPackages
WordPress *models.WordPressPackages
log *logrus.Entry
errs []error
@@ -79,7 +82,7 @@ func (l *base) getPlatform() models.Platform {
func (l *base) runningKernel() (release, version string, err error) {
r := l.exec("uname -r", noSudo)
if !r.isSuccess() {
return "", "", fmt.Errorf("Failed to SSH: %s", r)
return "", "", xerrors.Errorf("Failed to SSH: %s", r)
}
release = strings.TrimSpace(r.Stdout)
@@ -87,7 +90,7 @@ func (l *base) runningKernel() (release, version string, err error) {
case config.Debian:
r := l.exec("uname -a", noSudo)
if !r.isSuccess() {
return "", "", fmt.Errorf("Failed to SSH: %s", r)
return "", "", xerrors.Errorf("Failed to SSH: %s", r)
}
ss := strings.Fields(r.Stdout)
if 6 < len(ss) {
@@ -118,7 +121,7 @@ func (l *base) allContainers() (containers []config.Container, err error) {
}
return l.parseLxcPs(stdout)
default:
return containers, fmt.Errorf(
return containers, xerrors.Errorf(
"Not supported yet: %s", l.ServerInfo.ContainerType)
}
}
@@ -144,7 +147,7 @@ func (l *base) runningContainers() (containers []config.Container, err error) {
}
return l.parseLxcPs(stdout)
default:
return containers, fmt.Errorf(
return containers, xerrors.Errorf(
"Not supported yet: %s", l.ServerInfo.ContainerType)
}
}
@@ -170,7 +173,7 @@ func (l *base) exitedContainers() (containers []config.Container, err error) {
}
return l.parseLxcPs(stdout)
default:
return containers, fmt.Errorf(
return containers, xerrors.Errorf(
"Not supported yet: %s", l.ServerInfo.ContainerType)
}
}
@@ -179,7 +182,7 @@ func (l *base) dockerPs(option string) (string, error) {
cmd := fmt.Sprintf("docker ps %s", option)
r := l.exec(cmd, noSudo)
if !r.isSuccess() {
return "", fmt.Errorf("Failed to SSH: %s", r)
return "", xerrors.Errorf("Failed to SSH: %s", r)
}
return r.Stdout, nil
}
@@ -188,7 +191,7 @@ func (l *base) lxdPs(option string) (string, error) {
cmd := fmt.Sprintf("lxc list %s", option)
r := l.exec(cmd, noSudo)
if !r.isSuccess() {
return "", fmt.Errorf("failed to SSH: %s", r)
return "", xerrors.Errorf("failed to SSH: %s", r)
}
return r.Stdout, nil
}
@@ -197,7 +200,7 @@ func (l *base) lxcPs(option string) (string, error) {
cmd := fmt.Sprintf("lxc-ls %s 2>/dev/null", option)
r := l.exec(cmd, sudo)
if !r.isSuccess() {
return "", fmt.Errorf("failed to SSH: %s", r)
return "", xerrors.Errorf("failed to SSH: %s", r)
}
return r.Stdout, nil
}
@@ -210,7 +213,7 @@ func (l *base) parseDockerPs(stdout string) (containers []config.Container, err
break
}
if len(fields) != 3 {
return containers, fmt.Errorf("Unknown format: %s", line)
return containers, xerrors.Errorf("Unknown format: %s", line)
}
containers = append(containers, config.Container{
ContainerID: fields[0],
@@ -232,7 +235,7 @@ func (l *base) parseLxdPs(stdout string) (containers []config.Container, err err
break
}
if len(fields) != 1 {
return containers, fmt.Errorf("Unknown format: %s", line)
return containers, xerrors.Errorf("Unknown format: %s", line)
}
containers = append(containers, config.Container{
ContainerID: fields[0],
@@ -265,7 +268,7 @@ func (l *base) ip() ([]string, []string, error) {
// 2: eth0 inet6 fe80::5054:ff:fe2a:864c/64 scope link \ valid_lft forever preferred_lft forever
r := l.exec("/sbin/ip -o addr", noSudo)
if !r.isSuccess() {
return nil, nil, fmt.Errorf("Failed to detect IP address: %v", r)
return nil, nil, xerrors.Errorf("Failed to detect IP address: %v", r)
}
ipv4Addrs, ipv6Addrs := l.parseIP(r.Stdout)
return ipv4Addrs, ipv6Addrs, nil
@@ -358,7 +361,7 @@ func (l *base) detectRunningOnAws() (ok bool, instanceID string, err error) {
return false, "", nil
}
}
return false, "", fmt.Errorf(
return false, "", xerrors.Errorf(
"Failed to curl or wget to AWS instance metadata on %s. container: %s",
l.ServerInfo.ServerName, l.ServerInfo.Container.Name)
}
@@ -388,22 +391,23 @@ func (l *base) convertToModel() models.ScanResult {
}
return models.ScanResult{
JSONVersion: models.JSONVersion,
ServerName: l.ServerInfo.ServerName,
ScannedAt: time.Now(),
ScanMode: l.ServerInfo.Mode.String(),
Family: l.Distro.Family,
Release: l.Distro.Release,
Container: container,
Platform: l.Platform,
IPv4Addrs: l.ServerInfo.IPv4Addrs,
IPv6Addrs: l.ServerInfo.IPv6Addrs,
ScannedCves: l.VulnInfos,
RunningKernel: l.Kernel,
Packages: l.Packages,
SrcPackages: l.SrcPackages,
Optional: l.ServerInfo.Optional,
Errors: errs,
JSONVersion: models.JSONVersion,
ServerName: l.ServerInfo.ServerName,
ScannedAt: time.Now(),
ScanMode: l.ServerInfo.Mode.String(),
Family: l.Distro.Family,
Release: l.Distro.Release,
Container: container,
Platform: l.Platform,
IPv4Addrs: l.ServerInfo.IPv4Addrs,
IPv6Addrs: l.ServerInfo.IPv6Addrs,
ScannedCves: l.VulnInfos,
RunningKernel: l.Kernel,
Packages: l.Packages,
SrcPackages: l.SrcPackages,
WordPressPackages: l.WordPress,
Optional: l.ServerInfo.Optional,
Errors: errs,
}
}
@@ -427,7 +431,7 @@ func (l *base) detectInitSystem() (string, error) {
f = func(cmd string) (string, error) {
r := l.exec(cmd, sudo)
if !r.isSuccess() {
return "", fmt.Errorf("Failed to stat %s: %s", cmd, r)
return "", xerrors.Errorf("Failed to stat %s: %s", cmd, r)
}
scanner := bufio.NewScanner(strings.NewReader(r.Stdout))
scanner.Scan()
@@ -449,7 +453,7 @@ func (l *base) detectInitSystem() (string, error) {
}
return sysVinit, nil
}
return "", fmt.Errorf("Failed to detect a init system: %s", line)
return "", xerrors.Errorf("Failed to detect a init system: %s", line)
}
return f("stat /proc/1/exe")
}
@@ -458,7 +462,7 @@ func (l *base) detectServiceName(pid string) (string, error) {
cmd := fmt.Sprintf("systemctl status --quiet --no-pager %s", pid)
r := l.exec(cmd, noSudo)
if !r.isSuccess() {
return "", fmt.Errorf("Failed to stat %s: %s", cmd, r)
return "", xerrors.Errorf("Failed to stat %s: %s", cmd, r)
}
return l.parseSystemctlStatus(r.Stdout), nil
}
@@ -473,3 +477,125 @@ func (l *base) parseSystemctlStatus(stdout string) string {
}
return ss[1]
}
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 {
return nil
}
if hasEmptyOpt {
return xerrors.Errorf("%s has empty WordPress opts: %s",
l.getServerInfo().GetServerName(), wpOpts)
}
cmd := fmt.Sprintf("sudo -u %s -i -- %s cli version",
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)
}
wp, err := l.detectWordPress()
if err != nil {
return xerrors.Errorf("Failed to scan wordpress: %w", err)
}
l.WordPress = wp
return nil
}
func (l *base) detectWordPress() (*models.WordPressPackages, error) {
ver, err := l.detectWpCore()
if err != nil {
return nil, err
}
themes, err := l.detectWpThemes()
if err != nil {
return nil, err
}
plugins, err := l.detectWpPlugins()
if err != nil {
return nil, err
}
pkgs := models.WordPressPackages{
models.WpPackage{
Name: models.WPCore,
Version: ver,
Type: models.WPCore,
},
}
pkgs = append(pkgs, themes...)
pkgs = append(pkgs, plugins...)
return &pkgs, nil
}
func (l *base) detectWpCore() (string, error) {
cmd := fmt.Sprintf("sudo -u %s -i -- %s core version --path=%s",
l.ServerInfo.WordPress.OSUser,
l.ServerInfo.WordPress.CmdPath,
l.ServerInfo.WordPress.DocRoot)
r := exec(l.ServerInfo, cmd, noSudo)
if !r.isSuccess() {
return "", xerrors.Errorf("Failed to get wp core version: %s", r)
}
return strings.TrimSpace(r.Stdout), nil
}
func (l *base) detectWpThemes() ([]models.WpPackage, error) {
cmd := fmt.Sprintf("sudo -u %s -i -- %s theme list --path=%s --format=json",
l.ServerInfo.WordPress.OSUser,
l.ServerInfo.WordPress.CmdPath,
l.ServerInfo.WordPress.DocRoot)
var themes []models.WpPackage
r := exec(l.ServerInfo, cmd, noSudo)
if !r.isSuccess() {
return nil, xerrors.Errorf("Failed to get a list of WordPress plugins: %s", r)
}
err := json.Unmarshal([]byte(r.Stdout), &themes)
if err != nil {
return nil, xerrors.Errorf("Failed to unmarshal wp theme list: %w", cmd, err)
}
for i := range themes {
themes[i].Type = models.WPTheme
}
return themes, nil
}
func (l *base) detectWpPlugins() ([]models.WpPackage, error) {
cmd := fmt.Sprintf("sudo -u %s -i -- %s plugin list --path=%s --format=json",
l.ServerInfo.WordPress.OSUser,
l.ServerInfo.WordPress.CmdPath,
l.ServerInfo.WordPress.DocRoot)
var plugins []models.WpPackage
r := exec(l.ServerInfo, cmd, noSudo)
if !r.isSuccess() {
return nil, xerrors.Errorf("Failed to wp plugin list: %s", r)
}
if err := json.Unmarshal([]byte(r.Stdout), &plugins); err != nil {
return nil, err
}
for i := range plugins {
plugins[i].Type = models.WPPlugin
}
return plugins, nil
}

View File

@@ -30,6 +30,7 @@ import (
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
version "github.com/knqyf263/go-deb-version"
"golang.org/x/xerrors"
)
// inherit OsTypeInterface
@@ -62,7 +63,7 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
return false, deb, nil
}
if r.ExitStatus == 255 {
return false, deb, fmt.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, 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)
}
util.Log.Debugf("Not Debian like Linux. %s", r)
return false, deb, nil
@@ -160,7 +161,7 @@ func (o *debian) checkIfSudoNoPasswd() error {
r := o.exec(cmd, sudo)
if !r.isSuccess() {
o.log.Errorf("sudo error on %s", r)
return fmt.Errorf("Failed to sudo: %s", r)
return xerrors.Errorf("Failed to sudo: %s", r)
}
}
@@ -171,7 +172,7 @@ func (o *debian) checkIfSudoNoPasswd() error {
r := o.exec(cmd, sudo)
if !r.isSuccess() {
o.log.Errorf("sudo error on %s", r)
return fmt.Errorf("Failed to sudo: %s", r)
return xerrors.Errorf("Failed to sudo: %s", r)
}
}
@@ -230,7 +231,7 @@ func (o *debian) checkDeps() error {
}
dep.logFunc(msg)
if dep.required {
return fmt.Errorf(msg)
return xerrors.New(msg)
}
continue
}
@@ -242,7 +243,7 @@ func (o *debian) checkDeps() error {
}
dep.logFunc(msg)
if dep.required {
return fmt.Errorf(msg)
return xerrors.New(msg)
}
}
@@ -323,7 +324,7 @@ func (o *debian) rebootRequired() (bool, error) {
case 1:
return false, nil
default:
return false, fmt.Errorf("Failed to check reboot reauired: %s", r)
return false, xerrors.Errorf("Failed to check reboot reauired: %s", r)
}
}
@@ -333,7 +334,7 @@ func (o *debian) scanInstalledPackages() (models.Packages, models.Packages, mode
updatable := models.Packages{}
r := o.exec(dpkgQuery, noSudo)
if !r.isSuccess() {
return nil, nil, nil, fmt.Errorf("Failed to SSH: %s", r)
return nil, nil, nil, xerrors.Errorf("Failed to SSH: %s", r)
}
installed, srcPacks, err := o.parseInstalledPackages(r.Stdout)
@@ -364,7 +365,7 @@ func (o *debian) scanInstalledPackages() (models.Packages, models.Packages, mode
// Fill the candidate versions of upgradable packages
err = o.fillCandidateVersion(updatable)
if err != nil {
return nil, nil, nil, fmt.Errorf("Failed to fill candidate versions. err: %s", err)
return nil, nil, nil, xerrors.Errorf("Failed to fill candidate versions. err: %s", err)
}
installed.MergeNewVersion(updatable)
@@ -383,7 +384,7 @@ func (o *debian) parseInstalledPackages(stdout string) (models.Packages, models.
if trimmed := strings.TrimSpace(line); len(trimmed) != 0 {
name, status, version, srcName, srcVersion, err := o.parseScannedPackagesLine(trimmed)
if err != nil || len(status) < 2 {
return nil, nil, fmt.Errorf(
return nil, nil, xerrors.Errorf(
"Debian: Failed to parse package line: %s", line)
}
@@ -448,14 +449,14 @@ func (o *debian) parseScannedPackagesLine(line string) (name, status, version, s
return
}
return "", "", "", "", "", fmt.Errorf("Unknown format: %s", line)
return "", "", "", "", "", xerrors.Errorf("Unknown format: %s", line)
}
func (o *debian) aptGetUpdate() error {
o.log.Infof("apt-get update...")
cmd := util.PrependProxyEnv("apt-get update")
if r := o.exec(cmd, sudo); !r.isSuccess() {
return fmt.Errorf("Failed to apt-get update: %s", r)
return xerrors.Errorf("Failed to apt-get update: %s", r)
}
return nil
}
@@ -477,7 +478,7 @@ func (o *debian) scanUnsecurePackages(updatable models.Packages) (models.VulnInf
// Collect CVE information of upgradable packages
vulnInfos, err := o.scanChangelogs(updatable, meta)
if err != nil {
return nil, fmt.Errorf("Failed to scan unsecure packages. err: %s", err)
return nil, xerrors.Errorf("Failed to scan unsecure packages. err: %s", err)
}
return vulnInfos, nil
@@ -487,7 +488,7 @@ func (o *debian) ensureChangelogCache(current cache.Meta) (*cache.Meta, error) {
// Search from cache
cached, found, err := cache.DB.GetMeta(current.Name)
if err != nil {
return nil, fmt.Errorf(
return nil, xerrors.Errorf(
"Failed to get meta. Please remove cache.db and then try again. err: %s", err)
}
@@ -495,7 +496,7 @@ func (o *debian) ensureChangelogCache(current cache.Meta) (*cache.Meta, error) {
o.log.Debugf("Not found in meta: %s", current.Name)
err = cache.DB.EnsureBuckets(current)
if err != nil {
return nil, fmt.Errorf("Failed to ensure buckets. err: %s", err)
return nil, xerrors.Errorf("Failed to ensure buckets. err: %s", err)
}
return &current, nil
}
@@ -505,7 +506,7 @@ func (o *debian) ensureChangelogCache(current cache.Meta) (*cache.Meta, error) {
o.log.Debugf("Need to refesh meta: %s", current.Name)
err = cache.DB.EnsureBuckets(current)
if err != nil {
return nil, fmt.Errorf("Failed to ensure buckets. err: %s", err)
return nil, xerrors.Errorf("Failed to ensure buckets. err: %s", err)
}
return &current, nil
@@ -526,17 +527,17 @@ func (o *debian) fillCandidateVersion(updatables models.Packages) (err error) {
cmd := fmt.Sprintf("LANGUAGE=en_US.UTF-8 apt-cache policy %s", strings.Join(names, " "))
r := o.exec(cmd, noSudo)
if !r.isSuccess() {
return fmt.Errorf("Failed to SSH: %s", r)
return xerrors.Errorf("Failed to SSH: %s", r)
}
packAptPolicy := o.splitAptCachePolicy(r.Stdout)
for k, v := range packAptPolicy {
ver, err := o.parseAptCachePolicy(v, k)
if err != nil {
return fmt.Errorf("Failed to parse %s", err)
return xerrors.Errorf("Failed to parse %w", err)
}
pack, ok := updatables[k]
if !ok {
return fmt.Errorf("Not found: %s", k)
return xerrors.Errorf("Not found: %s", k)
}
pack.NewVersion = ver.Candidate
pack.Repository = ver.Repo
@@ -551,7 +552,7 @@ func (o *debian) getUpdatablePackNames() (packNames []string, err error) {
if r.isSuccess(0, 1) {
return o.parseAptGetUpgrade(r.Stdout)
}
return packNames, fmt.Errorf(
return packNames, xerrors.Errorf(
"Failed to %s. status: %d, stdout: %s, stderr: %s",
cmd, r.ExitStatus, r.Stdout, r.Stderr)
}
@@ -573,11 +574,11 @@ func (o *debian) parseAptGetUpgrade(stdout string) (updatableNames []string, err
if len(result) == 2 {
nUpdatable, err := strconv.Atoi(result[1])
if err != nil {
return nil, fmt.Errorf(
return nil, xerrors.Errorf(
"Failed to scan upgradable packages number. line: %s", line)
}
if nUpdatable != len(updatableNames) {
return nil, fmt.Errorf(
return nil, xerrors.Errorf(
"Failed to scan upgradable packages, expected: %s, detected: %d",
result[1], len(updatableNames))
}
@@ -593,7 +594,7 @@ func (o *debian) parseAptGetUpgrade(stdout string) (updatableNames []string, err
}
if !stopLineFound {
// There are upgrades, but not found the stop line.
return nil, fmt.Errorf("Failed to scan upgradable packages")
return nil, xerrors.New("Failed to scan upgradable packages")
}
return
}
@@ -677,11 +678,11 @@ func (o *debian) scanChangelogs(updatablePacks models.Packages, meta *cache.Meta
case err := <-errChan:
errs = append(errs, err)
case <-timeout:
errs = append(errs, fmt.Errorf("Timeout scanPackageCveIDs"))
errs = append(errs, xerrors.New("Timeout scanPackageCveIDs"))
}
}
if 0 < len(errs) {
return nil, fmt.Errorf("%v", errs)
return nil, xerrors.Errorf("errs: %w", errs)
}
var cveIDs []DetectedCveID
@@ -691,9 +692,9 @@ func (o *debian) scanChangelogs(updatablePacks models.Packages, meta *cache.Meta
o.log.Debugf("%d Cves are found. cves: %v", len(cveIDs), cveIDs)
vinfos := models.VulnInfos{}
for cveID, names := range cvePackages {
affected := models.PackageStatuses{}
affected := models.PackageFixStatuses{}
for _, n := range names {
affected = append(affected, models.PackageStatus{Name: n})
affected = append(affected, models.PackageFixStatus{Name: n})
}
vinfos[cveID.CveID] = models.VulnInfo{
@@ -764,7 +765,7 @@ func (o *debian) fetchParseChangelog(pack models.Package) ([]DetectedCveID, *mod
err := cache.DB.PutChangelog(
o.getServerInfo().GetServerName(), pack.Name, pack.Changelog.Contents)
if err != nil {
return nil, nil, fmt.Errorf("Failed to put changelog into cache")
return nil, nil, xerrors.New("Failed to put changelog into cache")
}
}
@@ -838,7 +839,7 @@ var cveRe = regexp.MustCompile(`(CVE-\d{4}-\d{4,})`)
func (o *debian) parseChangelog(changelog, name, ver string, confidence models.Confidence) ([]DetectedCveID, *models.Package, error) {
installedVer, err := version.NewVersion(ver)
if err != nil {
return nil, nil, fmt.Errorf("Failed to parse installed version: %s, %s", ver, err)
return nil, nil, xerrors.Errorf("Failed to parse installed version: %s, err: %w", ver, err)
}
buf, cveIDs := []string{}, []string{}
scanner := bufio.NewScanner(strings.NewReader(changelog))
@@ -876,7 +877,7 @@ func (o *debian) parseChangelog(changelog, name, ver string, confidence models.C
Contents: "",
Method: models.FailedToFindVersionInChangelog,
}
return nil, &pack, fmt.Errorf(
return nil, &pack, xerrors.Errorf(
"Failed to scan CVE IDs. The version is not in changelog. name: %s, version: %s",
name, ver)
}
@@ -958,7 +959,7 @@ func (o *debian) parseAptCachePolicy(stdout, name string) (packCandidateVer, err
}
nextline:
}
return ver, fmt.Errorf("Unknown Format: %s", stdout)
return ver, xerrors.Errorf("Unknown Format: %s", stdout)
}
func (o *debian) checkrestart() error {
@@ -971,7 +972,7 @@ func (o *debian) checkrestart() error {
cmd := "LANGUAGE=en_US.UTF-8 checkrestart"
r := o.exec(cmd, sudo)
if !r.isSuccess() {
return fmt.Errorf(
return xerrors.Errorf(
"Failed to %s. status: %d, stdout: %s, stderr: %s",
cmd, r.ExitStatus, r.Stdout, r.Stderr)
}

View File

@@ -33,6 +33,7 @@ import (
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
"golang.org/x/xerrors"
"github.com/cenkalti/backoff"
conf "github.com/future-architect/vuls/config"
@@ -124,7 +125,11 @@ func parallelExec(fn func(osTypeInterface) error, timeoutSec ...int) {
if len(s.getErrs()) == 0 {
successes = append(successes, s)
} else {
util.Log.Errorf("Error: %s, err: %s",
fmtstr := "Error on %s, err: %s"
if conf.Conf.Debug {
fmtstr = "Error: %s, err: %+v"
}
util.Log.Errorf(fmtstr,
s.getServerInfo().GetServerName(), s.getErrs())
errServers = append(errServers, s)
}
@@ -148,7 +153,7 @@ func parallelExec(fn func(osTypeInterface) error, timeoutSec ...int) {
msg := fmt.Sprintf("Timed out: %s",
s.getServerInfo().GetServerName())
util.Log.Errorf(msg)
s.setErrs([]error{fmt.Errorf(msg)})
s.setErrs([]error{xerrors.New(msg)})
errServers = append(errServers, s)
}
}
@@ -222,8 +227,8 @@ func sshExecNative(c conf.ServerInfo, cmd string, sudo bool) (result execResult)
var session *ssh.Session
if session, err = client.NewSession(); err != nil {
result.Error = fmt.Errorf(
"Failed to create a new session. servername: %s, err: %s",
result.Error = xerrors.Errorf(
"Failed to create a new session. servername: %s, err: %w",
c.ServerName, err)
result.ExitStatus = 999
return
@@ -237,8 +242,8 @@ func sshExecNative(c conf.ServerInfo, cmd string, sudo bool) (result execResult)
ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
}
if err = session.RequestPty("xterm", 400, 1000, modes); err != nil {
result.Error = fmt.Errorf(
"Failed to request for pseudo terminal. servername: %s, err: %s",
result.Error = xerrors.Errorf(
"Failed to request for pseudo terminal. servername: %s, err: %w",
c.ServerName, err)
result.ExitStatus = 999
return
@@ -462,7 +467,7 @@ func addKeyAuth(auths []ssh.AuthMethod, keypath string, keypassword string) ([]s
// get first pem block
block, _ := pem.Decode(pemBytes)
if block == nil {
return auths, fmt.Errorf("no key found in %s", keypath)
return auths, xerrors.Errorf("no key found in %s", keypath)
}
// handle plain and encrypted keyfiles
@@ -499,6 +504,6 @@ func parsePemBlock(block *pem.Block) (interface{}, error) {
case "DSA PRIVATE KEY":
return ssh.ParseDSAPrivateKey(block.Bytes)
default:
return nil, fmt.Errorf("Unsupported key type %q", block.Type)
return nil, xerrors.Errorf("Unsupported key type %q", block.Type)
}
}

View File

@@ -18,13 +18,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package scan
import (
"fmt"
"net"
"strings"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"golang.org/x/xerrors"
)
// inherit OsTypeInterface
@@ -69,7 +69,7 @@ func detectFreebsd(c config.ServerInfo) (itsMe bool, bsd osTypeInterface) {
func (o *bsd) checkScanMode() error {
if o.getServerInfo().Mode.IsOffline() {
return fmt.Errorf("Remove offline scan mode, FreeBSD needs internet connection")
return xerrors.New("Remove offline scan mode, FreeBSD needs internet connection")
}
return nil
}
@@ -101,7 +101,7 @@ func (o *bsd) postScan() error {
func (o *bsd) detectIPAddr() (err error) {
r := o.exec("/sbin/ifconfig", noSudo)
if !r.isSuccess() {
return fmt.Errorf("Failed to detect IP address: %v", r)
return xerrors.Errorf("Failed to detect IP address: %v", r)
}
o.ServerInfo.IPv4Addrs, o.ServerInfo.IPv6Addrs = o.parseIfconfig(r.Stdout)
return nil
@@ -173,7 +173,7 @@ func (o *bsd) parseInstalledPackages(string) (models.Packages, models.SrcPackage
func (o *bsd) rebootRequired() (bool, error) {
r := o.exec("freebsd-version -k", noSudo)
if !r.isSuccess() {
return false, fmt.Errorf("Failed to SSH: %s", r)
return false, xerrors.Errorf("Failed to SSH: %s", r)
}
return o.Kernel.Release != strings.TrimSpace(r.Stdout), nil
}
@@ -182,7 +182,7 @@ func (o *bsd) scanInstalledPackages() (models.Packages, error) {
cmd := util.PrependProxyEnv("pkg version -v")
r := o.exec(cmd, noSudo)
if !r.isSuccess() {
return nil, fmt.Errorf("Failed to SSH: %s", r)
return nil, xerrors.Errorf("Failed to SSH: %s", r)
}
return o.parsePkgVersion(r.Stdout), nil
}
@@ -192,13 +192,13 @@ func (o *bsd) scanUnsecurePackages() (models.VulnInfos, error) {
cmd := "rm -f " + vulndbPath
r := o.exec(cmd, noSudo)
if !r.isSuccess(0) {
return nil, fmt.Errorf("Failed to SSH: %s", r)
return nil, xerrors.Errorf("Failed to SSH: %s", r)
}
cmd = util.PrependProxyEnv("pkg audit -F -r -f " + vulndbPath)
r = o.exec(cmd, noSudo)
if !r.isSuccess(0, 1) {
return nil, fmt.Errorf("Failed to SSH: %s", r)
return nil, xerrors.Errorf("Failed to SSH: %s", r)
}
if r.ExitStatus == 0 {
// no vulnerabilities
@@ -214,7 +214,7 @@ func (o *bsd) scanUnsecurePackages() (models.VulnInfos, error) {
}
pack, found := o.Packages[name]
if !found {
return nil, fmt.Errorf("Vulnerable package: %s is not found", name)
return nil, xerrors.Errorf("Vulnerable package: %s is not found", name)
}
packAdtRslt = append(packAdtRslt, pkgAuditResult{
pack: pack,
@@ -247,9 +247,9 @@ func (o *bsd) scanUnsecurePackages() (models.VulnInfos, error) {
})
}
affected := models.PackageStatuses{}
affected := models.PackageFixStatuses{}
for name := range packs {
affected = append(affected, models.PackageStatus{
affected = append(affected, models.PackageFixStatus{
Name: name,
})
}

View File

@@ -27,6 +27,7 @@ import (
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"golang.org/x/xerrors"
ver "github.com/knqyf263/go-rpm-version"
)
@@ -160,7 +161,7 @@ func (o *redhatBase) execCheckIfSudoNoPasswd(cmds []cmd) error {
r := o.exec(util.PrependProxyEnv(cmd), sudo)
if !r.isSuccess(c.expectedStatusCodes...) {
o.log.Errorf("Check sudo or proxy settings: %s", r)
return fmt.Errorf("Failed to sudo: %s", r)
return xerrors.Errorf("Failed to sudo: %s", r)
}
}
o.log.Infof("Sudo... Pass")
@@ -173,7 +174,7 @@ func (o *redhatBase) execCheckDeps(packNames []string) error {
if r := o.exec(cmd, noSudo); !r.isSuccess() {
msg := fmt.Sprintf("%s is not installed", name)
o.log.Errorf(msg)
return fmt.Errorf(msg)
return xerrors.New(msg)
}
}
o.log.Infof("Dependencies ... Pass")
@@ -191,12 +192,12 @@ func (o *redhatBase) preCure() error {
func (o *redhatBase) postScan() error {
if o.isExecYumPS() {
if err := o.yumPS(); err != nil {
return fmt.Errorf("Failed to execute yum-ps: %s", err)
return xerrors.Errorf("Failed to execute yum-ps: %w", err)
}
}
if o.isExecNeedsRestarting() {
if err := o.needsRestarting(); err != nil {
return fmt.Errorf("Failed to execute need-restarting: %s", err)
return xerrors.Errorf("Failed to execute need-restarting: %w", err)
}
}
return nil
@@ -258,7 +259,7 @@ func (o *redhatBase) rebootRequired() (bool, error) {
r := o.exec("rpm -q --last kernel", noSudo)
scanner := bufio.NewScanner(strings.NewReader(r.Stdout))
if !r.isSuccess(0, 1) {
return false, fmt.Errorf("Failed to detect the last installed kernel : %v", r)
return false, xerrors.Errorf("Failed to detect the last installed kernel : %v", r)
}
if !r.isSuccess() || !scanner.Scan() {
return false, nil
@@ -280,7 +281,7 @@ func (o *redhatBase) scanInstalledPackages() (models.Packages, error) {
r := o.exec(rpmQa(o.Distro), noSudo)
if !r.isSuccess() {
return nil, fmt.Errorf("Scan packages failed: %s", r)
return nil, xerrors.Errorf("Scan packages failed: %s", r)
}
installed, _, err := o.parseInstalledPackages(r.Stdout)
if err != nil {
@@ -332,7 +333,7 @@ func (o *redhatBase) parseInstalledPackagesLine(line string) (models.Package, er
fields := strings.Fields(line)
if len(fields) != 5 {
return models.Package{},
fmt.Errorf("Failed to parse package line: %s", line)
xerrors.Errorf("Failed to parse package line: %s", line)
}
ver := ""
epoch := fields[1]
@@ -358,7 +359,7 @@ func (o *redhatBase) scanUpdatablePackages() (models.Packages, error) {
r := o.exec(util.PrependProxyEnv(cmd), o.sudo.repoquery())
if !r.isSuccess() {
return nil, fmt.Errorf("Failed to SSH: %s", r)
return nil, xerrors.Errorf("Failed to SSH: %s", r)
}
// Collect Updateble packages, installed, candidate version and repository.
@@ -393,7 +394,7 @@ func (o *redhatBase) parseUpdatablePacksLines(stdout string) (models.Packages, e
func (o *redhatBase) parseUpdatablePacksLine(line string) (models.Package, error) {
fields := strings.Fields(line)
if len(fields) < 5 {
return models.Package{}, fmt.Errorf("Unknown format: %s, fields: %s", line, fields)
return models.Package{}, xerrors.Errorf("Unknown format: %s, fields: %s", line, fields)
}
ver := ""
@@ -562,7 +563,7 @@ func (o *redhatBase) getAvailableChangelogs(packNames []string) (map[string]stri
r := o.exec(util.PrependProxyEnv(cmd), o.sudo.yumChangelog())
if !r.isSuccess(0, 1) {
return nil, fmt.Errorf("Failed to SSH: %s", r)
return nil, xerrors.Errorf("Failed to SSH: %s", r)
}
return o.divideChangelogsIntoEachPackages(r.Stdout), nil
@@ -723,7 +724,7 @@ func (o *redhatBase) getDiffChangelog(pack models.Package, availableChangelog st
if len(diff) == 0 || !found {
return availableChangelog,
fmt.Errorf("Failed to find the version in changelog: %s-%s-%s",
xerrors.Errorf("Failed to find the version in changelog: %s-%s-%s",
pack.Name, pack.Version, pack.Release)
}
return strings.TrimSpace(strings.Join(diff, "\n")), nil
@@ -760,12 +761,12 @@ func (o *redhatBase) scanChangelogs(updatable models.Packages) (models.VulnInfos
for name, cveIDs := range packCveIDs {
for _, cid := range cveIDs {
if v, ok := vinfos[cid]; ok {
v.AffectedPackages = append(v.AffectedPackages, models.PackageStatus{Name: name})
v.AffectedPackages = append(v.AffectedPackages, models.PackageFixStatus{Name: name})
vinfos[cid] = v
} else {
vinfos[cid] = models.VulnInfo{
CveID: cid,
AffectedPackages: models.PackageStatuses{{Name: name}},
AffectedPackages: models.PackageFixStatuses{{Name: name}},
Confidences: models.Confidences{models.ChangelogExactMatch},
}
}
@@ -784,14 +785,14 @@ type distroAdvisoryCveIDs struct {
func (o *redhatBase) scanUsingYum(updatable models.Packages) (models.VulnInfos, error) {
if o.Distro.Family == config.CentOS {
// CentOS has no security channel.
return nil, fmt.Errorf(
return nil, xerrors.New(
"yum updateinfo is not suppported on CentOS")
}
// get advisoryID(RHSA, ALAS, ELSA) - package name,version
major, err := (o.Distro.MajorVersion())
if err != nil {
return nil, fmt.Errorf("Not implemented yet: %s, err: %s", o.Distro, err)
return nil, xerrors.Errorf("Not implemented yet: %s, err: %w", o.Distro, err)
}
var cmd string
@@ -799,7 +800,7 @@ func (o *redhatBase) scanUsingYum(updatable models.Packages) (models.VulnInfos,
cmd = "yum repolist --color=never"
r := o.exec(util.PrependProxyEnv(cmd), o.sudo.yumRepolist())
if !r.isSuccess() {
return nil, fmt.Errorf("Failed to SSH: %s", r)
return nil, xerrors.Errorf("Failed to SSH: %s", r)
}
}
@@ -813,7 +814,7 @@ func (o *redhatBase) scanUsingYum(updatable models.Packages) (models.VulnInfos,
}
r := o.exec(util.PrependProxyEnv(cmd), o.sudo.yumUpdateInfo())
if !r.isSuccess() {
return nil, fmt.Errorf("Failed to SSH: %s", r)
return nil, xerrors.Errorf("Failed to SSH: %s", r)
}
advIDPackNamesList, err := o.parseYumUpdateinfoListAvailable(r.Stdout)
@@ -823,7 +824,7 @@ func (o *redhatBase) scanUsingYum(updatable models.Packages) (models.VulnInfos,
for _, packName := range advIDPackNames.PackNames {
pack, found := updatable[packName]
if !found {
return nil, fmt.Errorf(
return nil, xerrors.Errorf(
"Package not found. pack: %#v", packName)
}
packages[pack.Name] = pack
@@ -843,7 +844,7 @@ func (o *redhatBase) scanUsingYum(updatable models.Packages) (models.VulnInfos,
}
r = o.exec(util.PrependProxyEnv(cmd), o.sudo.yumUpdateInfo())
if !r.isSuccess() {
return nil, fmt.Errorf("Failed to SSH: %s", r)
return nil, xerrors.Errorf("Failed to SSH: %s", r)
}
advisoryCveIDsList, err := o.parseYumUpdateinfo(r.Stdout)
if err != nil {
@@ -863,13 +864,13 @@ func (o *redhatBase) scanUsingYum(updatable models.Packages) (models.VulnInfos,
packs := dict[advIDCveIDs.DistroAdvisory.AdvisoryID]
for _, pack := range packs {
vinfo.AffectedPackages = append(vinfo.AffectedPackages,
models.PackageStatus{Name: pack.Name})
models.PackageFixStatus{Name: pack.Name})
}
} else {
packs := dict[advIDCveIDs.DistroAdvisory.AdvisoryID]
affected := models.PackageStatuses{}
affected := models.PackageFixStatuses{}
for _, p := range packs {
affected = append(affected, models.PackageStatus{Name: p.Name})
affected = append(affected, models.PackageFixStatus{Name: p.Name})
}
vinfo = models.VulnInfo{
CveID: cveID,
@@ -931,7 +932,7 @@ func (o *redhatBase) parseYumUpdateinfo(stdout string) (result []distroAdvisoryC
switch o.Distro.Family {
case config.CentOS:
// CentOS has no security channel.
return result, fmt.Errorf(
return result, xerrors.New(
"yum updateinfo is not suppported on CentOS")
case config.RedHat, config.Amazon, config.Oracle:
// nop
@@ -1118,7 +1119,7 @@ func (o *redhatBase) parseYumUpdateinfoListAvailable(stdout string) (advisoryIDP
fields := strings.Fields(line)
if len(fields) != 3 {
return []advisoryIDPacks{}, fmt.Errorf(
return []advisoryIDPacks{}, xerrors.Errorf(
"Unknown format. line: %s", line)
}
@@ -1151,21 +1152,21 @@ func (o *redhatBase) yumPS() error {
cmd := "LANGUAGE=en_US.UTF-8 yum info yum"
r := o.exec(util.PrependProxyEnv(cmd), noSudo)
if !r.isSuccess() {
return fmt.Errorf("Failed to SSH: %s", r)
return xerrors.Errorf("Failed to SSH: %s", r)
}
if !o.checkYumPsInstalled(r.Stdout) {
switch o.Distro.Family {
case config.RedHat, config.Oracle:
return nil
default:
return fmt.Errorf("yum-plugin-ps is not installed")
return xerrors.New("yum-plugin-ps is not installed")
}
}
cmd = "LANGUAGE=en_US.UTF-8 yum -q ps all --color=never"
r = o.exec(util.PrependProxyEnv(cmd), sudo)
if !r.isSuccess() {
return fmt.Errorf("Failed to SSH: %s", r)
return xerrors.Errorf("Failed to SSH: %s", r)
}
packs := o.parseYumPS(r.Stdout)
for name, pack := range packs {
@@ -1260,7 +1261,7 @@ func (o *redhatBase) needsRestarting() error {
cmd := "LANGUAGE=en_US.UTF-8 needs-restarting"
r := o.exec(cmd, sudo)
if !r.isSuccess() {
return fmt.Errorf("Failed to SSH: %s", r)
return xerrors.Errorf("Failed to SSH: %s", r)
}
procs := o.parseNeedsRestarting(r.Stdout)
for _, proc := range procs {
@@ -1335,7 +1336,7 @@ func (o *redhatBase) procPathToFQPN(execCommand string) (string, error) {
cmd := `LANGUAGE=en_US.UTF-8 rpm -qf --queryformat "%{NAME}-%{EPOCH}:%{VERSION}-%{RELEASE}.%{ARCH}\n" ` + path
r := o.exec(cmd, noSudo)
if !r.isSuccess() {
return "", fmt.Errorf("Failed to SSH: %s", r)
return "", xerrors.Errorf("Failed to SSH: %s", r)
}
fqpn := strings.TrimSpace(r.Stdout)
return strings.Replace(fqpn, "-(none):", "-", -1), nil

View File

@@ -1,11 +1,10 @@
package scan
import (
"fmt"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"golang.org/x/xerrors"
)
// inherit OsTypeInterface
@@ -43,7 +42,7 @@ func (o *rhel) checkDeps() error {
} else if o.getServerInfo().Mode.IsDeep() {
return o.execCheckDeps(o.depsDeep())
}
return fmt.Errorf("Unknown scan mode")
return xerrors.New("Unknown scan mode")
}
func (o *rhel) depsFast() []string {

View File

@@ -18,7 +18,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package scan
import (
"errors"
"fmt"
"net/http"
"os"
@@ -30,13 +29,14 @@ import (
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/report"
"github.com/future-architect/vuls/util"
"golang.org/x/xerrors"
)
var (
errOSFamilyHeader = errors.New("X-Vuls-OS-Family header is required")
errOSReleaseHeader = errors.New("X-Vuls-OS-Release header is required")
errKernelVersionHeader = errors.New("X-Vuls-Kernel-Version header is required")
errServerNameHeader = errors.New("X-Vuls-Server-Name header is required")
errOSFamilyHeader = xerrors.New("X-Vuls-OS-Family header is required")
errOSReleaseHeader = xerrors.New("X-Vuls-OS-Release header is required")
errKernelVersionHeader = xerrors.New("X-Vuls-Kernel-Version header is required")
errServerNameHeader = xerrors.New("X-Vuls-Server-Name header is required")
)
var servers, errServers []osTypeInterface
@@ -56,6 +56,7 @@ type osTypeInterface interface {
preCure() error
postScan() error
scanWordPress() error
scanPackages() error
convertToModel() models.ScanResult
@@ -120,7 +121,7 @@ func detectOS(c config.ServerInfo) (osType osTypeInterface) {
itsMe, osType, fatalErr = detectDebianWithRetry(c)
if fatalErr != nil {
osType.setErrs([]error{
fmt.Errorf("Failed to detect OS: %s", fatalErr)})
xerrors.Errorf("Failed to detect OS: %w", fatalErr)})
return
}
@@ -150,7 +151,7 @@ func detectOS(c config.ServerInfo) (osType osTypeInterface) {
}
//TODO darwin https://github.com/mizzy/specinfra/blob/master/lib/specinfra/helper/detect_os/darwin.rb
osType.setErrs([]error{fmt.Errorf("Unknown OS Type")})
osType.setErrs([]error{xerrors.New("Unknown OS Type")})
return
}
@@ -179,7 +180,7 @@ func PrintSSHableServerNames() bool {
func InitServers(timeoutSec int) error {
servers, errServers = detectServerOSes(timeoutSec)
if len(servers) == 0 {
return fmt.Errorf("No scannable servers")
return xerrors.New("No scannable servers")
}
actives, inactives := detectContainerOSes(timeoutSec)
@@ -240,7 +241,7 @@ func detectServerOSes(timeoutSec int) (servers, errServers []osTypeInterface) {
u := &unknown{}
u.setServerInfo(sInfo)
u.setErrs([]error{
fmt.Errorf("Timed out"),
xerrors.New("Timed out"),
})
errServers = append(errServers, u)
util.Log.Errorf("(%d/%d) Timed out: %s",
@@ -300,7 +301,7 @@ func detectContainerOSes(timeoutSec int) (actives, inactives []osTypeInterface)
u := &unknown{}
u.setServerInfo(sInfo)
u.setErrs([]error{
fmt.Errorf("Timed out"),
xerrors.New("Timed out"),
})
inactives = append(inactives)
util.Log.Errorf("Timed out: %s", servername)
@@ -319,8 +320,8 @@ func detectContainerOSesOnServer(containerHost osTypeInterface) (oses []osTypeIn
running, err := containerHost.runningContainers()
if err != nil {
containerHost.setErrs([]error{fmt.Errorf(
"Failed to get running containers on %s. err: %s",
containerHost.setErrs([]error{xerrors.Errorf(
"Failed to get running containers on %s. err: %w",
containerHost.getServerInfo().ServerName, err)})
return append(oses, containerHost)
}
@@ -351,8 +352,8 @@ func detectContainerOSesOnServer(containerHost osTypeInterface) (oses []osTypeIn
exitedContainers, err := containerHost.exitedContainers()
if err != nil {
containerHost.setErrs([]error{fmt.Errorf(
"Failed to get exited containers on %s. err: %s",
containerHost.setErrs([]error{xerrors.Errorf(
"Failed to get exited containers on %s. err: %w",
containerHost.getServerInfo().ServerName, err)})
return append(oses, containerHost)
}
@@ -386,7 +387,7 @@ func detectContainerOSesOnServer(containerHost osTypeInterface) (oses []osTypeIn
}
}
if 0 < len(exited) || 0 < len(unknown) {
containerHost.setErrs([]error{fmt.Errorf(
containerHost.setErrs([]error{xerrors.Errorf(
"Some containers on %s are exited or unknown. exited: %s, unknown: %s",
containerHost.getServerInfo().ServerName, exited, unknown)})
return append(oses, containerHost)
@@ -398,7 +399,7 @@ func detectContainerOSesOnServer(containerHost osTypeInterface) (oses []osTypeIn
func CheckScanModes() error {
for _, s := range servers {
if err := s.checkScanMode(); err != nil {
return fmt.Errorf("servers.%s.scanMode err: %s",
return xerrors.Errorf("servers.%s.scanMode err: %w",
s.getServerInfo().GetServerName(), err)
}
}
@@ -456,7 +457,7 @@ func detectPlatforms(timeoutSec int) {
// Scan scan
func Scan(timeoutSec int) error {
if len(servers) == 0 {
return fmt.Errorf("No server defined. Check the configuration")
return xerrors.New("No server defined. Check the configuration")
}
if err := setupChangelogCache(); err != nil {
@@ -534,7 +535,7 @@ func ViaHTTP(header http.Header, body string) (models.ScanResult, error) {
redhatBase: redhatBase{base: base},
}
default:
return models.ScanResult{}, fmt.Errorf("Server mode for %s is not implemented yet", family)
return models.ScanResult{}, xerrors.Errorf("Server mode for %s is not implemented yet", family)
}
installedPackages, srcPackages, err := osType.parseInstalledPackages(body)
@@ -590,6 +591,9 @@ func scanVulns(jsonDir string, scannedAt time.Time, timeoutSec int) error {
if err = o.scanPackages(); err != nil {
return err
}
if err = o.scanWordPress(); err != nil {
return xerrors.Errorf("Failed to scan WordPress: %w", err)
}
return o.postScan()
}, timeoutSec)
@@ -617,7 +621,7 @@ func scanVulns(jsonDir string, scannedAt time.Time, timeoutSec int) error {
}
for _, w := range ws {
if err := w.Write(results...); err != nil {
return fmt.Errorf("Failed to write summary report: %s", err)
return xerrors.Errorf("Failed to write summary report: %s", err)
}
}
@@ -636,20 +640,20 @@ func EnsureResultDir(scannedAt time.Time) (currentDir string, err error) {
}
jsonDir := filepath.Join(resultsDir, jsonDirName)
if err := os.MkdirAll(jsonDir, 0700); err != nil {
return "", fmt.Errorf("Failed to create dir: %s", err)
return "", xerrors.Errorf("Failed to create dir: %w", err)
}
symlinkPath := filepath.Join(resultsDir, "current")
if _, err := os.Lstat(symlinkPath); err == nil {
if err := os.Remove(symlinkPath); err != nil {
return "", fmt.Errorf(
"Failed to remove symlink. path: %s, err: %s", symlinkPath, err)
return "", xerrors.Errorf(
"Failed to remove symlink. path: %s, err: %w", symlinkPath, err)
}
}
if err := os.Symlink(jsonDir, symlinkPath); err != nil {
return "", fmt.Errorf(
"Failed to create symlink: path: %s, err: %s", symlinkPath, err)
return "", xerrors.Errorf(
"Failed to create symlink: path: %s, err: %w", symlinkPath, err)
}
return jsonDir, nil
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"golang.org/x/xerrors"
)
// inherit OsTypeInterface
@@ -160,7 +161,7 @@ func (o *suse) scanUpdatablePackages() (models.Packages, error) {
}
r := o.exec(cmd, noSudo)
if !r.isSuccess() {
return nil, fmt.Errorf("Failed to scan updatable packages: %v", r)
return nil, xerrors.Errorf("Failed to scan updatable packages: %v", r)
}
return o.parseZypperLULines(r.Stdout)
}
@@ -186,7 +187,7 @@ func (o *suse) parseZypperLULines(stdout string) (models.Packages, error) {
func (o *suse) parseZypperLUOneLine(line string) (*models.Package, error) {
ss := strings.Split(line, "|")
if len(ss) != 6 {
return nil, fmt.Errorf("zypper -q lu Unknown format: %s", line)
return nil, xerrors.Errorf("zypper -q lu Unknown format: %s", line)
}
available := strings.Split(strings.TrimSpace(ss[4]), "-")
return &models.Package{

271
wordpress/wordpress.go Normal file
View File

@@ -0,0 +1,271 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package wordpress
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"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
type WpCveInfos struct {
ReleaseDate string `json:"release_date"`
ChangelogURL string `json:"changelog_url"`
// Status string `json:"status"`
LatestVersion string `json:"latest_version"`
LastUpdated string `json:"last_updated"`
// Popular bool `json:"popular"`
Vulnerabilities []WpCveInfo `json:"vulnerabilities"`
Error string `json:"error"`
}
//WpCveInfo is for wpvulndb's 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"`
VulnType string `json:"vuln_type"`
References References `json:"references"`
FixedIn string `json:"fixed_in"`
}
//References is for wpvulndb's 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.
// https://wpvulndb.com/
func FillWordPress(r *models.ScanResult, token string) (int, error) {
// Core
ver := strings.Replace(r.WordPressPackages.CoreVersion(), ".", "", -1)
if ver == "" {
return 0, xerrors.New("Failed to get WordPress core version")
}
url := fmt.Sprintf("https://wpvulndb.com/api/v3/wordpresses/%s", ver)
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)
}
wpVinfos, err := convertToVinfos(models.WPCore, body)
if err != nil {
return 0, err
}
//TODO add a flag ignore inactive plugin or themes such as -wp-ignore-inactive flag to cmd line option or config.toml
// Themes
for _, p := range r.WordPressPackages.Themes() {
url := fmt.Sprintf("https://wpvulndb.com/api/v3/themes/%s", p.Name)
body, err := httpRequest(url, token)
if err != nil {
return 0, err
}
if body == "" {
continue
}
templateVinfos, err := convertToVinfos(p.Name, body)
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 {
return 0, xerrors.Errorf("Not a semantic versioning: %w", err)
}
if ok {
wpVinfos = append(wpVinfos, v)
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)
}
}
}
}
// Plugins
for _, p := range r.WordPressPackages.Plugins() {
url := fmt.Sprintf("https://wpvulndb.com/api/v3/plugins/%s", p.Name)
body, err := httpRequest(url, token)
if err != nil {
return 0, err
}
if body == "" {
continue
}
pluginVinfos, err := convertToVinfos(p.Name, body)
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 {
return 0, xerrors.Errorf("Not a semantic versioning: %w", err)
}
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)
}
}
}
}
for _, wpVinfo := range wpVinfos {
if vinfo, ok := r.ScannedCves[wpVinfo.CveID]; ok {
vinfo.CveContents[models.WPVulnDB] = wpVinfo.CveContents[models.WPVulnDB]
vinfo.VulnType = wpVinfo.VulnType
vinfo.Confidences = append(vinfo.Confidences, wpVinfo.Confidences...)
vinfo.WpPackageFixStats = append(vinfo.WpPackageFixStats, wpVinfo.WpPackageFixStats...)
r.ScannedCves[wpVinfo.CveID] = vinfo
} else {
r.ScannedCves[wpVinfo.CveID] = wpVinfo
}
}
return len(wpVinfos), nil
}
func match(installedVer, fixedIn string) (bool, error) {
v1, err := version.NewVersion(installedVer)
if err != nil {
return false, err
}
v2, err := version.NewVersion(fixedIn)
if err != nil {
return false, err
}
return v1.LessThan(v2), nil
}
func convertToVinfos(pkgName, body string) (vinfos []models.VulnInfo, err error) {
if body == "" {
return
}
// "pkgName" : CVE Detailed data
pkgnameCves := map[string]WpCveInfos{}
if err = json.Unmarshal([]byte(body), &pkgnameCves); err != nil {
return nil, xerrors.Errorf("Failed to unmarshal %s. err: %w", body, err)
}
for _, v := range pkgnameCves {
vs := extractToVulnInfos(pkgName, v.Vulnerabilities)
vinfos = append(vinfos, vs...)
}
return vinfos, nil
}
func extractToVulnInfos(pkgName string, cves []WpCveInfo) (vinfos []models.VulnInfo) {
for _, vulnerability := range cves {
var cveIDs []string
if len(vulnerability.References.Cve) == 0 {
cveIDs = append(cveIDs, fmt.Sprintf("WPVDBID-%d", vulnerability.ID))
}
for _, cveNumber := range vulnerability.References.Cve {
cveIDs = append(cveIDs, "CVE-"+cveNumber)
}
var refs []models.Reference
for _, url := range vulnerability.References.URL {
refs = append(refs, models.Reference{
Link: url,
})
}
for _, cveID := range cveIDs {
vinfos = append(vinfos, models.VulnInfo{
CveID: cveID,
CveContents: models.NewCveContents(
models.CveContent{
Type: models.WPVulnDB,
CveID: cveID,
Title: vulnerability.Title,
References: refs,
},
),
VulnType: vulnerability.VulnType,
Confidences: []models.Confidence{
models.WPVulnDBMatch,
},
WpPackageFixStats: []models.WpPackageFixStatus{{
Name: pkgName,
FixedIn: vulnerability.FixedIn,
}},
})
}
}
return
}
func httpRequest(url, token string) (string, error) {
util.Log.Debugf("%s", url)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return "", err
}
req.Header.Set("Authorization", fmt.Sprintf("Token token=%s", token))
resp, err := new(http.Client).Do(req)
if err != nil {
return "", err
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != 200 && resp.StatusCode != 404 {
return "", xerrors.Errorf("status: %s", resp.Status)
} else if resp.StatusCode == 404 {
// This package is not in WPVulnDB
return "", nil
}
return string(body), nil
}

View File

@@ -0,0 +1 @@
package wordpress