feat(scan): support dnf modules (#1114)

* feat(scan): support dnf modules

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

* chore: refactor

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

* fix func name

* chore: update go mods
This commit is contained in:
Kota Kanbe
2021-01-06 11:36:41 +09:00
committed by GitHub
parent 8405e0fad6
commit b13f93a2d3
10 changed files with 219 additions and 34 deletions

7
go.mod
View File

@@ -43,7 +43,7 @@ require (
github.com/knqyf263/gost v0.1.7
github.com/kotakanbe/go-cve-dictionary v0.5.6
github.com/kotakanbe/go-pingscanner v0.1.0
github.com/kotakanbe/goval-dictionary v0.2.16
github.com/kotakanbe/goval-dictionary v0.3.0
github.com/kotakanbe/logrus-prefixed-formatter v0.0.0-20180123152602-928f7356cb96
github.com/magiconair/properties v1.8.4 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
@@ -65,10 +65,11 @@ require (
github.com/takuzoo3868/go-msfdb v0.1.3
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.16.0 // indirect
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect
golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5
golang.org/x/sys v0.0.0-20201218084310-7d0127a74742 // indirect
golang.org/x/sys v0.0.0-20210105210732-16f7687f5001 // indirect
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1

23
go.sum
View File

@@ -342,6 +342,8 @@ github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7a
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-redis/redis v6.15.7+incompatible h1:3skhDh95XQMpnqeqNftPkQD9jL9e5e36z/1SUm6dy1U=
github.com/go-redis/redis v6.15.7+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-redis/redis/v8 v8.4.0 h1:J5NCReIgh3QgUJu398hUncxDExN4gMOHI11NVbVicGQ=
github.com/go-redis/redis/v8 v8.4.0/go.mod h1:A1tbYoHSa1fXwN+//ljcCYYJeLmVrwL9hbQN45Jdy0M=
github.com/go-redis/redis/v8 v8.4.4 h1:fGqgxCTR1sydaKI00oQf3OmkU/DIe/I/fYXvGklCIuc=
@@ -583,14 +585,13 @@ github.com/kotakanbe/go-cve-dictionary v0.5.6 h1:xTq6AcWYkmdqHCwL5DiqH+/C0Ga4IHl
github.com/kotakanbe/go-cve-dictionary v0.5.6/go.mod h1:CtZPPDJUrU/+3TvUcD1xFHVWWlM9SSEZYRZ11pblmDQ=
github.com/kotakanbe/go-pingscanner v0.1.0 h1:VG4/9l0i8WeToXclj7bIGoAZAu7a07Z3qmQiIfU0gT0=
github.com/kotakanbe/go-pingscanner v0.1.0/go.mod h1:/761QZzuZFcfN8h/1QuawUA+pKukp3qcNj5mxJCOiAk=
github.com/kotakanbe/goval-dictionary v0.2.16 h1:AmlzIWS5LiMnYyVDXxPqtzbGVD8LrPNGj4uSE8YrcW8=
github.com/kotakanbe/goval-dictionary v0.2.16/go.mod h1:sNRtiJrWJg92zrT9vucHI5C1R8rCBW32X1nGAOQ2sK8=
github.com/kotakanbe/goval-dictionary v0.3.0 h1:f8itkjyrcrHaEWQcqquldifQYRndErxFHyjtMi+rbHc=
github.com/kotakanbe/goval-dictionary v0.3.0/go.mod h1:NFnlcNWtD4dXkovJqGG+IFNba4q3qXYBbq56O9fHL0o=
github.com/kotakanbe/logrus-prefixed-formatter v0.0.0-20180123152602-928f7356cb96 h1:xNVK0mQJdQjw+QYeaMM4G6fvucWr8rTGGIhlPakx1wU=
github.com/kotakanbe/logrus-prefixed-formatter v0.0.0-20180123152602-928f7356cb96/go.mod h1:ljq48H1V+0Vh0u7ucA3LjR4AfkAeCpxrf7LaaCk8Vmo=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@@ -601,7 +602,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
github.com/labstack/gommon v0.2.9/go.mod h1:E8ZTmW9vw5az5/ZyHWCp0Lw4OH2ecsaBP1C/NKavGG4=
github.com/labstack/echo/v4 v4.1.17 h1:PQIBaRplyRy3OjwILGkPg89JRtH2x5bssi59G2EL3fo=
github.com/labstack/echo/v4 v4.1.17/go.mod h1:Tn2yRQL/UclUalpb5rPdXDevbkJ+lp/2svdyFBg6CHQ=
github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
@@ -654,6 +656,8 @@ github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsO
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
github.com/mattn/go-sqlite3 v1.14.5 h1:1IdxlwTNazvbKJQSxoJ5/9ECbEeaTTyeU7sEAZ5KKTQ=
github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
@@ -968,9 +972,12 @@ golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620 h1:3wPMTskHO3+O6jqTEXyFcsnuxMQOqYSaHsDxcbUXpqA=
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1061,6 +1068,8 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201216054612-986b41b23924 h1:QsnDpLLOKwHBBDa8nDws4DYNc/ryVW2vCpxCs09d4PY=
golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1102,7 +1111,6 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1139,13 +1147,14 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201006155630-ac719f4daadf/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201218084310-7d0127a74742 h1:+CBz4km/0KPU3RGTwARGh/noP3bEwtHcq+0YcBQM2JQ=
golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210105210732-16f7687f5001 h1:/dSxr6gT0FNI1MO5WLJo8mTmItROeOKTkDn+7OwWBos=
golang.org/x/sys v0.0.0-20210105210732-16f7687f5001/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=

View File

@@ -48,6 +48,7 @@ type ScanResult struct {
RunningKernel Kernel `json:"runningKernel"`
Packages Packages `json:"packages"`
SrcPackages SrcPackages `json:",omitempty"`
EnabledDnfModules []string `json:"enabledDnfModules,omitempty"` // for dnf modules
WordPressPackages *WordPressPackages `json:",omitempty"`
LibraryScanners LibraryScanners `json:"libraries,omitempty"`
CweDict CweDict `json:"cweDict,omitempty"`

View File

@@ -78,6 +78,7 @@ type request struct {
arch string
binaryPackNames []string
isSrcPack bool
modularityLabel string // RHEL 8 or later only
}
type response struct {
@@ -147,7 +148,7 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult) (
select {
case res := <-resChan:
for _, def := range res.defs {
affected, notFixedYet, fixedIn := isOvalDefAffected(def, res.request, r.Family, r.RunningKernel)
affected, notFixedYet, fixedIn := isOvalDefAffected(def, res.request, r.Family, r.RunningKernel, r.EnabledDnfModules)
if !affected {
continue
}
@@ -250,7 +251,7 @@ func getDefsByPackNameFromOvalDB(driver db.DB, r *models.ScanResult) (relatedDef
return relatedDefs, xerrors.Errorf("Failed to get %s OVAL info by package: %#v, err: %w", r.Family, req, err)
}
for _, def := range definitions {
affected, notFixedYet, fixedIn := isOvalDefAffected(def, req, r.Family, r.RunningKernel)
affected, notFixedYet, fixedIn := isOvalDefAffected(def, req, r.Family, r.RunningKernel, r.EnabledDnfModules)
if !affected {
continue
}
@@ -291,12 +292,27 @@ func major(version string) string {
return ver[0:strings.Index(ver, ".")]
}
func isOvalDefAffected(def ovalmodels.Definition, req request, family string, running models.Kernel) (affected, notFixedYet bool, fixedIn string) {
func isOvalDefAffected(def ovalmodels.Definition, req request, family string, running models.Kernel, enabledMods []string) (affected, notFixedYet bool, fixedIn string) {
for _, ovalPack := range def.AffectedPacks {
if req.packName != ovalPack.Name {
continue
}
isModularityLabelEmptyOrSame := false
if ovalPack.ModularityLabel != "" {
for _, mod := range enabledMods {
if mod == ovalPack.ModularityLabel {
isModularityLabelEmptyOrSame = true
break
}
}
} else {
isModularityLabelEmptyOrSame = true
}
if !isModularityLabelEmptyOrSame {
continue
}
if running.Release != "" {
switch family {
case config.RedHat, config.CentOS:

View File

@@ -202,6 +202,7 @@ func TestIsOvalDefAffected(t *testing.T) {
req request
family string
kernel models.Kernel
mods []string
}
var tests = []struct {
in in
@@ -1076,9 +1077,85 @@ func TestIsOvalDefAffected(t *testing.T) {
notFixedYet: false,
fixedIn: "3.1.0",
},
// dnf module
{
in: in{
family: config.RedHat,
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "nginx",
Version: "1.16.1-1.module+el8.3.0+8844+e5e7039f.1",
NotFixedYet: false,
ModularityLabel: "nginx:1.16",
},
},
},
req: request{
packName: "nginx",
versionRelease: "1.16.0-1.module+el8.3.0+8844+e5e7039f.1",
},
mods: []string{
"nginx:1.16",
},
},
affected: true,
notFixedYet: false,
fixedIn: "1.16.1-1.module+el8.3.0+8844+e5e7039f.1",
},
// dnf module 2
{
in: in{
family: config.RedHat,
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "nginx",
Version: "1.16.1-1.module+el8.3.0+8844+e5e7039f.1",
NotFixedYet: false,
ModularityLabel: "nginx:1.16",
},
},
},
req: request{
packName: "nginx",
versionRelease: "1.16.2-1.module+el8.3.0+8844+e5e7039f.1",
},
mods: []string{
"nginx:1.16",
},
},
affected: false,
notFixedYet: false,
},
// dnf module 3
{
in: in{
family: config.RedHat,
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "nginx",
Version: "1.16.1-1.module+el8.3.0+8844+e5e7039f.1",
NotFixedYet: false,
ModularityLabel: "nginx:1.16",
},
},
},
req: request{
packName: "nginx",
versionRelease: "1.16.0-1.module+el8.3.0+8844+e5e7039f.1",
},
mods: []string{
"nginx:1.14",
},
},
affected: false,
notFixedYet: false,
},
}
for i, tt := range tests {
affected, notFixedYet, fixedIn := isOvalDefAffected(tt.in.def, tt.in.req, tt.in.family, tt.in.kernel)
affected, notFixedYet, fixedIn := isOvalDefAffected(tt.in.def, tt.in.req, tt.in.family, tt.in.kernel, tt.in.mods)
if tt.affected != affected {
t.Errorf("[%d] affected\nexpected: %v\n actual: %v\n", i, tt.affected, affected)
}

View File

@@ -7,21 +7,19 @@ import (
"strings"
"time"
"github.com/future-architect/vuls/libmanager"
gostdb "github.com/knqyf263/gost/db"
"github.com/future-architect/vuls/config"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/contrib/owasp-dependency-check/parser"
"github.com/future-architect/vuls/cwe"
"github.com/future-architect/vuls/exploit"
"github.com/future-architect/vuls/github"
"github.com/future-architect/vuls/gost"
"github.com/future-architect/vuls/libmanager"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/msf"
"github.com/future-architect/vuls/oval"
"github.com/future-architect/vuls/util"
"github.com/future-architect/vuls/wordpress"
gostdb "github.com/knqyf263/gost/db"
cvedb "github.com/kotakanbe/go-cve-dictionary/db"
cvemodels "github.com/kotakanbe/go-cve-dictionary/models"
ovaldb "github.com/kotakanbe/goval-dictionary/db"
@@ -170,7 +168,7 @@ func DetectPkgCves(dbclient DBClient, r *models.ScanResult) error {
}
} else if reuseScannedCves(r) {
util.Log.Infof("r.Release is empty. Use CVEs as it as.")
} else if r.Family == config.ServerTypePseudo {
} else if r.Family == c.ServerTypePseudo {
util.Log.Infof("pseudo type. Skip OVAL and gost detection")
} else {
return xerrors.Errorf("Failed to fill CVEs. r.Release is empty")
@@ -207,7 +205,7 @@ func DetectPkgCves(dbclient DBClient, r *models.ScanResult) error {
}
// DetectGitHubCves fetches CVEs from GitHub Security Alerts
func DetectGitHubCves(r *models.ScanResult, githubConfs map[string]config.GitHubConf) error {
func DetectGitHubCves(r *models.ScanResult, githubConfs map[string]c.GitHubConf) error {
if len(githubConfs) == 0 {
return nil
}
@@ -228,7 +226,7 @@ func DetectGitHubCves(r *models.ScanResult, githubConfs map[string]config.GitHub
}
// DetectWordPressCves detects CVEs of WordPress
func DetectWordPressCves(r *models.ScanResult, wpCnf *config.WordPressConf) error {
func DetectWordPressCves(r *models.ScanResult, wpCnf *c.WordPressConf) error {
if wpCnf.WPVulnDBToken == "" {
return nil
}
@@ -431,7 +429,7 @@ func fillWithMetasploit(driver metasploitdb.DB, r *models.ScanResult) (nMetasplo
// DetectCpeURIsCves detects CVEs of given CPE-URIs
func DetectCpeURIsCves(driver cvedb.DB, r *models.ScanResult, cpeURIs []string) error {
nCVEs := 0
if len(cpeURIs) != 0 && driver == nil && !config.Conf.CveDict.IsFetchViaHTTP() {
if len(cpeURIs) != 0 && driver == nil && !c.Conf.CveDict.IsFetchViaHTTP() {
return xerrors.Errorf("cpeURIs %s specified, but cve-dictionary DB not found. Fetch cve-dictionary before reporting. For details, see `https://github.com/kotakanbe/go-cve-dictionary#deploy-go-cve-dictionary`",
cpeURIs)
}

View File

@@ -449,6 +449,7 @@ func (l *base) convertToModel() models.ScanResult {
RunningKernel: l.Kernel,
Packages: l.Packages,
SrcPackages: l.SrcPackages,
EnabledDnfModules: l.EnabledDnfModules,
WordPressPackages: l.WordPress,
LibraryScanners: l.LibraryScanners,
Optional: l.ServerInfo.Optional,

View File

@@ -201,11 +201,14 @@ func (o *redhatBase) detectIPAddr() (err error) {
func (o *redhatBase) scanPackages() error {
installed, err := o.scanInstalledPackages()
if err != nil {
o.log.Errorf("Failed to scan installed packages: %s", err)
return err
return xerrors.Errorf("Failed to scan installed packages: %w", err)
}
o.Packages = installed
if o.EnabledDnfModules, err = o.detectEnabledDnfModules(); err != nil {
return xerrors.Errorf("Failed to detect installed dnf modules: %w", err)
}
rebootRequired, err := o.rebootRequired()
if err != nil {
err = xerrors.Errorf("Failed to detect the kernel reboot required: %w", err)
@@ -261,7 +264,7 @@ func (o *redhatBase) scanInstalledPackages() (models.Packages, error) {
Version: version,
}
r := o.exec(o.rpmQa(o.Distro), noSudo)
r := o.exec(o.rpmQa(), noSudo)
if !r.isSuccess() {
return nil, xerrors.Errorf("Scan packages failed: %s", r)
}
@@ -640,7 +643,7 @@ func (o *redhatBase) procPathToFQPN(execCommand string) (string, error) {
}
func (o *redhatBase) getPkgName(paths []string) (pkgNames []string, err error) {
cmd := o.rpmQf(o.Distro) + strings.Join(paths, " ")
cmd := o.rpmQf() + strings.Join(paths, " ")
r := o.exec(util.PrependProxyEnv(cmd), noSudo)
if !r.isSuccess(0, 2, 4, 8) {
return nil, xerrors.Errorf("Failed to rpm -qf: %s, cmd: %s", r, cmd)
@@ -659,36 +662,71 @@ func (o *redhatBase) getPkgName(paths []string) (pkgNames []string, err error) {
return pkgNames, nil
}
func (o *redhatBase) rpmQa(distro config.Distro) string {
func (o *redhatBase) rpmQa() string {
const old = `rpm -qa --queryformat "%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH}\n"`
const new = `rpm -qa --queryformat "%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH}\n"`
switch distro.Family {
switch o.Distro.Family {
case config.SUSEEnterpriseServer:
if v, _ := distro.MajorVersion(); v < 12 {
if v, _ := o.Distro.MajorVersion(); v < 12 {
return old
}
return new
default:
if v, _ := distro.MajorVersion(); v < 6 {
if v, _ := o.Distro.MajorVersion(); v < 6 {
return old
}
return new
}
}
func (o *redhatBase) rpmQf(distro config.Distro) string {
func (o *redhatBase) rpmQf() string {
const old = `rpm -qf --queryformat "%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH}\n" `
const new = `rpm -qf --queryformat "%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH}\n" `
switch distro.Family {
switch o.Distro.Family {
case config.SUSEEnterpriseServer:
if v, _ := distro.MajorVersion(); v < 12 {
if v, _ := o.Distro.MajorVersion(); v < 12 {
return old
}
return new
default:
if v, _ := distro.MajorVersion(); v < 6 {
if v, _ := o.Distro.MajorVersion(); v < 6 {
return old
}
return new
}
}
func (o *redhatBase) detectEnabledDnfModules() ([]string, error) {
switch o.Distro.Family {
case config.RedHat, config.CentOS:
//TODO OracleLinux
default:
return nil, nil
}
if v, _ := o.Distro.MajorVersion(); v < 8 {
return nil, nil
}
cmd := `dnf --cacheonly --color=never --quiet module list --enabled`
r := o.exec(util.PrependProxyEnv(cmd), noSudo)
if !r.isSuccess() {
return nil, xerrors.Errorf("Failed to dnf module list: %s, cmd: %s", r, cmd)
}
return o.parseDnfModuleList(r.Stdout)
}
func (o *redhatBase) parseDnfModuleList(stdout string) (labels []string, err error) {
scanner := bufio.NewScanner(strings.NewReader(stdout))
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "Hint:") || !strings.Contains(line, "[i]") {
continue
}
ss := strings.Fields(line)
if len(ss) < 2 {
continue
}
labels = append(labels, fmt.Sprintf("%s:%s", ss[0], ss[1]))
}
return
}

View File

@@ -397,3 +397,44 @@ func TestParseNeedsRestarting(t *testing.T) {
}
}
}
func Test_redhatBase_parseDnfModuleList(t *testing.T) {
type args struct {
stdout string
}
tests := []struct {
name string
args args
wantLabels []string
wantErr bool
}{
{
name: "Success",
args: args{
stdout: `Red Hat Enterprise Linux 8 for x86_64 - AppStream from RHUI (RPMs)
Name Stream Profiles Summary
virt rhel [d][e] common [d] Virtualization module
nginx 1.14 [d][e] common [d] [i] nginx webserver
Hint: [d]efault, [e]nabled, [x]disabled, [i]nstalled`,
},
wantLabels: []string{
"nginx:1.14",
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
o := &redhatBase{}
gotLabels, err := o.parseDnfModuleList(tt.args.stdout)
if (err != nil) != tt.wantErr {
t.Errorf("redhatBase.parseDnfModuleList() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(gotLabels, tt.wantLabels) {
t.Errorf("redhatBase.parseDnfModuleList() = %v, want %v", gotLabels, tt.wantLabels)
}
})
}
}

View File

@@ -70,6 +70,9 @@ type osPackages struct {
// installed source packages (Debian based only)
SrcPackages models.SrcPackages
// enabled dnf modules or packages
EnabledDnfModules []string
// unsecure packages
VulnInfos models.VulnInfos