Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9bcffcd721 | ||
|
|
787604de6a | ||
|
|
5164fb1423 | ||
|
|
07335617d3 | ||
|
|
e5855922c1 | ||
|
|
671be3f2f7 | ||
|
|
fe8d252c51 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -18,3 +18,5 @@ dist/
|
||||
vuls.*
|
||||
vuls
|
||||
!cmd/vuls
|
||||
future-vuls
|
||||
trivy-to-vuls
|
||||
|
||||
@@ -50,7 +50,7 @@ Vuls is a tool created to solve the problems listed above. It has the following
|
||||
|
||||
[Supports major Linux/FreeBSD](https://vuls.io/docs/en/supported-os.html)
|
||||
|
||||
- Alpine, Amazon Linux, CentOS, Alma Linux, Rocky Linux, Debian, Oracle Linux, Raspbian, RHEL, SUSE Enterprise Linux, Fedora, and Ubuntu
|
||||
- Alpine, Amazon Linux, CentOS, AlmaLinux, Rocky Linux, Debian, Oracle Linux, Raspbian, RHEL, openSUSE, openSUSE Leap, SUSE Enterprise Linux, Fedora, and Ubuntu
|
||||
- FreeBSD
|
||||
- Cloud, on-premise, Running Docker Container
|
||||
|
||||
|
||||
@@ -307,6 +307,13 @@ func (l Distro) MajorVersion() (int, error) {
|
||||
if 0 < len(l.Release) {
|
||||
return strconv.Atoi(strings.Split(strings.TrimPrefix(l.Release, "stream"), ".")[0])
|
||||
}
|
||||
case constant.OpenSUSE:
|
||||
if l.Release != "" {
|
||||
if l.Release == "tumbleweed" {
|
||||
return 0, nil
|
||||
}
|
||||
return strconv.Atoi(strings.Split(l.Release, ".")[0])
|
||||
}
|
||||
default:
|
||||
if 0 < len(l.Release) {
|
||||
return strconv.Atoi(strings.Split(l.Release, ".")[0])
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package config
|
||||
|
||||
// Load loads configuration
|
||||
func Load(path, keyPass string) error {
|
||||
var loader Loader
|
||||
loader = TOMLLoader{}
|
||||
return loader.Load(path, keyPass)
|
||||
func Load(path string) error {
|
||||
loader := TOMLLoader{}
|
||||
return loader.Load(path)
|
||||
}
|
||||
|
||||
// Loader is interface of concrete loader
|
||||
|
||||
68
config/os.go
68
config/os.go
@@ -147,8 +147,74 @@ func GetEOL(family, release string) (eol EOL, found bool) {
|
||||
StandardSupportUntil: time.Date(2022, 7, 1, 23, 59, 59, 0, time.UTC),
|
||||
},
|
||||
}[release]
|
||||
case constant.OpenSUSE:
|
||||
// https://en.opensuse.org/Lifetime
|
||||
eol, found = map[string]EOL{
|
||||
"10.2": {Ended: true},
|
||||
"10.3": {Ended: true},
|
||||
"11.0": {Ended: true},
|
||||
"11.1": {Ended: true},
|
||||
"11.2": {Ended: true},
|
||||
"11.3": {Ended: true},
|
||||
"11.4": {Ended: true},
|
||||
"12.1": {Ended: true},
|
||||
"12.2": {Ended: true},
|
||||
"12.3": {Ended: true},
|
||||
"13.1": {Ended: true},
|
||||
"13.2": {Ended: true},
|
||||
"tumbleweed": {},
|
||||
}[release]
|
||||
case constant.OpenSUSELeap:
|
||||
// https://en.opensuse.org/Lifetime
|
||||
eol, found = map[string]EOL{
|
||||
"42.1": {Ended: true},
|
||||
"42.2": {Ended: true},
|
||||
"42.3": {Ended: true},
|
||||
"15.0": {Ended: true},
|
||||
"15.1": {Ended: true},
|
||||
"15.2": {Ended: true},
|
||||
"15.3": {StandardSupportUntil: time.Date(2022, 11, 30, 23, 59, 59, 0, time.UTC)},
|
||||
"15.4": {StandardSupportUntil: time.Date(2023, 11, 30, 23, 59, 59, 0, time.UTC)},
|
||||
}[release]
|
||||
case constant.SUSEEnterpriseServer:
|
||||
//TODO
|
||||
// https://www.suse.com/lifecycle
|
||||
eol, found = map[string]EOL{
|
||||
"11": {Ended: true},
|
||||
"11.1": {Ended: true},
|
||||
"11.2": {Ended: true},
|
||||
"11.3": {Ended: true},
|
||||
"11.4": {Ended: true},
|
||||
"12": {Ended: true},
|
||||
"12.1": {Ended: true},
|
||||
"12.2": {Ended: true},
|
||||
"12.3": {Ended: true},
|
||||
"12.4": {Ended: true},
|
||||
"12.5": {StandardSupportUntil: time.Date(2024, 10, 31, 23, 59, 59, 0, time.UTC)},
|
||||
"15": {Ended: true},
|
||||
"15.1": {Ended: true},
|
||||
"15.2": {Ended: true},
|
||||
"15.3": {StandardSupportUntil: time.Date(2022, 11, 30, 23, 59, 59, 0, time.UTC)},
|
||||
"15.4": {StandardSupportUntil: time.Date(2023, 11, 30, 23, 59, 59, 0, time.UTC)},
|
||||
}[release]
|
||||
case constant.SUSEEnterpriseDesktop:
|
||||
// https://www.suse.com/lifecycle
|
||||
eol, found = map[string]EOL{
|
||||
"11": {Ended: true},
|
||||
"11.1": {Ended: true},
|
||||
"11.2": {Ended: true},
|
||||
"11.3": {Ended: true},
|
||||
"11.4": {Ended: true},
|
||||
"12": {Ended: true},
|
||||
"12.1": {Ended: true},
|
||||
"12.2": {Ended: true},
|
||||
"12.3": {Ended: true},
|
||||
"12.4": {Ended: true},
|
||||
"15": {Ended: true},
|
||||
"15.1": {Ended: true},
|
||||
"15.2": {Ended: true},
|
||||
"15.3": {StandardSupportUntil: time.Date(2022, 11, 30, 23, 59, 59, 0, time.UTC)},
|
||||
"15.4": {StandardSupportUntil: time.Date(2023, 11, 30, 23, 59, 59, 0, time.UTC)},
|
||||
}[release]
|
||||
case constant.Alpine:
|
||||
// https://github.com/aquasecurity/trivy/blob/master/pkg/detector/ospkg/alpine/alpine.go#L19
|
||||
// https://alpinelinux.org/releases/
|
||||
|
||||
@@ -15,7 +15,7 @@ type TOMLLoader struct {
|
||||
}
|
||||
|
||||
// Load load the configuration TOML file specified by path arg.
|
||||
func (c TOMLLoader) Load(pathToToml, _ string) error {
|
||||
func (c TOMLLoader) Load(pathToToml string) error {
|
||||
// util.Log.Infof("Loading config: %s", pathToToml)
|
||||
if _, err := toml.DecodeFile(pathToToml, &Conf); err != nil {
|
||||
return err
|
||||
@@ -149,18 +149,11 @@ func setDefaultIfEmpty(server *ServerInfo) error {
|
||||
}
|
||||
|
||||
if server.Port == "" {
|
||||
if Conf.Default.Port != "" {
|
||||
server.Port = Conf.Default.Port
|
||||
} else {
|
||||
server.Port = "22"
|
||||
}
|
||||
server.Port = Conf.Default.Port
|
||||
}
|
||||
|
||||
if server.User == "" {
|
||||
server.User = Conf.Default.User
|
||||
if server.User == "" && server.Port != "local" {
|
||||
return xerrors.Errorf("server.user is empty")
|
||||
}
|
||||
}
|
||||
|
||||
if server.SSHConfigPath == "" {
|
||||
|
||||
@@ -24,7 +24,7 @@ const (
|
||||
Rocky = "rocky"
|
||||
|
||||
// Fedora is
|
||||
// Fedora = "fedora"
|
||||
Fedora = "fedora"
|
||||
|
||||
// Amazon is
|
||||
Amazon = "amazon"
|
||||
@@ -53,9 +53,6 @@ const (
|
||||
// SUSEEnterpriseDesktop is
|
||||
SUSEEnterpriseDesktop = "suse.linux.enterprise.desktop"
|
||||
|
||||
// SUSEOpenstackCloud is
|
||||
SUSEOpenstackCloud = "suse.openstack.cloud"
|
||||
|
||||
// Alpine is
|
||||
Alpine = "alpine"
|
||||
|
||||
@@ -64,7 +61,4 @@ const (
|
||||
|
||||
// DeepSecurity is
|
||||
DeepSecurity = "deepsecurity"
|
||||
|
||||
//Fedora is
|
||||
Fedora = "fedora"
|
||||
)
|
||||
|
||||
8
go.mod
8
go.mod
@@ -28,7 +28,7 @@ require (
|
||||
github.com/google/subcommands v1.2.0
|
||||
github.com/gosuri/uitable v0.0.4
|
||||
github.com/hashicorp/go-uuid v1.0.2
|
||||
github.com/hashicorp/go-version v1.3.0
|
||||
github.com/hashicorp/go-version v1.4.0
|
||||
github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c
|
||||
github.com/jesseduffield/gocui v0.3.0
|
||||
github.com/k0kubun/pp v3.0.1+incompatible
|
||||
@@ -56,14 +56,14 @@ require (
|
||||
github.com/vulsio/go-kev v0.1.0
|
||||
github.com/vulsio/go-msfdb v0.2.1-0.20211028071756-4a9759bd9f14
|
||||
github.com/vulsio/gost v0.4.1-0.20211028071837-7ad032a6ffa8
|
||||
github.com/vulsio/goval-dictionary v0.7.0
|
||||
github.com/vulsio/goval-dictionary v0.7.1-0.20220212015000-031fc960b77c
|
||||
golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce // indirect
|
||||
golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
|
||||
gopkg.in/ini.v1 v1.66.3 // indirect
|
||||
gopkg.in/ini.v1 v1.66.4 // indirect
|
||||
gorm.io/driver/mysql v1.2.3 // indirect
|
||||
gorm.io/driver/postgres v1.2.3 // indirect
|
||||
gorm.io/driver/sqlite v1.2.6 // indirect
|
||||
@@ -112,7 +112,6 @@ require (
|
||||
github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
|
||||
github.com/hashicorp/go-safetemp v1.0.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/htcat/htcat v1.0.2 // indirect
|
||||
github.com/huandu/xstrings v1.3.2 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/inconshreveable/log15 v0.0.0-20201112154412-8562bdadbbac // indirect
|
||||
@@ -151,7 +150,6 @@ require (
|
||||
github.com/stretchr/testify v1.7.0 // indirect
|
||||
github.com/subosito/gotenv v1.2.0 // indirect
|
||||
github.com/ulikunitz/xz v0.5.10 // indirect
|
||||
github.com/ymomoi/goval-parser v0.0.0-20170813122243-0a0be1dd9d08 // indirect
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
|
||||
14
go.sum
14
go.sum
@@ -975,8 +975,9 @@ github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
|
||||
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw=
|
||||
github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go-version v1.4.0 h1:aAQzgqIrRKRa7w75CKpbBxYsmUoPjzVm1W59ca1L0J4=
|
||||
github.com/hashicorp/go-version v1.4.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
@@ -1001,7 +1002,6 @@ github.com/hashicorp/uuid v0.0.0-20160311170451-ebb0a03e909c/go.mod h1:fHzc09Uny
|
||||
github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c h1:aY2hhxLhjEAbfXOx2nRJxCXezC6CO2V/yN+OCr1srtk=
|
||||
github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/htcat/htcat v1.0.2 h1:zro95dGwkKDeZOgq9ei+9szd5qurGxBGfHY8hRehA7k=
|
||||
github.com/htcat/htcat v1.0.2/go.mod h1:i8ViQbjSi2+lJzM6Lx20FIxHENCz6mzJglK3HH06W3s=
|
||||
github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
|
||||
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
@@ -1763,8 +1763,8 @@ github.com/vulsio/go-msfdb v0.2.1-0.20211028071756-4a9759bd9f14 h1:2uYZw2gQ0kymw
|
||||
github.com/vulsio/go-msfdb v0.2.1-0.20211028071756-4a9759bd9f14/go.mod h1:NGdcwWxCK/ES8vZ/crzREqI69S5gH1MivCpSp1pa2Rc=
|
||||
github.com/vulsio/gost v0.4.1-0.20211028071837-7ad032a6ffa8 h1:jqsECpLRp1EAXGOdhPxHzqYjWP5l980GjJ8s/AUYH/4=
|
||||
github.com/vulsio/gost v0.4.1-0.20211028071837-7ad032a6ffa8/go.mod h1:DaWLus8dJ4DdhVsBe5TAEEZ3IdoTMIb/z2StR4Bhb7Q=
|
||||
github.com/vulsio/goval-dictionary v0.7.0 h1:pnzY1l1KztwlE9FNEUxUpfg08YvMdNt4AL4ohxTVyAY=
|
||||
github.com/vulsio/goval-dictionary v0.7.0/go.mod h1:aSJK5KAr0o+A0ccgdtQHvaMiAjXEv813QWcEtLr+mvo=
|
||||
github.com/vulsio/goval-dictionary v0.7.1-0.20220212015000-031fc960b77c h1:LV/HjQRJGhJiKq6huf6ywF09OW+btoo0Y96y01vVp7o=
|
||||
github.com/vulsio/goval-dictionary v0.7.1-0.20220212015000-031fc960b77c/go.mod h1:BEvFNaiPCKwYWtITjn+hGJQT9N8WfigSd7NXHNnbxkI=
|
||||
github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
|
||||
github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
|
||||
@@ -1779,8 +1779,6 @@ github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMx
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yashtewari/glob-intersection v0.0.0-20180916065949-5c77d914dd0b/go.mod h1:HptNXiXVDcJjXe9SqMd0v2FsL9f8dz4GnXgltU6q/co=
|
||||
github.com/ymomoi/goval-parser v0.0.0-20170813122243-0a0be1dd9d08 h1:OsHsjWw5m3P0r+RJITvigJu9dn6L8812S54x42jxeII=
|
||||
github.com/ymomoi/goval-parser v0.0.0-20170813122243-0a0be1dd9d08/go.mod h1:ox1Nt/rGgWuhVrNg+jKYonAs4BiQG1tRJwj4ue91iy4=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
@@ -2582,8 +2580,8 @@ gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.66.3 h1:jRskFVxYaMGAMUbN0UZ7niA9gzL9B49DOqE78vg0k3w=
|
||||
gopkg.in/ini.v1 v1.66.3/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4=
|
||||
gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
|
||||
@@ -46,24 +46,33 @@ func (deb Debian) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error
|
||||
|
||||
// Add linux and set the version of running kernel to search Gost.
|
||||
if r.Container.ContainerID == "" {
|
||||
newVer := ""
|
||||
if p, ok := r.Packages["linux-image-"+r.RunningKernel.Release]; ok {
|
||||
newVer = p.NewVersion
|
||||
}
|
||||
r.Packages["linux"] = models.Package{
|
||||
Name: "linux",
|
||||
Version: r.RunningKernel.Version,
|
||||
NewVersion: newVer,
|
||||
if r.RunningKernel.Version != "" {
|
||||
newVer := ""
|
||||
if p, ok := r.Packages["linux-image-"+r.RunningKernel.Release]; ok {
|
||||
newVer = p.NewVersion
|
||||
}
|
||||
r.Packages["linux"] = models.Package{
|
||||
Name: "linux",
|
||||
Version: r.RunningKernel.Version,
|
||||
NewVersion: newVer,
|
||||
}
|
||||
} else {
|
||||
logging.Log.Warnf("Since the exact kernel version is not available, the vulnerability in the linux package is not detected.")
|
||||
}
|
||||
}
|
||||
|
||||
stashLinuxPackage := r.Packages["linux"]
|
||||
var stashLinuxPackage models.Package
|
||||
if linux, ok := r.Packages["linux"]; ok {
|
||||
stashLinuxPackage = linux
|
||||
}
|
||||
nFixedCVEs, err := deb.detectCVEsWithFixState(r, "resolved")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
r.Packages["linux"] = stashLinuxPackage
|
||||
if stashLinuxPackage.Name != "" {
|
||||
r.Packages["linux"] = stashLinuxPackage
|
||||
}
|
||||
nUnfixedCVEs, err := deb.detectCVEsWithFixState(r, "open")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/future-architect/vuls/constant"
|
||||
)
|
||||
|
||||
// CveContents has CveContent
|
||||
@@ -333,6 +335,8 @@ func NewCveContentType(name string) CveContentType {
|
||||
return DebianSecurityTracker
|
||||
case "ubuntu_api":
|
||||
return UbuntuAPI
|
||||
case constant.OpenSUSE, constant.OpenSUSELeap, constant.SUSEEnterpriseServer, constant.SUSEEnterpriseDesktop:
|
||||
return SUSE
|
||||
case "microsoft":
|
||||
return Microsoft
|
||||
case "wordpress":
|
||||
|
||||
@@ -510,7 +510,7 @@ func (v VulnInfo) Cvss2Scores() (values []CveContentCvss) {
|
||||
|
||||
// Cvss3Scores returns CVSS V3 Score
|
||||
func (v VulnInfo) Cvss3Scores() (values []CveContentCvss) {
|
||||
order := []CveContentType{RedHatAPI, RedHat, Nvd, Jvn}
|
||||
order := []CveContentType{RedHatAPI, RedHat, SUSE, Nvd, Jvn}
|
||||
for _, ctype := range order {
|
||||
if conts, found := v.CveContents[ctype]; found {
|
||||
for _, cont := range conts {
|
||||
@@ -549,7 +549,7 @@ func (v VulnInfo) Cvss3Scores() (values []CveContentCvss) {
|
||||
}
|
||||
}
|
||||
|
||||
// Memo: Only RedHat, Oracle and Amazon has severity data in advisory.
|
||||
// Memo: Only RedHat, SUSE, Oracle and Amazon has severity data in advisory.
|
||||
for _, adv := range v.DistroAdvisories {
|
||||
if adv.Severity != "" {
|
||||
score := severityToCvssScoreRoughly(adv.Severity)
|
||||
|
||||
@@ -141,14 +141,18 @@ func (o Debian) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
|
||||
|
||||
// Add linux and set the version of running kernel to search OVAL.
|
||||
if r.Container.ContainerID == "" {
|
||||
newVer := ""
|
||||
if p, ok := r.Packages[linuxImage]; ok {
|
||||
newVer = p.NewVersion
|
||||
}
|
||||
r.Packages["linux"] = models.Package{
|
||||
Name: "linux",
|
||||
Version: r.RunningKernel.Version,
|
||||
NewVersion: newVer,
|
||||
if r.RunningKernel.Version != "" {
|
||||
newVer := ""
|
||||
if p, ok := r.Packages[linuxImage]; ok {
|
||||
newVer = p.NewVersion
|
||||
}
|
||||
r.Packages["linux"] = models.Package{
|
||||
Name: "linux",
|
||||
Version: r.RunningKernel.Version,
|
||||
NewVersion: newVer,
|
||||
}
|
||||
} else {
|
||||
logging.Log.Warnf("Since the exact kernel version is not available, the vulnerability in the linux package is not detected.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ package oval
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
@@ -225,8 +224,8 @@ func (o RedHatBase) convertToModel(cveID string, def *ovalmodels.Definition) *mo
|
||||
continue
|
||||
}
|
||||
|
||||
score2, vec2 := o.parseCvss2(cve.Cvss2)
|
||||
score3, vec3 := o.parseCvss3(cve.Cvss3)
|
||||
score2, vec2 := parseCvss2(cve.Cvss2)
|
||||
score3, vec3 := parseCvss3(cve.Cvss3)
|
||||
|
||||
sev2, sev3, severity := "", "", def.Advisory.Severity
|
||||
if cve.Impact != "" {
|
||||
@@ -262,39 +261,6 @@ func (o RedHatBase) convertToModel(cveID string, def *ovalmodels.Definition) *mo
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParseCvss2 divide CVSSv2 string into score and vector
|
||||
// 5/AV:N/AC:L/Au:N/C:N/I:N/A:P
|
||||
func (o RedHatBase) parseCvss2(scoreVector string) (score float64, vector string) {
|
||||
var err error
|
||||
ss := strings.Split(scoreVector, "/")
|
||||
if 1 < len(ss) {
|
||||
if score, err = strconv.ParseFloat(ss[0], 64); err != nil {
|
||||
return 0, ""
|
||||
}
|
||||
return score, strings.Join(ss[1:], "/")
|
||||
}
|
||||
return 0, ""
|
||||
}
|
||||
|
||||
// ParseCvss3 divide CVSSv3 string into score and vector
|
||||
// 5.6/CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L
|
||||
func (o RedHatBase) parseCvss3(scoreVector string) (score float64, vector string) {
|
||||
var err error
|
||||
for _, s := range []string{
|
||||
"/CVSS:3.0/",
|
||||
"/CVSS:3.1/",
|
||||
} {
|
||||
ss := strings.Split(scoreVector, s)
|
||||
if 1 < len(ss) {
|
||||
if score, err = strconv.ParseFloat(ss[0], 64); err != nil {
|
||||
return 0, ""
|
||||
}
|
||||
return score, strings.TrimPrefix(s, "/") + ss[1]
|
||||
}
|
||||
}
|
||||
return 0, ""
|
||||
}
|
||||
|
||||
// RedHat is the interface for RedhatBase OVAL
|
||||
type RedHat struct {
|
||||
RedHatBase
|
||||
|
||||
@@ -11,79 +11,6 @@ import (
|
||||
ovalmodels "github.com/vulsio/goval-dictionary/models"
|
||||
)
|
||||
|
||||
func TestParseCvss2(t *testing.T) {
|
||||
type out struct {
|
||||
score float64
|
||||
vector string
|
||||
}
|
||||
var tests = []struct {
|
||||
in string
|
||||
out out
|
||||
}{
|
||||
{
|
||||
in: "5/AV:N/AC:L/Au:N/C:N/I:N/A:P",
|
||||
out: out{
|
||||
score: 5.0,
|
||||
vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
|
||||
},
|
||||
},
|
||||
{
|
||||
in: "",
|
||||
out: out{
|
||||
score: 0,
|
||||
vector: "",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
s, v := RedHatBase{}.parseCvss2(tt.in)
|
||||
if s != tt.out.score || v != tt.out.vector {
|
||||
t.Errorf("\nexpected: %f, %s\n actual: %f, %s",
|
||||
tt.out.score, tt.out.vector, s, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseCvss3(t *testing.T) {
|
||||
type out struct {
|
||||
score float64
|
||||
vector string
|
||||
}
|
||||
var tests = []struct {
|
||||
in string
|
||||
out out
|
||||
}{
|
||||
{
|
||||
in: "5.6/CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
|
||||
out: out{
|
||||
score: 5.6,
|
||||
vector: "CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
|
||||
},
|
||||
},
|
||||
{
|
||||
in: "6.1/CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
|
||||
out: out{
|
||||
score: 6.1,
|
||||
vector: "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
|
||||
},
|
||||
},
|
||||
{
|
||||
in: "",
|
||||
out: out{
|
||||
score: 0,
|
||||
vector: "",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
s, v := RedHatBase{}.parseCvss3(tt.in)
|
||||
if s != tt.out.score || v != tt.out.vector {
|
||||
t.Errorf("\nexpected: %f, %s\n actual: %f, %s",
|
||||
tt.out.score, tt.out.vector, s, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPackNamesOfUpdate(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in models.ScanResult
|
||||
|
||||
26
oval/suse.go
26
oval/suse.go
@@ -4,8 +4,9 @@
|
||||
package oval
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/constant"
|
||||
"github.com/future-architect/vuls/logging"
|
||||
"github.com/future-architect/vuls/models"
|
||||
ovalmodels "github.com/vulsio/goval-dictionary/models"
|
||||
@@ -17,11 +18,10 @@ type SUSE struct {
|
||||
}
|
||||
|
||||
// NewSUSE creates OVAL client for SUSE
|
||||
func NewSUSE(cnf config.VulnDictInterface) SUSE {
|
||||
// TODO implement other family
|
||||
func NewSUSE(cnf config.VulnDictInterface, family string) SUSE {
|
||||
return SUSE{
|
||||
Base{
|
||||
family: constant.SUSEEnterpriseServer,
|
||||
family: family,
|
||||
Cnf: cnf,
|
||||
},
|
||||
}
|
||||
@@ -56,7 +56,7 @@ func (o SUSE) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
|
||||
for _, vuln := range r.ScannedCves {
|
||||
if conts, ok := vuln.CveContents[models.SUSE]; ok {
|
||||
for i, cont := range conts {
|
||||
cont.SourceLink = "https://security-tracker.debian.org/tracker/" + cont.CveID
|
||||
cont.SourceLink = fmt.Sprintf("https://www.suse.com/security/cve/%s.html", cont.CveID)
|
||||
vuln.CveContents[models.SUSE][i] = cont
|
||||
}
|
||||
}
|
||||
@@ -117,11 +117,23 @@ func (o SUSE) convertToModel(def *ovalmodels.Definition) *models.CveContent {
|
||||
RefID: r.RefID,
|
||||
})
|
||||
}
|
||||
|
||||
return &models.CveContent{
|
||||
cveCont := models.CveContent{
|
||||
CveID: def.Title,
|
||||
Title: def.Title,
|
||||
Summary: def.Description,
|
||||
References: refs,
|
||||
}
|
||||
|
||||
if 0 < len(def.Advisory.Cves) {
|
||||
if len(def.Advisory.Cves) == 1 {
|
||||
cve := def.Advisory.Cves[0]
|
||||
score3, vec3 := parseCvss3(cve.Cvss3)
|
||||
cveCont.Cvss3Score = score3
|
||||
cveCont.Cvss3Vector = vec3
|
||||
cveCont.Cvss3Severity = cve.Impact
|
||||
} else {
|
||||
logging.Log.Warnf("Unknown Oval format. Please register the issue as it needs to be investigated. https://github.com/future-architect/vuls/issues family: %s, defID: %s", o.family, def.DefinitionID)
|
||||
}
|
||||
}
|
||||
return &cveCont
|
||||
}
|
||||
|
||||
56
oval/util.go
56
oval/util.go
@@ -9,6 +9,7 @@ import (
|
||||
"net/http"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -398,7 +399,10 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family string, ru
|
||||
constant.Fedora,
|
||||
constant.Amazon,
|
||||
constant.Oracle,
|
||||
constant.OpenSUSE,
|
||||
constant.OpenSUSELeap,
|
||||
constant.SUSEEnterpriseServer,
|
||||
constant.SUSEEnterpriseDesktop,
|
||||
constant.Debian,
|
||||
constant.Raspbian,
|
||||
constant.Ubuntu:
|
||||
@@ -457,7 +461,10 @@ func lessThan(family, newVer string, packInOVAL ovalmodels.Package) (bool, error
|
||||
return vera.LessThan(verb), nil
|
||||
|
||||
case constant.Oracle,
|
||||
constant.OpenSUSE,
|
||||
constant.OpenSUSELeap,
|
||||
constant.SUSEEnterpriseServer,
|
||||
constant.SUSEEnterpriseDesktop,
|
||||
constant.Amazon,
|
||||
constant.Fedora:
|
||||
vera := rpmver.NewVersion(newVer)
|
||||
@@ -500,9 +507,14 @@ func NewOVALClient(family string, cnf config.GovalDictConf) (Client, error) {
|
||||
return NewRocky(&cnf), nil
|
||||
case constant.Oracle:
|
||||
return NewOracle(&cnf), nil
|
||||
case constant.OpenSUSE:
|
||||
return NewSUSE(&cnf, constant.OpenSUSE), nil
|
||||
case constant.OpenSUSELeap:
|
||||
return NewSUSE(&cnf, constant.OpenSUSELeap), nil
|
||||
case constant.SUSEEnterpriseServer:
|
||||
// TODO other suse family
|
||||
return NewSUSE(&cnf), nil
|
||||
return NewSUSE(&cnf, constant.SUSEEnterpriseServer), nil
|
||||
case constant.SUSEEnterpriseDesktop:
|
||||
return NewSUSE(&cnf, constant.SUSEEnterpriseDesktop), nil
|
||||
case constant.Alpine:
|
||||
return NewAlpine(&cnf), nil
|
||||
case constant.Amazon:
|
||||
@@ -535,9 +547,14 @@ func GetFamilyInOval(familyInScanResult string) (string, error) {
|
||||
return constant.Fedora, nil
|
||||
case constant.Oracle:
|
||||
return constant.Oracle, nil
|
||||
case constant.OpenSUSE:
|
||||
return constant.OpenSUSE, nil
|
||||
case constant.OpenSUSELeap:
|
||||
return constant.OpenSUSELeap, nil
|
||||
case constant.SUSEEnterpriseServer:
|
||||
// TODO other suse family
|
||||
return constant.SUSEEnterpriseServer, nil
|
||||
case constant.SUSEEnterpriseDesktop:
|
||||
return constant.SUSEEnterpriseDesktop, nil
|
||||
case constant.Alpine:
|
||||
return constant.Alpine, nil
|
||||
case constant.Amazon:
|
||||
@@ -554,3 +571,36 @@ func GetFamilyInOval(familyInScanResult string) (string, error) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ParseCvss2 divide CVSSv2 string into score and vector
|
||||
// 5/AV:N/AC:L/Au:N/C:N/I:N/A:P
|
||||
func parseCvss2(scoreVector string) (score float64, vector string) {
|
||||
var err error
|
||||
ss := strings.Split(scoreVector, "/")
|
||||
if 1 < len(ss) {
|
||||
if score, err = strconv.ParseFloat(ss[0], 64); err != nil {
|
||||
return 0, ""
|
||||
}
|
||||
return score, strings.Join(ss[1:], "/")
|
||||
}
|
||||
return 0, ""
|
||||
}
|
||||
|
||||
// ParseCvss3 divide CVSSv3 string into score and vector
|
||||
// 5.6/CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L
|
||||
func parseCvss3(scoreVector string) (score float64, vector string) {
|
||||
var err error
|
||||
for _, s := range []string{
|
||||
"/CVSS:3.0/",
|
||||
"/CVSS:3.1/",
|
||||
} {
|
||||
ss := strings.Split(scoreVector, s)
|
||||
if 1 < len(ss) {
|
||||
if score, err = strconv.ParseFloat(ss[0], 64); err != nil {
|
||||
return 0, ""
|
||||
}
|
||||
return score, strings.TrimPrefix(s, "/") + ss[1]
|
||||
}
|
||||
}
|
||||
return 0, ""
|
||||
}
|
||||
|
||||
@@ -2049,3 +2049,76 @@ func Test_ovalResult_Sort(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseCvss2(t *testing.T) {
|
||||
type out struct {
|
||||
score float64
|
||||
vector string
|
||||
}
|
||||
var tests = []struct {
|
||||
in string
|
||||
out out
|
||||
}{
|
||||
{
|
||||
in: "5/AV:N/AC:L/Au:N/C:N/I:N/A:P",
|
||||
out: out{
|
||||
score: 5.0,
|
||||
vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
|
||||
},
|
||||
},
|
||||
{
|
||||
in: "",
|
||||
out: out{
|
||||
score: 0,
|
||||
vector: "",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
s, v := parseCvss2(tt.in)
|
||||
if s != tt.out.score || v != tt.out.vector {
|
||||
t.Errorf("\nexpected: %f, %s\n actual: %f, %s",
|
||||
tt.out.score, tt.out.vector, s, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseCvss3(t *testing.T) {
|
||||
type out struct {
|
||||
score float64
|
||||
vector string
|
||||
}
|
||||
var tests = []struct {
|
||||
in string
|
||||
out out
|
||||
}{
|
||||
{
|
||||
in: "5.6/CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
|
||||
out: out{
|
||||
score: 5.6,
|
||||
vector: "CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
|
||||
},
|
||||
},
|
||||
{
|
||||
in: "6.1/CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
|
||||
out: out{
|
||||
score: 6.1,
|
||||
vector: "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
|
||||
},
|
||||
},
|
||||
{
|
||||
in: "",
|
||||
out: out{
|
||||
score: 0,
|
||||
vector: "",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
s, v := parseCvss3(tt.in)
|
||||
if s != tt.out.score || v != tt.out.vector {
|
||||
t.Errorf("\nexpected: %f, %s\n actual: %f, %s",
|
||||
tt.out.score, tt.out.vector, s, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,8 +373,8 @@ No CVE-IDs are found in updatable packages.
|
||||
if len(pack.AffectedProcs) != 0 {
|
||||
for _, p := range pack.AffectedProcs {
|
||||
if len(p.ListenPortStats) == 0 {
|
||||
data = append(data, []string{"",
|
||||
fmt.Sprintf(" - PID: %s %s, Port: []", p.PID, p.Name)})
|
||||
data = append(data, []string{"", fmt.Sprintf(" - PID: %s %s", p.PID, p.Name)})
|
||||
continue
|
||||
}
|
||||
|
||||
var ports []string
|
||||
@@ -412,8 +412,7 @@ No CVE-IDs are found in updatable packages.
|
||||
wp.Name, p.Version, p.Update, wp.FixedIn, p.Status)})
|
||||
}
|
||||
} else {
|
||||
data = append(data, []string{"WordPress",
|
||||
fmt.Sprintf("%s", wp.Name)})
|
||||
data = append(data, []string{"WordPress", wp.Name})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
|
||||
"github.com/aquasecurity/fanal/analyzer"
|
||||
dio "github.com/aquasecurity/go-dep-parser/pkg/io"
|
||||
debver "github.com/knqyf263/go-deb-version"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/constant"
|
||||
@@ -133,6 +134,10 @@ func (l *base) runningKernel() (release, version string, err error) {
|
||||
if 6 < len(ss) {
|
||||
version = ss[6]
|
||||
}
|
||||
if _, err := debver.NewVersion(version); err != nil {
|
||||
l.log.Warnf("kernel running version is invalid. skip kernel vulnerability detection. actual kernel version: %s, err: %s", version, err)
|
||||
version = ""
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -48,8 +48,7 @@ func detectDebian(c config.ServerInfo) (bool, osTypeInterface, error) {
|
||||
return false, nil, nil
|
||||
}
|
||||
if r.ExitStatus == 255 {
|
||||
deb := newDebian(c) // Panic occur when return value 2 is nil and 3 is non-nil
|
||||
return false, deb, xerrors.Errorf("Unable to connect via SSH. Scan with -vvv option to print SSH debugging messages and check SSH settings. If you have never SSH to the host to be scanned, SSH to the host before scanning in order to add the HostKey. %s@%s port: %s\n%s", c.User, c.Host, c.Port, r)
|
||||
return false, &unknown{base{ServerInfo: c}}, xerrors.Errorf("Unable to connect via SSH. Scan with -vvv option to print SSH debugging messages and check SSH settings.\n%s", r)
|
||||
}
|
||||
logging.Log.Debugf("Not Debian like Linux. %s", r)
|
||||
return false, nil, nil
|
||||
|
||||
@@ -186,10 +186,10 @@ func sshExecExternal(c config.ServerInfo, cmd string, sudo bool) (result execRes
|
||||
return execResult{Error: err}
|
||||
}
|
||||
|
||||
defaultSSHArgs := []string{"-tt"}
|
||||
args := []string{"-tt"}
|
||||
|
||||
if 0 < len(c.SSHConfigPath) {
|
||||
defaultSSHArgs = append(defaultSSHArgs, "-F", c.SSHConfigPath)
|
||||
if c.SSHConfigPath != "" {
|
||||
args = append(args, "-F", c.SSHConfigPath)
|
||||
} else {
|
||||
home, err := homedir.Dir()
|
||||
if err != nil {
|
||||
@@ -200,7 +200,7 @@ func sshExecExternal(c config.ServerInfo, cmd string, sudo bool) (result execRes
|
||||
}
|
||||
controlPath := filepath.Join(home, ".vuls", `controlmaster-%r-`+c.ServerName+`.%p`)
|
||||
|
||||
defaultSSHArgs = append(defaultSSHArgs,
|
||||
args = append(args,
|
||||
"-o", "StrictHostKeyChecking=yes",
|
||||
"-o", "LogLevel=quiet",
|
||||
"-o", "ConnectionAttempts=3",
|
||||
@@ -212,19 +212,22 @@ func sshExecExternal(c config.ServerInfo, cmd string, sudo bool) (result execRes
|
||||
}
|
||||
|
||||
if config.Conf.Vvv {
|
||||
defaultSSHArgs = append(defaultSSHArgs, "-vvv")
|
||||
args = append(args, "-vvv")
|
||||
}
|
||||
|
||||
if len(c.JumpServer) != 0 {
|
||||
defaultSSHArgs = append(defaultSSHArgs, "-J", strings.Join(c.JumpServer, ","))
|
||||
args = append(args, "-J", strings.Join(c.JumpServer, ","))
|
||||
}
|
||||
|
||||
args := append(defaultSSHArgs, fmt.Sprintf("%s@%s", c.User, c.Host))
|
||||
args = append(args, "-p", c.Port)
|
||||
if 0 < len(c.KeyPath) {
|
||||
if c.User != "" {
|
||||
args = append(args, "-l", c.User)
|
||||
}
|
||||
if c.Port != "" {
|
||||
args = append(args, "-p", c.Port)
|
||||
}
|
||||
if c.KeyPath != "" {
|
||||
args = append(args, "-i", c.KeyPath)
|
||||
args = append(args, "-o", "PasswordAuthentication=no")
|
||||
}
|
||||
args = append(args, c.Host)
|
||||
|
||||
cmd = decorateCmd(c, cmd, sudo)
|
||||
cmd = fmt.Sprintf("stty cols 1000; %s", cmd)
|
||||
|
||||
@@ -26,17 +26,17 @@ func detectRedhat(c config.ServerInfo) (bool, osTypeInterface) {
|
||||
fed := newFedora(c)
|
||||
result := releasePattern.FindStringSubmatch(strings.TrimSpace(r.Stdout))
|
||||
if len(result) != 3 {
|
||||
logging.Log.Warnf("Failed to parse Fedora version: %s", r)
|
||||
fed.setErrs([]error{xerrors.Errorf("Failed to parse /etc/fedora-release. r.Stdout: %s", r.Stdout)})
|
||||
return true, fed
|
||||
}
|
||||
release := result[2]
|
||||
ver, err := strconv.Atoi(release)
|
||||
major, err := strconv.Atoi(util.Major(release))
|
||||
if err != nil {
|
||||
logging.Log.Warnf("Failed to parse Fedora version: %s", release)
|
||||
fed.setErrs([]error{xerrors.Errorf("Failed to parse major version from release: %s", release)})
|
||||
return true, fed
|
||||
}
|
||||
if ver < 32 {
|
||||
logging.Log.Warnf("Versions prior to Fedora 32 are not supported, detected version is %s", release)
|
||||
if major < 32 {
|
||||
fed.setErrs([]error{xerrors.Errorf("Failed to init Fedora. err: not supported major version. versions prior to Fedora 32 are not supported, detected version is %s", release)})
|
||||
return true, fed
|
||||
}
|
||||
fed.setDistro(constant.Fedora, release)
|
||||
@@ -48,14 +48,22 @@ func detectRedhat(c config.ServerInfo) (bool, osTypeInterface) {
|
||||
// Need to discover Oracle Linux first, because it provides an
|
||||
// /etc/redhat-release that matches the upstream distribution
|
||||
if r := exec(c, "cat /etc/oracle-release", noSudo); r.isSuccess() {
|
||||
ora := newOracle(c)
|
||||
result := releasePattern.FindStringSubmatch(strings.TrimSpace(r.Stdout))
|
||||
if len(result) != 3 {
|
||||
logging.Log.Warnf("Failed to parse Oracle Linux version: %s", r)
|
||||
return true, newOracle(c)
|
||||
ora.setErrs([]error{xerrors.Errorf("Failed to parse /etc/oracle-release. r.Stdout: %s", r.Stdout)})
|
||||
return true, ora
|
||||
}
|
||||
|
||||
ora := newOracle(c)
|
||||
release := result[2]
|
||||
major, err := strconv.Atoi(util.Major(release))
|
||||
if err != nil {
|
||||
ora.setErrs([]error{xerrors.Errorf("Failed to parse major version from release: %s", release)})
|
||||
return true, ora
|
||||
}
|
||||
if major < 5 {
|
||||
ora.setErrs([]error{xerrors.Errorf("Failed to init Oracle Linux. err: not supported major version. versions prior to Oracle Linux 5 are not supported, detected version is %s", release)})
|
||||
return true, ora
|
||||
}
|
||||
ora.setDistro(constant.Oracle, release)
|
||||
return true, ora
|
||||
}
|
||||
@@ -63,40 +71,60 @@ func detectRedhat(c config.ServerInfo) (bool, osTypeInterface) {
|
||||
|
||||
if r := exec(c, "ls /etc/almalinux-release", noSudo); r.isSuccess() {
|
||||
if r := exec(c, "cat /etc/almalinux-release", noSudo); r.isSuccess() {
|
||||
alma := newAlma(c)
|
||||
result := releasePattern.FindStringSubmatch(strings.TrimSpace(r.Stdout))
|
||||
if len(result) != 3 {
|
||||
logging.Log.Warnf("Failed to parse Alma version: %s", r)
|
||||
return true, newAlma(c)
|
||||
alma.setErrs([]error{xerrors.Errorf("Failed to parse /etc/almalinux-release. r.Stdout: %s", r.Stdout)})
|
||||
return true, alma
|
||||
}
|
||||
|
||||
release := result[2]
|
||||
major, err := strconv.Atoi(util.Major(release))
|
||||
if err != nil {
|
||||
alma.setErrs([]error{xerrors.Errorf("Failed to parse major version from release: %s", release)})
|
||||
return true, alma
|
||||
}
|
||||
if major < 8 {
|
||||
alma.setErrs([]error{xerrors.Errorf("Failed to init AlmaLinux. err: not supported major version. versions prior to AlmaLinux 8 are not supported, detected version is %s", release)})
|
||||
return true, alma
|
||||
}
|
||||
switch strings.ToLower(result[1]) {
|
||||
case "alma", "almalinux":
|
||||
alma := newAlma(c)
|
||||
alma.setDistro(constant.Alma, release)
|
||||
return true, alma
|
||||
default:
|
||||
logging.Log.Warnf("Failed to parse Alma: %s", r)
|
||||
alma.setErrs([]error{xerrors.Errorf("Failed to parse AlmaLinux Name. release: %s", release)})
|
||||
return true, alma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if r := exec(c, "ls /etc/rocky-release", noSudo); r.isSuccess() {
|
||||
if r := exec(c, "cat /etc/rocky-release", noSudo); r.isSuccess() {
|
||||
rocky := newRocky(c)
|
||||
result := releasePattern.FindStringSubmatch(strings.TrimSpace(r.Stdout))
|
||||
if len(result) != 3 {
|
||||
logging.Log.Warnf("Failed to parse Rocky version: %s", r)
|
||||
return true, newRocky(c)
|
||||
rocky.setErrs([]error{xerrors.Errorf("Failed to parse /etc/rocky-release. r.Stdout: %s", r.Stdout)})
|
||||
return true, rocky
|
||||
}
|
||||
|
||||
release := result[2]
|
||||
major, err := strconv.Atoi(util.Major(release))
|
||||
if err != nil {
|
||||
rocky.setErrs([]error{xerrors.Errorf("Failed to parse major version from release: %s", release)})
|
||||
return true, rocky
|
||||
}
|
||||
if major < 8 {
|
||||
rocky.setErrs([]error{xerrors.Errorf("Failed to init Rocky Linux. err: not supported major version. versions prior to Rocky Linux 8 are not supported, detected version is %s", release)})
|
||||
return true, rocky
|
||||
}
|
||||
switch strings.ToLower(result[1]) {
|
||||
case "rocky", "rocky linux":
|
||||
rocky := newRocky(c)
|
||||
rocky.setDistro(constant.Rocky, release)
|
||||
return true, rocky
|
||||
default:
|
||||
logging.Log.Warnf("Failed to parse Rocky: %s", r)
|
||||
rocky.setErrs([]error{xerrors.Errorf("Failed to parse Rocky Linux Name. release: %s", release)})
|
||||
return true, rocky
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -107,30 +135,55 @@ func detectRedhat(c config.ServerInfo) (bool, osTypeInterface) {
|
||||
if r := exec(c, "cat /etc/centos-release", noSudo); r.isSuccess() {
|
||||
result := releasePattern.FindStringSubmatch(strings.TrimSpace(r.Stdout))
|
||||
if len(result) != 3 {
|
||||
logging.Log.Warnf("Failed to parse CentOS version: %s", r)
|
||||
return true, newCentOS(c)
|
||||
cent := newCentOS(c)
|
||||
cent.setErrs([]error{xerrors.Errorf("Failed to parse /etc/centos-release. r.Stdout: %s", r.Stdout)})
|
||||
return true, cent
|
||||
}
|
||||
|
||||
release := result[2]
|
||||
major, err := strconv.Atoi(util.Major(release))
|
||||
if err != nil {
|
||||
cent := newCentOS(c)
|
||||
cent.setErrs([]error{xerrors.Errorf("Failed to parse major version from release: %s", release)})
|
||||
return true, cent
|
||||
}
|
||||
switch strings.ToLower(result[1]) {
|
||||
case "centos", "centos linux":
|
||||
cent := newCentOS(c)
|
||||
if major < 5 {
|
||||
cent.setErrs([]error{xerrors.Errorf("Failed to init CentOS. err: not supported major version. versions prior to CentOS 5 are not supported, detected version is %s", release)})
|
||||
return true, cent
|
||||
}
|
||||
cent.setDistro(constant.CentOS, release)
|
||||
return true, cent
|
||||
case "centos stream":
|
||||
cent := newCentOS(c)
|
||||
if major < 8 {
|
||||
cent.setErrs([]error{xerrors.Errorf("Failed to init CentOS Stream. err: not supported major version. versions prior to CentOS Stream 8 are not supported, detected version is %s", release)})
|
||||
return true, cent
|
||||
}
|
||||
cent.setDistro(constant.CentOS, fmt.Sprintf("stream%s", release))
|
||||
return true, cent
|
||||
case "alma", "almalinux":
|
||||
alma := newAlma(c)
|
||||
if major < 8 {
|
||||
alma.setErrs([]error{xerrors.Errorf("Failed to init AlmaLinux. err: not supported major version. versions prior to AlmaLinux 8 are not supported, detected version is %s", release)})
|
||||
return true, alma
|
||||
}
|
||||
alma.setDistro(constant.Alma, release)
|
||||
return true, alma
|
||||
case "rocky", "rocky linux":
|
||||
rocky := newRocky(c)
|
||||
if major < 8 {
|
||||
rocky.setErrs([]error{xerrors.Errorf("Failed to init Rocky Linux. err: not supported major version. versions prior to Rocky Linux 8 are not supported, detected version is %s", release)})
|
||||
return true, rocky
|
||||
}
|
||||
rocky.setDistro(constant.Rocky, release)
|
||||
return true, rocky
|
||||
default:
|
||||
logging.Log.Warnf("Failed to parse CentOS: %s", r)
|
||||
cent := newCentOS(c)
|
||||
cent.setErrs([]error{xerrors.Errorf("Failed to parse CentOS Name. release: %s", release)})
|
||||
return true, cent
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,47 +192,74 @@ func detectRedhat(c config.ServerInfo) (bool, osTypeInterface) {
|
||||
// https://www.rackaid.com/blog/how-to-determine-centos-or-red-hat-version/
|
||||
// e.g.
|
||||
// $ cat /etc/redhat-release
|
||||
// Red Hat Enterprise Linux Server release 6.8 (Santiago)
|
||||
// CentOS release 6.5 (Final)
|
||||
// CentOS Stream release 8
|
||||
// AlmaLinux release 8.5 (Arctic Sphynx)
|
||||
// Rocky Linux release 8.5 (Green Obsidian)
|
||||
// Fedora release 35 (Thirty Five)
|
||||
if r := exec(c, "cat /etc/redhat-release", noSudo); r.isSuccess() {
|
||||
result := releasePattern.FindStringSubmatch(strings.TrimSpace(r.Stdout))
|
||||
if len(result) != 3 {
|
||||
logging.Log.Warnf("Failed to parse RedHat/CentOS version: %s", r)
|
||||
return true, newCentOS(c)
|
||||
rhel := newRHEL(c)
|
||||
rhel.setErrs([]error{xerrors.Errorf("Failed to parse /etc/redhat-release. r.Stdout: %s", r.Stdout)})
|
||||
return true, rhel
|
||||
}
|
||||
|
||||
release := result[2]
|
||||
ver, err := strconv.Atoi(release)
|
||||
major, err := strconv.Atoi(util.Major(release))
|
||||
if err != nil {
|
||||
logging.Log.Warnf("Failed to parse RedHat/CentOS version number: %s", release)
|
||||
return true, newCentOS(c)
|
||||
}
|
||||
if ver < 5 {
|
||||
logging.Log.Warnf("Versions prior to RedHat/CentOS 5 are not supported, detected version is %s", release)
|
||||
rhel := newRHEL(c)
|
||||
rhel.setErrs([]error{xerrors.Errorf("Failed to parse major version from release: %s", release)})
|
||||
return true, rhel
|
||||
}
|
||||
switch strings.ToLower(result[1]) {
|
||||
case "fedora":
|
||||
fed := newFedora(c)
|
||||
if major < 32 {
|
||||
fed.setErrs([]error{xerrors.Errorf("Failed to init Fedora. err: not supported major version. versions prior to Fedora 32 are not supported, detected version is %s", release)})
|
||||
return true, fed
|
||||
}
|
||||
fed.setDistro(constant.Fedora, release)
|
||||
return true, fed
|
||||
case "centos", "centos linux":
|
||||
cent := newCentOS(c)
|
||||
if major < 5 {
|
||||
cent.setErrs([]error{xerrors.Errorf("Failed to init CentOS. err: not supported major version. versions prior to CentOS 5 are not supported, detected version is %s", release)})
|
||||
return true, cent
|
||||
}
|
||||
cent.setDistro(constant.CentOS, release)
|
||||
return true, cent
|
||||
case "centos stream":
|
||||
cent := newCentOS(c)
|
||||
if major < 8 {
|
||||
cent.setErrs([]error{xerrors.Errorf("Failed to init CentOS Stream. err: not supported major version. versions prior to CentOS Stream 8 are not supported, detected version is %s", release)})
|
||||
return true, cent
|
||||
}
|
||||
cent.setDistro(constant.CentOS, fmt.Sprintf("stream%s", release))
|
||||
return true, cent
|
||||
case "alma", "almalinux":
|
||||
alma := newAlma(c)
|
||||
if major < 8 {
|
||||
alma.setErrs([]error{xerrors.Errorf("Failed to init AlmaLinux. err: not supported major version. versions prior to AlmaLinux 8 are not supported, detected version is %s", release)})
|
||||
return true, alma
|
||||
}
|
||||
alma.setDistro(constant.Alma, release)
|
||||
return true, alma
|
||||
case "rocky", "rocky linux":
|
||||
rocky := newRocky(c)
|
||||
if major < 8 {
|
||||
rocky.setErrs([]error{xerrors.Errorf("Failed to init Rocky Linux. err: not supported major version. versions prior to Rocky Linux 8 are not supported, detected version is %s", release)})
|
||||
return true, rocky
|
||||
}
|
||||
rocky.setDistro(constant.Rocky, release)
|
||||
return true, rocky
|
||||
default:
|
||||
// RHEL
|
||||
rhel := newRHEL(c)
|
||||
if major < 5 {
|
||||
rhel.setErrs([]error{xerrors.Errorf("Failed to init RedHat Enterprise Linux. err: not supported major version. versions prior to RedHat Enterprise Linux 5 are not supported, detected version is %s", release)})
|
||||
return true, rhel
|
||||
}
|
||||
rhel.setDistro(constant.RedHat, release)
|
||||
return true, rhel
|
||||
}
|
||||
@@ -534,12 +614,7 @@ func (o *redhatBase) parseUpdatablePacksLine(line string) (models.Package, error
|
||||
|
||||
func (o *redhatBase) isExecYumPS() bool {
|
||||
switch o.Distro.Family {
|
||||
case constant.Oracle,
|
||||
constant.OpenSUSE,
|
||||
constant.OpenSUSELeap,
|
||||
constant.SUSEEnterpriseServer,
|
||||
constant.SUSEEnterpriseDesktop,
|
||||
constant.SUSEOpenstackCloud:
|
||||
case constant.Oracle:
|
||||
return false
|
||||
}
|
||||
return !o.getServerInfo().Mode.IsFast()
|
||||
@@ -547,21 +622,33 @@ func (o *redhatBase) isExecYumPS() bool {
|
||||
|
||||
func (o *redhatBase) isExecNeedsRestarting() bool {
|
||||
switch o.Distro.Family {
|
||||
case constant.OpenSUSE,
|
||||
constant.OpenSUSELeap,
|
||||
constant.SUSEEnterpriseServer,
|
||||
constant.SUSEEnterpriseDesktop,
|
||||
constant.SUSEOpenstackCloud:
|
||||
// TODO zypper ps
|
||||
// https://github.com/future-architect/vuls/issues/696
|
||||
case constant.OpenSUSE, constant.OpenSUSELeap, constant.SUSEEnterpriseServer, constant.SUSEEnterpriseDesktop:
|
||||
if o.getServerInfo().Mode.IsOffline() {
|
||||
return false
|
||||
} else if o.getServerInfo().Mode.IsFastRoot() || o.getServerInfo().Mode.IsDeep() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
case constant.RedHat, constant.CentOS, constant.Alma, constant.Rocky, constant.Oracle, constant.Fedora:
|
||||
case constant.RedHat, constant.CentOS, constant.Alma, constant.Rocky, constant.Oracle:
|
||||
majorVersion, err := o.Distro.MajorVersion()
|
||||
if err != nil || majorVersion < 6 {
|
||||
o.log.Errorf("Not implemented yet: %s, err: %+v", o.Distro, err)
|
||||
return false
|
||||
}
|
||||
|
||||
if o.getServerInfo().Mode.IsOffline() {
|
||||
return false
|
||||
} else if o.getServerInfo().Mode.IsFastRoot() || o.getServerInfo().Mode.IsDeep() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
case constant.Fedora:
|
||||
majorVersion, err := o.Distro.MajorVersion()
|
||||
if err != nil || majorVersion < 13 {
|
||||
o.log.Errorf("Not implemented yet: %s, err: %+v", o.Distro, err)
|
||||
return false
|
||||
}
|
||||
|
||||
if o.getServerInfo().Mode.IsOffline() {
|
||||
return false
|
||||
} else if o.getServerInfo().Mode.IsFastRoot() ||
|
||||
@@ -571,10 +658,7 @@ func (o *redhatBase) isExecNeedsRestarting() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
if o.getServerInfo().Mode.IsFast() {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return !o.getServerInfo().Mode.IsFast()
|
||||
}
|
||||
|
||||
func (o *redhatBase) needsRestarting() error {
|
||||
@@ -702,7 +786,14 @@ func (o *redhatBase) rpmQa() string {
|
||||
const old = `rpm -qa --queryformat "%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH}\n"`
|
||||
const new = `rpm -qa --queryformat "%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH}\n"`
|
||||
switch o.Distro.Family {
|
||||
case constant.SUSEEnterpriseServer:
|
||||
case constant.OpenSUSE:
|
||||
if o.Distro.Release == "tumbleweed" {
|
||||
return new
|
||||
}
|
||||
return old
|
||||
case constant.OpenSUSELeap:
|
||||
return new
|
||||
case constant.SUSEEnterpriseServer, constant.SUSEEnterpriseDesktop:
|
||||
if v, _ := o.Distro.MajorVersion(); v < 12 {
|
||||
return old
|
||||
}
|
||||
@@ -719,7 +810,14 @@ func (o *redhatBase) rpmQf() string {
|
||||
const old = `rpm -qf --queryformat "%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH}\n" `
|
||||
const new = `rpm -qf --queryformat "%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH}\n" `
|
||||
switch o.Distro.Family {
|
||||
case constant.SUSEEnterpriseServer:
|
||||
case constant.OpenSUSE:
|
||||
if o.Distro.Release == "tumbleweed" {
|
||||
return new
|
||||
}
|
||||
return old
|
||||
case constant.OpenSUSELeap:
|
||||
return new
|
||||
case constant.SUSEEnterpriseServer, constant.SUSEEnterpriseDesktop:
|
||||
if v, _ := o.Distro.MajorVersion(); v < 12 {
|
||||
return old
|
||||
}
|
||||
|
||||
@@ -5,15 +5,19 @@ import (
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
ex "os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
debver "github.com/knqyf263/go-deb-version"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/future-architect/vuls/cache"
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/constant"
|
||||
"github.com/future-architect/vuls/logging"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -23,10 +27,9 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
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")
|
||||
errOSFamilyHeader = xerrors.New("X-Vuls-OS-Family header is required")
|
||||
errOSReleaseHeader = xerrors.New("X-Vuls-OS-Release header is required")
|
||||
errServerNameHeader = xerrors.New("X-Vuls-Server-Name header is required")
|
||||
)
|
||||
|
||||
var servers, errServers []osTypeInterface
|
||||
@@ -162,8 +165,15 @@ func ViaHTTP(header http.Header, body string, toLocalFile bool) (models.ScanResu
|
||||
}
|
||||
|
||||
kernelVersion := header.Get("X-Vuls-Kernel-Version")
|
||||
if family == constant.Debian && kernelVersion == "" {
|
||||
return models.ScanResult{}, errKernelVersionHeader
|
||||
if family == constant.Debian {
|
||||
if kernelVersion == "" {
|
||||
logging.Log.Warn("X-Vuls-Kernel-Version is empty. skip kernel vulnerability detection.")
|
||||
} else {
|
||||
if _, err := debver.NewVersion(kernelVersion); err != nil {
|
||||
logging.Log.Warnf("X-Vuls-Kernel-Version is invalid. skip kernel vulnerability detection. actual kernelVersion: %s, err: %s", kernelVersion, err)
|
||||
kernelVersion = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
serverName := header.Get("X-Vuls-Server-Name")
|
||||
@@ -278,6 +288,12 @@ func (s Scanner) detectServerOSes() (servers, errServers []osTypeInterface) {
|
||||
logging.Log.Debugf("Panic: %s on %s", p, srv.ServerName)
|
||||
}
|
||||
}()
|
||||
if err := validateSSHConfig(&srv); err != nil {
|
||||
checkOS := unknown{base{ServerInfo: srv}}
|
||||
checkOS.setErrs([]error{err})
|
||||
osTypeChan <- &checkOS
|
||||
return
|
||||
}
|
||||
osTypeChan <- s.detectOS(srv)
|
||||
}(target)
|
||||
}
|
||||
@@ -288,12 +304,10 @@ func (s Scanner) detectServerOSes() (servers, errServers []osTypeInterface) {
|
||||
case res := <-osTypeChan:
|
||||
if 0 < len(res.getErrs()) {
|
||||
errServers = append(errServers, res)
|
||||
logging.Log.Errorf("(%d/%d) Failed: %s, err: %+v",
|
||||
i+1, len(s.Targets), res.getServerInfo().ServerName, res.getErrs())
|
||||
logging.Log.Errorf("(%d/%d) Failed: %s, err: %+v", i+1, len(s.Targets), res.getServerInfo().ServerName, res.getErrs())
|
||||
} else {
|
||||
servers = append(servers, res)
|
||||
logging.Log.Infof("(%d/%d) Detected: %s: %s",
|
||||
i+1, len(s.Targets), res.getServerInfo().ServerName, res.getDistro())
|
||||
logging.Log.Infof("(%d/%d) Detected: %s: %s", i+1, len(s.Targets), res.getServerInfo().ServerName, res.getDistro())
|
||||
}
|
||||
case <-timeout:
|
||||
msg := "Timed out while detecting servers"
|
||||
@@ -319,6 +333,132 @@ func (s Scanner) detectServerOSes() (servers, errServers []osTypeInterface) {
|
||||
return
|
||||
}
|
||||
|
||||
func validateSSHConfig(c *config.ServerInfo) error {
|
||||
if isLocalExec(c.Port, c.Host) || c.Type == constant.ServerTypePseudo {
|
||||
return nil
|
||||
}
|
||||
|
||||
logging.Log.Debugf("Validating SSH Settings for Server:%s ...", c.GetServerName())
|
||||
|
||||
sshBinaryPath, err := ex.LookPath("ssh")
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to lookup ssh binary path. err: %w", err)
|
||||
}
|
||||
sshKeygenBinaryPath, err := ex.LookPath("ssh-keygen")
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to lookup ssh-keygen binary path. err: %w", err)
|
||||
}
|
||||
|
||||
sshConfigCmd := []string{sshBinaryPath, "-G"}
|
||||
if c.SSHConfigPath != "" {
|
||||
sshConfigCmd = append(sshConfigCmd, "-F", c.SSHConfigPath)
|
||||
}
|
||||
if c.Port != "" {
|
||||
sshConfigCmd = append(sshConfigCmd, "-p", c.Port)
|
||||
}
|
||||
if c.User != "" {
|
||||
sshConfigCmd = append(sshConfigCmd, "-l", c.User)
|
||||
}
|
||||
if len(c.JumpServer) > 0 {
|
||||
sshConfigCmd = append(sshConfigCmd, "-J", strings.Join(c.JumpServer, ","))
|
||||
}
|
||||
sshConfigCmd = append(sshConfigCmd, c.Host)
|
||||
cmd := strings.Join(sshConfigCmd, " ")
|
||||
logging.Log.Debugf("Executing... %s", strings.Replace(cmd, "\n", "", -1))
|
||||
r := localExec(*c, cmd, noSudo)
|
||||
if !r.isSuccess() {
|
||||
return xerrors.Errorf("Failed to print SSH configuration. err: %w", r.Error)
|
||||
}
|
||||
|
||||
var (
|
||||
hostname string
|
||||
strictHostKeyChecking string
|
||||
globalKnownHosts string
|
||||
userKnownHosts string
|
||||
proxyCommand string
|
||||
proxyJump string
|
||||
)
|
||||
for _, line := range strings.Split(r.Stdout, "\n") {
|
||||
switch {
|
||||
case strings.HasPrefix(line, "user "):
|
||||
user := strings.TrimPrefix(line, "user ")
|
||||
logging.Log.Debugf("Setting SSH User:%s for Server:%s ...", user, c.GetServerName())
|
||||
c.User = user
|
||||
case strings.HasPrefix(line, "hostname "):
|
||||
hostname = strings.TrimPrefix(line, "hostname ")
|
||||
case strings.HasPrefix(line, "port "):
|
||||
port := strings.TrimPrefix(line, "port ")
|
||||
logging.Log.Debugf("Setting SSH Port:%s for Server:%s ...", port, c.GetServerName())
|
||||
c.Port = port
|
||||
case strings.HasPrefix(line, "stricthostkeychecking "):
|
||||
strictHostKeyChecking = strings.TrimPrefix(line, "stricthostkeychecking ")
|
||||
case strings.HasPrefix(line, "globalknownhostsfile "):
|
||||
globalKnownHosts = strings.TrimPrefix(line, "globalknownhostsfile ")
|
||||
case strings.HasPrefix(line, "userknownhostsfile "):
|
||||
userKnownHosts = strings.TrimPrefix(line, "userknownhostsfile ")
|
||||
case strings.HasPrefix(line, "proxycommand "):
|
||||
proxyCommand = strings.TrimPrefix(line, "proxycommand ")
|
||||
case strings.HasPrefix(line, "proxyjump "):
|
||||
proxyJump = strings.TrimPrefix(line, "proxyjump ")
|
||||
}
|
||||
}
|
||||
if c.User == "" || c.Port == "" {
|
||||
return xerrors.New("Failed to find User or Port setting. Please check the User or Port settings for SSH")
|
||||
}
|
||||
if strictHostKeyChecking == "false" || proxyCommand != "" || proxyJump != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
logging.Log.Debugf("Checking if the host's public key is in known_hosts...")
|
||||
knownHostsPaths := []string{}
|
||||
for _, knownHosts := range []string{userKnownHosts, globalKnownHosts} {
|
||||
for _, knownHost := range strings.Split(knownHosts, " ") {
|
||||
if knownHost != "" && knownHost != "/dev/null" {
|
||||
knownHostsPaths = append(knownHostsPaths, knownHost)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(knownHostsPaths) == 0 {
|
||||
return xerrors.New("Failed to find any known_hosts to use. Please check the UserKnownHostsFile and GlobalKnownHostsFile settings for SSH")
|
||||
}
|
||||
|
||||
for _, knownHosts := range knownHostsPaths {
|
||||
if c.Port != "" && c.Port != "22" {
|
||||
cmd := fmt.Sprintf("%s -F %s -f %s", sshKeygenBinaryPath, fmt.Sprintf("\"[%s]:%s\"", hostname, c.Port), knownHosts)
|
||||
logging.Log.Debugf("Executing... %s", strings.Replace(cmd, "\n", "", -1))
|
||||
if r := localExec(*c, cmd, noSudo); r.isSuccess() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
cmd := fmt.Sprintf("%s -F %s -f %s", sshKeygenBinaryPath, hostname, knownHosts)
|
||||
logging.Log.Debugf("Executing... %s", strings.Replace(cmd, "\n", "", -1))
|
||||
if r := localExec(*c, cmd, noSudo); r.isSuccess() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
sshConnArgs := []string{}
|
||||
sshKeyScanArgs := []string{"-H"}
|
||||
if c.SSHConfigPath != "" {
|
||||
sshConnArgs = append(sshConnArgs, "-F", c.SSHConfigPath)
|
||||
}
|
||||
if c.KeyPath != "" {
|
||||
sshConnArgs = append(sshConnArgs, "-i", c.KeyPath)
|
||||
}
|
||||
if c.Port != "" {
|
||||
sshConnArgs = append(sshConnArgs, "-p", c.Port)
|
||||
sshKeyScanArgs = append(sshKeyScanArgs, "-p", c.Port)
|
||||
}
|
||||
if c.User != "" {
|
||||
sshConnArgs = append(sshConnArgs, "-l", c.User)
|
||||
}
|
||||
sshConnArgs = append(sshConnArgs, c.Host)
|
||||
sshKeyScanArgs = append(sshKeyScanArgs, fmt.Sprintf("%s >> %s", hostname, knownHostsPaths[0]))
|
||||
sshConnCmd := fmt.Sprintf("ssh %s", strings.Join(sshConnArgs, " "))
|
||||
sshKeyScancmd := fmt.Sprintf("ssh-keyscan %s", strings.Join(sshKeyScanArgs, " "))
|
||||
return xerrors.Errorf("Failed to find the host in known_hosts. Plaese exec `$ %s` or `$ %s`", sshConnCmd, sshKeyScancmd)
|
||||
}
|
||||
|
||||
func (s Scanner) detectContainerOSes(hosts []osTypeInterface) (actives, inactives []osTypeInterface) {
|
||||
logging.Log.Info("Detecting OS of containers... ")
|
||||
osTypesChan := make(chan []osTypeInterface, len(hosts))
|
||||
|
||||
@@ -34,14 +34,6 @@ func TestViaHTTP(t *testing.T) {
|
||||
},
|
||||
wantErr: errOSReleaseHeader,
|
||||
},
|
||||
{
|
||||
header: map[string]string{
|
||||
"X-Vuls-OS-Family": "debian",
|
||||
"X-Vuls-OS-Release": "8",
|
||||
"X-Vuls-Kernel-Release": "2.6.32-695.20.3.el6.x86_64",
|
||||
},
|
||||
wantErr: errKernelVersionHeader,
|
||||
},
|
||||
{
|
||||
header: map[string]string{
|
||||
"X-Vuls-OS-Family": "centos",
|
||||
@@ -95,6 +87,22 @@ func TestViaHTTP(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
header: map[string]string{
|
||||
"X-Vuls-OS-Family": "debian",
|
||||
"X-Vuls-OS-Release": "8.10",
|
||||
"X-Vuls-Kernel-Release": "3.16.0-4-amd64",
|
||||
},
|
||||
body: "",
|
||||
expectedResult: models.ScanResult{
|
||||
Family: "debian",
|
||||
Release: "8.10",
|
||||
RunningKernel: models.Kernel{
|
||||
Release: "3.16.0-4-amd64",
|
||||
Version: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
201
scanner/suse.go
201
scanner/suse.go
@@ -19,7 +19,7 @@ type suse struct {
|
||||
redhatBase
|
||||
}
|
||||
|
||||
// NewRedhat is constructor
|
||||
// newSUSE is constructor
|
||||
func newSUSE(c config.ServerInfo) *suse {
|
||||
r := &suse{
|
||||
redhatBase: redhatBase{
|
||||
@@ -43,6 +43,10 @@ func detectSUSE(c config.ServerInfo) (bool, osTypeInterface) {
|
||||
if r := exec(c, "cat /etc/os-release", noSudo); r.isSuccess() {
|
||||
s := newSUSE(c)
|
||||
name, ver := s.parseOSRelease(r.Stdout)
|
||||
if name == "" || ver == "" {
|
||||
s.setErrs([]error{xerrors.Errorf("Failed to parse /etc/os-release: %s", r.Stdout)})
|
||||
return true, s
|
||||
}
|
||||
s.setDistro(name, ver)
|
||||
return true, s
|
||||
}
|
||||
@@ -50,11 +54,10 @@ func detectSUSE(c config.ServerInfo) (bool, osTypeInterface) {
|
||||
} else if r := exec(c, "ls /etc/SuSE-release", noSudo); r.isSuccess() {
|
||||
if r := exec(c, "zypper -V", noSudo); r.isSuccess() {
|
||||
if r := exec(c, "cat /etc/SuSE-release", noSudo); r.isSuccess() {
|
||||
s := newSUSE(c)
|
||||
re := regexp.MustCompile(`openSUSE (\d+\.\d+|\d+)`)
|
||||
result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
|
||||
if len(result) == 2 {
|
||||
//TODO check opensuse or opensuse.leap
|
||||
s := newSUSE(c)
|
||||
s.setDistro(constant.OpenSUSE, result[1])
|
||||
return true, s
|
||||
}
|
||||
@@ -66,14 +69,12 @@ func detectSUSE(c config.ServerInfo) (bool, osTypeInterface) {
|
||||
re = regexp.MustCompile(`PATCHLEVEL = (\d+)`)
|
||||
result = re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
|
||||
if len(result) == 2 {
|
||||
s := newSUSE(c)
|
||||
s.setDistro(constant.SUSEEnterpriseServer,
|
||||
fmt.Sprintf("%s.%s", version, result[1]))
|
||||
s.setDistro(constant.SUSEEnterpriseServer, fmt.Sprintf("%s.%s", version, result[1]))
|
||||
return true, s
|
||||
}
|
||||
}
|
||||
logging.Log.Warnf("Failed to parse SUSE Linux version: %s", r)
|
||||
return true, newSUSE(c)
|
||||
s.setErrs([]error{xerrors.Errorf("Failed to parse /etc/SuSE-release: %s", r.Stdout)})
|
||||
return true, s
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,23 +83,24 @@ func detectSUSE(c config.ServerInfo) (bool, osTypeInterface) {
|
||||
}
|
||||
|
||||
func (o *suse) parseOSRelease(content string) (name string, ver string) {
|
||||
if strings.Contains(content, "ID=opensuse") {
|
||||
//TODO check opensuse or opensuse.leap
|
||||
if strings.Contains(content, `CPE_NAME="cpe:/o:opensuse:opensuse`) {
|
||||
name = constant.OpenSUSE
|
||||
} else if strings.Contains(content, `NAME="SLES"`) {
|
||||
name = constant.SUSEEnterpriseServer
|
||||
} else if strings.Contains(content, `NAME="SLES_SAP"`) {
|
||||
} else if strings.Contains(content, `CPE_NAME="cpe:/o:opensuse:tumbleweed`) {
|
||||
return constant.OpenSUSE, "tumbleweed"
|
||||
} else if strings.Contains(content, `CPE_NAME="cpe:/o:opensuse:leap`) {
|
||||
name = constant.OpenSUSELeap
|
||||
} else if strings.Contains(content, `CPE_NAME="cpe:/o:suse:sles`) {
|
||||
name = constant.SUSEEnterpriseServer
|
||||
} else if strings.Contains(content, `CPE_NAME="cpe:/o:suse:sled`) {
|
||||
name = constant.SUSEEnterpriseDesktop
|
||||
} else {
|
||||
logging.Log.Warnf("Failed to parse SUSE edition: %s", content)
|
||||
return "unknown", "unknown"
|
||||
return "", ""
|
||||
}
|
||||
|
||||
re := regexp.MustCompile(`VERSION_ID=\"(.+)\"`)
|
||||
result := re.FindStringSubmatch(strings.TrimSpace(content))
|
||||
if len(result) != 2 {
|
||||
logging.Log.Warnf("Failed to parse SUSE Linux version: %s", content)
|
||||
return "unknown", "unknown"
|
||||
return "", ""
|
||||
}
|
||||
return name, result[1]
|
||||
}
|
||||
@@ -108,14 +110,60 @@ func (o *suse) checkScanMode() error {
|
||||
}
|
||||
|
||||
func (o *suse) checkDeps() error {
|
||||
o.log.Infof("Dependencies... No need")
|
||||
return nil
|
||||
if o.getServerInfo().Mode.IsFast() {
|
||||
return o.execCheckDeps(o.depsFast())
|
||||
} else if o.getServerInfo().Mode.IsFastRoot() {
|
||||
return o.execCheckDeps(o.depsFastRoot())
|
||||
} else if o.getServerInfo().Mode.IsDeep() {
|
||||
return o.execCheckDeps(o.depsDeep())
|
||||
}
|
||||
return xerrors.New("Unknown scan mode")
|
||||
}
|
||||
|
||||
func (o *suse) depsFast() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (o *suse) depsFastRoot() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (o *suse) depsDeep() []string {
|
||||
return o.depsFastRoot()
|
||||
}
|
||||
|
||||
func (o *suse) checkIfSudoNoPasswd() error {
|
||||
// SUSE doesn't need root privilege
|
||||
o.log.Infof("sudo ... No need")
|
||||
return nil
|
||||
if o.getServerInfo().Mode.IsFast() {
|
||||
return o.execCheckIfSudoNoPasswd(o.sudoNoPasswdCmdsFast())
|
||||
} else if o.getServerInfo().Mode.IsFastRoot() {
|
||||
return o.execCheckIfSudoNoPasswd(o.sudoNoPasswdCmdsFastRoot())
|
||||
} else {
|
||||
return o.execCheckIfSudoNoPasswd(o.sudoNoPasswdCmdsDeep())
|
||||
}
|
||||
}
|
||||
|
||||
func (o *suse) sudoNoPasswdCmdsFast() []cmd {
|
||||
return []cmd{}
|
||||
}
|
||||
|
||||
func (o *suse) sudoNoPasswdCmdsDeep() []cmd {
|
||||
return o.sudoNoPasswdCmdsFastRoot()
|
||||
}
|
||||
|
||||
func (o *suse) sudoNoPasswdCmdsFastRoot() []cmd {
|
||||
if !o.ServerInfo.IsContainer() {
|
||||
return []cmd{
|
||||
{"zypper ps -s", exitStatusZero},
|
||||
{"which which", exitStatusZero},
|
||||
{"stat /proc/1/exe", exitStatusZero},
|
||||
{"ls -l /proc/1/exe", exitStatusZero},
|
||||
{"cat /proc/1/maps", exitStatusZero},
|
||||
{"lsof -i -P -n", exitStatusZero},
|
||||
}
|
||||
}
|
||||
return []cmd{
|
||||
{"zypper ps -s", exitStatusZero},
|
||||
}
|
||||
}
|
||||
|
||||
func (o *suse) scanPackages() error {
|
||||
@@ -176,13 +224,14 @@ func (o *suse) scanUpdatablePackages() (models.Packages, error) {
|
||||
return o.parseZypperLULines(r.Stdout)
|
||||
}
|
||||
|
||||
var warnRepoPattern = regexp.MustCompile(`Warning: Repository '.+' appears to be outdated\. Consider using a different mirror or server\.`)
|
||||
|
||||
func (o *suse) parseZypperLULines(stdout string) (models.Packages, error) {
|
||||
updatables := models.Packages{}
|
||||
scanner := bufio.NewScanner(strings.NewReader(stdout))
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if strings.Index(line, "S | Repository") != -1 ||
|
||||
strings.Index(line, "--+----------------") != -1 {
|
||||
if strings.Contains(line, "S | Repository") || strings.Contains(line, "--+----------------") || warnRepoPattern.MatchString(line) {
|
||||
continue
|
||||
}
|
||||
pack, err := o.parseZypperLUOneLine(line)
|
||||
@@ -213,3 +262,107 @@ func (o *suse) hasZypperColorOption() bool {
|
||||
r := o.exec(util.PrependProxyEnv(cmd), noSudo)
|
||||
return len(r.Stdout) > 0
|
||||
}
|
||||
|
||||
func (o *suse) postScan() error {
|
||||
if o.isExecYumPS() {
|
||||
if err := o.pkgPs(o.getOwnerPkgs); err != nil {
|
||||
err = xerrors.Errorf("Failed to execute zypper-ps: %w", err)
|
||||
o.log.Warnf("err: %+v", err)
|
||||
o.warns = append(o.warns, err)
|
||||
// Only warning this error
|
||||
}
|
||||
}
|
||||
|
||||
if o.isExecNeedsRestarting() {
|
||||
if err := o.needsRestarting(); err != nil {
|
||||
err = xerrors.Errorf("Failed to execute need-restarting: %w", err)
|
||||
o.log.Warnf("err: %+v", err)
|
||||
o.warns = append(o.warns, err)
|
||||
// Only warning this error
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *suse) needsRestarting() error {
|
||||
initName, err := o.detectInitSystem()
|
||||
if err != nil {
|
||||
o.log.Warn(err)
|
||||
// continue scanning
|
||||
}
|
||||
|
||||
cmd := "LANGUAGE=en_US.UTF-8 zypper ps -s"
|
||||
r := o.exec(cmd, sudo)
|
||||
if !r.isSuccess() {
|
||||
return xerrors.Errorf("Failed to SSH: %w", r)
|
||||
}
|
||||
procs := o.parseNeedsRestarting(r.Stdout)
|
||||
for _, proc := range procs {
|
||||
//TODO refactor
|
||||
fqpn, err := o.procPathToFQPN(proc.Path)
|
||||
if err != nil {
|
||||
o.log.Warnf("Failed to detect a package name of need restarting process from the command path: %s, %s",
|
||||
proc.Path, err)
|
||||
continue
|
||||
}
|
||||
pack, err := o.Packages.FindByFQPN(fqpn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if initName == systemd {
|
||||
name, err := o.detectServiceName(proc.PID)
|
||||
if err != nil {
|
||||
o.log.Warn(err)
|
||||
// continue scanning
|
||||
}
|
||||
proc.ServiceName = name
|
||||
proc.InitSystem = systemd
|
||||
}
|
||||
pack.NeedRestartProcs = append(pack.NeedRestartProcs, proc)
|
||||
o.Packages[pack.Name] = *pack
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *suse) parseNeedsRestarting(stdout string) []models.NeedRestartProcess {
|
||||
procs := []models.NeedRestartProcess{}
|
||||
|
||||
// PID | PPID | UID | User | Command | Service
|
||||
// ----+------+-----+------+---------+-----------
|
||||
// 9 | 7 | 0 | root | bash | containerd
|
||||
// 53 | 9 | 0 | root | zypper | containerd
|
||||
// 55 | 53 | 0 | root | lsof |
|
||||
|
||||
scanner := bufio.NewScanner(strings.NewReader(stdout))
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
ss := strings.Split(line, " | ")
|
||||
if len(ss) < 6 {
|
||||
continue
|
||||
}
|
||||
pid := strings.TrimSpace(ss[0])
|
||||
if strings.HasPrefix(pid, "PID") {
|
||||
continue
|
||||
}
|
||||
// https://unix.stackexchange.com/a/419375
|
||||
if pid == "1" {
|
||||
continue
|
||||
}
|
||||
|
||||
cmd := strings.TrimSpace(ss[4])
|
||||
whichCmd := fmt.Sprintf("LANGUAGE=en_US.UTF-8 which %s", cmd)
|
||||
r := o.exec(whichCmd, sudo)
|
||||
if !r.isSuccess() {
|
||||
o.log.Debugf("Failed to exec which %s: %s", cmd, r)
|
||||
continue
|
||||
}
|
||||
path := strings.TrimSpace(r.Stdout)
|
||||
|
||||
procs = append(procs, models.NeedRestartProcess{
|
||||
PID: pid,
|
||||
Path: path,
|
||||
HasInit: true,
|
||||
})
|
||||
}
|
||||
return procs
|
||||
}
|
||||
|
||||
@@ -105,28 +105,41 @@ func TestParseOSRelease(t *testing.T) {
|
||||
ver string
|
||||
}{
|
||||
{
|
||||
in: `NAME="openSUSE Leap"
|
||||
ID=opensuse
|
||||
VERSION_ID="42.3.4"`,
|
||||
in: `CPE_NAME="cpe:/o:opensuse:opensuse:13.2"
|
||||
VERSION_ID="13.2"`,
|
||||
name: constant.OpenSUSE,
|
||||
ver: "13.2",
|
||||
},
|
||||
{
|
||||
in: `CPE_NAME="cpe:/o:opensuse:tumbleweed:20220124"
|
||||
VERSION_ID="20220124"`,
|
||||
name: constant.OpenSUSE,
|
||||
ver: "tumbleweed",
|
||||
},
|
||||
{
|
||||
in: `CPE_NAME="cpe:/o:opensuse:leap:42.3"
|
||||
VERSION_ID="42.3.4"`,
|
||||
name: constant.OpenSUSELeap,
|
||||
ver: "42.3.4",
|
||||
},
|
||||
{
|
||||
in: `NAME="SLES"
|
||||
VERSION="12-SP1"
|
||||
VERSION_ID="12.1"
|
||||
ID="sles"`,
|
||||
in: `CPE_NAME="cpe:/o:suse:sles:12:sp1"
|
||||
VERSION_ID="12.1"`,
|
||||
name: constant.SUSEEnterpriseServer,
|
||||
ver: "12.1",
|
||||
},
|
||||
{
|
||||
in: `NAME="SLES_SAP"
|
||||
VERSION="12-SP1"
|
||||
VERSION_ID="12.1.0.1"
|
||||
ID="sles"`,
|
||||
in: `CPE_NAME="cpe:/o:suse:sles_sap:12:sp1"
|
||||
VERSION_ID="12.1.0.1"`,
|
||||
name: constant.SUSEEnterpriseServer,
|
||||
ver: "12.1.0.1",
|
||||
},
|
||||
{
|
||||
in: `CPE_NAME="cpe:/o:suse:sled:15"
|
||||
VERSION_ID="15"`,
|
||||
name: constant.SUSEEnterpriseDesktop,
|
||||
ver: "15",
|
||||
},
|
||||
}
|
||||
|
||||
r := newSUSE(config.ServerInfo{})
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
|
||||
func isRunningKernel(pack models.Package, family string, kernel models.Kernel) (isKernel, running bool) {
|
||||
switch family {
|
||||
case constant.SUSEEnterpriseServer:
|
||||
case constant.OpenSUSE, constant.OpenSUSELeap, constant.SUSEEnterpriseServer, constant.SUSEEnterpriseDesktop:
|
||||
if pack.Name == "kernel-default" {
|
||||
// Remove the last period and later because uname don't show that.
|
||||
ss := strings.Split(pack.Release, ".")
|
||||
|
||||
@@ -17,9 +17,8 @@ import (
|
||||
|
||||
// ConfigtestCmd is Subcommand
|
||||
type ConfigtestCmd struct {
|
||||
configPath string
|
||||
askKeyPassword bool
|
||||
timeoutSec int
|
||||
configPath string
|
||||
timeoutSec int
|
||||
}
|
||||
|
||||
// Name return subcommand name
|
||||
@@ -35,7 +34,6 @@ func (*ConfigtestCmd) Usage() string {
|
||||
[-config=/path/to/config.toml]
|
||||
[-log-to-file]
|
||||
[-log-dir=/path/to/log]
|
||||
[-ask-key-password]
|
||||
[-timeout=300]
|
||||
[-containers-only]
|
||||
[-http-proxy=http://192.168.0.1:8080]
|
||||
@@ -59,10 +57,6 @@ func (p *ConfigtestCmd) SetFlags(f *flag.FlagSet) {
|
||||
|
||||
f.IntVar(&p.timeoutSec, "timeout", 5*60, "Timeout(Sec)")
|
||||
|
||||
f.BoolVar(&p.askKeyPassword, "ask-key-password", false,
|
||||
"Ask ssh privatekey password before scanning",
|
||||
)
|
||||
|
||||
f.StringVar(&config.Conf.HTTPProxy, "http-proxy", "",
|
||||
"http://proxy-url:port (default: empty)")
|
||||
|
||||
@@ -79,18 +73,7 @@ func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfa
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
var keyPass string
|
||||
var err error
|
||||
if p.askKeyPassword {
|
||||
prompt := "SSH key password: "
|
||||
if keyPass, err = getPasswd(prompt); err != nil {
|
||||
logging.Log.Error(err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
err = config.Load(p.configPath, keyPass)
|
||||
if err != nil {
|
||||
if err := config.Load(p.configPath); err != nil {
|
||||
msg := []string{
|
||||
fmt.Sprintf("Error loading %s", p.configPath),
|
||||
"If you update Vuls and get this error, there may be incompatible changes in config.toml",
|
||||
|
||||
@@ -175,7 +175,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
|
||||
logging.Log = logging.NewCustomLogger(config.Conf.Debug, config.Conf.Quiet, config.Conf.LogToFile, config.Conf.LogDir, "", "")
|
||||
logging.Log.Infof("vuls-%s-%s", config.Version, config.Revision)
|
||||
|
||||
if err := config.Load(p.configPath, ""); err != nil {
|
||||
if err := config.Load(p.configPath); err != nil {
|
||||
logging.Log.Errorf("Error loading %s, %+v", p.configPath, err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ func (p *SaaSCmd) SetFlags(f *flag.FlagSet) {
|
||||
func (p *SaaSCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
logging.Log = logging.NewCustomLogger(config.Conf.Debug, config.Conf.Quiet, config.Conf.LogToFile, config.Conf.LogDir, "", "")
|
||||
logging.Log.Infof("vuls-%s-%s", config.Version, config.Revision)
|
||||
if err := config.Load(p.configPath, ""); err != nil {
|
||||
if err := config.Load(p.configPath); err != nil {
|
||||
logging.Log.Errorf("Error loading %s, %+v", p.configPath, err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
// ScanCmd is Subcommand of host discovery mode
|
||||
type ScanCmd struct {
|
||||
configPath string
|
||||
askKeyPassword bool
|
||||
timeoutSec int
|
||||
scanTimeoutSec int
|
||||
cacheDBPath string
|
||||
@@ -43,7 +42,6 @@ func (*ScanCmd) Usage() string {
|
||||
[-log-dir=/path/to/log]
|
||||
[-cachedb-path=/path/to/cache.db]
|
||||
[-http-proxy=http://192.168.0.1:8080]
|
||||
[-ask-key-password]
|
||||
[-timeout=300]
|
||||
[-timeout-scan=7200]
|
||||
[-debug]
|
||||
@@ -80,10 +78,6 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
|
||||
f.StringVar(&config.Conf.HTTPProxy, "http-proxy", "",
|
||||
"http://proxy-url:port (default: empty)")
|
||||
|
||||
f.BoolVar(&p.askKeyPassword, "ask-key-password", false,
|
||||
"Ask ssh privatekey password before scanning",
|
||||
)
|
||||
|
||||
f.BoolVar(&config.Conf.Pipe, "pipe", false, "Use stdin via PIPE")
|
||||
|
||||
f.BoolVar(&p.detectIPS, "ips", false, "retrieve IPS information")
|
||||
@@ -116,18 +110,7 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
|
||||
}
|
||||
}
|
||||
|
||||
var keyPass string
|
||||
var err error
|
||||
if p.askKeyPassword {
|
||||
prompt := "SSH key password: "
|
||||
if keyPass, err = getPasswd(prompt); err != nil {
|
||||
logging.Log.Error(err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
err = config.Load(p.configPath, keyPass)
|
||||
if err != nil {
|
||||
if err := config.Load(p.configPath); err != nil {
|
||||
msg := []string{
|
||||
fmt.Sprintf("Error loading %s", p.configPath),
|
||||
"If you update Vuls and get this error, there may be incompatible changes in config.toml",
|
||||
|
||||
@@ -93,7 +93,7 @@ func (p *ServerCmd) SetFlags(f *flag.FlagSet) {
|
||||
func (p *ServerCmd) Execute(_ context.Context, _ *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
logging.Log = logging.NewCustomLogger(config.Conf.Debug, config.Conf.Quiet, config.Conf.LogToFile, config.Conf.LogDir, "", "")
|
||||
logging.Log.Infof("vuls-%s-%s", config.Version, config.Revision)
|
||||
if err := config.Load(p.configPath, ""); err != nil {
|
||||
if err := config.Load(p.configPath); err != nil {
|
||||
logging.Log.Errorf("Error loading %s. err: %+v", p.configPath, err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ func (p *TuiCmd) SetFlags(f *flag.FlagSet) {
|
||||
func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
logging.Log = logging.NewCustomLogger(config.Conf.Debug, config.Conf.Quiet, config.Conf.LogToFile, config.Conf.LogDir, "", "")
|
||||
logging.Log.Infof("vuls-%s-%s", config.Version, config.Revision)
|
||||
if err := config.Load(p.configPath, ""); err != nil {
|
||||
if err := config.Load(p.configPath); err != nil {
|
||||
logging.Log.Errorf("Error loading %s, err: %+v", p.configPath, err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
@@ -1,29 +1,12 @@
|
||||
package subcmds
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/howeyc/gopass"
|
||||
homedir "github.com/mitchellh/go-homedir"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
func getPasswd(prompt string) (string, error) {
|
||||
for {
|
||||
fmt.Print(prompt)
|
||||
pass, err := gopass.GetPasswdMasked()
|
||||
if err != nil {
|
||||
return "", xerrors.New("Failed to read a password")
|
||||
}
|
||||
if 0 < len(pass) {
|
||||
return string(pass), nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func mkdirDotVuls() error {
|
||||
home, err := homedir.Dir()
|
||||
if err != nil {
|
||||
|
||||
@@ -719,8 +719,7 @@ func setChangelogLayout(g *gocui.Gui) error {
|
||||
if len(pack.AffectedProcs) != 0 {
|
||||
for _, p := range pack.AffectedProcs {
|
||||
if len(p.ListenPortStats) == 0 {
|
||||
lines = append(lines, fmt.Sprintf(" * PID: %s %s Port: []",
|
||||
p.PID, p.Name))
|
||||
lines = append(lines, fmt.Sprintf(" * PID: %s %s", p.PID, p.Name))
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -733,8 +732,7 @@ func setChangelogLayout(g *gocui.Gui) error {
|
||||
}
|
||||
}
|
||||
|
||||
lines = append(lines, fmt.Sprintf(" * PID: %s %s Port: %s",
|
||||
p.PID, p.Name, ports))
|
||||
lines = append(lines, fmt.Sprintf(" * PID: %s %s Port: %s", p.PID, p.Name, ports))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ func Major(version string) string {
|
||||
} else {
|
||||
ver = ss[1]
|
||||
}
|
||||
return ver[0:strings.Index(ver, ".")]
|
||||
return strings.Split(ver, ".")[0]
|
||||
}
|
||||
|
||||
// GetHTTPClient return http.Client
|
||||
|
||||
@@ -164,6 +164,10 @@ func Test_major(t *testing.T) {
|
||||
in: "",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
in: "4",
|
||||
expected: "4",
|
||||
},
|
||||
{
|
||||
in: "4.1",
|
||||
expected: "4",
|
||||
|
||||
Reference in New Issue
Block a user