Compare commits

...

7 Commits

Author SHA1 Message Date
MaineK00n
9bcffcd721 fix(configtest,scan): fix validateSSHConfig (#1395)
* fix(configtest,scan): support StrictHostKeyChecking no

* fix(configtest,scan): support ServerTypePseudo

* fix(configtest,scan): skip if using proxy
2022-02-17 08:15:23 +09:00
MaineK00n
787604de6a fix(suse): fix openSUSE, openSUSE Leap, SLES, SLED scan (#1384)
* fix(suse): fix openSUSE, openSUSE Leap scan

* docs: update README

* fix: unknown CveContent.Type

* fix: tui reporting

* fix: listening port was duplicated in format-full-text

* fix .gitignore

* fix: add EOL data for SLES12.5

Co-authored-by: Kota Kanbe <kotakanbe@gmail.com>
2022-02-15 17:11:54 +09:00
MaineK00n
5164fb1423 fix(util): Major() behavior for major version (#1393) 2022-02-15 07:59:29 +09:00
MaineK00n
07335617d3 fix(configtest,scan): support SSH config file (#1388)
* fix(configtest,scan): support SSH config file

* chore(subcmds): remove askKeyPassword flag
2022-02-12 21:50:56 +09:00
MaineK00n
e5855922c1 fix(redhat): detect RedHat version (#1387)
* fix(redhat): detect RedHat version

* fix err fmt string

Co-authored-by: Kota Kanbe <kotakanbe@gmail.com>
2022-02-12 20:09:51 +09:00
MaineK00n
671be3f2f7 feat(configtest,scan): detect known_hosts error (#1386) 2022-02-11 12:54:17 +09:00
MaineK00n
fe8d252c51 feat(debian): validate running kernel version (#1382)
* feat(debian): validate running kernel version

* chore(gost/debian): only stash when there is linux package
2022-02-11 12:36:48 +09:00
38 changed files with 830 additions and 359 deletions

2
.gitignore vendored
View File

@@ -18,3 +18,5 @@ dist/
vuls.*
vuls
!cmd/vuls
future-vuls
trivy-to-vuls

View File

@@ -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

View File

@@ -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])

View File

@@ -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

View File

@@ -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/

View File

@@ -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 == "" {

View File

@@ -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
View File

@@ -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
View File

@@ -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=

View File

@@ -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

View File

@@ -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":

View File

@@ -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)

View File

@@ -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.")
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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
}

View File

@@ -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, ""
}

View File

@@ -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)
}
}
}

View File

@@ -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})
}
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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))

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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{})

View File

@@ -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, ".")

View File

@@ -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",

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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",

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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 {

View File

@@ -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))
}
}
}

View File

@@ -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

View File

@@ -164,6 +164,10 @@ func Test_major(t *testing.T) {
in: "",
expected: "",
},
{
in: "4",
expected: "4",
},
{
in: "4.1",
expected: "4",