Compare commits

...

16 Commits

Author SHA1 Message Date
knqyf263
9006dd5d16 Update dependencies 2019-05-13 15:16:47 +09:00
knqyf263
27d776cdf2 Support .trivyignore 2019-05-13 13:56:01 +09:00
knqyf263
1827d3d5c9 Refactor 2019-05-13 10:04:01 +09:00
knqyf263
2b5782c920 Use NVD CVSS score 2019-05-13 04:11:51 +09:00
knqyf263
6284def316 Aanalyze commands 2019-05-13 04:11:34 +09:00
Masahiro
0c11078302 Add Cargo scanner (#5) 2019-05-12 23:08:02 +09:00
knqyf263
507fac9284 Move remic 2019-05-11 14:45:52 +09:00
Masahiro
22abb9dab1 Add options for remic (#4)
* Fix filename

* Add options
2019-05-11 14:36:20 +09:00
knqyf263
6463176bc0 Update README 2019-05-08 19:14:48 +09:00
knqyf263
b208bc4c12 Add --refresh option 2019-05-08 19:10:08 +09:00
knqyf263
53ad8c2f35 Output description and references to JSON 2019-05-08 15:25:35 +09:00
knqyf263
34ba0ca8d7 Use description as title 2019-05-08 15:12:34 +09:00
knqyf263
6d82700032 Add --ignore-unfixed option and sort vulnerabilities by a severity 2019-05-08 13:31:51 +09:00
knqyf263
a0a991ca16 Update README 2019-05-08 07:47:47 +09:00
knqyf263
989b893bf2 Add Dockerfile 2019-05-07 23:07:21 +09:00
knqyf263
d270edea75 Support --quiet option 2019-05-07 23:06:57 +09:00
39 changed files with 908 additions and 465 deletions

1
.dockerignore Normal file
View File

@@ -0,0 +1 @@
imgs

14
Dockerfile Normal file
View File

@@ -0,0 +1,14 @@
FROM golang:1.12-alpine AS builder
ADD go.mod go.sum /app/
WORKDIR /app/
RUN apk --no-cache add git
RUN go mod download
ADD . /app/
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o /trivy cmd/trivy/main.go
FROM alpine:3.9
RUN apk --no-cache add ca-certificates git
COPY --from=builder /trivy /usr/local/bin/trivy
RUN chmod +x /usr/local/bin/trivy
CMD ["trivy"]

124
README.md
View File

@@ -5,12 +5,31 @@
[![Go Report Card](https://goreportcard.com/badge/github.com/knqyf263/trivy)](https://goreportcard.com/report/github.com/knqyf263/trivy)
[![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://github.com/knqyf263/trivy/blob/master/LICENSE)
A Simple and Comprehensive Vulnerability Scanner for Containers
A Simple and Comprehensive Vulnerability Scanner for Containers, Compatible with CI
# Abstract
Scan containers
`Trivy` is a simple and comprehensive vulnerability scanner for containers.
`Trivy` detects vulnerabilities of OS packages (Alpine, RHEL, CentOS, etc.) and application dependencies (Bundler, Composer, npm, etc.).
`Trivy` is easy to use. Just install the binary and you're ready to scan. It can be scanned just by specifying a container image name.
It is considered to be used in CI. Before pushing to a container registry, you can scan your local container image easily.
See [here](#continuous-integration-ci) for details.
# Features
- Detect comprehensive vulnerabilities
- OS packages (Alpine, Red Hat Enterprise Linux, CentOS, Debian, Ubuntu)
- **Application dependencies** (Bundler, Composer, Pipenv, npm, Cargo)
- Simple
- Specify only an image name
- Easy installation
- **No need for prerequirements** such as installation of DB, libraries, etc.
- `apt-get install`, `yum install` and `brew install` is possible (See [Installation](#installation))
- High accuracy
- Especially Alpine
- **Compatible with CI**
- See [CI Example](#continuous-integration-ci)
# Installation
@@ -29,6 +48,12 @@ $ sudo yum -y update
$ sudo yum -y install trivy
```
or
```
$ rpm -ivh https://github.com/knqyf263/trivy/releases/download/v0.0.3/trivy_0.0.3_Linux-64bit.rpm
```
## Debian/Ubuntu
Replace `[CODE_NAME]` with your code name
@@ -43,6 +68,14 @@ $ sudo apt-get update
$ sudo apt-get install trivy
```
or
```
$ sudo apt-get install rpm
$ wget https://github.com/knqyf263/trivy/releases/download/v0.0.3/trivy_0.0.3_Linux-64bit.deb
$ sudo dpkg -i trivy_0.0.3_Linux-64bit.deb
```
## Mac OS X / Homebrew
You can use homebrew on OS X.
```
@@ -51,7 +84,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.
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.
## From source
@@ -60,24 +93,90 @@ $ go get -u github.com/knqyf263/trivy
```
# Examples
## Continuous Integration (CI)
Scan your image built in Travis CI/CircleCI. The test will fail if a vulnerability is found. When you don't want to fail the test, specify `--exit-code 0` .
**Note**: The first time take a while (faster by cache after the second time)
### Travis CI
```
$ cat .travis.yml
services:
- docker
before_install:
- docker build -t trivy-ci-test:latest .
- wget https://github.com/knqyf263/trivy/releases/download/v0.0.3/trivy_0.0.3_Linux-64bit.tar.gz
- tar zxvf trivy_0.0.3_Linux-64bit.tar.gz
script:
- ./trivy --exit-code 1 --quiet trivy-ci-test:latest
cache:
directories:
- $HOME/.cache/trivy
```
example: https://travis-ci.org/knqyf263/trivy-ci-test
repository: https://github.com/knqyf263/trivy-ci-test
### Circle CI
```
$ cat .circleci/config.yml
jobs:
build:
docker:
- image: docker:18.09-git
steps:
- checkout
- setup_remote_docker
- restore_cache:
key: vulnerability-db
- run:
name: Build image
command: docker build -t trivy-ci-test:latest .
- run:
name: Install trivy
command: |
wget https://github.com/knqyf263/trivy/releases/download/v0.0.4/trivy_0.0.4_Linux-64bit.tar.gz
tar zxvf trivy_0.0.4_Linux-64bit.tar.gz
mv trivy /usr/local/bin
- run:
name: Scan the local image with trivy
command: trivy --exit-code 1 --quiet trivy-ci-test:latest
- save_cache:
key: vulnerability-db
paths:
- $HOME/.cache/trivy
workflows:
version: 2
release:
jobs:
- build
```
example: https://circleci.com/gh/knqyf263/trivy-ci-test
repository: https://github.com/knqyf263/trivy-ci-test
# Usage
```
$ trivy -h
NAME:
trivy - A simple and comprehensive vulnerability scanner for containers
USAGE:
main [options] image_name
trivy [options] image_name
VERSION:
0.0.1
0.0.3
OPTIONS:
--format value, -f value format (table, json) (default: "table")
--input value, -i value input file path instead of image name
--severity value, -s value severities of vulnerabilities to be displayed (comma separated) (default: "CRITICAL,HIGH,MEDIUM,LOW,UNKNOWN")
--severity value, -s value severities of vulnerabilities to be displayed (comma separated) (default: "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL")
--output value, -o value output file name
--exit-code value Exit code when vulnerabilities were found (default: 0)
--skip-update skip db update
--clean, -c clean all cache
--quiet, -q suppress progress bar
--ignore-unfixed display only fixed vulnerabilities
--refresh refresh DB (usually used after version update of trivy
--debug, -d debug mode
--help, -h show help
--version, -v print the version
@@ -99,7 +198,7 @@ echo 'export HOMEBREW_GITHUB_API_TOKEN=your_token_here' >> ~/.zshrc
Try:
```
$ printf "protocol=https\nhost=github.com\n" | git credential-osxkeychain erase
$ printf "protocol=https\nhost=github.com\n" | git credential-osxkeychain erase
```
### Error: knqyf263/trivy/trivy 64 already installed
@@ -119,11 +218,18 @@ $ brew install knqyf263/trivy/trivy
```
## Others
### Detected version update of trivy. Please try again with --refresh option
Try again with `--refresh` option
```
$ trivy --refresh alpine:3.9
```
### Unknown error
Try again with `--clean` option
```
$ trivy --clean alpine:3.8
$ trivy --clean
```
# Contribute

View File

@@ -1,67 +0,0 @@
package main
import (
"os"
"strings"
"github.com/knqyf263/trivy/pkg/vulnsrc/vulnerability"
"github.com/knqyf263/trivy/pkg/remic"
"github.com/urfave/cli"
"github.com/knqyf263/trivy/pkg/log"
)
func main() {
cli.AppHelpTemplate = `NAME:
{{.Name}}{{if .Usage}} - {{.Usage}}{{end}}
USAGE:
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}}
VERSION:
{{.Version}}{{end}}{{end}}{{if .Description}}
DESCRIPTION:
{{.Description}}{{end}}{{if len .Authors}}
AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}:
{{range $index, $author := .Authors}}{{if $index}}
{{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}}
OPTIONS:
{{range $index, $option := .VisibleFlags}}{{if $index}}
{{end}}{{$option}}{{end}}{{end}}
`
app := cli.NewApp()
app.Name = "remic"
app.Version = "0.0.1"
app.ArgsUsage = "file"
app.Usage = "A simple and fast tool for detecting vulnerabilities in application dependencies"
app.Flags = []cli.Flag{
cli.StringFlag{
Name: "format, f",
Value: "table",
Usage: "format (table, json)",
},
cli.StringFlag{
Name: "severity, s",
Value: strings.Join(vulnerability.SeverityNames, ","),
Usage: "severity of vulnerabilities to be displayed",
},
cli.StringFlag{
Name: "output, o",
Usage: "output file name",
},
cli.BoolFlag{
Name: "debug, d",
Usage: "debug mode",
},
}
app.Action = func(c *cli.Context) error {
return remic.Run(c)
}
err := app.Run(os.Args)
if err != nil {
log.Logger.Fatal(err)
}
}

View File

@@ -73,6 +73,18 @@ OPTIONS:
Name: "clean, c",
Usage: "clean all cache",
},
cli.BoolFlag{
Name: "quiet, q",
Usage: "suppress progress bar",
},
cli.BoolFlag{
Name: "ignore-unfixed",
Usage: "display only fixed vulnerabilities",
},
cli.BoolFlag{
Name: "refresh",
Usage: "refresh DB (usually used after version update of trivy)",
},
cli.BoolFlag{
Name: "debug, d",
Usage: "debug mode",

6
go.mod
View File

@@ -3,15 +3,17 @@ module github.com/knqyf263/trivy
go 1.12
require (
github.com/BurntSushi/toml v0.3.1
github.com/briandowns/spinner v0.0.0-20190319032542-ac46072a5a91
github.com/emirpasic/gods v1.12.0 // indirect
github.com/etcd-io/bbolt v1.3.2
github.com/fatih/color v1.7.0
github.com/genuinetools/reg v0.16.0
github.com/gliderlabs/ssh v0.1.3 // indirect
github.com/golang/protobuf v1.3.1 // indirect
github.com/knqyf263/fanal v0.0.0-20190507123206-ceab60083e70
github.com/knqyf263/fanal v0.0.0-20190513061210-e1980f95d1f5
github.com/knqyf263/go-deb-version v0.0.0-20170509080151-9865fe14d09b
github.com/knqyf263/go-dep-parser v0.0.0-20190429154931-c377a5391790
github.com/knqyf263/go-dep-parser v0.0.0-20190511063217-d5d543bfc261
github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936
github.com/knqyf263/go-version v1.1.1
github.com/mattn/go-colorable v0.1.1 // indirect

13
go.sum
View File

@@ -4,6 +4,7 @@ cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU=
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/GoogleCloudPlatform/docker-credential-gcr v1.5.0 h1:wykTgKwhVr2t2qs+xI020s6W5dt614QqCHV+7W9dg64=
github.com/GoogleCloudPlatform/docker-credential-gcr v1.5.0/go.mod h1:BB1eHdMLYEFuFdBlRMb0N7YGVdM5s6Pt0njxgvfbGGs=
@@ -115,14 +116,12 @@ github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e h1:RgQk53JHp
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/knqyf263/berkeleydb v0.0.0-20190501065933-fafe01fb9662/go.mod h1:bu1CcN4tUtoRcI/B/RFHhxMNKFHVq/c3SV+UTyduoXg=
github.com/knqyf263/fanal v0.0.0-20190506110705-2b5cb3000ff6 h1:iSztZNfwEPMN2CvUX1SxNEclRZn+rwRMdsnAegxRJk4=
github.com/knqyf263/fanal v0.0.0-20190506110705-2b5cb3000ff6/go.mod h1:OiuWIClssf5WzbMcR8lfspdBVaP+vRQndY4kHeFgrDw=
github.com/knqyf263/fanal v0.0.0-20190507123206-ceab60083e70 h1:L27WBZxk7N70WilG91kgvs0EnV+JVCoOTsNQa8tMBJs=
github.com/knqyf263/fanal v0.0.0-20190507123206-ceab60083e70/go.mod h1:OiuWIClssf5WzbMcR8lfspdBVaP+vRQndY4kHeFgrDw=
github.com/knqyf263/fanal v0.0.0-20190513061210-e1980f95d1f5 h1:d6oqR7xZEMoNDp0x7/g/DCKZ1n0exzr76eyzOz8p2ls=
github.com/knqyf263/fanal v0.0.0-20190513061210-e1980f95d1f5/go.mod h1:/3MJJHzCSQm4xQMUnBAGx7aI5zvWA98twxekdDcLzbY=
github.com/knqyf263/go-deb-version v0.0.0-20170509080151-9865fe14d09b h1:DiDMmSwuY27PJxA2Gs0+uI/bQ/ehKARaGXRdlp+wFis=
github.com/knqyf263/go-deb-version v0.0.0-20170509080151-9865fe14d09b/go.mod h1:o8sgWoz3JADecfc/cTYD92/Et1yMqMy0utV1z+VaZao=
github.com/knqyf263/go-dep-parser v0.0.0-20190429154931-c377a5391790 h1:c02gG0yRNr25lcLOH+678SuuxxMUq36i48PQnmAweWk=
github.com/knqyf263/go-dep-parser v0.0.0-20190429154931-c377a5391790/go.mod h1:CtT+dtv38jSz5EYYCX21LgtVXP+J3soF2fzQT8lHCfY=
github.com/knqyf263/go-dep-parser v0.0.0-20190511063217-d5d543bfc261 h1:RPgPsbEsYj6LuOjZnKl2DvbfodNWRuWKZfWJkrD7l8s=
github.com/knqyf263/go-dep-parser v0.0.0-20190511063217-d5d543bfc261/go.mod h1:gSiqSkOFPstUZu/qZ4wnNJS69PtQQnPl397vxKHJ5mQ=
github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936 h1:HDjRqotkViMNcGMGicb7cgxklx8OwnjtCBmyWEqrRvM=
github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936/go.mod h1:i4sF0l1fFnY1aiw08QQSwVAFxHEm311Me3WsU/X7nL0=
github.com/knqyf263/go-rpmdb v0.0.0-20190501070121-10a1c42a10dc/go.mod h1:MrSSvdMpTSymaQWk1yFr9sxFSyQmKMj6jkbvGrchBV8=
@@ -155,8 +154,6 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88=
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=

View File

@@ -2,10 +2,11 @@ package db
import (
"encoding/json"
"github.com/knqyf263/trivy/pkg/log"
"os"
"path/filepath"
"github.com/knqyf263/trivy/pkg/log"
"golang.org/x/xerrors"
"github.com/knqyf263/trivy/pkg/utils"
@@ -14,11 +15,11 @@ import (
)
var (
db *bolt.DB
db *bolt.DB
dbDir = filepath.Join(utils.CacheDir(), "db")
)
func Init() (err error) {
dbDir := filepath.Join(utils.CacheDir(), "db")
if err = os.MkdirAll(dbDir, 0700); err != nil {
return xerrors.Errorf("failed to mkdir: %w", err)
}
@@ -32,6 +33,33 @@ func Init() (err error) {
return nil
}
func Reset() error {
if err := os.RemoveAll(dbDir); err != nil {
return xerrors.Errorf("failed to reset DB: %w", err)
}
return nil
}
func GetVersion() string {
var version string
value, err := Get("trivy", "metadata", "version")
if err != nil {
return ""
}
if err = json.Unmarshal(value, &version); err != nil {
return ""
}
return version
}
func SetVersion(version string) error {
err := Update("trivy", "metadata", "version", version)
if err != nil {
return xerrors.Errorf("failed to save DB version: %w", err)
}
return nil
}
func Update(rootBucket, nestedBucket, key string, value interface{}) error {
err := db.Update(func(tx *bolt.Tx) error {
return PutNestedBucket(tx, rootBucket, nestedBucket, key, value)

View File

@@ -4,9 +4,9 @@ import (
"os"
"path/filepath"
"strings"
"time"
"github.com/briandowns/spinner"
"github.com/knqyf263/trivy/pkg/db"
"github.com/knqyf263/trivy/pkg/log"
"github.com/knqyf263/trivy/pkg/utils"
"golang.org/x/xerrors"
@@ -38,8 +38,8 @@ func CloneOrPull(url, repoPath string) (map[string]struct{}, error) {
}
log.Logger.Debug("remove an existed directory")
s := spinner.New(spinner.CharSets[36], 100*time.Millisecond)
s.Suffix = " The first time will take a while..."
suffix := " The first time will take a while..."
s := utils.NewSpinner(suffix)
s.Start()
defer s.Stop()
@@ -54,6 +54,10 @@ func CloneOrPull(url, repoPath string) (map[string]struct{}, error) {
return nil, xerrors.Errorf("failed to clone repository: %w", err)
}
}
// Need to refresh all vulnerabilities
if db.GetVersion() == "" {
err = filepath.Walk(repoPath, func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
return nil

View File

@@ -1,85 +0,0 @@
package remic
import (
l "log"
"os"
"strings"
"github.com/knqyf263/trivy/pkg/scanner"
"github.com/knqyf263/trivy/pkg/vulnsrc/vulnerability"
"github.com/knqyf263/trivy/pkg/vulnsrc"
"github.com/urfave/cli"
"golang.org/x/xerrors"
"github.com/knqyf263/trivy/pkg/db"
"github.com/knqyf263/trivy/pkg/log"
"github.com/knqyf263/trivy/pkg/report"
)
func Run(c *cli.Context) (err error) {
debug := c.Bool("debug")
if err = log.InitLogger(debug); err != nil {
l.Fatal(err)
}
args := c.Args()
if len(args) == 0 {
return xerrors.New(`remic" requires at least 1 argument.`)
}
o := c.String("output")
output := os.Stdout
if o != "" {
if output, err = os.Create(o); err != nil {
return err
}
}
var severities []vulnerability.Severity
for _, s := range strings.Split(c.String("severity"), ",") {
severity, err := vulnerability.NewSeverity(s)
if err != nil {
return err
}
severities = append(severities, severity)
}
if err = db.Init(); err != nil {
return err
}
if err = vulnsrc.Update(); err != nil {
return err
}
fileName := args[0]
f, err := os.Open(fileName)
if err != nil {
return xerrors.Errorf("failed to open a file: %w", err)
}
defer f.Close()
result, err := scanner.ScanFile(f, severities)
if err != nil {
return xerrors.Errorf("failed to scan a file: %w", err)
}
var writer report.Writer
switch c.String("format") {
case "table":
writer = &report.TableWriter{Output: output}
case "json":
writer = &report.JsonWriter{Output: output}
default:
return xerrors.New("unknown format")
}
if err = writer.Write([]report.Result{result}); err != nil {
return xerrors.Errorf("failed to write results: %w", err)
}
return nil
}

View File

@@ -10,7 +10,6 @@ import (
"github.com/knqyf263/trivy/pkg/vulnsrc/vulnerability"
"github.com/knqyf263/trivy/pkg/types"
"github.com/olekukonko/tablewriter"
)
@@ -18,7 +17,7 @@ type Results []Result
type Result struct {
FileName string `json:"file"`
Vulnerabilities []types.Vulnerability
Vulnerabilities []vulnerability.DetectedVulnerability
}
type Writer interface {
@@ -42,8 +41,17 @@ func (tw TableWriter) write(result Result) {
severityCount := map[string]int{}
for _, v := range result.Vulnerabilities {
severityCount[v.Severity]++
title := v.Title
if title == "" {
title = v.Description
}
splittedTitle := strings.Split(title, " ")
if len(splittedTitle) >= 12 {
title = strings.Join(splittedTitle[:12], " ") + "..."
}
table.Append([]string{v.PkgName, v.VulnerabilityID, vulnerability.ColorizeSeverity(v.Severity),
v.InstalledVersion, v.FixedVersion, v.Title})
v.InstalledVersion, v.FixedVersion, title})
}
var results []string
@@ -71,7 +79,7 @@ type JsonWriter struct {
}
func (jw JsonWriter) Write(results Results) error {
out := map[string][]types.Vulnerability{}
out := map[string][]vulnerability.DetectedVulnerability{}
for _, result := range results {
out[result.FileName] = result.Vulnerabilities
}

View File

@@ -23,19 +23,14 @@ import (
)
func Run(c *cli.Context) (err error) {
cliVersion := c.App.Version
debug := c.Bool("debug")
if err = log.InitLogger(debug); err != nil {
l.Fatal(err)
}
log.Logger.Debugf("cache dir: %s", utils.CacheDir())
args := c.Args()
filePath := c.String("input")
if filePath == "" && len(args) == 0 {
log.Logger.Info(`trivy" requires at least 1 argument or --input option.`)
cli.ShowAppHelpAndExit(c, 1)
}
clean := c.Bool("clean")
if clean {
log.Logger.Info("Cleaning caches...")
@@ -45,7 +40,18 @@ func Run(c *cli.Context) (err error) {
if err = os.RemoveAll(utils.CacheDir()); err != nil {
return xerrors.New("failed to remove cache")
}
return nil
}
args := c.Args()
filePath := c.String("input")
if filePath == "" && len(args) == 0 {
log.Logger.Info(`trivy" requires at least 1 argument or --input option.`)
cli.ShowAppHelpAndExit(c, 1)
}
utils.Quiet = c.Bool("quiet")
o := c.String("output")
output := os.Stdout
if o != "" {
@@ -64,25 +70,47 @@ func Run(c *cli.Context) (err error) {
severities = append(severities, severity)
}
if c.Bool("refresh") {
log.Logger.Info("Resetting DB...")
if err = db.Reset(); err != nil {
return xerrors.Errorf("error in refresh DB: %w", err)
}
}
if err = db.Init(); err != nil {
return xerrors.Errorf("error in vulnerability DB initialize: %w", err)
}
dbVersion := db.GetVersion()
if dbVersion != "" && dbVersion != cliVersion {
log.Logger.Fatal("Detected version update of trivy. Please try again with --refresh option")
}
if !c.Bool("skip-update") {
if err = vulnsrc.Update(); err != nil {
return xerrors.Errorf("error in vulnerability DB update: %w", err)
}
}
ignoreUnfixed := c.Bool("ignore-unfixed")
var imageName string
if filePath == "" {
imageName = args[0]
}
results, err := scanner.ScanImage(imageName, filePath, severities)
vulns, err := scanner.ScanImage(imageName, filePath)
if err != nil {
return xerrors.Errorf("error in image scan: %w", err)
}
var results report.Results
for path, vuln := range vulns {
results = append(results, report.Result{
FileName: path,
Vulnerabilities: vulnerability.FillAndFilter(vuln, severities, ignoreUnfixed),
})
}
var writer report.Writer
switch c.String("format") {
case "table":
@@ -97,9 +125,16 @@ func Run(c *cli.Context) (err error) {
return xerrors.Errorf("failed to write results: %w", err)
}
for _, result := range results {
if len(result.Vulnerabilities) > 0 {
os.Exit(c.Int("exit-code"))
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 {
if len(result.Vulnerabilities) > 0 {
os.Exit(exitCode)
}
}
}

View File

@@ -5,11 +5,12 @@ import (
"os"
"strings"
"github.com/knqyf263/trivy/pkg/vulnsrc/vulnerability"
"github.com/knqyf263/go-dep-parser/pkg/bundler"
ptypes "github.com/knqyf263/go-dep-parser/pkg/types"
"github.com/knqyf263/go-version"
"github.com/knqyf263/trivy/pkg/scanner/utils"
"github.com/knqyf263/trivy/pkg/types"
"golang.org/x/xerrors"
)
@@ -25,8 +26,8 @@ func NewScanner() *Scanner {
return &Scanner{}
}
func (s *Scanner) Detect(pkgName string, pkgVer *version.Version) ([]types.Vulnerability, error) {
var vulns []types.Vulnerability
func (s *Scanner) Detect(pkgName string, pkgVer *version.Version) ([]vulnerability.DetectedVulnerability, error) {
var vulns []vulnerability.DetectedVulnerability
for _, advisory := range s.db[pkgName] {
if utils.MatchVersions(pkgVer, advisory.PatchedVersions) {
continue
@@ -42,7 +43,7 @@ func (s *Scanner) Detect(pkgName string, pkgVer *version.Version) ([]types.Vulne
vulnerabilityID = fmt.Sprintf("OSVDB-%s", advisory.Osvdb)
}
vuln := types.Vulnerability{
vuln := vulnerability.DetectedVulnerability{
VulnerabilityID: vulnerabilityID,
PkgName: strings.TrimSpace(advisory.Gem),
Title: strings.TrimSpace(advisory.Title),

View File

@@ -0,0 +1,111 @@
package cargo
import (
"io/ioutil"
"os"
"path/filepath"
"github.com/BurntSushi/toml"
"github.com/etcd-io/bbolt"
"github.com/knqyf263/trivy/pkg/db"
"github.com/knqyf263/trivy/pkg/vulnsrc/vulnerability"
"golang.org/x/xerrors"
"github.com/knqyf263/trivy/pkg/git"
"github.com/knqyf263/trivy/pkg/utils"
)
const (
dbURL = "https://github.com/RustSec/advisory-db.git"
)
var (
repoPath = filepath.Join(utils.CacheDir(), "rust-advisory-db")
)
type AdvisoryDB map[string][]Lockfile
type Lockfile struct {
Advisory `toml:"advisory"`
}
type Advisory struct {
Id string
Package string
Title string `toml:"title"`
Url string
Date string
Description string
Keywords []string
PatchedVersions []string `toml:"patched_versions"`
AffectedFunctions []string `toml:"affected_functions"`
}
func (s *Scanner) UpdateDB() (err error) {
if _, err := git.CloneOrPull(dbURL, repoPath); err != nil {
return xerrors.Errorf("error in %s security DB update: %w", s.Type(), err)
}
s.db, err = s.walk()
return err
}
func (s *Scanner) walk() (AdvisoryDB, error) {
advisoryDB := AdvisoryDB{}
root := filepath.Join(repoPath, "crates")
var vulns []vulnerability.Vulnerability
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
return nil
}
buf, err := ioutil.ReadFile(path)
if err != nil {
return xerrors.Errorf("failed to read a file: %w", err)
}
advisory := Lockfile{}
err = toml.Unmarshal(buf, &advisory)
if err != nil {
return xerrors.Errorf("failed to unmarshal TOML: %w", err)
}
// for detecting vulnerabilities
advisories, ok := advisoryDB[advisory.Package]
if !ok {
advisories = []Lockfile{}
}
advisoryDB[advisory.Package] = append(advisories, advisory)
// for displaying vulnerability detail
vulns = append(vulns, vulnerability.Vulnerability{
ID: advisory.Id,
References: []string{advisory.Url},
Title: advisory.Title,
Description: advisory.Description,
})
return nil
})
if err != nil {
return nil, xerrors.Errorf("error in file walk: %w", err)
}
if err = s.saveVulnerabilities(vulns); err != nil {
return nil, err
}
return advisoryDB, nil
}
func (s *Scanner) saveVulnerabilities(vulns []vulnerability.Vulnerability) error {
return vulnerability.BatchUpdate(func(b *bbolt.Bucket) error {
for _, vuln := range vulns {
if err := db.Put(b, vuln.ID, vulnerability.RustSec, vuln); err != nil {
return xerrors.Errorf("failed to save %s vulnerability: %w", s.Type(), err)
}
}
return nil
})
}

View File

@@ -0,0 +1,57 @@
package cargo
import (
"os"
"strings"
"github.com/knqyf263/trivy/pkg/vulnsrc/vulnerability"
"github.com/knqyf263/go-dep-parser/pkg/cargo"
ptypes "github.com/knqyf263/go-dep-parser/pkg/types"
"github.com/knqyf263/go-version"
"github.com/knqyf263/trivy/pkg/scanner/utils"
"golang.org/x/xerrors"
)
const (
scannerType = "cargo"
)
type Scanner struct {
db AdvisoryDB
}
func NewScanner() *Scanner {
return &Scanner{}
}
func (s *Scanner) Detect(pkgName string, pkgVer *version.Version) ([]vulnerability.DetectedVulnerability, error) {
var vulns []vulnerability.DetectedVulnerability
for _, advisory := range s.db[pkgName] {
if utils.MatchVersions(pkgVer, advisory.PatchedVersions) {
continue
}
vuln := vulnerability.DetectedVulnerability{
VulnerabilityID: advisory.Id,
PkgName: strings.TrimSpace(advisory.Package),
Title: strings.TrimSpace(advisory.Title),
InstalledVersion: pkgVer.String(),
FixedVersion: strings.Join(advisory.PatchedVersions, ", "),
}
vulns = append(vulns, vuln)
}
return vulns, nil
}
func (s *Scanner) ParseLockfile(f *os.File) ([]ptypes.Library, error) {
libs, err := cargo.Parse(f)
if err != nil {
return nil, xerrors.Errorf("invalid Cargo.lock format: %w", err)
}
return libs, nil
}
func (s *Scanner) Type() string {
return scannerType
}

View File

@@ -5,13 +5,14 @@ import (
"os"
"strings"
"github.com/knqyf263/trivy/pkg/vulnsrc/vulnerability"
"golang.org/x/xerrors"
"github.com/knqyf263/go-dep-parser/pkg/composer"
ptypes "github.com/knqyf263/go-dep-parser/pkg/types"
"github.com/knqyf263/go-version"
"github.com/knqyf263/trivy/pkg/scanner/utils"
"github.com/knqyf263/trivy/pkg/types"
)
const (
@@ -26,8 +27,8 @@ func NewScanner() *Scanner {
return &Scanner{}
}
func (s *Scanner) Detect(pkgName string, pkgVer *version.Version) ([]types.Vulnerability, error) {
var vulns []types.Vulnerability
func (s *Scanner) Detect(pkgName string, pkgVer *version.Version) ([]vulnerability.DetectedVulnerability, error) {
var vulns []vulnerability.DetectedVulnerability
ref := fmt.Sprintf("composer://%s", pkgName)
for _, advisory := range s.db[ref] {
var affectedVersions []string
@@ -45,7 +46,7 @@ func (s *Scanner) Detect(pkgName string, pkgVer *version.Version) ([]types.Vulne
continue
}
vuln := types.Vulnerability{
vuln := vulnerability.DetectedVulnerability{
VulnerabilityID: advisory.Cve,
PkgName: pkgName,
Title: strings.TrimSpace(advisory.Title),

View File

@@ -5,13 +5,14 @@ import (
"os"
"strings"
"github.com/knqyf263/trivy/pkg/vulnsrc/vulnerability"
"golang.org/x/xerrors"
"github.com/knqyf263/go-dep-parser/pkg/npm"
ptypes "github.com/knqyf263/go-dep-parser/pkg/types"
"github.com/knqyf263/go-version"
"github.com/knqyf263/trivy/pkg/scanner/utils"
"github.com/knqyf263/trivy/pkg/types"
)
const (
@@ -26,9 +27,9 @@ func NewScanner() *Scanner {
return &Scanner{}
}
func (s *Scanner) Detect(pkgName string, pkgVer *version.Version) ([]types.Vulnerability, error) {
func (s *Scanner) Detect(pkgName string, pkgVer *version.Version) ([]vulnerability.DetectedVulnerability, error) {
replacer := strings.NewReplacer(".alpha", "-alpha", ".beta", "-beta", ".rc", "-rc", " <", ", <", " >", ", >")
var vulns []types.Vulnerability
var vulns []vulnerability.DetectedVulnerability
for _, advisory := range s.db[pkgName] {
// e.g. <= 2.15.0 || >= 3.0.0 <= 3.8.2
// => {"<=2.15.0", ">= 3.0.0, <= 3.8.2"}
@@ -57,7 +58,7 @@ func (s *Scanner) Detect(pkgName string, pkgVer *version.Version) ([]types.Vulne
}
for _, cveID := range advisory.Cves {
vuln := types.Vulnerability{
vuln := vulnerability.DetectedVulnerability{
VulnerabilityID: cveID,
PkgName: pkgName,
Title: strings.TrimSpace(advisory.Title),

View File

@@ -4,13 +4,14 @@ import (
"os"
"strings"
"github.com/knqyf263/trivy/pkg/vulnsrc/vulnerability"
"golang.org/x/xerrors"
"github.com/knqyf263/go-dep-parser/pkg/pipenv"
ptypes "github.com/knqyf263/go-dep-parser/pkg/types"
"github.com/knqyf263/go-version"
"github.com/knqyf263/trivy/pkg/scanner/utils"
"github.com/knqyf263/trivy/pkg/types"
)
const (
@@ -25,8 +26,8 @@ func NewScanner() *Scanner {
return &Scanner{}
}
func (s *Scanner) Detect(pkgName string, pkgVer *version.Version) ([]types.Vulnerability, error) {
var vulns []types.Vulnerability
func (s *Scanner) Detect(pkgName string, pkgVer *version.Version) ([]vulnerability.DetectedVulnerability, error) {
var vulns []vulnerability.DetectedVulnerability
for _, advisory := range s.db[pkgName] {
if !utils.MatchVersions(pkgVer, advisory.Specs) {
continue
@@ -37,7 +38,7 @@ func (s *Scanner) Detect(pkgName string, pkgVer *version.Version) ([]types.Vulne
vulnerabilityID = advisory.ID
}
vuln := types.Vulnerability{
vuln := vulnerability.DetectedVulnerability{
VulnerabilityID: vulnerabilityID,
PkgName: pkgName,
Title: strings.TrimSpace(advisory.Advisory),

View File

@@ -4,8 +4,11 @@ import (
"os"
"path/filepath"
"github.com/knqyf263/trivy/pkg/vulnsrc/vulnerability"
"github.com/knqyf263/fanal/analyzer"
_ "github.com/knqyf263/fanal/analyzer/library/bundler"
_ "github.com/knqyf263/fanal/analyzer/library/cargo"
_ "github.com/knqyf263/fanal/analyzer/library/composer"
_ "github.com/knqyf263/fanal/analyzer/library/npm"
_ "github.com/knqyf263/fanal/analyzer/library/pipenv"
@@ -14,17 +17,17 @@ import (
"github.com/knqyf263/go-version"
"github.com/knqyf263/trivy/pkg/log"
"github.com/knqyf263/trivy/pkg/scanner/library/bundler"
"github.com/knqyf263/trivy/pkg/scanner/library/cargo"
"github.com/knqyf263/trivy/pkg/scanner/library/composer"
"github.com/knqyf263/trivy/pkg/scanner/library/npm"
"github.com/knqyf263/trivy/pkg/scanner/library/pipenv"
"github.com/knqyf263/trivy/pkg/types"
"golang.org/x/xerrors"
)
type Scanner interface {
UpdateDB() error
ParseLockfile(*os.File) ([]ptypes.Library, error)
Detect(string, *version.Version) ([]types.Vulnerability, error)
Detect(string, *version.Version) ([]vulnerability.DetectedVulnerability, error)
Type() string
}
@@ -33,6 +36,8 @@ func NewScanner(filename string) Scanner {
switch filename {
case "Gemfile.lock":
scanner = bundler.NewScanner()
case "Cargo.lock":
scanner = cargo.NewScanner()
case "composer.lock":
scanner = composer.NewScanner()
case "package-lock.json":
@@ -45,13 +50,13 @@ func NewScanner(filename string) Scanner {
return scanner
}
func Scan(files extractor.FileMap) (map[string][]types.Vulnerability, error) {
func Scan(files extractor.FileMap) (map[string][]vulnerability.DetectedVulnerability, error) {
results, err := analyzer.GetLibraries(files)
if err != nil {
return nil, xerrors.Errorf("failed to analyze libraries: %w", err)
}
vulnerabilities := map[string][]types.Vulnerability{}
vulnerabilities := map[string][]vulnerability.DetectedVulnerability{}
for path, pkgs := range results {
log.Logger.Debugf("Detecting library vulnerabilities, path: %s", path)
scanner := NewScanner(filepath.Base(string(path)))
@@ -71,8 +76,8 @@ func Scan(files extractor.FileMap) (map[string][]types.Vulnerability, error) {
return vulnerabilities, nil
}
func ScanFile(f *os.File) ([]types.Vulnerability, error) {
scanner := NewScanner(f.Name())
func ScanFile(f *os.File) ([]vulnerability.DetectedVulnerability, error) {
scanner := NewScanner(filepath.Base(f.Name()))
if scanner == nil {
return nil, xerrors.New("unknown file type")
}
@@ -89,7 +94,7 @@ func ScanFile(f *os.File) ([]types.Vulnerability, error) {
return vulns, nil
}
func scan(scanner Scanner, pkgs []ptypes.Library) ([]types.Vulnerability, error) {
func scan(scanner Scanner, pkgs []ptypes.Library) ([]vulnerability.DetectedVulnerability, error) {
log.Logger.Infof("Updating %s Security DB...", scanner.Type())
err := scanner.UpdateDB()
if err != nil {
@@ -97,7 +102,7 @@ func scan(scanner Scanner, pkgs []ptypes.Library) ([]types.Vulnerability, error)
}
log.Logger.Infof("Detecting %s vulnerabilities...", scanner.Type())
var vulnerabilities []types.Vulnerability
var vulnerabilities []vulnerability.DetectedVulnerability
for _, pkg := range pkgs {
v, err := version.NewVersion(pkg.Version)
if err != nil {

View File

@@ -3,17 +3,17 @@ package alpine
import (
"strings"
"github.com/knqyf263/trivy/pkg/vulnsrc/vulnerability"
"golang.org/x/xerrors"
version "github.com/knqyf263/go-rpm-version"
"github.com/knqyf263/trivy/pkg/scanner/utils"
"github.com/knqyf263/go-rpm-version"
"github.com/knqyf263/trivy/pkg/vulnsrc/alpine"
"github.com/knqyf263/fanal/analyzer"
"github.com/knqyf263/trivy/pkg/log"
"github.com/knqyf263/trivy/pkg/types"
)
type Scanner struct{}
@@ -22,15 +22,15 @@ func NewScanner() *Scanner {
return &Scanner{}
}
func (s *Scanner) Detect(osVer string, pkgs []analyzer.Package) ([]types.Vulnerability, error) {
func (s *Scanner) Detect(osVer string, pkgs []analyzer.Package) ([]vulnerability.DetectedVulnerability, error) {
log.Logger.Info("Detecting Alpine vulnerabilities...")
if strings.Count(osVer, ".") > 1 {
osVer = osVer[:strings.LastIndex(osVer, ".")]
}
log.Logger.Debugf("alpine: os version: %s", osVer)
log.Logger.Debugf("alpine: the number of packages: %s", len(pkgs))
log.Logger.Debugf("alpine: the number of packages: %d", len(pkgs))
var vulns []types.Vulnerability
var vulns []vulnerability.DetectedVulnerability
for _, pkg := range pkgs {
advisories, err := alpine.Get(osVer, pkg.Name)
if err != nil {
@@ -43,7 +43,7 @@ func (s *Scanner) Detect(osVer string, pkgs []analyzer.Package) ([]types.Vulnera
for _, adv := range advisories {
fixedVersion := version.NewVersion(adv.FixedVersion)
if installedVersion.LessThan(fixedVersion) {
vuln := types.Vulnerability{
vuln := vulnerability.DetectedVulnerability{
VulnerabilityID: adv.VulnerabilityID,
PkgName: pkg.Name,
InstalledVersion: installed,

View File

@@ -3,14 +3,16 @@ package debian
import (
"strings"
version "github.com/knqyf263/go-deb-version"
"github.com/knqyf263/trivy/pkg/vulnsrc/vulnerability"
"golang.org/x/xerrors"
"github.com/knqyf263/go-deb-version"
"github.com/knqyf263/trivy/pkg/scanner/utils"
"github.com/knqyf263/fanal/analyzer"
"github.com/knqyf263/trivy/pkg/log"
"github.com/knqyf263/trivy/pkg/types"
"github.com/knqyf263/trivy/pkg/vulnsrc/debian"
debianoval "github.com/knqyf263/trivy/pkg/vulnsrc/debian-oval"
)
@@ -21,7 +23,7 @@ func NewScanner() *Scanner {
return &Scanner{}
}
func (s *Scanner) Detect(osVer string, pkgs []analyzer.Package) ([]types.Vulnerability, error) {
func (s *Scanner) Detect(osVer string, pkgs []analyzer.Package) ([]vulnerability.DetectedVulnerability, error) {
log.Logger.Info("Detecting Debian vulnerabilities...")
if strings.Count(osVer, ".") > 0 {
@@ -30,7 +32,7 @@ func (s *Scanner) Detect(osVer string, pkgs []analyzer.Package) ([]types.Vulnera
log.Logger.Debugf("debian: os version: %s", osVer)
log.Logger.Debugf("debian: the number of packages: %s", len(pkgs))
var vulns []types.Vulnerability
var vulns []vulnerability.DetectedVulnerability
for _, pkg := range pkgs {
if pkg.Type != analyzer.TypeSource {
continue
@@ -55,7 +57,7 @@ func (s *Scanner) Detect(osVer string, pkgs []analyzer.Package) ([]types.Vulnera
}
if installedVersion.LessThan(fixedVersion) {
vuln := types.Vulnerability{
vuln := vulnerability.DetectedVulnerability{
VulnerabilityID: adv.VulnerabilityID,
PkgName: pkg.Name,
InstalledVersion: installed,
@@ -69,7 +71,7 @@ func (s *Scanner) Detect(osVer string, pkgs []analyzer.Package) ([]types.Vulnera
return nil, xerrors.Errorf("failed to get debian advisory: %w", err)
}
for _, adv := range advisories {
vuln := types.Vulnerability{
vuln := vulnerability.DetectedVulnerability{
VulnerabilityID: adv.VulnerabilityID,
PkgName: pkg.Name,
InstalledVersion: installed,

View File

@@ -3,16 +3,13 @@ package redhat
import (
"strings"
"golang.org/x/xerrors"
"github.com/knqyf263/trivy/pkg/scanner/utils"
"github.com/knqyf263/go-rpm-version"
"github.com/knqyf263/fanal/analyzer"
version "github.com/knqyf263/go-rpm-version"
"github.com/knqyf263/trivy/pkg/log"
"github.com/knqyf263/trivy/pkg/types"
"github.com/knqyf263/trivy/pkg/scanner/utils"
"github.com/knqyf263/trivy/pkg/vulnsrc/redhat"
"github.com/knqyf263/trivy/pkg/vulnsrc/vulnerability"
"golang.org/x/xerrors"
)
type Scanner struct{}
@@ -21,7 +18,7 @@ func NewScanner() *Scanner {
return &Scanner{}
}
func (s *Scanner) Detect(osVer string, pkgs []analyzer.Package) ([]types.Vulnerability, error) {
func (s *Scanner) Detect(osVer string, pkgs []analyzer.Package) ([]vulnerability.DetectedVulnerability, error) {
log.Logger.Info("Detecting RHEL/CentOS vulnerabilities...")
if strings.Count(osVer, ".") > 0 {
osVer = osVer[:strings.Index(osVer, ".")]
@@ -29,7 +26,7 @@ func (s *Scanner) Detect(osVer string, pkgs []analyzer.Package) ([]types.Vulnera
log.Logger.Debugf("redhat: os version: %s", osVer)
log.Logger.Debugf("redhat: the number of packages: %s", len(pkgs))
var vulns []types.Vulnerability
var vulns []vulnerability.DetectedVulnerability
for _, pkg := range pkgs {
advisories, err := redhat.Get(osVer, pkg.Name)
if err != nil {
@@ -42,7 +39,7 @@ func (s *Scanner) Detect(osVer string, pkgs []analyzer.Package) ([]types.Vulnera
fixedVersion := version.NewVersion(adv.FixedVersion)
if installedVersion.LessThan(fixedVersion) {
vuln := types.Vulnerability{
vuln := vulnerability.DetectedVulnerability{
VulnerabilityID: adv.VulnerabilityID,
PkgName: pkg.Name,
InstalledVersion: installed,

View File

@@ -2,6 +2,7 @@ package ospkg
import (
"github.com/knqyf263/fanal/analyzer"
_ "github.com/knqyf263/fanal/analyzer/command/apk"
fos "github.com/knqyf263/fanal/analyzer/os"
_ "github.com/knqyf263/fanal/analyzer/os/alpine"
_ "github.com/knqyf263/fanal/analyzer/os/debianbase"
@@ -14,15 +15,15 @@ import (
"github.com/knqyf263/trivy/pkg/scanner/ospkg/debian"
"github.com/knqyf263/trivy/pkg/scanner/ospkg/redhat"
"github.com/knqyf263/trivy/pkg/scanner/ospkg/ubuntu"
"github.com/knqyf263/trivy/pkg/types"
"github.com/knqyf263/trivy/pkg/vulnsrc/vulnerability"
"golang.org/x/xerrors"
)
type Scanner interface {
Detect(string, []analyzer.Package) ([]types.Vulnerability, error)
Detect(string, []analyzer.Package) ([]vulnerability.DetectedVulnerability, error)
}
func Scan(files extractor.FileMap) (string, string, []types.Vulnerability, error) {
func Scan(files extractor.FileMap) (string, string, []vulnerability.DetectedVulnerability, error) {
os, err := analyzer.GetOS(files)
if err != nil {
return "", "", nil, xerrors.Errorf("failed to analyze OS: %w", err)
@@ -48,6 +49,15 @@ func Scan(files extractor.FileMap) (string, string, []types.Vulnerability, error
}
log.Logger.Debugf("the number of packages: %d", len(pkgs))
pkgsFromCommands, err := analyzer.GetPackagesFromCommands(os, files)
if err != nil {
return "", "", nil, xerrors.Errorf("failed to analyze OS packages: %w", err)
}
log.Logger.Debugf("the number of packages from commands: %d", len(pkgsFromCommands))
pkgs = mergePkgs(pkgs, pkgsFromCommands)
log.Logger.Debugf("the number of packages: %d", len(pkgs))
vulns, err := s.Detect(os.Name, pkgs)
if err != nil {
return "", "", nil, xerrors.Errorf("failed to detect vulnerabilities: %w", err)
@@ -55,3 +65,17 @@ func Scan(files extractor.FileMap) (string, string, []types.Vulnerability, error
return os.Family, os.Name, vulns, nil
}
func mergePkgs(pkgs, pkgsFromCommands []analyzer.Package) []analyzer.Package {
uniqPkgs := map[string]struct{}{}
for _, pkg := range pkgs {
uniqPkgs[pkg.Name] = struct{}{}
}
for _, pkg := range pkgsFromCommands {
if _, ok := uniqPkgs[pkg.Name]; ok {
continue
}
pkgs = append(pkgs, pkg)
}
return pkgs
}

View File

@@ -1,13 +1,13 @@
package ubuntu
import (
"github.com/knqyf263/go-deb-version"
version "github.com/knqyf263/go-deb-version"
"github.com/knqyf263/trivy/pkg/scanner/utils"
"github.com/knqyf263/trivy/pkg/vulnsrc/vulnerability"
"golang.org/x/xerrors"
"github.com/knqyf263/fanal/analyzer"
"github.com/knqyf263/trivy/pkg/log"
"github.com/knqyf263/trivy/pkg/types"
"github.com/knqyf263/trivy/pkg/vulnsrc/ubuntu"
)
@@ -17,12 +17,12 @@ func NewScanner() *Scanner {
return &Scanner{}
}
func (s *Scanner) Detect(osVer string, pkgs []analyzer.Package) ([]types.Vulnerability, error) {
func (s *Scanner) Detect(osVer string, pkgs []analyzer.Package) ([]vulnerability.DetectedVulnerability, error) {
log.Logger.Info("Detecting Ubuntu vulnerabilities...")
log.Logger.Debugf("ubuntu: os version: %s", osVer)
log.Logger.Debugf("ubuntu: the number of packages: %s", len(pkgs))
var vulns []types.Vulnerability
var vulns []vulnerability.DetectedVulnerability
for _, pkg := range pkgs {
advisories, err := ubuntu.Get(osVer, pkg.Name)
if err != nil {
@@ -37,7 +37,7 @@ func (s *Scanner) Detect(osVer string, pkgs []analyzer.Package) ([]types.Vulnera
}
for _, adv := range advisories {
vuln := types.Vulnerability{
vuln := vulnerability.DetectedVulnerability{
VulnerabilityID: adv.VulnerabilityID,
PkgName: pkg.Name,
InstalledVersion: installed,

View File

@@ -6,36 +6,37 @@ import (
"fmt"
"os"
"github.com/knqyf263/trivy/pkg/log"
"golang.org/x/crypto/ssh/terminal"
"github.com/knqyf263/trivy/pkg/report"
"github.com/genuinetools/reg/registry"
"github.com/knqyf263/trivy/pkg/log"
"github.com/knqyf263/trivy/pkg/scanner/library"
"github.com/knqyf263/trivy/pkg/vulnsrc/vulnerability"
"github.com/knqyf263/trivy/pkg/types"
"github.com/knqyf263/trivy/pkg/scanner/ospkg"
"golang.org/x/crypto/ssh/terminal"
"golang.org/x/xerrors"
"github.com/knqyf263/fanal/analyzer"
"github.com/knqyf263/fanal/extractor"
)
var (
sources = []string{vulnerability.Nvd, vulnerability.RedHat, vulnerability.Debian,
vulnerability.DebianOVAL, vulnerability.Alpine, vulnerability.RubySec, vulnerability.PhpSecurityAdvisories,
vulnerability.NodejsSecurityWg, vulnerability.PythonSafetyDB}
)
func ScanImage(imageName, filePath string, severities []vulnerability.Severity) (report.Results, error) {
var results report.Results
func ScanImage(imageName, filePath string) (map[string][]vulnerability.DetectedVulnerability, error) {
var err error
results := map[string][]vulnerability.DetectedVulnerability{}
ctx := context.Background()
image, err := registry.ParseImage(imageName)
if err != nil {
return nil, xerrors.Errorf("invalid image: %w", err)
}
if image.Tag == "latest" {
log.Logger.Warn("You should avoid using the :latest tag as it is cached. You need to specify '--clean' option when :latest image is changed")
}
var target string
var files extractor.FileMap
if imageName != "" {
@@ -61,57 +62,32 @@ func ScanImage(imageName, filePath string, severities []vulnerability.Severity)
osFamily, osVersion, osVulns, err := ospkg.Scan(files)
if err != nil {
return nil, xerrors.New("failed to scan image")
return nil, xerrors.Errorf("failed to scan image: %w", err)
}
results = append(results, report.Result{
FileName: fmt.Sprintf("%s (%s %s)", target, osFamily, osVersion),
Vulnerabilities: processVulnerabilties(osVulns, severities),
})
imageDetail := fmt.Sprintf("%s (%s %s)", target, osFamily, osVersion)
results[imageDetail] = osVulns
libVulns, err := library.Scan(files)
if err != nil {
return nil, xerrors.New("failed to scan libraries")
return nil, xerrors.Errorf("failed to scan libraries: %w", err)
}
for path, vulns := range libVulns {
results = append(results, report.Result{
FileName: path,
Vulnerabilities: processVulnerabilties(vulns, severities),
})
results[path] = vulns
}
return results, nil
}
func ScanFile(f *os.File, severities []vulnerability.Severity) (report.Result, error) {
func ScanFile(f *os.File) (map[string][]vulnerability.DetectedVulnerability, error) {
vulns, err := library.ScanFile(f)
if err != nil {
return report.Result{}, xerrors.New("failed to scan libraries in file")
return nil, xerrors.Errorf("failed to scan libraries in file: %w", err)
}
result := report.Result{
FileName: f.Name(),
Vulnerabilities: processVulnerabilties(vulns, severities),
results := map[string][]vulnerability.DetectedVulnerability{
f.Name(): vulns,
}
return result, nil
}
func processVulnerabilties(vulns []types.Vulnerability, severities []vulnerability.Severity) []types.Vulnerability {
var vulnerabilities []types.Vulnerability
for _, vuln := range vulns {
sev, title := getDetail(vuln.VulnerabilityID)
// Filter vulnerabilities by severity
for _, s := range severities {
if s == sev {
vuln.Severity = fmt.Sprint(sev)
vuln.Title = title
vulnerabilities = append(vulnerabilities, vuln)
break
}
}
}
return vulnerabilities
return results, nil
}
func openStream(path string) (*os.File, error) {
@@ -125,61 +101,3 @@ func openStream(path string) (*os.File, error) {
}
return os.Open(path)
}
func getDetail(vulnID string) (vulnerability.Severity, string) {
details, err := vulnerability.Get(vulnID)
if err != nil {
log.Logger.Debug(err)
return vulnerability.SeverityUnknown, ""
} else if len(details) == 0 {
return vulnerability.SeverityUnknown, ""
}
severity := getSeverity(details)
title := getTitle(details)
return severity, title
}
func getSeverity(details map[string]vulnerability.Vulnerability) vulnerability.Severity {
for _, source := range sources {
d, ok := details[source]
if !ok {
continue
}
if d.Severity != 0 {
return d.Severity
} else if d.SeverityV3 != 0 {
return d.SeverityV3
} else if d.CvssScore > 0 {
return scoreToSeverity(d.CvssScore)
} else if d.CvssScoreV3 > 0 {
return scoreToSeverity(d.CvssScoreV3)
}
}
return vulnerability.SeverityUnknown
}
func getTitle(details map[string]vulnerability.Vulnerability) string {
for _, source := range sources {
d, ok := details[source]
if !ok {
continue
}
if d.Title != "" {
return d.Title
}
}
return ""
}
func scoreToSeverity(score float64) vulnerability.Severity {
if score >= 9.0 {
return vulnerability.SeverityCritical
} else if score >= 7.0 {
return vulnerability.SeverityHigh
} else if score >= 4.0 {
return vulnerability.SeverityMedium
} else if score > 0.0 {
return vulnerability.SeverityLow
}
return vulnerability.SeverityUnknown
}

View File

@@ -1,11 +1 @@
package types
type Vulnerability struct {
VulnerabilityID string
PkgName string
InstalledVersion string
FixedVersion string
Title string
Severity string
}

63
pkg/utils/progress.go Normal file
View File

@@ -0,0 +1,63 @@
package utils
import (
"time"
"github.com/briandowns/spinner"
pb "gopkg.in/cheggaaa/pb.v1"
)
var (
Quiet = false
)
type Spinner struct {
client *spinner.Spinner
}
func NewSpinner(suffix string) *Spinner {
if Quiet {
return &Spinner{}
}
s := spinner.New(spinner.CharSets[36], 100*time.Millisecond)
s.Suffix = suffix
return &Spinner{client: s}
}
func (s *Spinner) Start() {
if s.client == nil {
return
}
s.client.Start()
}
func (s *Spinner) Stop() {
if s.client == nil {
return
}
s.client.Stop()
}
type ProgressBar struct {
client *pb.ProgressBar
}
func PbStartNew(total int) *ProgressBar {
if Quiet {
return &ProgressBar{}
}
bar := pb.StartNew(total)
return &ProgressBar{client: bar}
}
func (p *ProgressBar) Increment() {
if p.client == nil {
return
}
p.client.Increment()
}
func (p *ProgressBar) Finish() {
if p.client == nil {
return
}
p.client.Finish()
}

View File

@@ -3,7 +3,6 @@ package alpine
import (
"encoding/json"
"fmt"
"gopkg.in/cheggaaa/pb.v1"
"io"
"path/filepath"
@@ -37,7 +36,7 @@ func Update(dir string, updatedFiles map[string]struct{}) error {
}
log.Logger.Debugf("Alpine updated files: %d", len(targets))
bar := pb.StartNew(len(targets))
bar := utils.PbStartNew(len(targets))
defer bar.Finish()
var cves []AlpineCVE

View File

@@ -3,7 +3,6 @@ package debianoval
import (
"encoding/json"
"fmt"
"gopkg.in/cheggaaa/pb.v1"
"io"
"os"
"path/filepath"
@@ -39,7 +38,7 @@ func Update(dir string, updatedFiles map[string]struct{}) error {
}
log.Logger.Debugf("Debian OVAL updated files: %d", len(targets))
bar := pb.StartNew(len(targets))
bar := utils.PbStartNew(len(targets))
defer bar.Finish()
var cves []DebianOVAL

View File

@@ -3,7 +3,6 @@ package debian
import (
"encoding/json"
"fmt"
"gopkg.in/cheggaaa/pb.v1"
"io"
"path/filepath"
"strings"
@@ -45,7 +44,7 @@ func Update(dir string, updatedFiles map[string]struct{}) error {
}
log.Logger.Debugf("Debian updated files: %d", len(targets))
bar := pb.StartNew(len(targets))
bar := utils.PbStartNew(len(targets))
defer bar.Finish()
var cves []DebianCVE

View File

@@ -2,7 +2,6 @@ package nvd
import (
"encoding/json"
"gopkg.in/cheggaaa/pb.v1"
"io"
"path/filepath"
@@ -19,9 +18,7 @@ import (
)
const (
nvdDir = "nvd"
rootBucket = "NVD"
nestedBucket = "dummy"
nvdDir = "nvd"
)
func Update(dir string, updatedFiles map[string]struct{}) error {
@@ -35,11 +32,11 @@ func Update(dir string, updatedFiles map[string]struct{}) error {
}
log.Logger.Debugf("NVD updated files: %d", len(targets))
bar := pb.StartNew(len(targets))
bar := utils.PbStartNew(len(targets))
defer bar.Finish()
var items []vulnerability.Item
var items []Item
err = utils.FileWalk(rootDir, targets, func(r io.Reader, _ string) error {
item := vulnerability.Item{}
item := Item{}
if err := json.NewDecoder(r).Decode(&item); err != nil {
return xerrors.Errorf("failed to decode NVD JSON: %w", err)
}
@@ -58,20 +55,35 @@ func Update(dir string, updatedFiles map[string]struct{}) error {
return nil
}
func save(items []vulnerability.Item) error {
func save(items []Item) error {
log.Logger.Debug("NVD batch update")
err := vulnerability.BatchUpdate(func(b *bolt.Bucket) error {
for _, item := range items {
cveID := item.Cve.Meta.ID
severity, _ := vulnerability.NewSeverity(item.Impact.BaseMetricV2.Severity)
severityV3, _ := vulnerability.NewSeverity(item.Impact.BaseMetricV3.CvssV3.BaseSeverity)
var references []string
for _, ref := range item.Cve.References.ReferenceDataList {
references = append(references, ref.URL)
}
var description string
for _, d := range item.Cve.Description.DescriptionDataList {
if d.Value != "" {
description = d.Value
break
}
}
vuln := vulnerability.Vulnerability{
Severity: severity,
SeverityV3: severityV3,
// TODO
References: []string{},
CvssScore: item.Impact.BaseMetricV2.CvssV2.BaseScore,
CvssScoreV3: item.Impact.BaseMetricV3.CvssV3.BaseScore,
Severity: severity,
SeverityV3: severityV3,
References: references,
Title: "",
Description: "",
Description: description,
}
if err := db.Put(b, cveID, vulnerability.Nvd, vuln); err != nil {

61
pkg/vulnsrc/nvd/types.go Normal file
View File

@@ -0,0 +1,61 @@
package nvd
type NVD struct {
CVEItems []Item `json:"CVE_Items"`
}
type Item struct {
Cve Cve
Impact Impact
}
type Cve struct {
Meta Meta `json:"CVE_data_meta"`
References References
Description Description
}
type Meta struct {
ID string
}
type Impact struct {
BaseMetricV2 BaseMetricV2
BaseMetricV3 BaseMetricV3
}
type BaseMetricV2 struct {
CvssV2 CvssV2
Severity string
}
type CvssV2 struct {
BaseScore float64
}
type BaseMetricV3 struct {
CvssV3 CvssV3
}
type CvssV3 struct {
BaseScore float64
BaseSeverity string
}
type References struct {
ReferenceDataList []ReferenceData `json:"reference_data"`
}
type ReferenceData struct {
Name string
Refsource string
URL string
}
type Description struct {
DescriptionDataList []DescriptionData `json:"description_data"`
}
type DescriptionData struct {
Lang string
Value string
}

View File

@@ -3,7 +3,6 @@ package redhat
import (
"encoding/json"
"fmt"
"gopkg.in/cheggaaa/pb.v1"
"io"
"io/ioutil"
"path/filepath"
@@ -41,7 +40,7 @@ func Update(dir string, updatedFiles map[string]struct{}) error {
}
log.Logger.Debugf("Red Hat updated files: %d", len(targets))
bar := pb.StartNew(len(targets))
bar := utils.PbStartNew(len(targets))
defer bar.Finish()
var cves []RedhatCVE

View File

@@ -3,7 +3,6 @@ package ubuntu
import (
"encoding/json"
"fmt"
"gopkg.in/cheggaaa/pb.v1"
"io"
"path/filepath"
@@ -56,7 +55,7 @@ func Update(dir string, updatedFiles map[string]struct{}) error {
}
log.Logger.Debugf("Ubuntu OVAL updated files: %d", len(targets))
bar := pb.StartNew(len(targets))
bar := utils.PbStartNew(len(targets))
defer bar.Finish()
var cves []UbuntuCVE

View File

@@ -12,6 +12,7 @@ const (
Amazon = "amazon"
Alpine = "alpine"
RubySec = "ruby-advisory-db"
RustSec = "rust-advisory-db"
PhpSecurityAdvisories = "php-security-advisories"
NodejsSecurityWg = "nodejs-security-wg"
PythonSafetyDB = "python-safety-db"

View File

@@ -0,0 +1,55 @@
package vulnerability
import (
"encoding/json"
bolt "github.com/etcd-io/bbolt"
"github.com/knqyf263/trivy/pkg/db"
"golang.org/x/xerrors"
)
const (
rootBucket = "vulnerability"
)
func Put(tx *bolt.Tx, cveID, source string, vuln Vulnerability) error {
root, err := tx.CreateBucketIfNotExists([]byte(rootBucket))
if err != nil {
return err
}
return db.Put(root, cveID, source, vuln)
}
func Update(cveID, source string, vuln Vulnerability) error {
return db.Update(rootBucket, cveID, source, vuln)
}
func BatchUpdate(fn func(b *bolt.Bucket) error) error {
return db.BatchUpdate(func(tx *bolt.Tx) error {
root, err := tx.CreateBucketIfNotExists([]byte(rootBucket))
if err != nil {
return err
}
return fn(root)
})
}
func Get(cveID string) (map[string]Vulnerability, error) {
values, err := db.ForEach(rootBucket, cveID)
if err != nil {
return nil, xerrors.Errorf("error in NVD get: %w", err)
}
if len(values) == 0 {
return nil, nil
}
vulns := map[string]Vulnerability{}
for source, value := range values {
var vuln Vulnerability
if err = json.Unmarshal(value, &vuln); err != nil {
return nil, xerrors.Errorf("failed to unmarshal Vulnerability JSON: %w", err)
}
vulns[source] = vuln
}
return vulns, nil
}

View File

@@ -43,6 +43,12 @@ func NewSeverity(severity string) (Severity, error) {
return SeverityUnknown, fmt.Errorf("unknown severity: %s", severity)
}
func CompareSeverityString(sev1, sev2 string) bool {
s1, _ := NewSeverity(sev1)
s2, _ := NewSeverity(sev2)
return s1 < s2
}
func ColorizeSeverity(severity string) string {
for i, name := range SeverityNames {
if severity == name {
@@ -59,37 +65,30 @@ func (s Severity) String() string {
type LastUpdated struct {
Date time.Time
}
type NVD struct {
CVEItems []Item `json:"CVE_Items"`
type Vulnerability struct {
ID string // e.g. CVE-2019-8331, OSVDB-104365
CvssScore float64
CvssScoreV3 float64
Severity Severity
SeverityV3 Severity
References []string
Title string
Description string
}
type Item struct {
Cve Cve
Impact Impact
type Advisory struct {
VulnerabilityID string
FixedVersion string
}
type Cve struct {
Meta Meta `json:"CVE_data_meta"`
}
type DetectedVulnerability struct {
VulnerabilityID string
PkgName string
InstalledVersion string
FixedVersion string
type Meta struct {
ID string
}
type Impact struct {
BaseMetricV2 BaseMetricV2
BaseMetricV3 BaseMetricV3
}
type BaseMetricV2 struct {
Severity string
}
type BaseMetricV3 struct {
CvssV3 CvssV3
}
type CvssV3 struct {
BaseSeverity string
Title string
Description string
Severity string
References []string
}

View File

@@ -1,71 +1,164 @@
package vulnerability
import (
"encoding/json"
"bufio"
"fmt"
"os"
"strings"
bolt "github.com/etcd-io/bbolt"
"github.com/knqyf263/trivy/pkg/db"
"golang.org/x/xerrors"
"github.com/knqyf263/trivy/pkg/utils"
"sort"
"github.com/knqyf263/trivy/pkg/log"
)
const (
rootBucket = "vulnerability"
trivyIgnore = ".trivyignore"
)
type Vulnerability struct {
ID string // e.g. CVE-2019-8331, OSVDB-104365
CvssScore float64
CvssScoreV3 float64
Severity Severity
SeverityV3 Severity
References []string
Title string
Description string
}
var (
sources = []string{Nvd, RedHat, Debian, DebianOVAL, Alpine,
RubySec, RustSec, PhpSecurityAdvisories, NodejsSecurityWg, PythonSafetyDB}
)
type Advisory struct {
VulnerabilityID string
FixedVersion string
}
func FillAndFilter(vulns []DetectedVulnerability, severities []Severity, ignoreUnfixed bool) []DetectedVulnerability {
ignoredIDs := getIgnoredIDs()
var vulnerabilities []DetectedVulnerability
for _, vuln := range vulns {
sev, title, description, references := getDetail(vuln.VulnerabilityID)
func Put(tx *bolt.Tx, cveID, source string, vuln Vulnerability) error {
root, err := tx.CreateBucketIfNotExists([]byte(rootBucket))
if err != nil {
return err
}
return db.Put(root, cveID, source, vuln)
}
// Filter vulnerabilities by severity
for _, s := range severities {
if s == sev {
vuln.Severity = fmt.Sprint(sev)
vuln.Title = title
vuln.Description = description
vuln.References = references
func Update(cveID, source string, vuln Vulnerability) error {
return db.Update(rootBucket, cveID, source, vuln)
}
func BatchUpdate(fn func(b *bolt.Bucket) error) error {
return db.BatchUpdate(func(tx *bolt.Tx) error {
root, err := tx.CreateBucketIfNotExists([]byte(rootBucket))
if err != nil {
return err
// Ignore unfixed vulnerabilities
if ignoreUnfixed && vuln.FixedVersion == "" {
continue
} else if utils.StringInSlice(vuln.VulnerabilityID, ignoredIDs) {
continue
}
vulnerabilities = append(vulnerabilities, vuln)
break
}
}
return fn(root)
}
sort.Slice(vulnerabilities, func(i, j int) bool {
if vulnerabilities[i].PkgName != vulnerabilities[j].PkgName {
return vulnerabilities[i].PkgName < vulnerabilities[j].PkgName
}
return CompareSeverityString(vulnerabilities[j].Severity, vulnerabilities[i].Severity)
})
return vulnerabilities
}
func Get(cveID string) (map[string]Vulnerability, error) {
values, err := db.ForEach(rootBucket, cveID)
func getIgnoredIDs() []string {
f, err := os.Open(trivyIgnore)
if err != nil {
return nil, xerrors.Errorf("error in NVD get: %w", err)
}
if len(values) == 0 {
return nil, nil
// trivy must work even if no .trivyignore exist
return nil
}
vulns := map[string]Vulnerability{}
for source, value := range values {
var vuln Vulnerability
if err = json.Unmarshal(value, &vuln); err != nil {
return nil, xerrors.Errorf("failed to unmarshal Vulnerability JSON: %w", err)
var ignoredIDs []string
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "#") || line == "" {
continue
}
vulns[source] = vuln
ignoredIDs = append(ignoredIDs, line)
}
return vulns, nil
return ignoredIDs
}
func getDetail(vulnID string) (Severity, string, string, []string) {
details, err := Get(vulnID)
if err != nil {
log.Logger.Debug(err)
return SeverityUnknown, "", "", nil
} else if len(details) == 0 {
return SeverityUnknown, "", "", nil
}
return getSeverity(details), getTitle(details), getDescription(details), getReferences(details)
}
func getSeverity(details map[string]Vulnerability) Severity {
for _, source := range sources {
d, ok := details[source]
if !ok {
continue
}
if d.CvssScore > 0 {
return scoreToSeverity(d.CvssScore)
} else if d.CvssScoreV3 > 0 {
return scoreToSeverity(d.CvssScoreV3)
} else if d.Severity != 0 {
return d.Severity
} else if d.SeverityV3 != 0 {
return d.SeverityV3
}
}
return SeverityUnknown
}
func getTitle(details map[string]Vulnerability) string {
for _, source := range sources {
d, ok := details[source]
if !ok {
continue
}
if d.Title != "" {
return d.Title
}
}
return ""
}
func getDescription(details map[string]Vulnerability) string {
for _, source := range sources {
d, ok := details[source]
if !ok {
continue
}
if d.Description != "" {
return d.Description
}
}
return ""
}
func getReferences(details map[string]Vulnerability) []string {
references := map[string]struct{}{}
for _, source := range sources {
d, ok := details[source]
if !ok {
continue
}
for _, ref := range d.References {
references[ref] = struct{}{}
}
}
var refs []string
for ref := range references {
refs = append(refs, ref)
}
return refs
}
func scoreToSeverity(score float64) Severity {
if score >= 9.0 {
return SeverityCritical
} else if score >= 7.0 {
return SeverityHigh
} else if score >= 4.0 {
return SeverityMedium
} else if score > 0.0 {
return SeverityLow
}
return SeverityUnknown
}

View File

@@ -1,6 +1,8 @@
package vulnsrc
import (
"path/filepath"
"github.com/knqyf263/trivy/pkg/git"
"github.com/knqyf263/trivy/pkg/log"
"github.com/knqyf263/trivy/pkg/utils"
@@ -11,7 +13,6 @@ import (
"github.com/knqyf263/trivy/pkg/vulnsrc/redhat"
"github.com/knqyf263/trivy/pkg/vulnsrc/ubuntu"
"golang.org/x/xerrors"
"path/filepath"
)
const (