From 1b9aafbbafddd96448e85d0ccadf0ff8b3364324 Mon Sep 17 00:00:00 2001 From: Kota Kanbe Date: Mon, 20 Feb 2017 17:32:58 +0900 Subject: [PATCH] Output confidence ranking of detection accuracy to JSON or Reporting --- README.ja.md | 144 ++++++++++++++++++++++++++++++++++++-------- README.md | 139 +++++++++++++++++++++++++++++++++++------- commands/report.go | 1 + commands/util.go | 6 +- models/models.go | 27 +++++++++ report/slack.go | 18 +++--- report/tui.go | 24 +++++--- report/util.go | 13 ++-- scan/debian.go | 74 ++++++++++++++--------- scan/debian_test.go | 53 ++++++++++------ scan/freebsd.go | 1 + scan/redhat.go | 6 +- 12 files changed, 386 insertions(+), 120 deletions(-) diff --git a/README.ja.md b/README.ja.md index 8ba8cd68..e12272db 100644 --- a/README.ja.md +++ b/README.ja.md @@ -255,20 +255,19 @@ One Line Summary View short summary. ``` -$ vuls report -format-short-text -cvedb-path=$PWD/cve.sqlite3 +$ vuls report -format-short-text -cvedb-path=$PWD/cve.sqlite3 --lang=ja 172-31-4-8 (amazon 2015.09) =========================== Total: 94 (High:19 Medium:54 Low:7 ?:14) 103 updatable packages -CVE-2016-0705 10.0 (High) Double free vulnerability in the dsa_priv_decode function in - crypto/dsa/dsa_ameth.c in OpenSSL 1.0.1 before 1.0.1s and 1.0.2 before 1.0.2g - allows remote attackers to cause a denial of service (memory corruption) or - possibly have unspecified other impact via a malformed DSA private key. - http://www.cvedetails.com/cve/CVE-2016-0705 - http://people.ubuntu.com/~ubuntu-security/cve/CVE-2016-0705 - libssl1.0.0-1.0.2f-2ubuntu1 -> libssl1.0.0-1.0.2g-1ubuntu4.5 - openssl-1.0.2f-2ubuntu1 -> openssl-1.0.2g-1ubuntu4.5 +CVE-2016-5636 10.0 (High) CPython の zipimport.c の get_data 関数における整数オーバーフローの脆弱性 + http://jvndb.jvn.jp/ja/contents/2016/JVNDB-2016-004528.html + https://access.redhat.com/security/cve/CVE-2016-5636 + python27-2.7.10-4.119.amzn1 -> python27-2.7.12-2.120.amzn1 + python27-devel-2.7.10-4.119.amzn1 -> python27-devel-2.7.12-2.120.amzn1 + python27-libs-2.7.10-4.119.amzn1 -> python27-libs-2.7.12-2.120.amzn1 + Candidate: 100 / YumUpdateSecurityMatch ... snip ... ```` @@ -276,30 +275,36 @@ CVE-2016-0705 10.0 (High) Double free vulnerability in the dsa_priv_decode View full report. ``` -$ vuls report -format-full-text -cvedb-path=$PWD/cve.sqlite3 +$ vuls report -format-full-text -cvedb-path=$PWD/cve.sqlite3 --lang=ja 172-31-4-82 (amazon 2015.09) ============================ Total: 94 (High:19 Medium:54 Low:7 ?:14) 103 updatable packages - -CVE-2016-0705 +CVE-2016-5636 ------------- Score 10.0 (High) Vector (AV:N/AC:L/Au:N/C:C/I:C/A:C) -Summary Double free vulnerability in the dsa_priv_decode function in - crypto/dsa/dsa_ameth.c in OpenSSL 1.0.1 before 1.0.1s and 1.0.2 before 1.0.2g - allows remote attackers to cause a denial of service (memory corruption) or - possibly have unspecified other impact via a malformed DSA private key. -CWE https://cwe.mitre.org/data/definitions/.html -NVD https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-0705 -MITRE https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-0705 -CVE Details http://www.cvedetails.com/cve/CVE-2016-0705 -CVSS Claculator https://nvd.nist.gov/cvss/v2-calculator?name=CVE-2016-0705&vector=(AV:N/AC:L/... -Ubuntu-CVE http://people.ubuntu.com/~ubuntu-security/cve/CVE-2016-0705 -Package libssl1.0.0-1.0.2f-2ubuntu1 -> libssl1.0.0-1.0.2g-1ubuntu4.5 - openssl-1.0.2f-2ubuntu1 -> openssl-1.0.2g-1ubuntu4.5 +Title CPython の zipimport.c の get_data 関数における整数オーバーフローの脆弱性 +Description CPython (別名 Python) の zipimport.c の get_data + 関数には、整数オーバーフローの脆弱性が存在します。 + 補足情報 : CWE による脆弱性タイプは、CWE-190: Integer Overflow or Wraparound + (整数オーバーフローまたはラップアラウンド) と識別されています。 + http://cwe.mitre.org/data/definitions/190.html +CWE-190 https://cwe.mitre.org/data/definitions/190.html +CWE-190(JVN) http://jvndb.jvn.jp/ja/cwe/CWE-190.html +JVN http://jvndb.jvn.jp/ja/contents/2016/JVNDB-2016-004528.html +NVD https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-5636 +MITRE https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-5636 +CVE Details http://www.cvedetails.com/cve/CVE-2016-5636 +CVSS Claculator https://nvd.nist.gov/cvss/v2-calculator?name=CVE-2016-5636&vector=(AV:N/AC:L/... +RHEL-CVE https://access.redhat.com/security/cve/CVE-2016-5636 +ALAS-2016-724 https://alas.aws.amazon.com/ALAS-2016-724.html +Package python27-2.7.10-4.119.amzn1 -> python27-2.7.12-2.120.amzn1 + python27-devel-2.7.10-4.119.amzn1 -> python27-devel-2.7.12-2.120.amzn1 + python27-libs-2.7.10-4.119.amzn1 -> python27-libs-2.7.12-2.120.amzn1 +Confidence 100 / YumUpdateSecurityMatch ... snip ... ``` @@ -925,6 +930,97 @@ report: Send report via Slack ``` +## How to read a report + +### Example + +``` +$ vuls report -format-full-text + +172-31-4-82 (amazon 2015.09) +============================ +Total: 94 (High:19 Medium:54 Low:7 ?:14) 103 updatable packages + +CVE-2016-5636 +------------- +Score 10.0 (High) +Vector (AV:N/AC:L/Au:N/C:C/I:C/A:C) +Summary Integer overflow in the get_data function in zipimport.c in CPython (aka Python) + before 2.7.12, 3.x before 3.4.5, and 3.5.x before 3.5.2 allows remote attackers + to have unspecified impact via a negative data size value, which triggers a + heap-based buffer overflow. +CWE https://cwe.mitre.org/data/definitions/190.html +NVD https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-5636 +MITRE https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-5636 +CVE Details http://www.cvedetails.com/cve/CVE-2016-5636 +CVSS Claculator https://nvd.nist.gov/cvss/v2-calculator?name=CVE-2016-5636&vector=(AV:N/AC:L/... +RHEL-CVE https://access.redhat.com/security/cve/CVE-2016-5636 +ALAS-2016-724 https://alas.aws.amazon.com/ALAS-2016-724.html +Package python27-2.7.10-4.119.amzn1 -> python27-2.7.12-2.120.amzn1 + python27-devel-2.7.10-4.119.amzn1 -> python27-devel-2.7.12-2.120.amzn1 + python27-libs-2.7.10-4.119.amzn1 -> python27-libs-2.7.12-2.120.amzn1 +Confidence 100 / YumUpdateSecurityMatch + +... snip ... +``` + +### Summary part + +``` +172-31-4-82 (amazon 2015.09) +============================ +Total: 94 (High:19 Medium:54 Low:7 ?:14) 103 updatable packages +``` + +- `172-31-4-82` means that it is a scan report of `servers.172-31-4-82` defined in cocnfig.toml. +- `(amazon 2015.09)` means that the version of the OS is Amazon Linux 2015.09. +- `Total: 94 (High:19 Medium:54 Low:7 ?:14)` means that a total of 94 vulnerabilities exist, and the distribution of CVSS Severity is displayed. +- `103 updatable packages` means that there are 103 updateable packages on the target server. + +### Detailed Part + +``` +CVE-2016-5636 +------------- +Score 10.0 (High) +Vector (AV:N/AC:L/Au:N/C:C/I:C/A:C) +Summary Integer overflow in the get_data function in zipimport.c in CPython (aka Python) + before 2.7.12, 3.x before 3.4.5, and 3.5.x before 3.5.2 allows remote attackers + to have unspecified impact via a negative data size value, which triggers a + heap-based buffer overflow. +CWE https://cwe.mitre.org/data/definitions/190.html +NVD https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-5636 +MITRE https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-5636 +CVE Details http://www.cvedetails.com/cve/CVE-2016-5636 +CVSS Claculator https://nvd.nist.gov/cvss/v2-calculator?name=CVE-2016-5636&vector=(AV:N/AC:L/... +RHEL-CVE https://access.redhat.com/security/cve/CVE-2016-5636 +ALAS-2016-724 https://alas.aws.amazon.com/ALAS-2016-724.html +Package python27-2.7.10-4.119.amzn1 -> python27-2.7.12-2.120.amzn1 + python27-devel-2.7.10-4.119.amzn1 -> python27-devel-2.7.12-2.120.amzn1 + python27-libs-2.7.10-4.119.amzn1 -> python27-libs-2.7.12-2.120.amzn1 +Confidence 100 / YumUpdateSecurityMatch +``` + +- `Score` means CVSS Score. +- `Vector` means [CVSS Vector](https://nvd.nist.gov/CVSS/Vector-v2.aspx) +- `Summary` means Summary of the CVE. +- `CWE` means [CWE - Common Weakness Enumeration](https://nvd.nist.gov/cwe.cfm) of the CVE. +- `NVD` `MITRE` `CVE Details` `CVSS Caluculator` +- `RHEL-CVE` means the URL of OS distributor support. +- `Package` shows the package version information including this vulnerability. +- `Confidence` means the reliability of detection. + - `100` is highly reliable + - `YumUpdateSecurityMatch` is the method of detecting this vulnerability. +- Item list of `Confidence` + | Detection Method | Confidence | OS |Description| + |:-----------------------|-------------------:|:---------------------------------|:--| + | YumUpdateSecurityMatch | 100 | RHEL, Amazon Linux |Detection using yum-plugin-security| + | ChangelogExactMatch | 95 | CentOS, Ubuntu, Debian, Raspbian |Exact version match between changelog and package version| + | ChangelogLenientMatch | 50 | Ubuntu, Debian, Raspbian |Lenient version match between changelog and package version| + | PkgAuditMatch | 100 | FreeBSD |Detection using pkg audit| + | CpeNameMatch | 100 | All |Search for NVD information with CPE name specified in config.toml| + + ## Example: Send scan results to Slack ``` $ vuls report \ diff --git a/README.md b/README.md index 3ee8add1..e59857de 100644 --- a/README.md +++ b/README.md @@ -254,15 +254,16 @@ $ vuls report -format-short-text =========================== Total: 94 (High:19 Medium:54 Low:7 ?:14) 103 updatable packages -CVE-2016-0705 10.0 (High) Double free vulnerability in the dsa_priv_decode function in - crypto/dsa/dsa_ameth.c in OpenSSL 1.0.1 before 1.0.1s and 1.0.2 before 1.0.2g - allows remote attackers to cause a denial of service (memory corruption) or - possibly have unspecified other impact via a malformed DSA private key. - http://www.cvedetails.com/cve/CVE-2016-0705 - http://people.ubuntu.com/~ubuntu-security/cve/CVE-2016-0705 - libssl1.0.0-1.0.2f-2ubuntu1 -> libssl1.0.0-1.0.2g-1ubuntu4.5 - openssl-1.0.2f-2ubuntu1 -> openssl-1.0.2g-1ubuntu4.5 - +CVE-2016-5636 10.0 (High) Integer overflow in the get_data function in zipimport.c in CPython (aka Python) + before 2.7.12, 3.x before 3.4.5, and 3.5.x before 3.5.2 allows remote attackers + to have unspecified impact via a negative data size value, which triggers a + heap-based buffer overflow. + http://www.cvedetails.com/cve/CVE-2016-5636 + https://access.redhat.com/security/cve/CVE-2016-5636 + python27-2.7.10-4.119.amzn1 -> python27-2.7.12-2.120.amzn1 + python27-devel-2.7.10-4.119.amzn1 -> python27-devel-2.7.12-2.120.amzn1 + python27-libs-2.7.10-4.119.amzn1 -> python27-libs-2.7.12-2.120.amzn1 + Candidate: 100 / YumUpdateSecurityMatch ... snip ... ```` @@ -275,23 +276,25 @@ $ vuls report -format-full-text ============================ Total: 94 (High:19 Medium:54 Low:7 ?:14) 103 updatable packages - -CVE-2016-0705 +CVE-2016-5636 ------------- Score 10.0 (High) Vector (AV:N/AC:L/Au:N/C:C/I:C/A:C) -Summary Double free vulnerability in the dsa_priv_decode function in - crypto/dsa/dsa_ameth.c in OpenSSL 1.0.1 before 1.0.1s and 1.0.2 before 1.0.2g - allows remote attackers to cause a denial of service (memory corruption) or - possibly have unspecified other impact via a malformed DSA private key. -CWE https://cwe.mitre.org/data/definitions/.html -NVD https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-0705 -MITRE https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-0705 -CVE Details http://www.cvedetails.com/cve/CVE-2016-0705 -CVSS Claculator https://nvd.nist.gov/cvss/v2-calculator?name=CVE-2016-0705&vector=(AV:N/AC:L/... -Ubuntu-CVE http://people.ubuntu.com/~ubuntu-security/cve/CVE-2016-0705 -Package libssl1.0.0-1.0.2f-2ubuntu1 -> libssl1.0.0-1.0.2g-1ubuntu4.5 - openssl-1.0.2f-2ubuntu1 -> openssl-1.0.2g-1ubuntu4.5 +Summary Integer overflow in the get_data function in zipimport.c in CPython (aka Python) + before 2.7.12, 3.x before 3.4.5, and 3.5.x before 3.5.2 allows remote attackers + to have unspecified impact via a negative data size value, which triggers a + heap-based buffer overflow. +CWE https://cwe.mitre.org/data/definitions/190.html +NVD https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-5636 +MITRE https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-5636 +CVE Details http://www.cvedetails.com/cve/CVE-2016-5636 +CVSS Claculator https://nvd.nist.gov/cvss/v2-calculator?name=CVE-2016-5636&vector=(AV:N/AC:L/... +RHEL-CVE https://access.redhat.com/security/cve/CVE-2016-5636 +ALAS-2016-724 https://alas.aws.amazon.com/ALAS-2016-724.html +Package python27-2.7.10-4.119.amzn1 -> python27-2.7.12-2.120.amzn1 + python27-devel-2.7.10-4.119.amzn1 -> python27-devel-2.7.12-2.120.amzn1 + python27-libs-2.7.10-4.119.amzn1 -> python27-libs-2.7.12-2.120.amzn1 +Confidence 100 / YumUpdateSecurityMatch ... snip ... ``` @@ -933,6 +936,96 @@ report: Send report via Slack ``` +## How to read a report + +### Example + +``` +$ vuls report -format-full-text + +172-31-4-82 (amazon 2015.09) +============================ +Total: 94 (High:19 Medium:54 Low:7 ?:14) 103 updatable packages + +CVE-2016-5636 +------------- +Score 10.0 (High) +Vector (AV:N/AC:L/Au:N/C:C/I:C/A:C) +Summary Integer overflow in the get_data function in zipimport.c in CPython (aka Python) + before 2.7.12, 3.x before 3.4.5, and 3.5.x before 3.5.2 allows remote attackers + to have unspecified impact via a negative data size value, which triggers a + heap-based buffer overflow. +CWE https://cwe.mitre.org/data/definitions/190.html +NVD https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-5636 +MITRE https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-5636 +CVE Details http://www.cvedetails.com/cve/CVE-2016-5636 +CVSS Claculator https://nvd.nist.gov/cvss/v2-calculator?name=CVE-2016-5636&vector=(AV:N/AC:L/... +RHEL-CVE https://access.redhat.com/security/cve/CVE-2016-5636 +ALAS-2016-724 https://alas.aws.amazon.com/ALAS-2016-724.html +Package python27-2.7.10-4.119.amzn1 -> python27-2.7.12-2.120.amzn1 + python27-devel-2.7.10-4.119.amzn1 -> python27-devel-2.7.12-2.120.amzn1 + python27-libs-2.7.10-4.119.amzn1 -> python27-libs-2.7.12-2.120.amzn1 +Confidence 100 / YumUpdateSecurityMatch + +... snip ... +``` + +### Summary part + +``` +172-31-4-82 (amazon 2015.09) +============================ +Total: 94 (High:19 Medium:54 Low:7 ?:14) 103 updatable packages +``` + +- `172-31-4-82` means that it is a scan report of `servers.172-31-4-82` defined in cocnfig.toml. +- `(amazon 2015.09)` means that the version of the OS is Amazon Linux 2015.09. +- `Total: 94 (High:19 Medium:54 Low:7 ?:14)` means that a total of 94 vulnerabilities exist, and the distribution of CVSS Severity is displayed. +- `103 updatable packages` means that there are 103 updateable packages on the target server. + +### Detailed Part + +``` +CVE-2016-5636 +------------- +Score 10.0 (High) +Vector (AV:N/AC:L/Au:N/C:C/I:C/A:C) +Summary Integer overflow in the get_data function in zipimport.c in CPython (aka Python) + before 2.7.12, 3.x before 3.4.5, and 3.5.x before 3.5.2 allows remote attackers + to have unspecified impact via a negative data size value, which triggers a + heap-based buffer overflow. +CWE https://cwe.mitre.org/data/definitions/190.html +NVD https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-5636 +MITRE https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-5636 +CVE Details http://www.cvedetails.com/cve/CVE-2016-5636 +CVSS Claculator https://nvd.nist.gov/cvss/v2-calculator?name=CVE-2016-5636&vector=(AV:N/AC:L/... +RHEL-CVE https://access.redhat.com/security/cve/CVE-2016-5636 +ALAS-2016-724 https://alas.aws.amazon.com/ALAS-2016-724.html +Package python27-2.7.10-4.119.amzn1 -> python27-2.7.12-2.120.amzn1 + python27-devel-2.7.10-4.119.amzn1 -> python27-devel-2.7.12-2.120.amzn1 + python27-libs-2.7.10-4.119.amzn1 -> python27-libs-2.7.12-2.120.amzn1 +Confidence 100 / YumUpdateSecurityMatch +``` + +- `Score` means CVSS Score. +- `Vector` means [CVSS Vector](https://nvd.nist.gov/CVSS/Vector-v2.aspx) +- `Summary` means Summary of the CVE. +- `CWE` means [CWE - Common Weakness Enumeration](https://nvd.nist.gov/cwe.cfm) of the CVE. +- `NVD` `MITRE` `CVE Details` `CVSS Caluculator` +- `RHEL-CVE` means the URL of OS distributor support. +- `Package` shows the package version information including this vulnerability. +- `Confidence` means the reliability of detection. + - `100` is highly reliable + - `YumUpdateSecurityMatch` is the method of detecting this vulnerability. +- Item list of `Confidence` + | Detection Method | Confidence | OS |Description| + |:-----------------------|-------------------:|:---------------------------------|:--| + | YumUpdateSecurityMatch | 100 | RHEL, Amazon Linux |Detection using yum-plugin-security| + | ChangelogExactMatch | 95 | CentOS, Ubuntu, Debian, Raspbian |Exact version match between changelog and package version| + | ChangelogLenientMatch | 50 | Ubuntu, Debian, Raspbian |Lenient version match between changelog and package version| + | PkgAuditMatch | 100 | FreeBSD |Detection using pkg audit| + | CpeNameMatch | 100 | All |Search for NVD information with CPE name specified in config.toml| + ## Example: Send scan results to Slack ``` $ vuls report \ diff --git a/commands/report.go b/commands/report.go index dbc86ba7..c8a548e0 100644 --- a/commands/report.go +++ b/commands/report.go @@ -368,6 +368,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{} util.Log.Error(err) return subcommands.ExitFailure } + util.Log.Infof("Loaded: %s", jsonDir) var results []models.ScanResult for _, r := range history.ScanResults { diff --git a/commands/util.go b/commands/util.go index 81588edd..11bdb6d8 100644 --- a/commands/util.go +++ b/commands/util.go @@ -200,11 +200,13 @@ func scanVulnByCpeNames(cpeNames []string, scannedVulns []models.VulnInfo) ([]mo names := val.CpeNames names = util.AppendIfMissing(names, name) val.CpeNames = names + val.Confidence = models.CpeNameMatch set[detail.CveID] = val } else { v := models.VulnInfo{ - CveID: detail.CveID, - CpeNames: []string{name}, + CveID: detail.CveID, + CpeNames: []string{name}, + Confidence: models.CpeNameMatch, } v.NilSliceToEmpty() set[detail.CveID] = v diff --git a/models/models.go b/models/models.go index 88661ee0..09cf8e33 100644 --- a/models/models.go +++ b/models/models.go @@ -239,12 +239,39 @@ type NWLink struct { LinkState string } +// Confidence is a ranking how confident the CVE-ID was deteted correctly +// Score: 0 - 100 +type Confidence struct { + Score int + DetectionMethod string +} + +func (c Confidence) String() string { + return fmt.Sprintf("%d / %s", c.Score, c.DetectionMethod) +} + +// CpeNameMatch is a ranking how confident the CVE-ID was deteted correctly +var CpeNameMatch = Confidence{100, "CpeNameMatch"} + +// YumUpdateSecurityMatch is a ranking how confident the CVE-ID was deteted correctly +var YumUpdateSecurityMatch = Confidence{100, "YumUpdateSecurityMatch"} + +// PkgAuditMatch is a ranking how confident the CVE-ID was deteted correctly +var PkgAuditMatch = Confidence{100, "PkgAuditMatch"} + +// ChangelogExactMatch is a ranking how confident the CVE-ID was deteted correctly +var ChangelogExactMatch = Confidence{95, "ChangelogExactMatch"} + +// ChangelogLenientMatch is a ranking how confident the CVE-ID was deteted correctly +var ChangelogLenientMatch = Confidence{50, "ChangelogLenientMatch"} + // VulnInfos is VulnInfo list, getter/setter, sortable methods. type VulnInfos []VulnInfo // VulnInfo holds a vulnerability information and unsecure packages type VulnInfo struct { CveID string + Confidence Confidence Packages PackageInfoList DistroAdvisories []DistroAdvisory // for Aamazon, RHEL, FreeBSD CpeNames []string diff --git a/report/slack.go b/report/slack.go index 17fba6c2..a0737609 100644 --- a/report/slack.go +++ b/report/slack.go @@ -221,36 +221,38 @@ func color(cvssScore float64) string { } func attachmentText(cveInfo models.CveInfo, osFamily string) string { - linkText := links(cveInfo, osFamily) - switch { case config.Conf.Lang == "ja" && 0 < cveInfo.CveDetail.Jvn.CvssScore(): jvn := cveInfo.CveDetail.Jvn - return fmt.Sprintf("*%4.1f (%s)* <%s|%s>\n%s\n%s", + return fmt.Sprintf("*%4.1f (%s)* <%s|%s>\n%s\n%s\n*Confidence:* %v", cveInfo.CveDetail.CvssScore(config.Conf.Lang), jvn.CvssSeverity(), - fmt.Sprintf(cvssV2CalcURLTemplate, cveInfo.CveDetail.CveID, jvn.CvssVector()), + fmt.Sprintf(cvssV2CalcURLTemplate, + cveInfo.CveDetail.CveID, jvn.CvssVector()), jvn.CvssVector(), jvn.CveTitle(), linkText, + cveInfo.VulnInfo.Confidence, ) - case 0 < cveInfo.CveDetail.CvssScore("en"): nvd := cveInfo.CveDetail.Nvd - return fmt.Sprintf("*%4.1f (%s)* <%s|%s>\n%s\n%s", + return fmt.Sprintf("*%4.1f (%s)* <%s|%s>\n%s\n%s\n*Confidence:* %v", cveInfo.CveDetail.CvssScore(config.Conf.Lang), nvd.CvssSeverity(), - fmt.Sprintf(cvssV2CalcURLTemplate, cveInfo.CveDetail.CveID, nvd.CvssVector()), + fmt.Sprintf(cvssV2CalcURLTemplate, + cveInfo.CveDetail.CveID, nvd.CvssVector()), nvd.CvssVector(), nvd.CveSummary(), linkText, + cveInfo.VulnInfo.Confidence, ) default: nvd := cveInfo.CveDetail.Nvd - return fmt.Sprintf("?\n%s\n%s", nvd.CveSummary(), linkText) + return fmt.Sprintf("?\n%s\n%s\n*Confidence:* %v", + nvd.CveSummary(), linkText, cveInfo.VulnInfo.Confidence) } } diff --git a/report/tui.go b/report/tui.go index d51a058a..f6fbfeda 100644 --- a/report/tui.go +++ b/report/tui.go @@ -569,11 +569,9 @@ func summaryLines() string { cols = []string{ fmt.Sprintf(indexFormat, i+1), d.CveDetail.CveID, - fmt.Sprintf("| %-4.1f(%s)", - d.CveDetail.CvssScore(config.Conf.Lang), - d.CveDetail.Jvn.CvssSeverity(), - ), - // strings.Join(packs, ","), + fmt.Sprintf("| %4.1f", + d.CveDetail.CvssScore(config.Conf.Lang)), + fmt.Sprintf("| %3d |", d.VulnInfo.Confidence.Score), summary, } } else { @@ -581,18 +579,17 @@ func summaryLines() string { var cvssScore string if d.CveDetail.CvssScore("en") <= 0 { - cvssScore = "| ?" + cvssScore = "| ?" } else { - cvssScore = fmt.Sprintf("| %-4.1f(%s)", - d.CveDetail.CvssScore(config.Conf.Lang), - d.CveDetail.Nvd.CvssSeverity(), - ) + cvssScore = fmt.Sprintf("| %4.1f", + d.CveDetail.CvssScore(config.Conf.Lang)) } cols = []string{ fmt.Sprintf(indexFormat, i+1), d.CveDetail.CveID, cvssScore, + fmt.Sprintf("| %3d |", d.VulnInfo.Confidence.Score), summary, } } @@ -644,6 +641,7 @@ type dataForTmpl struct { CvssVector string CvssSeverity string Summary string + Confidence models.Confidence CweURL string VulnSiteLinks []string References []cve.Reference @@ -723,6 +721,7 @@ func detailLines() (string, error) { CvssSeverity: cvssSeverity, CvssVector: cvssVector, Summary: summary, + Confidence: cveInfo.VulnInfo.Confidence, CweURL: cweURL, VulnSiteLinks: links, References: refs, @@ -753,6 +752,11 @@ Summary {{.Summary }} +Confidence +-------------- + + {{.Confidence }} + CWE -------------- diff --git a/report/util.go b/report/util.go index 748de464..fc39fe41 100644 --- a/report/util.go +++ b/report/util.go @@ -128,11 +128,12 @@ No CVE-IDs are found in updatable packages. switch { case config.Conf.Lang == "ja" && 0 < d.CveDetail.Jvn.CvssScore(): - summary := fmt.Sprintf("%s\n%s\n%s\n%s", + summary := fmt.Sprintf("%s\n%s\n%s\n%sCandidate: %v", d.CveDetail.Jvn.CveTitle(), d.CveDetail.Jvn.Link(), distroLinks(d, r.Family)[0].url, packsVer, + d.VulnInfo.Confidence, ) scols = []string{ d.CveDetail.CveID, @@ -144,12 +145,13 @@ No CVE-IDs are found in updatable packages. } case 0 < d.CveDetail.CvssScore("en"): - summary := fmt.Sprintf("%s\n%s/%s\n%s\n%s", + summary := fmt.Sprintf("%s\n%s/%s\n%s\n%sCandidate: %v", d.CveDetail.Nvd.CveSummary(), cveDetailsBaseURL, d.CveDetail.CveID, distroLinks(d, r.Family)[0].url, packsVer, + d.VulnInfo.Confidence, ) scols = []string{ d.CveDetail.CveID, @@ -160,8 +162,8 @@ No CVE-IDs are found in updatable packages. summary, } default: - summary := fmt.Sprintf("%s\n%s", - distroLinks(d, r.Family)[0].url, packsVer) + summary := fmt.Sprintf("%s\n%sCandidate: %v", + distroLinks(d, r.Family)[0].url, packsVer, d.VulnInfo.Confidence) scols = []string{ d.CveDetail.CveID, "?", @@ -277,6 +279,7 @@ func toPlainTextUnknownCve(cveInfo models.CveInfo, osFamily string) string { } dtable = addPackageInfos(dtable, cveInfo.Packages) dtable = addCpeNames(dtable, cveInfo.CpeNames) + dtable.AddRow("Confidence", cveInfo.VulnInfo.Confidence) return fmt.Sprintf("%s", dtable) } @@ -319,6 +322,7 @@ func toPlainTextDetailsLangJa(cveInfo models.CveInfo, osFamily string) string { dtable = addPackageInfos(dtable, cveInfo.Packages) dtable = addCpeNames(dtable, cveInfo.CpeNames) + dtable.AddRow("Confidence", cveInfo.VulnInfo.Confidence) return fmt.Sprintf("%s", dtable) } @@ -359,6 +363,7 @@ func toPlainTextDetailsLangEn(d models.CveInfo, osFamily string) string { } dtable = addPackageInfos(dtable, d.Packages) dtable = addCpeNames(dtable, d.CpeNames) + dtable.AddRow("Confidence", d.VulnInfo.Confidence) return fmt.Sprintf("%s\n", dtable) } diff --git a/scan/debian.go b/scan/debian.go index c06292e5..110b900d 100644 --- a/scan/debian.go +++ b/scan/debian.go @@ -421,10 +421,9 @@ func (o *debian) parseAptGetUpgrade(stdout string) (upgradableNames []string, er } func (o *debian) scanVulnInfos(upgradablePacks []models.PackageInfo, meta *cache.Meta) (models.VulnInfos, error) { - type strarray []string resChan := make(chan struct { models.PackageInfo - strarray + DetectedCveIDs }, len(upgradablePacks)) errChan := make(chan error, len(upgradablePacks)) reqChan := make(chan models.PackageInfo, len(upgradablePacks)) @@ -448,10 +447,10 @@ func (o *debian) scanVulnInfos(upgradablePacks []models.PackageInfo, meta *cache func(p models.PackageInfo) { changelog := o.getChangelogCache(meta, p) if 0 < len(changelog) { - cveIDs := o.getCveIDFromChangelog(changelog, p.Name, p.Version) + cveIDs := o.getCveIDsFromChangelog(changelog, p.Name, p.Version) resChan <- struct { models.PackageInfo - strarray + DetectedCveIDs }{p, cveIDs} return } @@ -464,7 +463,7 @@ func (o *debian) scanVulnInfos(upgradablePacks []models.PackageInfo, meta *cache } else { resChan <- struct { models.PackageInfo - strarray + DetectedCveIDs }{p, cveIDs} } }(pack) @@ -472,14 +471,14 @@ func (o *debian) scanVulnInfos(upgradablePacks []models.PackageInfo, meta *cache } } - // { CVE ID: [packageInfo] } - cvePackages := make(map[string][]models.PackageInfo) + // { DetectedCveID{} : [packageInfo] } + cvePackages := make(map[DetectedCveID][]models.PackageInfo) errs := []error{} for i := 0; i < len(upgradablePacks); i++ { select { case pair := <-resChan: pack := pair.PackageInfo - cveIDs := pair.strarray + cveIDs := pair.DetectedCveIDs for _, cveID := range cveIDs { cvePackages[cveID] = appendPackIfMissing(cvePackages[cveID], pack) } @@ -495,7 +494,7 @@ func (o *debian) scanVulnInfos(upgradablePacks []models.PackageInfo, meta *cache return nil, fmt.Errorf("%v", errs) } - var cveIDs []string + var cveIDs []DetectedCveID for k := range cvePackages { cveIDs = append(cveIDs, k) } @@ -503,8 +502,9 @@ func (o *debian) scanVulnInfos(upgradablePacks []models.PackageInfo, meta *cache var vinfos models.VulnInfos for k, v := range cvePackages { vinfos = append(vinfos, models.VulnInfo{ - CveID: k, - Packages: v, + CveID: k.CveID, + Confidence: k.Confidence, + Packages: v, }) } @@ -545,7 +545,7 @@ func (o *debian) getChangelogCache(meta *cache.Meta, pack models.PackageInfo) st return changelog } -func (o *debian) scanPackageCveIDs(pack models.PackageInfo) ([]string, error) { +func (o *debian) scanPackageCveIDs(pack models.PackageInfo) ([]DetectedCveID, error) { cmd := "" switch o.Distro.Family { case "ubuntu", "raspbian": @@ -569,11 +569,11 @@ func (o *debian) scanPackageCveIDs(pack models.PackageInfo) ([]string, error) { } } // No error will be returned. Only logging. - return o.getCveIDFromChangelog(r.Stdout, pack.Name, pack.Version), nil + return o.getCveIDsFromChangelog(r.Stdout, pack.Name, pack.Version), nil } -func (o *debian) getCveIDFromChangelog(changelog string, - packName string, versionOrLater string) []string { +func (o *debian) getCveIDsFromChangelog(changelog string, + packName string, versionOrLater string) []DetectedCveID { if cveIDs, err := o.parseChangelog(changelog, packName, versionOrLater); err == nil { return cveIDs @@ -595,31 +595,43 @@ func (o *debian) getCveIDFromChangelog(changelog string, // Only logging the error. o.log.Error(err) - return []string{} + return []DetectedCveID{} } +// DetectedCveID has CveID, Confidence and DetectionMethod fields +// LenientMatching will be true if this vulnerability is not detected by accurate version matching. +// see https://github.com/future-architect/vuls/pull/328 +type DetectedCveID struct { + CveID string + Confidence models.Confidence +} + +// DetectedCveIDs is a slice of DetectedCveID +type DetectedCveIDs []DetectedCveID + // Collect CVE-IDs included in the changelog. // The version which specified in argument(versionOrLater) is excluded. func (o *debian) parseChangelog(changelog string, - packName string, versionOrLater string) (cveIDs []string, err error) { + packName string, versionOrLater string) (cves []DetectedCveID, err error) { + cveIDs := []string{} cveRe := regexp.MustCompile(`(CVE-\d{4}-\d{4,})`) stopRe := regexp.MustCompile(fmt.Sprintf(`\(%s\)`, regexp.QuoteMeta(versionOrLater))) stopLineFound := false - leniantStopLineFound := false - versionOrLaterLeniant := versionOrLater - if i := strings.IndexRune(versionOrLaterLeniant, '+'); i >= 0 { - versionOrLaterLeniant = versionOrLaterLeniant[:i] + lenientStopLineFound := false + versionOrLaterlenient := versionOrLater + if i := strings.IndexRune(versionOrLaterlenient, '+'); i >= 0 { + versionOrLaterlenient = versionOrLaterlenient[:i] } - leniantRe := regexp.MustCompile(fmt.Sprintf(`\(%s\)`, regexp.QuoteMeta(versionOrLaterLeniant))) + lenientRe := regexp.MustCompile(fmt.Sprintf(`\(%s\)`, regexp.QuoteMeta(versionOrLaterlenient))) lines := strings.Split(changelog, "\n") for _, line := range lines { - if matche := stopRe.MatchString(line); matche { + if match := stopRe.MatchString(line); match { // o.log.Debugf("Found the stop line: %s", line) stopLineFound = true break - } else if matchel := leniantRe.MatchString(line); matchel { - leniantStopLineFound = true + } else if matchl := lenientRe.MatchString(line); matchl { + lenientStopLineFound = true break } else if matches := cveRe.FindAllString(line, -1); 0 < len(matches) { for _, m := range matches { @@ -627,13 +639,21 @@ func (o *debian) parseChangelog(changelog string, } } } - if !stopLineFound && !leniantStopLineFound { - return []string{}, fmt.Errorf( + if !stopLineFound && !lenientStopLineFound { + return cves, fmt.Errorf( "Failed to scan CVE IDs. The version is not in changelog. name: %s, version: %s", packName, versionOrLater, ) } + + for _, id := range cveIDs { + confidence := models.ChangelogExactMatch + if lenientStopLineFound { + confidence = models.ChangelogLenientMatch + } + cves = append(cves, DetectedCveID{id, confidence}) + } return } diff --git a/scan/debian_test.go b/scan/debian_test.go index 946be3d0..4b6c343f 100644 --- a/scan/debian_test.go +++ b/scan/debian_test.go @@ -62,7 +62,7 @@ func TestGetCveIDParsingChangelog(t *testing.T) { var tests = []struct { in []string - expected []string + expected []DetectedCveID }{ { // verubuntu1 @@ -84,10 +84,10 @@ systemd (227-3) unstable; urgency=medium systemd (227-2) unstable; urgency=medium systemd (227-1) unstable; urgency=medium`, }, - []string{ - "CVE-2015-2325", - "CVE-2015-2326", - "CVE-2015-3210", + []DetectedCveID{ + {"CVE-2015-2325", models.ChangelogExactMatch}, + {"CVE-2015-2326", models.ChangelogExactMatch}, + {"CVE-2015-3210", models.ChangelogExactMatch}, }, }, { @@ -107,10 +107,10 @@ CVE-2015-3210: heap buffer overflow in pcre_compile2() / pcre3 (2:8.35-7.1) unstable; urgency=medium pcre3 (2:8.35-7) unstable; urgency=medium`, }, - []string{ - "CVE-2015-2325", - "CVE-2015-2326", - "CVE-2015-3210", + []DetectedCveID{ + {"CVE-2015-2325", models.ChangelogExactMatch}, + {"CVE-2015-2326", models.ChangelogExactMatch}, + {"CVE-2015-3210", models.ChangelogExactMatch}, }, }, { @@ -138,10 +138,10 @@ sysvinit (2.88dsf-59) unstable; urgency=medium sysvinit (2.88dsf-58) unstable; urgency=low sysvinit (2.88dsf-57) unstable; urgency=low`, }, - []string{ - "CVE-2015-2325", - "CVE-2015-2326", - "CVE-2015-3210", + []DetectedCveID{ + {"CVE-2015-2325", models.ChangelogExactMatch}, + {"CVE-2015-2326", models.ChangelogExactMatch}, + {"CVE-2015-3210", models.ChangelogExactMatch}, }, }, { @@ -176,26 +176,39 @@ util-linux (2.26.2-6ubuntu2) wily; urgency=medium util-linux (2.26.2-6ubuntu1) wily; urgency=medium util-linux (2.26.2-6) unstable; urgency=medium`, }, + []DetectedCveID{ + {"CVE-2015-2325", models.ChangelogExactMatch}, + {"CVE-2015-2326", models.ChangelogExactMatch}, + {"CVE-2015-3210", models.ChangelogExactMatch}, + {"CVE-2016-1000000", models.ChangelogExactMatch}, + }, + }, + { + // https://github.com/future-architect/vuls/pull/350 []string{ - "CVE-2015-2325", - "CVE-2015-2326", - "CVE-2015-3210", - "CVE-2016-1000000", + "tar", + "1.27.1-2+b1", + `tar (1.27.1-2+deb8u1) jessie-security; urgency=high + * CVE-2016-6321: Bypassing the extract path name. +tar (1.27.1-2) unstable; urgency=low`, + }, + []DetectedCveID{ + {"CVE-2016-6321", models.ChangelogLenientMatch}, }, }, } d := newDebian(config.ServerInfo{}) for _, tt := range tests { - actual := d.getCveIDFromChangelog(tt.in[2], tt.in[0], tt.in[1]) + actual := d.getCveIDsFromChangelog(tt.in[2], tt.in[0], tt.in[1]) if len(actual) != len(tt.expected) { t.Errorf("Len of return array are'nt same. expected %#v, actual %#v", tt.expected, actual) t.Errorf(pp.Sprintf("%s", tt.in)) continue } for i := range tt.expected { - if actual[i] != tt.expected[i] { - t.Errorf("expected %s, actual %s", tt.expected[i], actual[i]) + if !reflect.DeepEqual(tt.expected[i], actual[i]) { + t.Errorf("expected %v, actual %v", tt.expected[i], actual[i]) } } } diff --git a/scan/freebsd.go b/scan/freebsd.go index a3df6ec4..7aaf1790 100644 --- a/scan/freebsd.go +++ b/scan/freebsd.go @@ -167,6 +167,7 @@ func (o *bsd) scanUnsecurePackages() (vulnInfos []models.VulnInfo, err error) { CveID: k, Packages: packs, DistroAdvisories: disAdvs, + Confidence: models.PkgAuditMatch, }) } return diff --git a/scan/redhat.go b/scan/redhat.go index 67c2e05f..0d0bbbee 100644 --- a/scan/redhat.go +++ b/scan/redhat.go @@ -352,8 +352,9 @@ func (o *redhat) scanUnsecurePackagesUsingYumCheckUpdate() (models.VulnInfos, er for k, v := range cveIDPackInfoMap { // Amazon, RHEL do not use this method, so VendorAdvisory do not set. vinfos = append(vinfos, models.VulnInfo{ - CveID: k, - Packages: v, + CveID: k, + Packages: v, + Confidence: models.ChangelogExactMatch, }) } return vinfos, nil @@ -670,6 +671,7 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (models.VulnInfos, CveID: cveID, DistroAdvisories: []models.DistroAdvisory{advIDCveIDs.DistroAdvisory}, Packages: dict[advIDCveIDs.DistroAdvisory.AdvisoryID], + Confidence: models.YumUpdateSecurityMatch, } vinfos = append(vinfos, cpinfo) }