mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-16 09:30:59 -08:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
507fac9284 | ||
|
|
22abb9dab1 | ||
|
|
6463176bc0 | ||
|
|
b208bc4c12 | ||
|
|
53ad8c2f35 | ||
|
|
34ba0ca8d7 |
38
README.md
38
README.md
@@ -5,12 +5,31 @@
|
||||
[](https://goreportcard.com/report/github.com/knqyf263/trivy)
|
||||
[](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)
|
||||
- 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
|
||||
|
||||
@@ -141,11 +160,10 @@ 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.3
|
||||
OPTIONS:
|
||||
@@ -157,10 +175,11 @@ OPTIONS:
|
||||
--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
|
||||
|
||||
```
|
||||
|
||||
# Q&A
|
||||
@@ -199,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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -81,6 +81,10 @@ OPTIONS:
|
||||
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",
|
||||
|
||||
34
pkg/db/db.go
34
pkg/db/db.go
@@ -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)
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/knqyf263/trivy/pkg/db"
|
||||
|
||||
"github.com/knqyf263/trivy/pkg/log"
|
||||
"github.com/knqyf263/trivy/pkg/utils"
|
||||
"golang.org/x/xerrors"
|
||||
@@ -52,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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -42,8 +42,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
|
||||
|
||||
47
pkg/run.go
47
pkg/run.go
@@ -23,21 +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)
|
||||
}
|
||||
|
||||
utils.Quiet = c.Bool("quiet")
|
||||
|
||||
clean := c.Bool("clean")
|
||||
if clean {
|
||||
log.Logger.Info("Cleaning caches...")
|
||||
@@ -47,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 != "" {
|
||||
@@ -66,10 +70,22 @@ 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)
|
||||
@@ -101,9 +117,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ func Scan(files extractor.FileMap) (map[string][]types.Vulnerability, error) {
|
||||
}
|
||||
|
||||
func ScanFile(f *os.File) ([]types.Vulnerability, error) {
|
||||
scanner := NewScanner(f.Name())
|
||||
scanner := NewScanner(filepath.Base(f.Name()))
|
||||
if scanner == nil {
|
||||
return nil, xerrors.New("unknown file type")
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"github.com/genuinetools/reg/registry"
|
||||
|
||||
"github.com/knqyf263/trivy/pkg/log"
|
||||
|
||||
"github.com/knqyf263/trivy/pkg/report"
|
||||
@@ -37,6 +39,14 @@ func ScanImage(imageName, filePath string, severities []vulnerability.Severity,
|
||||
var err error
|
||||
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 != "" {
|
||||
@@ -62,7 +72,7 @@ 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)
|
||||
|
||||
}
|
||||
|
||||
@@ -73,7 +83,7 @@ func ScanImage(imageName, filePath string, severities []vulnerability.Severity,
|
||||
|
||||
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{
|
||||
@@ -85,14 +95,14 @@ func ScanImage(imageName, filePath string, severities []vulnerability.Severity,
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func ScanFile(f *os.File, severities []vulnerability.Severity) (report.Result, error) {
|
||||
func ScanFile(f *os.File, severities []vulnerability.Severity, ignoreUnfixed bool) (report.Result, error) {
|
||||
vulns, err := library.ScanFile(f)
|
||||
if err != nil {
|
||||
return report.Result{}, xerrors.New("failed to scan libraries in file")
|
||||
return report.Result{}, xerrors.Errorf("failed to scan libraries in file: %w", err)
|
||||
}
|
||||
result := report.Result{
|
||||
FileName: f.Name(),
|
||||
Vulnerabilities: processVulnerabilties(vulns, severities, false),
|
||||
Vulnerabilities: processVulnerabilties(vulns, severities, ignoreUnfixed),
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
@@ -100,13 +110,15 @@ func ScanFile(f *os.File, severities []vulnerability.Severity) (report.Result, e
|
||||
func processVulnerabilties(vulns []types.Vulnerability, severities []vulnerability.Severity, ignoreUnfixed bool) []types.Vulnerability {
|
||||
var vulnerabilities []types.Vulnerability
|
||||
for _, vuln := range vulns {
|
||||
sev, title := getDetail(vuln.VulnerabilityID)
|
||||
sev, title, description, references := getDetail(vuln.VulnerabilityID)
|
||||
|
||||
// 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
|
||||
|
||||
// Ignore unfixed vulnerabilities
|
||||
if ignoreUnfixed && vuln.FixedVersion == "" {
|
||||
@@ -138,17 +150,15 @@ func openStream(path string) (*os.File, error) {
|
||||
return os.Open(path)
|
||||
}
|
||||
|
||||
func getDetail(vulnID string) (vulnerability.Severity, string) {
|
||||
func getDetail(vulnID string) (vulnerability.Severity, string, string, []string) {
|
||||
details, err := vulnerability.Get(vulnID)
|
||||
if err != nil {
|
||||
log.Logger.Debug(err)
|
||||
return vulnerability.SeverityUnknown, ""
|
||||
return vulnerability.SeverityUnknown, "", "", nil
|
||||
} else if len(details) == 0 {
|
||||
return vulnerability.SeverityUnknown, ""
|
||||
return vulnerability.SeverityUnknown, "", "", nil
|
||||
}
|
||||
severity := getSeverity(details)
|
||||
title := getTitle(details)
|
||||
return severity, title
|
||||
return getSeverity(details), getTitle(details), getDescription(details), getReferences(details)
|
||||
}
|
||||
|
||||
func getSeverity(details map[string]vulnerability.Vulnerability) vulnerability.Severity {
|
||||
@@ -183,6 +193,37 @@ func getTitle(details map[string]vulnerability.Vulnerability) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func getDescription(details map[string]vulnerability.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.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) vulnerability.Severity {
|
||||
if score >= 9.0 {
|
||||
return vulnerability.SeverityCritical
|
||||
|
||||
@@ -6,6 +6,8 @@ type Vulnerability struct {
|
||||
InstalledVersion string
|
||||
FixedVersion string
|
||||
|
||||
Title string
|
||||
Severity string
|
||||
Title string
|
||||
Description string
|
||||
Severity string
|
||||
References []string
|
||||
}
|
||||
|
||||
@@ -18,9 +18,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
nvdDir = "nvd"
|
||||
rootBucket = "NVD"
|
||||
nestedBucket = "dummy"
|
||||
nvdDir = "nvd"
|
||||
)
|
||||
|
||||
func Update(dir string, updatedFiles map[string]struct{}) error {
|
||||
@@ -36,9 +34,9 @@ func Update(dir string, updatedFiles map[string]struct{}) error {
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -57,20 +55,33 @@ 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{},
|
||||
Severity: severity,
|
||||
SeverityV3: severityV3,
|
||||
References: references,
|
||||
Title: "",
|
||||
Description: "",
|
||||
Description: description,
|
||||
}
|
||||
|
||||
if err := db.Put(b, cveID, vulnerability.Nvd, vuln); err != nil {
|
||||
|
||||
55
pkg/vulnsrc/nvd/types.go
Normal file
55
pkg/vulnsrc/nvd/types.go
Normal file
@@ -0,0 +1,55 @@
|
||||
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 {
|
||||
Severity string
|
||||
}
|
||||
|
||||
type BaseMetricV3 struct {
|
||||
CvssV3 CvssV3
|
||||
}
|
||||
|
||||
type CvssV3 struct {
|
||||
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
|
||||
}
|
||||
@@ -65,37 +65,3 @@ func (s Severity) String() string {
|
||||
type LastUpdated struct {
|
||||
Date time.Time
|
||||
}
|
||||
|
||||
type NVD struct {
|
||||
CVEItems []Item `json:"CVE_Items"`
|
||||
}
|
||||
|
||||
type Item struct {
|
||||
Cve Cve
|
||||
Impact Impact
|
||||
}
|
||||
|
||||
type Cve struct {
|
||||
Meta Meta `json:"CVE_data_meta"`
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
|
||||
Reference in New Issue
Block a user