Compare commits

...

11 Commits

Author SHA1 Message Date
Kota Kanbe
464d523c42 Display fixed-in version for each package in report (#801)
* refactor(model): PackageFixStatus.Name to BinName

* refacotr(oval): change var name

* feat(report): Add FixedIn in JSON

* refactor(tui): chage args

* display fixedin in report

* refactor(model): change fileld name

* remove unused field of PackageFixStatus
2020-04-08 21:26:34 +09:00
Kota Kanbe
0f6a1987d4 fix(configtest): yum-utils instead of dnf-utils on RHEL8, Cent8 (#948) 2020-04-06 19:40:05 +09:00
Shigechika AIKAWA
20c6247ce5 fix CentOS8 configtest always failed (#947) 2020-04-06 15:47:08 +09:00
gy741
a10dd67e0f Fix typo in models/scanresults.go (#942) 2020-04-06 15:00:43 +09:00
segatomo
5729ad6026 Add CWE Top25 and SANS Top25 (#925)
* add top25 rank

* add CweTop25 and SansTop25

* fix report

* add cwetop25 and sanstop25 url

* fix condition branch

* fix condition branch
2020-03-03 17:33:06 +09:00
Tomoya Amachi
9aa0d87a21 feat : scan with image digest (#939) 2020-03-03 16:51:06 +09:00
ishiDACo
fe3f1b9924 Update OWASP Dependency Check parser for dependency-check.2.2.xsd schema (#936) 2020-02-27 10:08:26 +09:00
Kota Kanbe
00e52a88fa Update README.md 2020-02-01 09:27:17 +09:00
Kota Kanbe
5811dffe7a fix(report): Support CVSS 3.1 for Red Hat OVAL #930 (#932) 2020-01-30 22:48:04 +09:00
sadayuki-matsuno
7278982af4 update fanal (#931) 2020-01-30 20:40:49 +09:00
nyao
c17b4154ec fix(config): fix double checking ResultsDir Path (#927) 2019-12-12 09:29:12 +09:00
32 changed files with 703 additions and 165 deletions

View File

@@ -170,7 +170,7 @@ Vuls has some options to detect the vulnerabilities
- Auto-generation of configuration file template
- Auto-detection of servers set using CIDR, generate configuration file template
- Email and Slack notification is possible (supports Japanese language)
- Scan result is viewable on accessory software, TUI Viewer in a terminal or Web UI ([VulsRepo](https://github.com/future-architect/vulsrepo)).
- Scan result is viewable on accessory software, TUI Viewer in a terminal or Web UI ([VulsRepo](https://github.com/ishiDACo/vulsrepo)).
----

View File

@@ -177,13 +177,6 @@ func (c Config) ValidateOnConfigtest() bool {
func (c Config) ValidateOnScan() bool {
errs := c.checkSSHKeyExist()
if len(c.ResultsDir) != 0 {
if ok, _ := valid.IsFilePath(c.ResultsDir); !ok {
errs = append(errs, xerrors.Errorf(
"JSON base directory must be a *Absolute* file path. -results-dir: %s", c.ResultsDir))
}
}
if runtime.GOOS == "windows" && !c.SSHNative {
errs = append(errs, xerrors.New("-ssh-native-insecure is needed on windows"))
}
@@ -1098,6 +1091,7 @@ type WordPressConf struct {
type Image struct {
Name string `json:"name"`
Tag string `json:"tag"`
Digest string `json:"digest"`
DockerOption types.DockerOption `json:"dockerOption,omitempty"`
Cpes []string `json:"cpes,omitempty"`
OwaspDCXMLPath string `json:"owaspDCXMLPath"`
@@ -1105,6 +1099,13 @@ type Image struct {
IgnoreCves []string `json:"ignoreCves,omitempty"`
}
func (i *Image) GetFullName() string {
if i.Digest != "" {
return i.Name + "@" + i.Digest
}
return i.Name + ":" + i.Tag
}
// GitHubConf is used for GitHub integration
type GitHubConf struct {
Token string `json:"-"`

View File

@@ -298,8 +298,11 @@ func IsValidImage(c Image) error {
if c.Name == "" {
return xerrors.New("Invalid arguments : no image name")
}
if c.Tag == "" {
return xerrors.New("Invalid arguments : no image tag")
if c.Tag == "" && c.Digest == "" {
return xerrors.New("Invalid arguments : no image tag and digest")
}
if c.Tag != "" && c.Digest != "" {
return xerrors.New("Invalid arguments : you can either set image tag or digest")
}
return nil
}

View File

@@ -42,3 +42,62 @@ func TestToCpeURI(t *testing.T) {
}
}
}
func TestIsValidImage(t *testing.T) {
var tests = []struct {
name string
img Image
errOccur bool
}{
{
name: "ok with tag",
img: Image{
Name: "ok",
Tag: "ok",
},
errOccur: false,
},
{
name: "ok with digest",
img: Image{
Name: "ok",
Digest: "ok",
},
errOccur: false,
},
{
name: "no image name with tag",
img: Image{
Tag: "ok",
},
errOccur: true,
},
{
name: "no image name with digest",
img: Image{
Digest: "ok",
},
errOccur: true,
},
{
name: "no tag and digest",
img: Image{
Name: "ok",
},
errOccur: true,
},
}
for i, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := IsValidImage(tt.img)
actual := err != nil
if actual != tt.errOccur {
t.Errorf("[%d] act: %v, exp: %v",
i, actual, tt.errOccur)
}
})
}
}

View File

@@ -6,6 +6,7 @@ import (
"os"
"strings"
"github.com/knqyf263/go-cpe/naming"
log "github.com/sirupsen/logrus"
"golang.org/x/xerrors"
)
@@ -15,12 +16,11 @@ type analysis struct {
}
type dependency struct {
Identifiers []identifier `xml:"identifiers>identifier"`
Identifiers []vulnerabilityId `xml:"identifiers>vulnerabilityIds"`
}
type identifier struct {
Name string `xml:"name"`
Type string `xml:"type,attr"`
type vulnerabilityId struct {
Id string `xml:"id"`
}
func appendIfMissing(slice []string, str string) []string {
@@ -55,11 +55,16 @@ func Parse(path string) ([]string, error) {
cpes := []string{}
for _, d := range anal.Dependencies {
for _, ident := range d.Identifiers {
if ident.Type == "cpe" {
name := strings.TrimPrefix(ident.Name, "(")
name = strings.TrimSuffix(name, ")")
cpes = appendIfMissing(cpes, name)
id := ident.Id // Start with cpe:2.3:
// Convert from CPE 2.3 to CPE 2.2
if strings.HasPrefix(id, "cpe:2.3:") {
wfn, err := naming.UnbindFS(id)
if err != nil {
return []string{}, err
}
id = naming.BindToURI(wfn)
}
cpes = appendIfMissing(cpes, id)
}
}
return cpes, nil

33
cwe/cwe.go Normal file
View File

@@ -0,0 +1,33 @@
package cwe
// CweTopTwentyfive2019 has CWE-ID in CWE Top 25
var CweTopTwentyfive2019 = map[string]string{
"119": "1",
"79": "2",
"20": "3",
"200": "4",
"125": "5",
"89": "6",
"416": "7",
"190": "8",
"352": "9",
"22": "10",
"78": "11",
"787": "12",
"287": "13",
"476": "14",
"732": "16",
"434": "16",
"611": "17",
"94": "18",
"798": "19",
"400": "20",
"772": "21",
"426": "22",
"502": "23",
"269": "24",
"295": "25",
}
// CweTopTwentyfive2019URL has CWE Top25 links
var CweTopTwentyfive2019URL = "https://cwe.mitre.org/top25/archive/2019/2019_cwe_top25.html"

33
cwe/sans.go Normal file
View File

@@ -0,0 +1,33 @@
package cwe
// SansTopTwentyfive has CWE-ID in CWE/SANS Top 25
var SansTopTwentyfive = map[string]string{
"89": "1",
"78": "2",
"120": "3",
"79": "4",
"306": "5",
"862": "6",
"798": "7",
"311": "8",
"434": "9",
"807": "10",
"250": "11",
"352": "12",
"22": "13",
"494": "14",
"863": "15",
"829": "16",
"732": "17",
"676": "18",
"327": "19",
"131": "20",
"307": "21",
"601": "22",
"134": "23",
"190": "24",
"759": "25",
}
// SansTopTwentyfiveURL
var SansTopTwentyfiveURL = "https://www.sans.org/top25-software-errors/"

4
go.mod
View File

@@ -14,11 +14,11 @@ require (
github.com/Azure/go-autorest/autorest/to v0.3.0 // indirect
github.com/BurntSushi/toml v0.3.1
github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91
github.com/aquasecurity/fanal v0.0.0-20190819081512-f04452b627c6
github.com/aquasecurity/fanal v0.0.0-20200124194549-91468b8e0460
github.com/aquasecurity/go-dep-parser v0.0.0-20190819075924-ea223f0ef24b
github.com/aquasecurity/trivy v0.1.6
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a
github.com/aws/aws-sdk-go v1.24.0
github.com/aws/aws-sdk-go v1.25.31
github.com/boltdb/bolt v1.3.1
github.com/cenkalti/backoff v2.2.1+incompatible
github.com/dnaeon/go-vcr v1.0.1 // indirect

10
go.sum
View File

@@ -46,6 +46,8 @@ github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYU
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/aquasecurity/fanal v0.0.0-20190819081512-f04452b627c6 h1:pkl+kEW4KeLDPLfDtzjXa+zHOcS4YWSQuSTZ2kWO2GE=
github.com/aquasecurity/fanal v0.0.0-20190819081512-f04452b627c6/go.mod h1:enEz4FFetw4XAbkffaYgyCVq1556R9Ry+noqT4rq9BE=
github.com/aquasecurity/fanal v0.0.0-20200124194549-91468b8e0460 h1:8Dsyp9pt2I7MTSTbUlf/lLBK7IsIrcPTfXrl7Bx3NrA=
github.com/aquasecurity/fanal v0.0.0-20200124194549-91468b8e0460/go.mod h1:S2D937GMywJzh6KiLQEyt/0yqmfAngUFvuQ9UmkIZSw=
github.com/aquasecurity/go-dep-parser v0.0.0-20190819075924-ea223f0ef24b h1:55Ulc/gvfWm4ylhVaR7MxOwujRjA6et7KhmUbSgUFf4=
github.com/aquasecurity/go-dep-parser v0.0.0-20190819075924-ea223f0ef24b/go.mod h1:BpNTD9vHfrejKsED9rx04ldM1WIbeyXGYxUrqTVwxVQ=
github.com/aquasecurity/trivy v0.1.6 h1:bATT+9swX+tKw1QibOHQbofMUflRRpPF9wmiMTcZQgI=
@@ -54,8 +56,8 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/aws/aws-sdk-go v1.19.11/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.24.0 h1:TtQCY3HaFXeo1yg2JAdfSbpN/Nsd9V5SCfDksEf2nSE=
github.com/aws/aws-sdk-go v1.24.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.25.31 h1:14mdh3HsTgRekePPkYcCbAaEXJknc3mN7f4XfsiMMDA=
github.com/aws/aws-sdk-go v1.25.31/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
@@ -466,6 +468,8 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190909003024-a7b16738d86b h1:XfVGCX+0T4WOStkaOsJRllbsiImhB2jgVBGc9L0lPGc=
golang.org/x/net v0.0.0-20190909003024-a7b16738d86b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191108221443-4ba9e2ef068c h1:SRpq/kuj/xNci/RdvEs+RSvpfxqvLAzTKuKGlzoGdZQ=
golang.org/x/net v0.0.0-20191108221443-4ba9e2ef068c/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
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 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
@@ -495,6 +499,8 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190909082730-f460065e899a h1:mIzbOulag9/gXacgxKlFVwpCOWSfBT3/pDyyCwGA9as=
golang.org/x/sys v0.0.0-20190909082730-f460065e899a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd h1:3x5uuvBgE6oaXJjCOvpCC1IpgJogqQ+PqGGU3ZxAgII=
golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=

View File

@@ -120,18 +120,23 @@ func (p Package) FormatNewVer() string {
}
// FormatVersionFromTo formats installed and new package version
func (p Package) FormatVersionFromTo(notFixedYet bool, status string) string {
func (p Package) FormatVersionFromTo(stat PackageFixStatus) string {
to := p.FormatNewVer()
if notFixedYet {
if status != "" {
to = status
if stat.NotFixedYet {
if stat.FixState != "" {
to = stat.FixState
} else {
to = "Not Fixed Yet"
}
} else if p.NewVersion == "" {
to = "Unknown"
}
return fmt.Sprintf("%s-%s -> %s", p.Name, p.FormatVer(), to)
var fixedIn string
if stat.FixedIn != "" {
fixedIn = fmt.Sprintf(" (FixedIn: %s)", stat.FixedIn)
}
return fmt.Sprintf("%s-%s -> %s%s",
p.Name, p.FormatVer(), to, fixedIn)
}
// FormatChangelog formats the changelog

View File

@@ -175,3 +175,125 @@ func TestFindByBinName(t *testing.T) {
}
}
}
func TestPackage_FormatVersionFromTo(t *testing.T) {
type fields struct {
Name string
Version string
Release string
NewVersion string
NewRelease string
Arch string
Repository string
Changelog Changelog
AffectedProcs []AffectedProcess
NeedRestartProcs []NeedRestartProcess
}
type args struct {
stat PackageFixStatus
}
tests := []struct {
name string
fields fields
args args
want string
}{
{
name: "fixed",
fields: fields{
Name: "packA",
Version: "1.0.0",
Release: "a",
NewVersion: "1.0.1",
NewRelease: "b",
},
args: args{
stat: PackageFixStatus{
NotFixedYet: false,
FixedIn: "1.0.1-b",
},
},
want: "packA-1.0.0-a -> 1.0.1-b (FixedIn: 1.0.1-b)",
},
{
name: "nfy",
fields: fields{
Name: "packA",
Version: "1.0.0",
Release: "a",
},
args: args{
stat: PackageFixStatus{
NotFixedYet: true,
},
},
want: "packA-1.0.0-a -> Not Fixed Yet",
},
{
name: "nfy",
fields: fields{
Name: "packA",
Version: "1.0.0",
Release: "a",
},
args: args{
stat: PackageFixStatus{
NotFixedYet: false,
FixedIn: "1.0.1-b",
},
},
want: "packA-1.0.0-a -> Unknown (FixedIn: 1.0.1-b)",
},
{
name: "nfy2",
fields: fields{
Name: "packA",
Version: "1.0.0",
Release: "a",
},
args: args{
stat: PackageFixStatus{
NotFixedYet: true,
FixedIn: "1.0.1-b",
FixState: "open",
},
},
want: "packA-1.0.0-a -> open (FixedIn: 1.0.1-b)",
},
{
name: "nfy3",
fields: fields{
Name: "packA",
Version: "1.0.0",
Release: "a",
},
args: args{
stat: PackageFixStatus{
NotFixedYet: true,
FixedIn: "1.0.1-b",
FixState: "open",
},
},
want: "packA-1.0.0-a -> open (FixedIn: 1.0.1-b)",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := Package{
Name: tt.fields.Name,
Version: tt.fields.Version,
Release: tt.fields.Release,
NewVersion: tt.fields.NewVersion,
NewRelease: tt.fields.NewRelease,
Arch: tt.fields.Arch,
Repository: tt.fields.Repository,
Changelog: tt.fields.Changelog,
AffectedProcs: tt.fields.AffectedProcs,
NeedRestartProcs: tt.fields.NeedRestartProcs,
}
if got := p.FormatVersionFromTo(tt.args.stat); got != tt.want {
t.Errorf("Package.FormatVersionFromTo() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -62,7 +62,7 @@ type ScanResult struct {
type CweDict map[string]CweDictEntry
// Get the name, url, top10URL for the specified cweID, lang
func (c CweDict) Get(cweID, lang string) (name, url, top10Rank, top10URL string) {
func (c CweDict) Get(cweID, lang string) (name, url, top10Rank, top10URL, cweTop25Rank, cweTop25URL, sansTop25Rank, sansTop25URL string) {
cweNum := strings.TrimPrefix(cweID, "CWE-")
switch config.Conf.Lang {
case "ja":
@@ -70,6 +70,14 @@ func (c CweDict) Get(cweID, lang string) (name, url, top10Rank, top10URL string)
top10Rank = dict.OwaspTopTen2017
top10URL = cwe.OwaspTopTen2017GitHubURLJa[dict.OwaspTopTen2017]
}
if dict, ok := c[cweNum]; ok && dict.CweTopTwentyfive2019 != "" {
cweTop25Rank = dict.CweTopTwentyfive2019
cweTop25URL = cwe.CweTopTwentyfive2019URL
}
if dict, ok := c[cweNum]; ok && dict.SansTopTwentyfive != "" {
sansTop25Rank = dict.SansTopTwentyfive
sansTop25URL = cwe.SansTopTwentyfiveURL
}
if dict, ok := cwe.CweDictJa[cweNum]; ok {
name = dict.Name
url = fmt.Sprintf("http://jvndb.jvn.jp/ja/cwe/%s.html", cweID)
@@ -84,6 +92,14 @@ func (c CweDict) Get(cweID, lang string) (name, url, top10Rank, top10URL string)
top10Rank = dict.OwaspTopTen2017
top10URL = cwe.OwaspTopTen2017GitHubURLEn[dict.OwaspTopTen2017]
}
if dict, ok := c[cweNum]; ok && dict.CweTopTwentyfive2019 != "" {
cweTop25Rank = dict.CweTopTwentyfive2019
cweTop25URL = cwe.CweTopTwentyfive2019URL
}
if dict, ok := c[cweNum]; ok && dict.SansTopTwentyfive != "" {
sansTop25Rank = dict.SansTopTwentyfive
sansTop25URL = cwe.SansTopTwentyfiveURL
}
url = fmt.Sprintf("https://cwe.mitre.org/data/definitions/%s.html", cweID)
if dict, ok := cwe.CweDictEn[cweNum]; ok {
name = dict.Name
@@ -94,9 +110,11 @@ func (c CweDict) Get(cweID, lang string) (name, url, top10Rank, top10URL string)
// CweDictEntry is a entry of CWE
type CweDictEntry struct {
En *cwe.Cwe `json:"en,omitempty"`
Ja *cwe.Cwe `json:"ja,omitempty"`
OwaspTopTen2017 string `json:"owaspTopTen2017"`
En *cwe.Cwe `json:"en,omitempty"`
Ja *cwe.Cwe `json:"ja,omitempty"`
OwaspTopTen2017 string `json:"owaspTopTen2017"`
CweTopTwentyfive2019 string `json:"cweTopTwentyfive2019"`
SansTopTwentyfive string `json:"sansTopTwentyfive"`
}
// Kernel has the Release, version and whether need restart
@@ -255,7 +273,7 @@ func (r ScanResult) FilterInactiveWordPressLibs() ScanResult {
return r
}
// ReportFileName returns the filename on localhost without extention
// ReportFileName returns the filename on localhost without extension
func (r ScanResult) ReportFileName() (name string) {
if len(r.Container.ContainerID) == 0 {
return fmt.Sprintf("%s", r.ServerName)
@@ -263,7 +281,7 @@ func (r ScanResult) ReportFileName() (name string) {
return fmt.Sprintf("%s@%s", r.Container.Name, r.ServerName)
}
// ReportKeyName returns the name of key on S3, Azure-Blob without extention
// ReportKeyName returns the name of key on S3, Azure-Blob without extension
func (r ScanResult) ReportKeyName() (name string) {
timestr := r.ScannedAt.Format(time.RFC3339)
if len(r.Container.ContainerID) == 0 {
@@ -445,8 +463,9 @@ type Container struct {
// Image has Container information
type Image struct {
Name string `json:"name"`
Tag string `json:"tag"`
Name string `json:"name"`
Tag string `json:"tag"`
Digest string `json:"digest"`
}
// Platform has platform information

View File

@@ -136,9 +136,10 @@ func (ps PackageFixStatuses) Sort() {
// PackageFixStatus has name and other status abount the package
type PackageFixStatus struct {
Name string `json:"name"`
NotFixedYet bool `json:"notFixedYet"`
FixState string `json:"fixState"`
Name string `json:"name,omitempty"`
NotFixedYet bool `json:"notFixedYet,omitempty"`
FixState string `json:"fixState,omitempty"`
FixedIn string `json:"fixedIn,omitempty"`
}
// VulnInfo has a vulnerability information and unsecure packages
@@ -534,15 +535,16 @@ func (v VulnInfo) MaxCvss2Score() CveContentCvss {
func (v VulnInfo) AttackVector() string {
for _, cnt := range v.CveContents {
if strings.HasPrefix(cnt.Cvss2Vector, "AV:N") ||
strings.HasPrefix(cnt.Cvss3Vector, "CVSS:3.0/AV:N") {
strings.Contains(cnt.Cvss3Vector, "AV:N") {
return "AV:N"
} else if strings.HasPrefix(cnt.Cvss2Vector, "AV:A") ||
strings.HasPrefix(cnt.Cvss3Vector, "CVSS:3.0/AV:A") {
strings.Contains(cnt.Cvss3Vector, "AV:A") {
return "AV:A"
} else if strings.HasPrefix(cnt.Cvss2Vector, "AV:L") ||
strings.HasPrefix(cnt.Cvss3Vector, "CVSS:3.0/AV:L") {
strings.Contains(cnt.Cvss3Vector, "AV:L") {
return "AV:L"
} else if strings.HasPrefix(cnt.Cvss3Vector, "CVSS:3.0/AV:P") {
} else if strings.Contains(cnt.Cvss3Vector, "AV:P") {
// no AV:P in CVSS v2
return "AV:P"
}
}

View File

@@ -1080,3 +1080,86 @@ func TestDistroAdvisories_AppendIfMissing(t *testing.T) {
})
}
}
func TestVulnInfo_AttackVector(t *testing.T) {
type fields struct {
CveContents CveContents
}
tests := []struct {
name string
fields fields
want string
}{
{
name: "2.0:N",
fields: fields{
CveContents: NewCveContents(
CveContent{
Type: "foo",
Cvss2Vector: "AV:N/AC:L/Au:N/C:C/I:C/A:C",
},
),
},
want: "AV:N",
},
{
name: "2.0:A",
fields: fields{
CveContents: NewCveContents(
CveContent{
Type: "foo",
Cvss2Vector: "AV:A/AC:L/Au:N/C:C/I:C/A:C",
},
),
},
want: "AV:A",
},
{
name: "2.0:L",
fields: fields{
CveContents: NewCveContents(
CveContent{
Type: "foo",
Cvss2Vector: "AV:L/AC:L/Au:N/C:C/I:C/A:C",
},
),
},
want: "AV:L",
},
{
name: "3.0:N",
fields: fields{
CveContents: NewCveContents(
CveContent{
Type: "foo",
Cvss3Vector: "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
},
),
},
want: "AV:N",
},
{
name: "3.1:N",
fields: fields{
CveContents: NewCveContents(
CveContent{
Type: "foo",
Cvss3Vector: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
},
),
},
want: "AV:N",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
v := VulnInfo{
CveContents: tt.fields.CveContents,
}
if got := v.AttackVector(); got != tt.want {
t.Errorf("VulnInfo.AttackVector() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -42,17 +42,28 @@ func (o DebianBase) update(r *models.ScanResult, defPacks defPacks) {
vinfo.CveContents = cveContents
}
// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
// uniq(vinfo.PackNames + defPacks.binpkgStat)
for _, pack := range vinfo.AffectedPackages {
defPacks.actuallyAffectedPackNames[pack.Name] = pack.NotFixedYet
defPacks.binpkgFixstat[pack.Name] = fixStat{
notFixedYet: pack.NotFixedYet,
fixedIn: pack.FixedIn,
isSrcPack: false,
}
}
// update notFixedYet of SrcPackage
for binName := range defPacks.actuallyAffectedPackNames {
// Update package status of source packages.
// In the case of Debian based Linux, sometimes source package name is difined as affected package in OVAL.
// To display binary package name showed in apt-get, need to convert source name to binary name.
for binName := range defPacks.binpkgFixstat {
if srcPack, ok := r.SrcPackages.FindByBinName(binName); ok {
for _, p := range defPacks.def.AffectedPacks {
if p.Name == srcPack.Name {
defPacks.actuallyAffectedPackNames[binName] = p.NotFixedYet
defPacks.binpkgFixstat[binName] = fixStat{
notFixedYet: p.NotFixedYet,
fixedIn: p.Version,
isSrcPack: true,
srcPackName: srcPack.Name,
}
}
}
}
@@ -134,9 +145,9 @@ func (o Debian) FillWithOval(driver db.DB, r *models.ScanResult) (nCVEs int, err
for _, defPacks := range relatedDefs.entries {
// Remove "linux" added above for oval search
// linux is not a real package name (key of affected packages in OVAL)
if notFixedYet, ok := defPacks.actuallyAffectedPackNames["linux"]; ok {
defPacks.actuallyAffectedPackNames[linuxImage] = notFixedYet
delete(defPacks.actuallyAffectedPackNames, "linux")
if notFixedYet, ok := defPacks.binpkgFixstat["linux"]; ok {
defPacks.binpkgFixstat[linuxImage] = notFixedYet
delete(defPacks.binpkgFixstat, "linux")
for i, p := range defPacks.def.AffectedPacks {
if p.Name == "linux" {
p.Name = linuxImage
@@ -309,11 +320,11 @@ func (o Ubuntu) fillWithOval(driver db.DB, r *models.ScanResult, kernelNamesInOv
}
for _, defPacks := range relatedDefs.entries {
// Remove "linux" added above to search for oval
// Remove "linux" added above for searching oval
// "linux" is not a real package name (key of affected packages in OVAL)
if nfy, ok := defPacks.actuallyAffectedPackNames[kernelPkgInOVAL]; isOVALKernelPkgAdded && ok {
defPacks.actuallyAffectedPackNames[linuxImage] = nfy
delete(defPacks.actuallyAffectedPackNames, kernelPkgInOVAL)
if nfy, ok := defPacks.binpkgFixstat[kernelPkgInOVAL]; isOVALKernelPkgAdded && ok {
defPacks.binpkgFixstat[linuxImage] = nfy
delete(defPacks.binpkgFixstat, kernelPkgInOVAL)
for i, p := range defPacks.def.AffectedPacks {
if p.Name == kernelPkgInOVAL {
p.Name = linuxImage

View File

@@ -33,8 +33,11 @@ func TestPackNamesOfUpdateDebian(t *testing.T) {
CveID: "CVE-2000-1000",
},
},
actuallyAffectedPackNames: map[string]bool{
"packB": true,
binpkgFixstat: map[string]fixStat{
"packB": fixStat{
notFixedYet: true,
fixedIn: "1.0.0",
},
},
},
out: models.ScanResult{
@@ -42,7 +45,7 @@ func TestPackNamesOfUpdateDebian(t *testing.T) {
"CVE-2000-1000": models.VulnInfo{
AffectedPackages: models.PackageFixStatuses{
{Name: "packA"},
{Name: "packB", NotFixedYet: true},
{Name: "packB", NotFixedYet: true, FixedIn: "1.0.0"},
{Name: "packC"},
},
},
@@ -57,7 +60,7 @@ func TestPackNamesOfUpdateDebian(t *testing.T) {
e := tt.out.ScannedCves["CVE-2000-1000"].AffectedPackages
a := tt.in.ScannedCves["CVE-2000-1000"].AffectedPackages
if !reflect.DeepEqual(a, e) {
t.Errorf("[%d] expected: %v\n actual: %v\n", i, e, a)
t.Errorf("[%d] expected: %#v\n actual: %#v\n", i, e, a)
}
}
}

View File

@@ -120,10 +120,16 @@ func (o RedHatBase) update(r *models.ScanResult, defPacks defPacks) (nCVEs int)
// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
for _, pack := range vinfo.AffectedPackages {
if nfy, ok := defPacks.actuallyAffectedPackNames[pack.Name]; !ok {
defPacks.actuallyAffectedPackNames[pack.Name] = pack.NotFixedYet
} else if nfy {
defPacks.actuallyAffectedPackNames[pack.Name] = true
if stat, ok := defPacks.binpkgFixstat[pack.Name]; !ok {
defPacks.binpkgFixstat[pack.Name] = fixStat{
notFixedYet: pack.NotFixedYet,
fixedIn: pack.FixedIn,
}
} else if stat.notFixedYet {
defPacks.binpkgFixstat[pack.Name] = fixStat{
notFixedYet: true,
fixedIn: pack.FixedIn,
}
}
}
vinfo.AffectedPackages = defPacks.toPackStatuses()
@@ -219,12 +225,17 @@ func (o RedHatBase) parseCvss2(scoreVector string) (score float64, vector string
// 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
ss := strings.Split(scoreVector, "/CVSS:3.0/")
if 1 < len(ss) {
if score, err = strconv.ParseFloat(ss[0], 64); err != nil {
return 0, ""
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 score, fmt.Sprintf("CVSS:3.0/%s", ss[1])
}
return 0, ""
}

View File

@@ -59,6 +59,13 @@ func TestParseCvss3(t *testing.T) {
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{
@@ -103,8 +110,11 @@ func TestPackNamesOfUpdate(t *testing.T) {
},
},
},
actuallyAffectedPackNames: map[string]bool{
"packB": true,
binpkgFixstat: map[string]fixStat{
"packB": fixStat{
notFixedYet: true,
fixedIn: "1.0.0",
},
},
},
out: models.ScanResult{

View File

@@ -75,7 +75,10 @@ func (o SUSE) update(r *models.ScanResult, defPacks defPacks) {
// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
for _, pack := range vinfo.AffectedPackages {
defPacks.actuallyAffectedPackNames[pack.Name] = pack.NotFixedYet
defPacks.binpkgFixstat[pack.Name] = fixStat{
notFixedYet: pack.NotFixedYet,
fixedIn: pack.FixedIn,
}
}
vinfo.AffectedPackages = defPacks.toPackStatuses()
vinfo.AffectedPackages.Sort()

View File

@@ -27,32 +27,42 @@ type defPacks struct {
def ovalmodels.Definition
// BinaryPackageName : NotFixedYet
actuallyAffectedPackNames map[string]bool
binpkgFixstat map[string]fixStat
}
type fixStat struct {
notFixedYet bool
fixedIn string
isSrcPack bool
srcPackName string
}
func (e defPacks) toPackStatuses() (ps models.PackageFixStatuses) {
for name, notFixedYet := range e.actuallyAffectedPackNames {
for name, stat := range e.binpkgFixstat {
ps = append(ps, models.PackageFixStatus{
Name: name,
NotFixedYet: notFixedYet,
NotFixedYet: stat.notFixedYet,
FixedIn: stat.fixedIn,
})
}
return
}
func (e *ovalResult) upsert(def ovalmodels.Definition, packName string, notFixedYet bool) (upserted bool) {
func (e *ovalResult) upsert(def ovalmodels.Definition, packName string, fstat fixStat) (upserted bool) {
// alpine's entry is empty since Alpine secdb is not OVAL format
if def.DefinitionID != "" {
for i, entry := range e.entries {
if entry.def.DefinitionID == def.DefinitionID {
e.entries[i].actuallyAffectedPackNames[packName] = notFixedYet
e.entries[i].binpkgFixstat[packName] = fstat
return true
}
}
}
e.entries = append(e.entries, defPacks{
def: def,
actuallyAffectedPackNames: map[string]bool{packName: notFixedYet},
def: def,
binpkgFixstat: map[string]fixStat{
packName: fstat,
},
})
return false
@@ -134,17 +144,27 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult) (
select {
case res := <-resChan:
for _, def := range res.defs {
affected, notFixedYet := isOvalDefAffected(def, res.request, r.Family, r.RunningKernel)
affected, notFixedYet, fixedIn := isOvalDefAffected(def, res.request, r.Family, r.RunningKernel)
if !affected {
continue
}
if res.request.isSrcPack {
for _, n := range res.request.binaryPackNames {
relatedDefs.upsert(def, n, false)
fs := fixStat{
srcPackName: res.request.packName,
isSrcPack: true,
notFixedYet: notFixedYet,
fixedIn: fixedIn,
}
relatedDefs.upsert(def, n, fs)
}
} else {
relatedDefs.upsert(def, res.request.packName, notFixedYet)
fs := fixStat{
notFixedYet: notFixedYet,
fixedIn: fixedIn,
}
relatedDefs.upsert(def, res.request.packName, fs)
}
}
case err := <-errChan:
@@ -227,17 +247,27 @@ 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 := isOvalDefAffected(def, req, r.Family, r.RunningKernel)
affected, notFixedYet, fixedIn := isOvalDefAffected(def, req, r.Family, r.RunningKernel)
if !affected {
continue
}
if req.isSrcPack {
for _, n := range req.binaryPackNames {
relatedDefs.upsert(def, n, false)
for _, binName := range req.binaryPackNames {
fs := fixStat{
notFixedYet: false,
isSrcPack: true,
fixedIn: fixedIn,
srcPackName: req.packName,
}
relatedDefs.upsert(def, binName, fs)
}
} else {
relatedDefs.upsert(def, req.packName, notFixedYet)
fs := fixStat{
notFixedYet: notFixedYet,
fixedIn: fixedIn,
}
relatedDefs.upsert(def, req.packName, fs)
}
}
}
@@ -255,7 +285,7 @@ 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) {
func isOvalDefAffected(def ovalmodels.Definition, req request, family string, running models.Kernel) (affected, notFixedYet bool, fixedIn string) {
for _, ovalPack := range def.AffectedPacks {
if req.packName != ovalPack.Name {
continue
@@ -274,7 +304,7 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family string, ru
}
if ovalPack.NotFixedYet {
return true, true
return true, true, ovalPack.Version
}
// Compare between the installed version vs the version in OVAL
@@ -282,9 +312,14 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family string, ru
if err != nil {
util.Log.Debugf("Failed to parse versions: %s, Ver: %#v, OVAL: %#v, DefID: %s",
err, req.versionRelease, ovalPack, def.DefinitionID)
return false, false
return false, false, ovalPack.Version
}
if less {
if req.isSrcPack {
// Unable to judge whether fixed or not-fixed of src package(Ubuntu, Debian)
return true, false, ovalPack.Version
}
// If the version of installed is less than in OVAL
switch family {
case config.RedHat,
@@ -293,7 +328,7 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family string, ru
config.Debian,
config.Ubuntu:
// Use fixed state in OVAL for these distros.
return true, false
return true, false, ovalPack.Version
}
// But CentOS can't judge whether fixed or unfixed.
@@ -304,7 +339,7 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family string, ru
// In these mode, the blow field was set empty.
// Vuls can not judge fixed or unfixed.
if req.newVersionRelease == "" {
return true, false
return true, false, ovalPack.Version
}
// compare version: newVer vs oval
@@ -312,12 +347,12 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family string, ru
if err != nil {
util.Log.Debugf("Failed to parse versions: %s, NewVer: %#v, OVAL: %#v, DefID: %s",
err, req.newVersionRelease, ovalPack, def.DefinitionID)
return false, false
return false, false, ovalPack.Version
}
return true, less
return true, less, ovalPack.Version
}
}
return false, false
return false, false, ""
}
var centosVerPattern = regexp.MustCompile(`\.[es]l(\d+)(?:_\d+)?(?:\.centos)?`)

View File

@@ -12,12 +12,12 @@ import (
func TestUpsert(t *testing.T) {
var tests = []struct {
res ovalResult
def ovalmodels.Definition
packName string
notFixedYet bool
upserted bool
out ovalResult
res ovalResult
def ovalmodels.Definition
packName string
fixStat fixStat
upserted bool
out ovalResult
}{
//insert
{
@@ -25,17 +25,23 @@ func TestUpsert(t *testing.T) {
def: ovalmodels.Definition{
DefinitionID: "1111",
},
packName: "pack1",
notFixedYet: true,
upserted: false,
packName: "pack1",
fixStat: fixStat{
notFixedYet: true,
fixedIn: "1.0.0",
},
upserted: false,
out: ovalResult{
[]defPacks{
{
def: ovalmodels.Definition{
DefinitionID: "1111",
},
actuallyAffectedPackNames: map[string]bool{
"pack1": true,
binpkgFixstat: map[string]fixStat{
"pack1": fixStat{
notFixedYet: true,
fixedIn: "1.0.0",
},
},
},
},
@@ -49,16 +55,22 @@ func TestUpsert(t *testing.T) {
def: ovalmodels.Definition{
DefinitionID: "1111",
},
actuallyAffectedPackNames: map[string]bool{
"pack1": true,
binpkgFixstat: map[string]fixStat{
"pack1": fixStat{
notFixedYet: true,
fixedIn: "1.0.0",
},
},
},
{
def: ovalmodels.Definition{
DefinitionID: "2222",
},
actuallyAffectedPackNames: map[string]bool{
"pack3": true,
binpkgFixstat: map[string]fixStat{
"pack3": fixStat{
notFixedYet: true,
fixedIn: "2.0.0",
},
},
},
},
@@ -66,26 +78,38 @@ func TestUpsert(t *testing.T) {
def: ovalmodels.Definition{
DefinitionID: "1111",
},
packName: "pack2",
notFixedYet: false,
upserted: true,
packName: "pack2",
fixStat: fixStat{
notFixedYet: false,
fixedIn: "3.0.0",
},
upserted: true,
out: ovalResult{
[]defPacks{
{
def: ovalmodels.Definition{
DefinitionID: "1111",
},
actuallyAffectedPackNames: map[string]bool{
"pack1": true,
"pack2": false,
binpkgFixstat: map[string]fixStat{
"pack1": fixStat{
notFixedYet: true,
fixedIn: "1.0.0",
},
"pack2": fixStat{
notFixedYet: false,
fixedIn: "3.0.0",
},
},
},
{
def: ovalmodels.Definition{
DefinitionID: "2222",
},
actuallyAffectedPackNames: map[string]bool{
"pack3": true,
binpkgFixstat: map[string]fixStat{
"pack3": fixStat{
notFixedYet: true,
fixedIn: "2.0.0",
},
},
},
},
@@ -93,7 +117,7 @@ func TestUpsert(t *testing.T) {
},
}
for i, tt := range tests {
upserted := tt.res.upsert(tt.def, tt.packName, tt.notFixedYet)
upserted := tt.res.upsert(tt.def, tt.packName, tt.fixStat)
if tt.upserted != upserted {
t.Errorf("[%d]\nexpected: %t\n actual: %t\n", i, tt.upserted, upserted)
}
@@ -121,17 +145,27 @@ func TestDefpacksToPackStatuses(t *testing.T) {
{
Name: "a",
NotFixedYet: true,
Version: "1.0.0",
},
{
Name: "b",
NotFixedYet: false,
Version: "2.0.0",
},
},
},
actuallyAffectedPackNames: map[string]bool{
"a": true,
"b": true,
"c": true,
binpkgFixstat: map[string]fixStat{
"a": fixStat{
notFixedYet: true,
fixedIn: "1.0.0",
isSrcPack: false,
},
"b": fixStat{
notFixedYet: true,
fixedIn: "1.0.0",
isSrcPack: true,
srcPackName: "lib-b",
},
},
},
},
@@ -139,14 +173,12 @@ func TestDefpacksToPackStatuses(t *testing.T) {
{
Name: "a",
NotFixedYet: true,
FixedIn: "1.0.0",
},
{
Name: "b",
NotFixedYet: true,
},
{
Name: "c",
NotFixedYet: true,
FixedIn: "1.0.0",
},
},
},
@@ -173,6 +205,7 @@ func TestIsOvalDefAffected(t *testing.T) {
in in
affected bool
notFixedYet bool
fixedIn string
}{
// 0. Ubuntu ovalpack.NotFixedYet == true
{
@@ -187,6 +220,7 @@ func TestIsOvalDefAffected(t *testing.T) {
{
Name: "b",
NotFixedYet: true,
Version: "1.0.0",
},
},
},
@@ -196,6 +230,7 @@ func TestIsOvalDefAffected(t *testing.T) {
},
affected: true,
notFixedYet: true,
fixedIn: "1.0.0",
},
// 1. Ubuntu
// ovalpack.NotFixedYet == false
@@ -226,6 +261,7 @@ func TestIsOvalDefAffected(t *testing.T) {
},
affected: true,
notFixedYet: false,
fixedIn: "1.0.0-1",
},
// 2. Ubuntu
// ovalpack.NotFixedYet == false
@@ -285,6 +321,7 @@ func TestIsOvalDefAffected(t *testing.T) {
},
},
affected: true,
fixedIn: "1.0.0-3",
notFixedYet: false,
},
// 4. Ubuntu
@@ -318,6 +355,7 @@ func TestIsOvalDefAffected(t *testing.T) {
},
affected: true,
notFixedYet: false,
fixedIn: "1.0.0-2",
},
// 5 RedHat
{
@@ -345,6 +383,7 @@ func TestIsOvalDefAffected(t *testing.T) {
},
affected: true,
notFixedYet: false,
fixedIn: "0:1.2.3-45.el6_7.8",
},
// 6 RedHat
{
@@ -372,6 +411,7 @@ func TestIsOvalDefAffected(t *testing.T) {
},
affected: true,
notFixedYet: false,
fixedIn: "0:1.2.3-45.el6_7.8",
},
// 7 RedHat
{
@@ -451,6 +491,7 @@ func TestIsOvalDefAffected(t *testing.T) {
},
affected: true,
notFixedYet: false,
fixedIn: "0:1.2.3-45.el6_7.8",
},
// 10 RedHat
{
@@ -478,6 +519,7 @@ func TestIsOvalDefAffected(t *testing.T) {
},
affected: true,
notFixedYet: false,
fixedIn: "0:1.2.3-45.el6_7.8",
},
// 11 RedHat
{
@@ -504,6 +546,7 @@ func TestIsOvalDefAffected(t *testing.T) {
},
affected: true,
notFixedYet: false,
fixedIn: "0:1.2.3-45.el6_7.8",
},
// 12 RedHat
{
@@ -583,6 +626,7 @@ func TestIsOvalDefAffected(t *testing.T) {
},
affected: true,
notFixedYet: false,
fixedIn: "0:1.2.3-45.el6_7.8",
},
// 15
{
@@ -662,6 +706,7 @@ func TestIsOvalDefAffected(t *testing.T) {
},
affected: true,
notFixedYet: true,
fixedIn: "0:1.2.3-45.el6_7.8",
},
// 18
{
@@ -689,6 +734,7 @@ func TestIsOvalDefAffected(t *testing.T) {
},
affected: true,
notFixedYet: false,
fixedIn: "0:1.2.3-45.el6_7.8",
},
// 19
{
@@ -716,6 +762,7 @@ func TestIsOvalDefAffected(t *testing.T) {
},
affected: true,
notFixedYet: false,
fixedIn: "0:1.2.3-45.el6_7.8",
},
// 20
{
@@ -794,6 +841,7 @@ func TestIsOvalDefAffected(t *testing.T) {
},
affected: true,
notFixedYet: false,
fixedIn: "0:1.2.3-45.el6_7.8",
},
{
in: in{
@@ -870,6 +918,7 @@ func TestIsOvalDefAffected(t *testing.T) {
},
affected: true,
notFixedYet: true,
fixedIn: "0:1.2.3-45.el6_7.8",
},
{
in: in{
@@ -896,6 +945,7 @@ func TestIsOvalDefAffected(t *testing.T) {
},
affected: true,
notFixedYet: false,
fixedIn: "0:1.2.3-45.el6_7.8",
},
{
in: in{
@@ -922,6 +972,7 @@ func TestIsOvalDefAffected(t *testing.T) {
},
affected: true,
notFixedYet: false,
fixedIn: "0:1.2.3-45.el6_7.8",
},
{
in: in{
@@ -1021,16 +1072,20 @@ func TestIsOvalDefAffected(t *testing.T) {
},
affected: true,
notFixedYet: false,
fixedIn: "3.1.0",
},
}
for i, tt := range tests {
affected, notFixedYet := 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)
if tt.affected != affected {
t.Errorf("[%d] affected\nexpected: %v\n actual: %v\n", i, tt.affected, affected)
}
if tt.notFixedYet != notFixedYet {
t.Errorf("[%d] notfixedyet\nexpected: %v\n actual: %v\n", i, tt.notFixedYet, notFixedYet)
}
if tt.fixedIn != fixedIn {
t.Errorf("[%d] fixedIn\nexpected: %v\n actual: %v\n", i, tt.fixedIn, fixedIn)
}
}
}

View File

@@ -467,6 +467,12 @@ func fillCweDict(r *models.ScanResult) {
if rank, ok := cwe.OwaspTopTen2017[id]; ok {
entry.OwaspTopTen2017 = rank
}
if rank, ok := cwe.CweTopTwentyfive2019[id]; ok {
entry.CweTopTwentyfive2019 = rank
}
if rank, ok := cwe.SansTopTwentyfive[id]; ok {
entry.SansTopTwentyfive = rank
}
entry.En = &e
} else {
util.Log.Debugf("CWE-ID %s is not found in English CWE Dict", id)
@@ -478,6 +484,12 @@ func fillCweDict(r *models.ScanResult) {
if rank, ok := cwe.OwaspTopTen2017[id]; ok {
entry.OwaspTopTen2017 = rank
}
if rank, ok := cwe.CweTopTwentyfive2019[id]; ok {
entry.CweTopTwentyfive2019 = rank
}
if rank, ok := cwe.SansTopTwentyfive[id]; ok {
entry.SansTopTwentyfive = rank
}
entry.Ja = &e
} else {
util.Log.Debugf("CWE-ID %s is not found in Japanese CWE Dict", id)
@@ -530,7 +542,7 @@ func EnsureUUIDs(configPath string, results models.ScanResults) error {
server.UUIDs[r.ServerName] = uuid
}
} else if r.IsImage() {
name = fmt.Sprintf("%s:%s@%s", r.Image.Name, r.Image.Tag, r.ServerName)
name = fmt.Sprintf("%s%s@%s", r.Image.Tag, r.Image.Digest, r.ServerName)
if uuid := getOrCreateServerUUID(r, server); uuid != "" {
server.UUIDs[r.ServerName] = uuid
}

View File

@@ -329,14 +329,24 @@ func attachmentText(vinfo models.VulnInfo, osFamily string, cweDict map[string]m
func cweIDs(vinfo models.VulnInfo, osFamily string, cweDict models.CweDict) string {
links := []string{}
for _, c := range vinfo.CveContents.UniqCweIDs(osFamily) {
name, url, top10Rank, top10URL := cweDict.Get(c.Value, osFamily)
name, url, top10Rank, top10URL, cweTop25Rank, cweTop25URL, sansTop25Rank, sansTop25URL := cweDict.Get(c.Value, osFamily)
line := ""
if top10Rank != "" {
line = fmt.Sprintf("<%s|[OWASP Top %s]>",
top10URL, top10Rank)
}
links = append(links, fmt.Sprintf("%s <%s|%s>: %s",
line, url, c.Value, name))
if cweTop25Rank != "" {
line = fmt.Sprintf("<%s|[CWE Top %s]>",
cweTop25URL, cweTop25Rank)
}
if sansTop25Rank != "" {
line = fmt.Sprintf("<%s|[CWE/SANS Top %s]>",
sansTop25URL, sansTop25Rank)
}
if top10Rank == "" && cweTop25Rank == "" && sansTop25Rank == "" {
links = append(links, fmt.Sprintf("%s <%s|%s>: %s",
line, url, c.Value, name))
}
}
return strings.Join(links, "\n")
}

View File

@@ -706,12 +706,10 @@ func setChangelogLayout(g *gocui.Gui) error {
var line string
if pack.Repository != "" {
line = fmt.Sprintf("* %s (%s)",
pack.FormatVersionFromTo(affected.NotFixedYet, affected.FixState),
pack.FormatVersionFromTo(affected),
pack.Repository)
} else {
line = fmt.Sprintf("* %s",
pack.FormatVersionFromTo(affected.NotFixedYet, affected.FixState),
)
line = fmt.Sprintf("* %s", pack.FormatVersionFromTo(affected))
}
lines = append(lines, line)

View File

@@ -217,14 +217,28 @@ No CVE-IDs are found in updatable packages.
}
cweURLs, top10URLs := []string{}, []string{}
cweTop25URLs, sansTop25URLs := []string{}, []string{}
for _, v := range vuln.CveContents.UniqCweIDs(r.Family) {
name, url, top10Rank, top10URL := r.CweDict.Get(v.Value, r.Lang)
name, url, top10Rank, top10URL, cweTop25Rank, cweTop25URL, sansTop25Rank, sansTop25URL := r.CweDict.Get(v.Value, r.Lang)
if top10Rank != "" {
data = append(data, []string{"CWE",
fmt.Sprintf("[OWASP Top%s] %s: %s (%s)",
top10Rank, v.Value, name, v.Type)})
top10URLs = append(top10URLs, top10URL)
} else {
}
if cweTop25Rank != "" {
data = append(data, []string{"CWE",
fmt.Sprintf("[CWE Top%s] %s: %s (%s)",
cweTop25Rank, v.Value, name, v.Type)})
cweTop25URLs = append(cweTop25URLs, cweTop25URL)
}
if sansTop25Rank != "" {
data = append(data, []string{"CWE",
fmt.Sprintf("[CWE/SANS Top%s] %s: %s (%s)",
sansTop25Rank, v.Value, name, v.Type)})
sansTop25URLs = append(sansTop25URLs, sansTop25URL)
}
if top10Rank == "" && cweTop25Rank == "" && sansTop25Rank == "" {
data = append(data, []string{"CWE", fmt.Sprintf("%s: %s (%s)",
v.Value, name, v.Type)})
}
@@ -237,12 +251,10 @@ No CVE-IDs are found in updatable packages.
var line string
if pack.Repository != "" {
line = fmt.Sprintf("%s (%s)",
pack.FormatVersionFromTo(affected.NotFixedYet, affected.FixState),
pack.FormatVersionFromTo(affected),
pack.Repository)
} else {
line = fmt.Sprintf("%s",
pack.FormatVersionFromTo(affected.NotFixedYet, affected.FixState),
)
line = pack.FormatVersionFromTo(affected)
}
data = append(data, []string{"Affected Pkg", line})
@@ -309,6 +321,12 @@ No CVE-IDs are found in updatable packages.
for _, url := range top10URLs {
data = append(data, []string{"OWASP Top10", url})
}
if len(cweTop25URLs) != 0 {
data = append(data, []string{"CWE Top25", cweTop25URLs[0]})
}
if len(sansTop25URLs) != 0 {
data = append(data, []string{"SANS/CWE Top25", sansTop25URLs[0]})
}
for _, alert := range vuln.AlertDict.Ja {
data = append(data, []string{"JPCERT Alert", alert.URL})

View File

@@ -417,8 +417,9 @@ func (l *base) convertToModel() models.ScanResult {
}
image := models.Image{
Name: l.ServerInfo.Image.Name,
Tag: l.ServerInfo.Image.Tag,
Name: l.ServerInfo.Image.Name,
Tag: l.ServerInfo.Image.Tag,
Digest: l.ServerInfo.Image.Digest,
}
errs, warns := []string{}, []string{}

View File

@@ -49,11 +49,8 @@ func (o *centos) depsFast() []string {
}
// repoquery
majorVersion, _ := o.Distro.MajorVersion()
if majorVersion < 8 {
return []string{"yum-utils"}
}
return []string{"dnf-utils"}
// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8
return []string{"yum-utils"}
}
func (o *centos) depsFastRoot() []string {
@@ -62,11 +59,8 @@ func (o *centos) depsFastRoot() []string {
}
// repoquery
majorVersion, _ := o.Distro.MajorVersion()
if majorVersion < 8 {
return []string{"yum-utils"}
}
return []string{"dnf-utils"}
// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8
return []string{"yum-utils"}
}
func (o *centos) depsDeep() []string {

View File

@@ -7,6 +7,9 @@ import (
"time"
"github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/fanal/extractor/docker"
"github.com/aquasecurity/fanal/utils"
"golang.org/x/xerrors"
fanalos "github.com/aquasecurity/fanal/analyzer/os"
@@ -28,8 +31,8 @@ import (
_ "github.com/aquasecurity/fanal/analyzer/os/alpine"
_ "github.com/aquasecurity/fanal/analyzer/os/amazonlinux"
_ "github.com/aquasecurity/fanal/analyzer/os/debianbase"
_ "github.com/aquasecurity/fanal/analyzer/os/opensuse"
_ "github.com/aquasecurity/fanal/analyzer/os/redhatbase"
_ "github.com/aquasecurity/fanal/analyzer/os/suse"
// Register package analyzers
_ "github.com/aquasecurity/fanal/analyzer/pkg/apk"
@@ -102,15 +105,21 @@ func convertLibWithScanner(libs map[analyzer.FilePath][]godeptypes.Library) ([]m
func scanImage(c config.ServerInfo) (os *analyzer.OS, pkgs []analyzer.Package, libs map[analyzer.FilePath][]godeptypes.Library, err error) {
ctx := context.Background()
domain := c.Image.Name + ":" + c.Image.Tag
domain := c.Image.GetFullName()
util.Log.Info("Start fetch container... ", domain)
fanalCache := cache.Initialize(utils.CacheDir())
// Configure dockerOption
dockerOption := c.Image.DockerOption
if dockerOption.Timeout == 0 {
dockerOption.Timeout = 60 * time.Second
}
files, err := analyzer.Analyze(ctx, domain, dockerOption)
ext, err := docker.NewDockerExtractor(dockerOption, fanalCache)
if err != nil {
return nil, nil, nil, xerrors.Errorf("Failed initialize docker extractor%w", err)
}
ac := analyzer.Config{Extractor: ext}
files, err := ac.Analyze(ctx, domain, dockerOption)
if err != nil {
return nil, nil, nil, xerrors.Errorf("Failed scan files %q, %w", domain, err)

View File

@@ -1,9 +1,9 @@
package scan
import (
"sort"
"os"
"reflect"
"sort"
"testing"
"github.com/future-architect/vuls/cache"

View File

@@ -315,7 +315,7 @@ if-not-architecture 0 100 200 amzn-main`
}
func TestParseNeedsRestarting(t *testing.T) {
r := newCentOS(config.ServerInfo{})
r := newRHEL(config.ServerInfo{})
r.Distro = config.Distro{Family: "centos"}
var tests = []struct {
@@ -323,7 +323,7 @@ func TestParseNeedsRestarting(t *testing.T) {
out []models.NeedRestartProcess
}{
{
`1 : /usr/lib/systemd/systemd --switched-root --system --deserialize 21
`1 : /usr/lib/systemd/systemd --switched-root --system --deserialize 21kk
437 : /usr/sbin/NetworkManager --no-daemon`,
[]models.NeedRestartProcess{
{

View File

@@ -55,11 +55,8 @@ func (o *rhel) depsFastRoot() []string {
}
// repoquery
majorVersion, _ := o.Distro.MajorVersion()
if majorVersion < 8 {
return []string{"yum-utils"}
}
return []string{"dnf-utils"}
// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8
return []string{"yum-utils"}
}
func (o *rhel) depsDeep() []string {

View File

@@ -497,11 +497,11 @@ func detectImageOSesOnServer(containerHost osTypeInterface) (oses []osTypeInterf
return
}
for idx, containerConf := range containerHostInfo.Images {
for idx, img := range containerHostInfo.Images {
copied := containerHostInfo
// change servername for original
copied.ServerName = fmt.Sprintf("%s:%s@%s", idx, containerConf.Tag, containerHostInfo.ServerName)
copied.Image = containerConf
copied.ServerName = fmt.Sprintf("%s@%s", idx, containerHostInfo.ServerName)
copied.Image = img
copied.Type = ""
os := detectOS(copied)
oses = append(oses, os)