feat(scan): WordPress Vulnerability Scan (core, plugin, theme) (#769)
https://github.com/future-architect/vuls/pull/769
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
90
Gopkg.lock
generated
@@ -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
|
||||
|
||||
@@ -41,3 +41,7 @@
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/xerrors"
|
||||
|
||||
25
README.md
25
README.md
@@ -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
12
cache/bolt.go
vendored
@@ -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))
|
||||
})
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
128
config/config.go
128
config/config.go
@@ -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
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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{},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
17
gost/util.go
17
gost/util.go
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"},
|
||||
},
|
||||
|
||||
@@ -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}
|
||||
)
|
||||
|
||||
@@ -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
88
models/wordpress.go
Normal 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"`
|
||||
}
|
||||
@@ -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"},
|
||||
|
||||
18
oval/oval.go
18
oval/oval.go
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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},
|
||||
},
|
||||
|
||||
24
oval/util.go
24
oval/util.go
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
13
report/s3.go
13
report/s3.go
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
188
scan/base.go
188
scan/base.go
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 ¤t, 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 ¤t, 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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
271
wordpress/wordpress.go
Normal 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
|
||||
}
|
||||
1
wordpress/wordpress_test.go
Normal file
1
wordpress/wordpress_test.go
Normal file
@@ -0,0 +1 @@
|
||||
package wordpress
|
||||
Reference in New Issue
Block a user