Compare commits

...

6 Commits

Author SHA1 Message Date
Teppei Fukuda
073b315737 No error on unsupported OS (#40) 2019-05-21 09:24:45 +09:00
Nicolas CARPi
47c46fbe17 Fix some typos in the README or improve phrasing (#38)
also add .circleci to .dockerignore
2019-05-21 08:59:16 +09:00
mitsuyoshi
39572968bb fixed cache bug (#36) 2019-05-21 01:03:33 +09:00
Nao YONASHIRO
4383764cae fix: unknown format case (#35) 2019-05-21 00:17:44 +09:00
Kirill Motkov
e0ef0563ce Some code improvements. (#34)
* Rewrite some if-else chains as a switch statements. Based on Go style
guide: https://golang.org/doc/effective_go.html#switch
* Simplify some functions.
* Some comments formatting.
2019-05-21 00:17:18 +09:00
Teppei Fukuda
d9cf2c487d Update JSON schema (#33)
* Update JSON schema

* Add JSON example to README
2019-05-20 22:43:28 +09:00
10 changed files with 149 additions and 43 deletions

View File

@@ -1 +1,2 @@
.circleci
imgs

123
README.md
View File

@@ -136,7 +136,7 @@ $ sudo dpkg -i trivy_0.0.15_Linux-64bit.deb
## Mac OS X / Homebrew
You can use homebrew on OS X.
You can use homebrew on Mac OS.
```
$ brew tap knqyf263/trivy
@@ -145,7 +145,7 @@ $ brew install knqyf263/trivy/trivy
## Binary (Including Windows)
Go to [the releases page](https://github.com/knqyf263/trivy/releases), find the version you want, and download the zip file. Unpack the zip file, and put the binary to somewhere you want (on UNIX-y systems, /usr/local/bin or the like). Make sure it has execution bits turned on.
Get the latest version from [this page](https://github.com/knqyf263/trivy/releases/latest), and download the archive file for your operating system/architecture. Unpack the archive, and put the binary somewhere in your `$PATH` (on UNIX-y systems, /usr/local/bin or the like). Make sure it has execution bits turned on.
You need to install `rpm` command for scanning RHEL/CentOS.
@@ -516,6 +516,115 @@ $ trivy -f json -o results.json golang:1.12-alpine
</details>
<details>
<summary>JSON</summary>
```
[
{
"Target": "php-app/composer.lock",
"Vulnerabilities": null
},
{
"Target": "node-app/package-lock.json",
"Vulnerabilities": [
{
"VulnerabilityID": "CVE-2018-16487",
"PkgName": "lodash",
"InstalledVersion": "4.17.4",
"FixedVersion": "\u003e=4.17.11",
"Title": "lodash: Prototype pollution in utilities function",
"Description": "A prototype pollution vulnerability was found in lodash \u003c4.17.11 where the functions merge, mergeWith, and defaultsDeep can be tricked into adding or modifying properties of Object.prototype.",
"Severity": "HIGH",
"References": [
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-16487",
]
}
]
},
{
"Target": "trivy-ci-test (alpine 3.7.1)",
"Vulnerabilities": [
{
"VulnerabilityID": "CVE-2018-16840",
"PkgName": "curl",
"InstalledVersion": "7.61.0-r0",
"FixedVersion": "7.61.1-r1",
"Title": "curl: Use-after-free when closing \"easy\" handle in Curl_close()",
"Description": "A heap use-after-free flaw was found in curl versions from 7.59.0 through 7.61.1 in the code related to closing an easy handle. ",
"Severity": "HIGH",
"References": [
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-16840",
]
},
{
"VulnerabilityID": "CVE-2019-3822",
"PkgName": "curl",
"InstalledVersion": "7.61.0-r0",
"FixedVersion": "7.61.1-r2",
"Title": "curl: NTLMv2 type-3 header stack buffer overflow",
"Description": "libcurl versions from 7.36.0 to before 7.64.0 are vulnerable to a stack-based buffer overflow. ",
"Severity": "HIGH",
"References": [
"https://curl.haxx.se/docs/CVE-2019-3822.html",
"https://lists.apache.org/thread.html/8338a0f605bdbb3a6098bb76f666a95fc2b2f53f37fa1ecc89f1146f@%3Cdevnull.infra.apache.org%3E"
]
},
{
"VulnerabilityID": "CVE-2018-16839",
"PkgName": "curl",
"InstalledVersion": "7.61.0-r0",
"FixedVersion": "7.61.1-r1",
"Title": "curl: Integer overflow leading to heap-based buffer overflow in Curl_sasl_create_plain_message()",
"Description": "Curl versions 7.33.0 through 7.61.1 are vulnerable to a buffer overrun in the SASL authentication code that may lead to denial of service.",
"Severity": "HIGH",
"References": [
"https://github.com/curl/curl/commit/f3a24d7916b9173c69a3e0ee790102993833d6c5",
]
},
{
"VulnerabilityID": "CVE-2018-19486",
"PkgName": "git",
"InstalledVersion": "2.15.2-r0",
"FixedVersion": "2.15.3-r0",
"Title": "git: Improper handling of PATH allows for commands to be executed from the current directory",
"Description": "Git before 2.19.2 on Linux and UNIX executes commands from the current working directory (as if '.' were at the end of $PATH) in certain cases involving the run_command() API and run-command.c, because there was a dangerous change from execvp to execv during 2017.",
"Severity": "HIGH",
"References": [
"https://usn.ubuntu.com/3829-1/",
]
},
{
"VulnerabilityID": "CVE-2018-17456",
"PkgName": "git",
"InstalledVersion": "2.15.2-r0",
"FixedVersion": "2.15.3-r0",
"Title": "git: arbitrary code execution via .gitmodules",
"Description": "Git before 2.14.5, 2.15.x before 2.15.3, 2.16.x before 2.16.5, 2.17.x before 2.17.2, 2.18.x before 2.18.1, and 2.19.x before 2.19.1 allows remote code execution during processing of a recursive \"git clone\" of a superproject if a .gitmodules file has a URL field beginning with a '-' character.",
"Severity": "HIGH",
"References": [
"http://www.securitytracker.com/id/1041811",
]
}
]
},
{
"Target": "python-app/Pipfile.lock",
"Vulnerabilities": null
},
{
"Target": "ruby-app/Gemfile.lock",
"Vulnerabilities": null
},
{
"Target": "rust-app/Cargo.lock",
"Vulnerabilities": null
}
]
```
</details>
### Filter the vulnerabilities by severities
```
@@ -942,7 +1051,7 @@ The results of [composer:1.7.2](https://hub.docker.com/_/composer?tab=tags) usin
<img src="imgs/alpine.png" width="500">
`Trivy` has high accuracy and high precision, while GCR did not detect any vulnerability. Althogh Docker Hub has many True Positive, it also has many False Positive.
`Trivy` has high accuracy and high precision, while GCR did not detect any vulnerability. Although Docker Hub has many True Positive, it also has many False Positive.
### RHEL/CentOS
@@ -968,8 +1077,8 @@ In the case of other OS, the result is similar to other container scanners.
However, the purpose of this database is to make it possible to know what packages has backported fixes.
As README says, it is not a complete database of all security issues in Alpine.
`Trivy` collects vulnerability information in Alpine Linux from [Alpine LInux Redmine](https://bugs.alpinelinux.org/projects/alpine/issues).
Then, those vulnerabilities will be saved on [vuln-list](https://github.com/knqyf263/vuln-list/tree/master/alpine)
`Trivy` collects vulnerability information in Alpine Linux from [Alpine Linux Redmine](https://bugs.alpinelinux.org/projects/alpine/issues).
Then, those vulnerabilities will be saved on [vuln-list](https://github.com/knqyf263/vuln-list/tree/master/alpine).
`alpine-secdb` has 6959 vulnerabilities (as of 2019/05/12).
`vuln-list` has 11101 vulnerabilities related with Alpine Linux (as of 2019/05/12).
@@ -977,7 +1086,7 @@ There is a difference in detection accuracy because the number of vulnerabilitie
In addition, `Trivy` analyzes the middle layer as well and find out which version of the library was used for static linking.
`Clair` can not handle the following cases because it analyzes the image after applying the all layers.
`Clair` can not handle the following cases because it analyzes the image after applying all layers.
```
RUN apk add --no-cache sqlite-dev \
@@ -1001,7 +1110,7 @@ Also, `Anchore Engine` needs some steps to start scanning.
## vs Quay, Docker Hub, GCR
As `Quay` seems to use `Clair` internally, it has the same accuracy with `Clair`. `Docker Hub` can scan only official images. `GCR` hardly detects vulnerability on Alpine Linux. Also, it is locked to a specific registry.
As `Quay` seems to use `Clair` internally, it has the same accuracy than `Clair`. `Docker Hub` can scan only official images. `GCR` hardly detects vulnerabilities on Alpine Linux. Also, it is locked to a specific registry.
`Trivy` can be used regardless of the registry. In addition, it is easy to be integrated with CI/CD services.

View File

@@ -103,9 +103,7 @@ OPTIONS:
},
}
app.Action = func(c *cli.Context) error {
return pkg.Run(c)
}
app.Action = pkg.Run
err := app.Run(os.Args)
if err != nil {

View File

@@ -105,9 +105,7 @@ func Put(root *bolt.Bucket, nestedBucket, key string, value interface{}) error {
return nested.Put([]byte(key), v)
}
func BatchUpdate(fn func(tx *bolt.Tx) error) error {
err := db.Batch(func(tx *bolt.Tx) error {
return fn(tx)
})
err := db.Batch(fn)
if err != nil {
return xerrors.Errorf("error in batch update: %w", err)
}

View File

@@ -17,8 +17,8 @@ import (
type Results []Result
type Result struct {
FileName string `json:"file"`
Vulnerabilities []vulnerability.DetectedVulnerability
FileName string `json:"Target"`
Vulnerabilities []vulnerability.DetectedVulnerability `json:"Vulnerabilities"`
}
type Writer interface {
@@ -84,11 +84,7 @@ type JsonWriter struct {
}
func (jw JsonWriter) Write(results Results) error {
out := map[string][]vulnerability.DetectedVulnerability{}
for _, result := range results {
out[result.FileName] = result.Vulnerabilities
}
output, err := json.MarshalIndent(out, "", " ")
output, err := json.MarshalIndent(results, "", " ")
if err != nil {
return xerrors.Errorf("failed to marshal json: %w", err)
}

View File

@@ -102,6 +102,10 @@ func Run(c *cli.Context) (err error) {
}
}
if err = db.SetVersion(cliVersion); err != nil {
return xerrors.Errorf("unexpected error: %w", err)
}
// When specifying no image name and file name
if noTarget {
return nil
@@ -156,23 +160,19 @@ func Run(c *cli.Context) (err error) {
}
var writer report.Writer
switch c.String("format") {
switch format := c.String("format"); format {
case "table":
writer = &report.TableWriter{Output: output}
case "json":
writer = &report.JsonWriter{Output: output}
default:
xerrors.New("unknown format")
return xerrors.Errorf("unknown format: %v", format)
}
if err = writer.Write(results); err != nil {
return xerrors.Errorf("failed to write results: %w", err)
}
if err = db.SetVersion(cliVersion); err != nil {
return xerrors.Errorf("unexpected error: %w", err)
}
exitCode := c.Int("exit-code")
if exitCode != 0 {
for _, result := range results {

View File

@@ -41,7 +41,8 @@ func Scan(files extractor.FileMap) (string, string, []vulnerability.DetectedVuln
case fos.RedHat, fos.CentOS:
s = redhat.NewScanner()
default:
return "", "", nil, xerrors.Errorf("unsupported os : %s", os.Family)
log.Logger.Warnf("unsupported os : %s", os.Family)
return "", "", nil, nil
}
pkgs, err := analyzer.GetPackages(files)
if err != nil {

View File

@@ -48,8 +48,10 @@ func ScanImage(imageName, filePath string) (map[string][]vulnerability.DetectedV
return nil, xerrors.Errorf("failed to scan image: %w", err)
}
imageDetail := fmt.Sprintf("%s (%s %s)", target, osFamily, osVersion)
results[imageDetail] = osVulns
if osFamily != "" {
imageDetail := fmt.Sprintf("%s (%s %s)", target, osFamily, osVersion)
results[imageDetail] = osVulns
}
libVulns, err := library.Scan(files)
if err != nil {

View File

@@ -89,17 +89,16 @@ func getDetail(vulnID string) (Severity, string, string, []string) {
func getSeverity(details map[string]Vulnerability) Severity {
for _, source := range sources {
d, ok := details[source]
if !ok {
switch d, ok := details[source]; {
case !ok:
continue
}
if d.CvssScore > 0 {
case d.CvssScore > 0:
return scoreToSeverity(d.CvssScore)
} else if d.CvssScoreV3 > 0 {
case d.CvssScoreV3 > 0:
return scoreToSeverity(d.CvssScoreV3)
} else if d.Severity != 0 {
case d.Severity != 0:
return d.Severity
} else if d.SeverityV3 != 0 {
case d.SeverityV3 != 0:
return d.SeverityV3
}
}
@@ -151,14 +150,16 @@ func getReferences(details map[string]Vulnerability) []string {
}
func scoreToSeverity(score float64) Severity {
if score >= 9.0 {
switch {
case score >= 9.0:
return SeverityCritical
} else if score >= 7.0 {
case score >= 7.0:
return SeverityHigh
} else if score >= 4.0 {
case score >= 4.0:
return SeverityMedium
} else if score > 0.0 {
case score > 0.0:
return SeverityLow
default:
return SeverityUnknown
}
return SeverityUnknown
}

View File

@@ -47,7 +47,7 @@ func Update() (err error) {
return xerrors.Errorf("error in Alpine OVAL update: %w", err)
}
//Update RedHat
// Update RedHat
log.Logger.Info("Updating RedHat data...")
if err = redhat.Update(dir, updatedFiles); err != nil {
return xerrors.Errorf("error in RedHat update: %w", err)
@@ -65,7 +65,7 @@ func Update() (err error) {
return xerrors.Errorf("error in Debian OVAL update: %w", err)
}
//Update Ubuntu
// Update Ubuntu
log.Logger.Info("Updating Ubuntu data...")
if err = ubuntu.Update(dir, updatedFiles); err != nil {
return xerrors.Errorf("error in Ubuntu update: %w", err)