Compare commits
30 Commits
v0.15.14
...
detect-pkg
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
169db6e320 | ||
|
|
79c7fc972c | ||
|
|
fb9cebd3e2 | ||
|
|
43d987e200 | ||
|
|
ffdb78962f | ||
|
|
321dae37ce | ||
|
|
a31797af0b | ||
|
|
32999cf432 | ||
|
|
88218f5d92 | ||
|
|
15761933ac | ||
|
|
0b62842f0e | ||
|
|
6bceddeeda | ||
|
|
2dcbff8cd5 | ||
|
|
8659668177 | ||
|
|
e07b6a9160 | ||
|
|
aac5ef1438 | ||
|
|
d780a73297 | ||
|
|
9ef8cee36e | ||
|
|
77808a2c05 | ||
|
|
177e553d12 | ||
|
|
40f8272a28 | ||
|
|
a7eb1141ae | ||
|
|
c73ed7f32f | ||
|
|
f047a6fe0c | ||
|
|
7f15a86d6a | ||
|
|
da1e515253 | ||
|
|
591786fde6 | ||
|
|
47e6ea249d | ||
|
|
4a72295de7 | ||
|
|
9ed5f2cac5 |
2
.github/workflows/golangci.yml
vendored
2
.github/workflows/golangci.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
with:
|
||||
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
|
||||
version: v1.32
|
||||
version: v1.42
|
||||
args: --timeout=10m
|
||||
|
||||
# Optional: working directory, useful for monorepos
|
||||
|
||||
@@ -1,14 +1,44 @@
|
||||
name: golang-ci
|
||||
|
||||
linters-settings:
|
||||
errcheck:
|
||||
revive:
|
||||
# see https://github.com/mgechev/revive#available-rules for details.
|
||||
ignore-generated-header: true
|
||||
severity: warning
|
||||
confidence: 0.8
|
||||
rules:
|
||||
- name: blank-imports
|
||||
- name: context-as-argument
|
||||
- name: context-keys-type
|
||||
- name: dot-imports
|
||||
- name: error-return
|
||||
- name: error-strings
|
||||
- name: error-naming
|
||||
- name: exported
|
||||
- name: if-return
|
||||
- name: increment-decrement
|
||||
- name: var-naming
|
||||
- name: var-declaration
|
||||
- name: package-comments
|
||||
- name: range
|
||||
- name: receiver-naming
|
||||
- name: time-naming
|
||||
- name: unexported-return
|
||||
- name: indent-error-flow
|
||||
- name: errorf
|
||||
- name: empty-block
|
||||
- name: superfluous-else
|
||||
- name: unused-parameter
|
||||
- name: unreachable-code
|
||||
- name: redefines-builtin-id
|
||||
# errcheck:
|
||||
#exclude: /path/to/file.txt
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- goimports
|
||||
- golint
|
||||
- revive
|
||||
- govet
|
||||
- misspell
|
||||
- errcheck
|
||||
|
||||
30
.revive.toml
Normal file
30
.revive.toml
Normal file
@@ -0,0 +1,30 @@
|
||||
ignoreGeneratedHeader = false
|
||||
severity = "warning"
|
||||
confidence = 0.8
|
||||
errorCode = 0
|
||||
warningCode = 0
|
||||
|
||||
[rule.blank-imports]
|
||||
[rule.context-as-argument]
|
||||
[rule.context-keys-type]
|
||||
[rule.dot-imports]
|
||||
[rule.error-return]
|
||||
[rule.error-strings]
|
||||
[rule.error-naming]
|
||||
[rule.exported]
|
||||
[rule.if-return]
|
||||
[rule.increment-decrement]
|
||||
[rule.var-naming]
|
||||
[rule.var-declaration]
|
||||
[rule.package-comments]
|
||||
[rule.range]
|
||||
[rule.receiver-naming]
|
||||
[rule.time-naming]
|
||||
[rule.unexported-return]
|
||||
[rule.indent-error-flow]
|
||||
[rule.errorf]
|
||||
[rule.empty-block]
|
||||
[rule.superfluous-else]
|
||||
[rule.unused-parameter]
|
||||
[rule.unreachable-code]
|
||||
[rule.redefines-builtin-id]
|
||||
11
GNUmakefile
11
GNUmakefile
@@ -17,8 +17,7 @@ PKGS = $(shell go list ./...)
|
||||
VERSION := $(shell git describe --tags --abbrev=0)
|
||||
REVISION := $(shell git rev-parse --short HEAD)
|
||||
BUILDTIME := $(shell date "+%Y%m%d_%H%M%S")
|
||||
LDFLAGS := -X 'github.com/future-architect/vuls/config.Version=$(VERSION)' \
|
||||
-X 'github.com/future-architect/vuls/config.Revision=build-$(BUILDTIME)_$(REVISION)'
|
||||
LDFLAGS := -X 'github.com/future-architect/vuls/config.Version=$(VERSION)' -X 'github.com/future-architect/vuls/config.Revision=build-$(BUILDTIME)_$(REVISION)'
|
||||
GO := GO111MODULE=on go
|
||||
CGO_UNABLED := CGO_ENABLED=0 go
|
||||
GO_OFF := GO111MODULE=off go
|
||||
@@ -42,8 +41,8 @@ install-scanner: ./cmd/scanner/main.go
|
||||
$(CGO_UNABLED) install -tags=scanner -ldflags "$(LDFLAGS)" ./cmd/scanner
|
||||
|
||||
lint:
|
||||
$(GO_OFF) get -u golang.org/x/lint/golint
|
||||
golint $(PKGS)
|
||||
$(GO_OFF) get -u github.com/mgechev/revive
|
||||
revive -config ./.revive.toml -formatter plain $(PKGS)
|
||||
|
||||
vet:
|
||||
echo $(PKGS) | xargs env $(GO) vet || exit;
|
||||
@@ -78,11 +77,11 @@ clean:
|
||||
|
||||
# trivy-to-vuls
|
||||
build-trivy-to-vuls: pretest fmt
|
||||
$(GO) build -o trivy-to-vuls contrib/trivy/cmd/*.go
|
||||
$(GO) build -a -ldflags "$(LDFLAGS)" -o trivy-to-vuls contrib/trivy/cmd/*.go
|
||||
|
||||
# future-vuls
|
||||
build-future-vuls: pretest fmt
|
||||
$(GO) build -o future-vuls contrib/future-vuls/cmd/*.go
|
||||
$(GO) build -a -ldflags "$(LDFLAGS)" -o future-vuls contrib/future-vuls/cmd/*.go
|
||||
|
||||
|
||||
# integration-test
|
||||
|
||||
@@ -186,11 +186,14 @@ see [vulsdoc](https://vuls.io/docs/en/how-to-contribute.html)
|
||||
|
||||
----
|
||||
|
||||
## Stargazers over time
|
||||
## Sponsors
|
||||
|
||||
[](https://starcharts.herokuapp.com/future-architect/vuls)
|
||||
| | |
|
||||
| ------------- | ------------- |
|
||||
| <a href="https://www.tines.com/?utm_source=oss&utm_medium=sponsorship&utm_campaign=vuls"><img src="img/sponsor/tines.png" align="left" width="600px" ></a> | Tines is no-code automation for security teams. Build powerful, reliable workflows without a development team. |
|
||||
| <a href="https://www.sakura.ad.jp/"><img src="https://vuls.io/img/icons/sakura.svg" align="left" width="600px" ></a> | SAKURA internet Inc. is an Internet company founded in 1996. We provide cloud computing services such as "Sakura's Shared Server", "Sakura's VPS", and "Sakura's Cloud" to meet the needs of a wide range of customers, from individuals and corporations to the education and public sectors, using its own data centers in Japan. Based on the philosophy of "changing what you want to do into what you can do," we offer DX solutions for all fields. |
|
||||
|
||||
-----;
|
||||
----
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -7,6 +7,6 @@ type JSONLoader struct {
|
||||
}
|
||||
|
||||
// Load load the configuration JSON file specified by path arg.
|
||||
func (c JSONLoader) Load(path, sudoPass, keyPass string) (err error) {
|
||||
func (c JSONLoader) Load(_, _, _ string) (err error) {
|
||||
return xerrors.New("Not implement yet")
|
||||
}
|
||||
|
||||
@@ -110,6 +110,7 @@ func GetEOL(family, release string) (eol EOL, found bool) {
|
||||
"8": {Ended: true},
|
||||
"9": {StandardSupportUntil: time.Date(2022, 6, 30, 23, 59, 59, 0, time.UTC)},
|
||||
"10": {StandardSupportUntil: time.Date(2024, 6, 30, 23, 59, 59, 0, time.UTC)},
|
||||
"11": {StandardSupportUntil: time.Date(2026, 6, 30, 23, 59, 59, 0, time.UTC)},
|
||||
}[major(release)]
|
||||
case constant.Raspbian:
|
||||
// Not found
|
||||
|
||||
@@ -290,6 +290,14 @@ func TestEOL_IsStandardSupportEnded(t *testing.T) {
|
||||
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
|
||||
stdEnded: false,
|
||||
extEnded: false,
|
||||
found: true,
|
||||
},
|
||||
{
|
||||
name: "Debian 12 is not supported yet",
|
||||
fields: fields{family: Debian, release: "12"},
|
||||
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
|
||||
stdEnded: false,
|
||||
extEnded: false,
|
||||
found: false,
|
||||
},
|
||||
//alpine
|
||||
|
||||
@@ -84,7 +84,7 @@ func (s ScanMode) String() string {
|
||||
return ss + " mode"
|
||||
}
|
||||
|
||||
func setScanMode(server *ServerInfo, d ServerInfo) error {
|
||||
func setScanMode(server *ServerInfo) error {
|
||||
if len(server.ScanMode) == 0 {
|
||||
server.ScanMode = Conf.Default.ScanMode
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ type TOMLLoader struct {
|
||||
}
|
||||
|
||||
// Load load the configuration TOML file specified by path arg.
|
||||
func (c TOMLLoader) Load(pathToToml, keyPass string) error {
|
||||
func (c TOMLLoader) Load(pathToToml, _ string) error {
|
||||
// util.Log.Infof("Loading config: %s", pathToToml)
|
||||
if _, err := toml.DecodeFile(pathToToml, &Conf); err != nil {
|
||||
return err
|
||||
@@ -34,11 +34,11 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
|
||||
index := 0
|
||||
for name, server := range Conf.Servers {
|
||||
server.ServerName = name
|
||||
if err := setDefaultIfEmpty(&server, Conf.Default); err != nil {
|
||||
if err := setDefaultIfEmpty(&server); err != nil {
|
||||
return xerrors.Errorf("Failed to set default value to config. server: %s, err: %w", name, err)
|
||||
}
|
||||
|
||||
if err := setScanMode(&server, Conf.Default); err != nil {
|
||||
if err := setScanMode(&server); err != nil {
|
||||
return xerrors.Errorf("Failed to set ScanMode: %w", err)
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func setDefaultIfEmpty(server *ServerInfo, d ServerInfo) error {
|
||||
func setDefaultIfEmpty(server *ServerInfo) error {
|
||||
if server.Type != constant.ServerTypePseudo {
|
||||
if len(server.Host) == 0 {
|
||||
return xerrors.Errorf("server.host is empty")
|
||||
|
||||
@@ -81,6 +81,14 @@ func main() {
|
||||
return
|
||||
},
|
||||
}
|
||||
var cmdVersion = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Show version",
|
||||
Long: "Show version",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("future-vuls-%s-%s\n", config.Version, config.Revision)
|
||||
},
|
||||
}
|
||||
cmdFvulsUploader.PersistentFlags().StringVar(&serverUUID, "uuid", "", "server uuid. ENV: VULS_SERVER_UUID")
|
||||
cmdFvulsUploader.PersistentFlags().StringVar(&configFile, "config", "", "config file (default is $HOME/.cobra.yaml)")
|
||||
cmdFvulsUploader.PersistentFlags().BoolVarP(&stdIn, "stdin", "s", false, "input from stdin. ENV: VULS_STDIN")
|
||||
@@ -92,6 +100,7 @@ func main() {
|
||||
|
||||
var rootCmd = &cobra.Command{Use: "future-vuls"}
|
||||
rootCmd.AddCommand(cmdFvulsUploader)
|
||||
rootCmd.AddCommand(cmdVersion)
|
||||
if err = rootCmd.Execute(); err != nil {
|
||||
fmt.Println("Failed to execute command", err)
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/contrib/trivy/parser"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -34,45 +34,55 @@ func main() {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
buf := new(bytes.Buffer)
|
||||
if _, err = buf.ReadFrom(reader); err != nil {
|
||||
fmt.Printf("Failed to read file. err: %+v\n", err)
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
trivyJSON = buf.Bytes()
|
||||
} else {
|
||||
if trivyJSON, err = ioutil.ReadFile(jsonFilePath); err != nil {
|
||||
fmt.Println("Failed to read file", err)
|
||||
fmt.Printf("Failed to read file. err: %+v\n", err)
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
scanResult := &models.ScanResult{
|
||||
JSONVersion: models.JSONVersion,
|
||||
ScannedCves: models.VulnInfos{},
|
||||
}
|
||||
if scanResult, err = parser.Parse(trivyJSON, scanResult); err != nil {
|
||||
fmt.Println("Failed to execute command", err)
|
||||
parser, err := parser.NewParser(trivyJSON)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to new parser. err: %+v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
scanResult, err := parser.Parse(trivyJSON)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to parse. err: %+v\n", err)
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
var resultJSON []byte
|
||||
if resultJSON, err = json.MarshalIndent(scanResult, "", " "); err != nil {
|
||||
fmt.Println("Failed to create json", err)
|
||||
fmt.Printf("Failed to create json. err: %+v\n", err)
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
fmt.Println(string(resultJSON))
|
||||
return
|
||||
},
|
||||
}
|
||||
|
||||
var cmdVersion = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Show version",
|
||||
Long: "Show version",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("trivy-to-vuls-%s-%s\n", config.Version, config.Revision)
|
||||
},
|
||||
}
|
||||
|
||||
cmdTrivyToVuls.Flags().BoolVarP(&stdIn, "stdin", "s", false, "input from stdin")
|
||||
cmdTrivyToVuls.Flags().StringVarP(&jsonDir, "trivy-json-dir", "d", "./", "trivy json dir")
|
||||
cmdTrivyToVuls.Flags().StringVarP(&jsonFileName, "trivy-json-file-name", "f", "results.json", "trivy json file name")
|
||||
|
||||
var rootCmd = &cobra.Command{Use: "trivy-to-vuls"}
|
||||
rootCmd.AddCommand(cmdTrivyToVuls)
|
||||
rootCmd.AddCommand(cmdVersion)
|
||||
if err = rootCmd.Execute(); err != nil {
|
||||
fmt.Println("Failed to execute command", err)
|
||||
fmt.Printf("Failed to execute command. err: %+v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
@@ -2,179 +2,32 @@ package parser
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/aquasecurity/fanal/analyzer/os"
|
||||
"github.com/aquasecurity/trivy/pkg/report"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
v2 "github.com/future-architect/vuls/contrib/trivy/parser/v2"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// Parse :
|
||||
func Parse(vulnJSON []byte, scanResult *models.ScanResult) (result *models.ScanResult, err error) {
|
||||
var trivyResults report.Results
|
||||
if err = json.Unmarshal(vulnJSON, &trivyResults); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pkgs := models.Packages{}
|
||||
vulnInfos := models.VulnInfos{}
|
||||
uniqueLibraryScannerPaths := map[string]models.LibraryScanner{}
|
||||
for _, trivyResult := range trivyResults {
|
||||
if IsTrivySupportedOS(trivyResult.Type) {
|
||||
overrideServerData(scanResult, &trivyResult)
|
||||
}
|
||||
for _, vuln := range trivyResult.Vulnerabilities {
|
||||
if _, ok := vulnInfos[vuln.VulnerabilityID]; !ok {
|
||||
vulnInfos[vuln.VulnerabilityID] = models.VulnInfo{
|
||||
CveID: vuln.VulnerabilityID,
|
||||
Confidences: models.Confidences{
|
||||
{
|
||||
Score: 100,
|
||||
DetectionMethod: models.TrivyMatchStr,
|
||||
},
|
||||
},
|
||||
AffectedPackages: models.PackageFixStatuses{},
|
||||
CveContents: models.CveContents{},
|
||||
LibraryFixedIns: models.LibraryFixedIns{},
|
||||
// VulnType : "",
|
||||
}
|
||||
}
|
||||
vulnInfo := vulnInfos[vuln.VulnerabilityID]
|
||||
var notFixedYet bool
|
||||
fixState := ""
|
||||
if len(vuln.FixedVersion) == 0 {
|
||||
notFixedYet = true
|
||||
fixState = "Affected"
|
||||
}
|
||||
var references models.References
|
||||
for _, reference := range vuln.References {
|
||||
references = append(references, models.Reference{
|
||||
Source: "trivy",
|
||||
Link: reference,
|
||||
})
|
||||
}
|
||||
|
||||
sort.Slice(references, func(i, j int) bool {
|
||||
return references[i].Link < references[j].Link
|
||||
})
|
||||
|
||||
var published time.Time
|
||||
if vuln.PublishedDate != nil {
|
||||
published = *vuln.PublishedDate
|
||||
}
|
||||
|
||||
var lastModified time.Time
|
||||
if vuln.LastModifiedDate != nil {
|
||||
lastModified = *vuln.LastModifiedDate
|
||||
}
|
||||
|
||||
vulnInfo.CveContents = models.CveContents{
|
||||
models.Trivy: []models.CveContent{{
|
||||
Cvss3Severity: vuln.Severity,
|
||||
References: references,
|
||||
Title: vuln.Title,
|
||||
Summary: vuln.Description,
|
||||
Published: published,
|
||||
LastModified: lastModified,
|
||||
}},
|
||||
}
|
||||
// do only if image type is Vuln
|
||||
if IsTrivySupportedOS(trivyResult.Type) {
|
||||
pkgs[vuln.PkgName] = models.Package{
|
||||
Name: vuln.PkgName,
|
||||
Version: vuln.InstalledVersion,
|
||||
}
|
||||
vulnInfo.AffectedPackages = append(vulnInfo.AffectedPackages, models.PackageFixStatus{
|
||||
Name: vuln.PkgName,
|
||||
NotFixedYet: notFixedYet,
|
||||
FixState: fixState,
|
||||
FixedIn: vuln.FixedVersion,
|
||||
})
|
||||
} else {
|
||||
// LibraryScanの結果
|
||||
vulnInfo.LibraryFixedIns = append(vulnInfo.LibraryFixedIns, models.LibraryFixedIn{
|
||||
Key: trivyResult.Type,
|
||||
Name: vuln.PkgName,
|
||||
Path: trivyResult.Target,
|
||||
FixedIn: vuln.FixedVersion,
|
||||
})
|
||||
libScanner := uniqueLibraryScannerPaths[trivyResult.Target]
|
||||
libScanner.Libs = append(libScanner.Libs, types.Library{
|
||||
Name: vuln.PkgName,
|
||||
Version: vuln.InstalledVersion,
|
||||
})
|
||||
uniqueLibraryScannerPaths[trivyResult.Target] = libScanner
|
||||
}
|
||||
vulnInfos[vuln.VulnerabilityID] = vulnInfo
|
||||
}
|
||||
}
|
||||
// flatten and unique libraries
|
||||
libraryScanners := make([]models.LibraryScanner, 0, len(uniqueLibraryScannerPaths))
|
||||
for path, v := range uniqueLibraryScannerPaths {
|
||||
uniqueLibrary := map[string]types.Library{}
|
||||
for _, lib := range v.Libs {
|
||||
uniqueLibrary[lib.Name+lib.Version] = lib
|
||||
}
|
||||
|
||||
var libraries []types.Library
|
||||
for _, library := range uniqueLibrary {
|
||||
libraries = append(libraries, library)
|
||||
}
|
||||
|
||||
sort.Slice(libraries, func(i, j int) bool {
|
||||
return libraries[i].Name < libraries[j].Name
|
||||
})
|
||||
|
||||
libscanner := models.LibraryScanner{
|
||||
Path: path,
|
||||
Libs: libraries,
|
||||
}
|
||||
libraryScanners = append(libraryScanners, libscanner)
|
||||
}
|
||||
sort.Slice(libraryScanners, func(i, j int) bool {
|
||||
return libraryScanners[i].Path < libraryScanners[j].Path
|
||||
})
|
||||
scanResult.ScannedCves = vulnInfos
|
||||
scanResult.Packages = pkgs
|
||||
scanResult.LibraryScanners = libraryScanners
|
||||
return scanResult, nil
|
||||
// Parser is a parser interface
|
||||
type Parser interface {
|
||||
Parse(vulnJSON []byte) (result *models.ScanResult, err error)
|
||||
}
|
||||
|
||||
// IsTrivySupportedOS :
|
||||
func IsTrivySupportedOS(family string) bool {
|
||||
supportedFamilies := []string{
|
||||
os.RedHat,
|
||||
os.Debian,
|
||||
os.Ubuntu,
|
||||
os.CentOS,
|
||||
os.Fedora,
|
||||
os.Amazon,
|
||||
os.Oracle,
|
||||
os.Windows,
|
||||
os.OpenSUSE,
|
||||
os.OpenSUSELeap,
|
||||
os.OpenSUSETumbleweed,
|
||||
os.SLES,
|
||||
os.Photon,
|
||||
os.Alpine,
|
||||
}
|
||||
for _, supportedFamily := range supportedFamilies {
|
||||
if family == supportedFamily {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
// Report is used for judgeing the scheme version of trivy
|
||||
type Report struct {
|
||||
SchemaVersion int `json:",omitempty"`
|
||||
}
|
||||
|
||||
func overrideServerData(scanResult *models.ScanResult, trivyResult *report.Result) {
|
||||
scanResult.Family = trivyResult.Type
|
||||
scanResult.ServerName = trivyResult.Target
|
||||
scanResult.Optional = map[string]interface{}{
|
||||
"trivy-target": trivyResult.Target,
|
||||
// NewParser make a parser for the schema version of trivy
|
||||
func NewParser(vulnJSON []byte) (Parser, error) {
|
||||
r := Report{}
|
||||
if err := json.Unmarshal(vulnJSON, &r); err != nil {
|
||||
return nil, xerrors.Errorf("Failed to parse JSON. Please use the latest version of trivy, trivy-to-vuls and future-vuls")
|
||||
}
|
||||
switch r.SchemaVersion {
|
||||
case 2:
|
||||
return v2.ParserV2{}, nil
|
||||
default:
|
||||
return nil, xerrors.Errorf("Failed to parse trivy json. SchemeVersion %d is not supported yet. Please contact support", r.SchemaVersion)
|
||||
}
|
||||
scanResult.ScannedAt = time.Now()
|
||||
scanResult.ScannedBy = "trivy"
|
||||
scanResult.ScannedVia = "trivy"
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
60
contrib/trivy/parser/v2/parser.go
Normal file
60
contrib/trivy/parser/v2/parser.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/report"
|
||||
|
||||
"github.com/future-architect/vuls/constant"
|
||||
"github.com/future-architect/vuls/contrib/trivy/pkg"
|
||||
"github.com/future-architect/vuls/models"
|
||||
)
|
||||
|
||||
// ParserV2 is a parser for scheme v2
|
||||
type ParserV2 struct {
|
||||
}
|
||||
|
||||
// Parse trivy's JSON and convert to the Vuls struct
|
||||
func (p ParserV2) Parse(vulnJSON []byte) (result *models.ScanResult, err error) {
|
||||
var report report.Report
|
||||
if err = json.Unmarshal(vulnJSON, &report); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
scanResult, err := pkg.Convert(report.Results)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
setScanResultMeta(scanResult, &report)
|
||||
return scanResult, nil
|
||||
}
|
||||
|
||||
func setScanResultMeta(scanResult *models.ScanResult, report *report.Report) {
|
||||
for _, r := range report.Results {
|
||||
const trivyTarget = "trivy-target"
|
||||
if pkg.IsTrivySupportedOS(r.Type) {
|
||||
scanResult.Family = r.Type
|
||||
scanResult.ServerName = r.Target
|
||||
scanResult.Optional = map[string]interface{}{
|
||||
trivyTarget: r.Target,
|
||||
}
|
||||
} else if pkg.IsTrivySupportedLib(r.Type) {
|
||||
if scanResult.Family == "" {
|
||||
scanResult.Family = constant.ServerTypePseudo
|
||||
}
|
||||
if scanResult.ServerName == "" {
|
||||
scanResult.ServerName = "library scan by trivy"
|
||||
}
|
||||
if _, ok := scanResult.Optional[trivyTarget]; !ok {
|
||||
scanResult.Optional = map[string]interface{}{
|
||||
trivyTarget: r.Target,
|
||||
}
|
||||
}
|
||||
}
|
||||
scanResult.ScannedAt = time.Now()
|
||||
scanResult.ScannedBy = "trivy"
|
||||
scanResult.ScannedVia = "trivy"
|
||||
}
|
||||
}
|
||||
725
contrib/trivy/parser/v2/parser_test.go
Normal file
725
contrib/trivy/parser/v2/parser_test.go
Normal file
@@ -0,0 +1,725 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/d4l3k/messagediff"
|
||||
|
||||
"github.com/future-architect/vuls/models"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
vulnJSON []byte
|
||||
expected *models.ScanResult
|
||||
}{
|
||||
"image redis": {
|
||||
vulnJSON: redisTrivy,
|
||||
expected: redisSR,
|
||||
},
|
||||
"image struts": {
|
||||
vulnJSON: strutsTrivy,
|
||||
expected: strutsSR,
|
||||
},
|
||||
"image osAndLib": {
|
||||
vulnJSON: osAndLibTrivy,
|
||||
expected: osAndLibSR,
|
||||
},
|
||||
}
|
||||
|
||||
for testcase, v := range cases {
|
||||
actual, err := ParserV2{}.Parse(v.vulnJSON)
|
||||
if err != nil {
|
||||
t.Errorf("%s", err)
|
||||
}
|
||||
|
||||
diff, equal := messagediff.PrettyDiff(
|
||||
v.expected,
|
||||
actual,
|
||||
messagediff.IgnoreStructField("ScannedAt"),
|
||||
messagediff.IgnoreStructField("Title"),
|
||||
messagediff.IgnoreStructField("Summary"),
|
||||
messagediff.IgnoreStructField("LastModified"),
|
||||
messagediff.IgnoreStructField("Published"),
|
||||
)
|
||||
if !equal {
|
||||
t.Errorf("test: %s, diff %s", testcase, diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var redisTrivy = []byte(`
|
||||
{
|
||||
"SchemaVersion": 2,
|
||||
"ArtifactName": "redis",
|
||||
"ArtifactType": "container_image",
|
||||
"Metadata": {
|
||||
"OS": {
|
||||
"Family": "debian",
|
||||
"Name": "10.10"
|
||||
},
|
||||
"ImageID": "sha256:ddcca4b8a6f0367b5de2764dfe76b0a4bfa6d75237932185923705da47004347",
|
||||
"DiffIDs": [
|
||||
"sha256:f68ef921efae588b3dd5cc466a1ca9c94c24785f1fa9420bea15ecc2dedbe781",
|
||||
"sha256:b6fc243eaea74d1a41b242da4c3ec5166db80f38c4d57a10ce8860c00d902ace",
|
||||
"sha256:ec92e47b7c52dacc26df07ee13e8e81c099b5a5661ccc97b06692a9c9d01e772",
|
||||
"sha256:4be6d4460d3615186717f21ffc0023b168dce48967d01934bbe31127901d3d5c",
|
||||
"sha256:992463b683270e164936e9c48fa395d05a7b8b5cc0aa208e4fa81aa9158fcae1",
|
||||
"sha256:0083597d42d190ddb86c35587a7b196fe18d79382520544b5f715c1e4792b19a"
|
||||
],
|
||||
"RepoTags": [
|
||||
"redis:latest"
|
||||
],
|
||||
"RepoDigests": [
|
||||
"redis@sha256:66ce9bc742609650afc3de7009658473ed601db4e926a5b16d239303383bacad"
|
||||
],
|
||||
"ImageConfig": {
|
||||
"architecture": "amd64",
|
||||
"container": "fa59f1c2817c9095f8f7272a4ab9b11db0332b33efb3a82c00a3d1fec8763684",
|
||||
"created": "2021-08-17T14:30:06.550779326Z",
|
||||
"docker_version": "20.10.7",
|
||||
"history": [
|
||||
{
|
||||
"created": "2021-08-17T01:24:06Z",
|
||||
"created_by": "/bin/sh -c #(nop) ADD file:87b4e60fe3af680c6815448374365a44e9ea461bc8ade2960b4639c25aed3ba9 in / "
|
||||
},
|
||||
{
|
||||
"created": "2021-08-17T14:30:06Z",
|
||||
"created_by": "/bin/sh -c #(nop) CMD [\"redis-server\"]",
|
||||
"empty_layer": true
|
||||
}
|
||||
],
|
||||
"os": "linux",
|
||||
"rootfs": {
|
||||
"type": "layers",
|
||||
"diff_ids": [
|
||||
"sha256:f68ef921efae588b3dd5cc466a1ca9c94c24785f1fa9420bea15ecc2dedbe781",
|
||||
"sha256:b6fc243eaea74d1a41b242da4c3ec5166db80f38c4d57a10ce8860c00d902ace",
|
||||
"sha256:ec92e47b7c52dacc26df07ee13e8e81c099b5a5661ccc97b06692a9c9d01e772",
|
||||
"sha256:4be6d4460d3615186717f21ffc0023b168dce48967d01934bbe31127901d3d5c",
|
||||
"sha256:992463b683270e164936e9c48fa395d05a7b8b5cc0aa208e4fa81aa9158fcae1",
|
||||
"sha256:0083597d42d190ddb86c35587a7b196fe18d79382520544b5f715c1e4792b19a"
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
"Cmd": [
|
||||
"redis-server"
|
||||
],
|
||||
"Entrypoint": [
|
||||
"docker-entrypoint.sh"
|
||||
],
|
||||
"Env": [
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
"GOSU_VERSION=1.12",
|
||||
"REDIS_VERSION=6.2.5",
|
||||
"REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-6.2.5.tar.gz",
|
||||
"REDIS_DOWNLOAD_SHA=4b9a75709a1b74b3785e20a6c158cab94cf52298aa381eea947a678a60d551ae"
|
||||
],
|
||||
"Image": "sha256:befbd3fc62bffcd0115008969a014faaad07828b2c54b4bcfd2d9fc3aa2508cd",
|
||||
"Volumes": {
|
||||
"/data": {}
|
||||
},
|
||||
"WorkingDir": "/data"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Results": [
|
||||
{
|
||||
"Target": "redis (debian 10.10)",
|
||||
"Class": "os-pkgs",
|
||||
"Type": "debian",
|
||||
"Packages": [
|
||||
{
|
||||
"Name": "adduser",
|
||||
"Version": "3.118",
|
||||
"SrcName": "adduser",
|
||||
"SrcVersion": "3.118",
|
||||
"Layer": {
|
||||
"DiffID": "sha256:f68ef921efae588b3dd5cc466a1ca9c94c24785f1fa9420bea15ecc2dedbe781"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "apt",
|
||||
"Version": "1.8.2.3",
|
||||
"SrcName": "apt",
|
||||
"SrcVersion": "1.8.2.3",
|
||||
"Layer": {
|
||||
"DiffID": "sha256:f68ef921efae588b3dd5cc466a1ca9c94c24785f1fa9420bea15ecc2dedbe781"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "bsdutils",
|
||||
"Version": "1:2.33.1-0.1",
|
||||
"SrcName": "util-linux",
|
||||
"SrcVersion": "2.33.1-0.1",
|
||||
"Layer": {
|
||||
"DiffID": "sha256:f68ef921efae588b3dd5cc466a1ca9c94c24785f1fa9420bea15ecc2dedbe781"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "pkgA",
|
||||
"Version": "1:2.33.1-0.1",
|
||||
"SrcName": "util-linux",
|
||||
"SrcVersion": "2.33.1-0.1",
|
||||
"Layer": {
|
||||
"DiffID": "sha256:f68ef921efae588b3dd5cc466a1ca9c94c24785f1fa9420bea15ecc2dedbe781"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Vulnerabilities": [
|
||||
{
|
||||
"VulnerabilityID": "CVE-2011-3374",
|
||||
"PkgName": "apt",
|
||||
"InstalledVersion": "1.8.2.3",
|
||||
"Layer": {
|
||||
"DiffID": "sha256:f68ef921efae588b3dd5cc466a1ca9c94c24785f1fa9420bea15ecc2dedbe781"
|
||||
},
|
||||
"SeveritySource": "debian",
|
||||
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2011-3374",
|
||||
"Description": "It was found that apt-key in apt, all versions, do not correctly validate gpg keys with the master keyring, leading to a potential man-in-the-middle attack.",
|
||||
"Severity": "LOW",
|
||||
"CweIDs": [
|
||||
"CWE-347"
|
||||
],
|
||||
"CVSS": {
|
||||
"nvd": {
|
||||
"V2Vector": "AV:N/AC:M/Au:N/C:N/I:P/A:N",
|
||||
"V3Vector": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N",
|
||||
"V2Score": 4.3,
|
||||
"V3Score": 3.7
|
||||
}
|
||||
},
|
||||
"References": [
|
||||
"https://access.redhat.com/security/cve/cve-2011-3374"
|
||||
],
|
||||
"PublishedDate": "2019-11-26T00:15:00Z",
|
||||
"LastModifiedDate": "2021-02-09T16:08:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
`)
|
||||
var redisSR = &models.ScanResult{
|
||||
JSONVersion: 4,
|
||||
ServerName: "redis (debian 10.10)",
|
||||
Family: "debian",
|
||||
ScannedBy: "trivy",
|
||||
ScannedVia: "trivy",
|
||||
ScannedCves: models.VulnInfos{
|
||||
"CVE-2011-3374": {
|
||||
CveID: "CVE-2011-3374",
|
||||
Confidences: models.Confidences{
|
||||
models.Confidence{
|
||||
Score: 100,
|
||||
DetectionMethod: "TrivyMatch",
|
||||
},
|
||||
},
|
||||
AffectedPackages: models.PackageFixStatuses{
|
||||
models.PackageFixStatus{
|
||||
Name: "apt",
|
||||
NotFixedYet: true,
|
||||
FixState: "Affected",
|
||||
FixedIn: "",
|
||||
}},
|
||||
CveContents: models.CveContents{
|
||||
"trivy": []models.CveContent{{
|
||||
Title: "",
|
||||
Summary: "It was found that apt-key in apt, all versions, do not correctly validate gpg keys with the master keyring, leading to a potential man-in-the-middle attack.",
|
||||
Cvss3Severity: "LOW",
|
||||
References: models.References{
|
||||
{Source: "trivy", Link: "https://access.redhat.com/security/cve/cve-2011-3374"},
|
||||
},
|
||||
}},
|
||||
},
|
||||
LibraryFixedIns: models.LibraryFixedIns{},
|
||||
},
|
||||
},
|
||||
LibraryScanners: models.LibraryScanners{},
|
||||
Packages: models.Packages{
|
||||
"apt": models.Package{
|
||||
Name: "apt",
|
||||
Version: "1.8.2.3",
|
||||
},
|
||||
"adduser": models.Package{
|
||||
Name: "adduser",
|
||||
Version: "3.118",
|
||||
},
|
||||
"bsdutils": models.Package{
|
||||
Name: "bsdutils",
|
||||
Version: "1:2.33.1-0.1",
|
||||
},
|
||||
"pkgA": models.Package{
|
||||
Name: "pkgA",
|
||||
Version: "1:2.33.1-0.1",
|
||||
},
|
||||
},
|
||||
SrcPackages: models.SrcPackages{
|
||||
"util-linux": models.SrcPackage{
|
||||
Name: "util-linux",
|
||||
Version: "2.33.1-0.1",
|
||||
BinaryNames: []string{"bsdutils", "pkgA"},
|
||||
},
|
||||
},
|
||||
Optional: map[string]interface{}{
|
||||
"trivy-target": "redis (debian 10.10)",
|
||||
},
|
||||
}
|
||||
|
||||
var strutsTrivy = []byte(`
|
||||
{
|
||||
"SchemaVersion": 2,
|
||||
"ArtifactName": "/data/struts-1.2.7/lib",
|
||||
"ArtifactType": "filesystem",
|
||||
"Metadata": {
|
||||
"ImageConfig": {
|
||||
"architecture": "",
|
||||
"created": "0001-01-01T00:00:00Z",
|
||||
"os": "",
|
||||
"rootfs": {
|
||||
"type": "",
|
||||
"diff_ids": null
|
||||
},
|
||||
"config": {}
|
||||
}
|
||||
},
|
||||
"Results": [
|
||||
{
|
||||
"Target": "Java",
|
||||
"Class": "lang-pkgs",
|
||||
"Type": "jar",
|
||||
"Packages": [
|
||||
{
|
||||
"Name": "oro:oro",
|
||||
"Version": "2.0.7",
|
||||
"Layer": {}
|
||||
},
|
||||
{
|
||||
"Name": "struts:struts",
|
||||
"Version": "1.2.7",
|
||||
"Layer": {}
|
||||
},
|
||||
{
|
||||
"Name": "commons-beanutils:commons-beanutils",
|
||||
"Version": "1.7.0",
|
||||
"Layer": {}
|
||||
}
|
||||
],
|
||||
"Vulnerabilities": [
|
||||
{
|
||||
"VulnerabilityID": "CVE-2014-0114",
|
||||
"PkgName": "commons-beanutils:commons-beanutils",
|
||||
"InstalledVersion": "1.7.0",
|
||||
"FixedVersion": "1.9.2",
|
||||
"Layer": {},
|
||||
"SeveritySource": "nvd",
|
||||
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2014-0114",
|
||||
"Title": "Apache Struts 1: Class Loader manipulation via request parameters",
|
||||
"Description": "Apache Commons BeanUtils, as distributed in lib/commons-beanutils-1.8.0.jar in Apache Struts 1.x through 1.3.10 and in other products requiring commons-beanutils through 1.9.2, does not suppress the class property, which allows remote attackers to \"manipulate\" the ClassLoader and execute arbitrary code via the class parameter, as demonstrated by the passing of this parameter to the getClass method of the ActionForm object in Struts 1.",
|
||||
"Severity": "HIGH",
|
||||
"CweIDs": [
|
||||
"CWE-20"
|
||||
],
|
||||
"CVSS": {
|
||||
"nvd": {
|
||||
"V2Vector": "AV:N/AC:L/Au:N/C:P/I:P/A:P",
|
||||
"V2Score": 7.5
|
||||
},
|
||||
"redhat": {
|
||||
"V2Vector": "AV:N/AC:L/Au:N/C:P/I:P/A:P",
|
||||
"V2Score": 7.5
|
||||
}
|
||||
},
|
||||
"References": [
|
||||
"http://advisories.mageia.org/MGASA-2014-0219.html"
|
||||
],
|
||||
"PublishedDate": "2014-04-30T10:49:00Z",
|
||||
"LastModifiedDate": "2021-01-26T18:15:00Z"
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "CVE-2012-1007",
|
||||
"PkgName": "struts:struts",
|
||||
"InstalledVersion": "1.2.7",
|
||||
"Layer": {},
|
||||
"SeveritySource": "nvd",
|
||||
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2012-1007",
|
||||
"Title": "struts: multiple XSS flaws",
|
||||
"Description": "Multiple cross-site scripting (XSS) vulnerabilities in Apache Struts 1.3.10 allow remote attackers to inject arbitrary web script or HTML via (1) the name parameter to struts-examples/upload/upload-submit.do, or the message parameter to (2) struts-cookbook/processSimple.do or (3) struts-cookbook/processDyna.do.",
|
||||
"Severity": "MEDIUM",
|
||||
"CweIDs": [
|
||||
"CWE-79"
|
||||
],
|
||||
"CVSS": {
|
||||
"nvd": {
|
||||
"V2Vector": "AV:N/AC:M/Au:N/C:N/I:P/A:N",
|
||||
"V2Score": 4.3
|
||||
},
|
||||
"redhat": {
|
||||
"V2Vector": "AV:N/AC:M/Au:N/C:N/I:P/A:N",
|
||||
"V2Score": 4.3
|
||||
}
|
||||
},
|
||||
"References": [
|
||||
"https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2012-1007"
|
||||
],
|
||||
"PublishedDate": "2012-02-07T04:09:00Z",
|
||||
"LastModifiedDate": "2018-10-17T01:29:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}`)
|
||||
|
||||
var strutsSR = &models.ScanResult{
|
||||
JSONVersion: 4,
|
||||
ServerName: "library scan by trivy",
|
||||
Family: "pseudo",
|
||||
ScannedBy: "trivy",
|
||||
ScannedVia: "trivy",
|
||||
ScannedCves: models.VulnInfos{
|
||||
"CVE-2014-0114": {
|
||||
CveID: "CVE-2014-0114",
|
||||
Confidences: models.Confidences{
|
||||
models.Confidence{
|
||||
Score: 100,
|
||||
DetectionMethod: "TrivyMatch",
|
||||
},
|
||||
},
|
||||
CveContents: models.CveContents{
|
||||
"trivy": []models.CveContent{{
|
||||
Title: "Apache Struts 1: Class Loader manipulation via request parameters",
|
||||
Summary: "Apache Commons BeanUtils, as distributed in lib/commons-beanutils-1.8.0.jar in Apache Struts 1.x through 1.3.10 and in other products requiring commons-beanutils through 1.9.2, does not suppress the class property, which allows remote attackers to \"manipulate\" the ClassLoader and execute arbitrary code via the class parameter, as demonstrated by the passing of this parameter to the getClass method of the ActionForm object in Struts 1.",
|
||||
Cvss3Severity: "HIGH",
|
||||
References: models.References{
|
||||
{Source: "trivy", Link: "http://advisories.mageia.org/MGASA-2014-0219.html"},
|
||||
},
|
||||
}},
|
||||
},
|
||||
LibraryFixedIns: models.LibraryFixedIns{
|
||||
models.LibraryFixedIn{
|
||||
Key: "jar",
|
||||
Name: "commons-beanutils:commons-beanutils",
|
||||
FixedIn: "1.9.2",
|
||||
//TODO use Artifactname?
|
||||
Path: "Java",
|
||||
},
|
||||
},
|
||||
AffectedPackages: models.PackageFixStatuses{},
|
||||
},
|
||||
"CVE-2012-1007": {
|
||||
CveID: "CVE-2012-1007",
|
||||
Confidences: models.Confidences{
|
||||
models.Confidence{
|
||||
Score: 100,
|
||||
DetectionMethod: "TrivyMatch",
|
||||
},
|
||||
},
|
||||
CveContents: models.CveContents{
|
||||
"trivy": []models.CveContent{{
|
||||
Title: "struts: multiple XSS flaws",
|
||||
Summary: "Multiple cross-site scripting (XSS) vulnerabilities in Apache Struts 1.3.10 allow remote attackers to inject arbitrary web script or HTML via (1) the name parameter to struts-examples/upload/upload-submit.do, or the message parameter to (2) struts-cookbook/processSimple.do or (3) struts-cookbook/processDyna.do.",
|
||||
Cvss3Severity: "MEDIUM",
|
||||
References: models.References{
|
||||
{Source: "trivy", Link: "https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2012-1007"},
|
||||
},
|
||||
}},
|
||||
},
|
||||
LibraryFixedIns: models.LibraryFixedIns{
|
||||
models.LibraryFixedIn{
|
||||
Key: "jar",
|
||||
Name: "struts:struts",
|
||||
FixedIn: "",
|
||||
//TODO use Artifactname?
|
||||
Path: "Java",
|
||||
},
|
||||
},
|
||||
AffectedPackages: models.PackageFixStatuses{},
|
||||
},
|
||||
},
|
||||
LibraryScanners: models.LibraryScanners{
|
||||
models.LibraryScanner{
|
||||
Type: "jar",
|
||||
LockfilePath: "Java",
|
||||
Libs: []models.Library{
|
||||
{
|
||||
Name: "commons-beanutils:commons-beanutils",
|
||||
Version: "1.7.0",
|
||||
},
|
||||
{
|
||||
Name: "oro:oro",
|
||||
Version: "2.0.7",
|
||||
},
|
||||
{
|
||||
Name: "struts:struts",
|
||||
Version: "1.2.7",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Packages: models.Packages{},
|
||||
SrcPackages: models.SrcPackages{},
|
||||
Optional: map[string]interface{}{
|
||||
"trivy-target": "Java",
|
||||
},
|
||||
}
|
||||
|
||||
var osAndLibTrivy = []byte(`
|
||||
{
|
||||
"SchemaVersion": 2,
|
||||
"ArtifactName": "quay.io/fluentd_elasticsearch/fluentd:v2.9.0",
|
||||
"ArtifactType": "container_image",
|
||||
"Metadata": {
|
||||
"OS": {
|
||||
"Family": "debian",
|
||||
"Name": "10.2"
|
||||
},
|
||||
"ImageID": "sha256:5a992077baba51b97f27591a10d54d2f2723dc9c81a3fe419e261023f2554933",
|
||||
"DiffIDs": [
|
||||
"sha256:25165eb51d15842f870f97873e0a58409d5e860e6108e3dd829bd10e484c0065"
|
||||
],
|
||||
"RepoTags": [
|
||||
"quay.io/fluentd_elasticsearch/fluentd:v2.9.0"
|
||||
],
|
||||
"RepoDigests": [
|
||||
"quay.io/fluentd_elasticsearch/fluentd@sha256:54716d825ec9791ffb403ac17a1e82159c98ac6161e02b2a054595ad01aa6726"
|
||||
],
|
||||
"ImageConfig": {
|
||||
"architecture": "amd64",
|
||||
"container": "232f3fc7ddffd71dc3ff52c6c0c3a5feea2f51acffd9b53850a8fc6f1a15319a",
|
||||
"created": "2020-03-04T13:59:39.161374106Z",
|
||||
"docker_version": "19.03.4",
|
||||
"history": [
|
||||
{
|
||||
"created": "2020-03-04T13:59:39.161374106Z",
|
||||
"created_by": "/bin/sh -c #(nop) CMD [\"/run.sh\"]",
|
||||
"empty_layer": true
|
||||
}
|
||||
],
|
||||
"os": "linux",
|
||||
"rootfs": {
|
||||
"type": "layers",
|
||||
"diff_ids": [
|
||||
"sha256:25165eb51d15842f870f97873e0a58409d5e860e6108e3dd829bd10e484c0065"
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
"Cmd": [
|
||||
"/run.sh"
|
||||
],
|
||||
"Env": [
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
"LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2"
|
||||
],
|
||||
"Image": "sha256:2a538358cddc4824e9eff1531e0c63ae5e3cda85d2984c647df9b1c816b9b86b",
|
||||
"ExposedPorts": {
|
||||
"80/tcp": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Results": [
|
||||
{
|
||||
"Target": "quay.io/fluentd_elasticsearch/fluentd:v2.9.0 (debian 10.2)",
|
||||
"Class": "os-pkgs",
|
||||
"Type": "debian",
|
||||
"Packages": [
|
||||
{
|
||||
"Name": "libgnutls30",
|
||||
"Version": "3.6.7-4",
|
||||
"SrcName": "gnutls28",
|
||||
"SrcVersion": "3.6.7-4",
|
||||
"Layer": {
|
||||
"Digest": "sha256:000eee12ec04cc914bf96e8f5dee7767510c2aca3816af6078bd9fbe3150920c",
|
||||
"DiffID": "sha256:831c5620387fb9efec59fc82a42b948546c6be601e3ab34a87108ecf852aa15f"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Vulnerabilities": [
|
||||
{
|
||||
"VulnerabilityID": "CVE-2021-20231",
|
||||
"PkgName": "libgnutls30",
|
||||
"InstalledVersion": "3.6.7-4",
|
||||
"FixedVersion": "3.6.7-4+deb10u7",
|
||||
"Layer": {
|
||||
"Digest": "sha256:000eee12ec04cc914bf96e8f5dee7767510c2aca3816af6078bd9fbe3150920c",
|
||||
"DiffID": "sha256:831c5620387fb9efec59fc82a42b948546c6be601e3ab34a87108ecf852aa15f"
|
||||
},
|
||||
"SeveritySource": "nvd",
|
||||
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2021-20231",
|
||||
"Title": "gnutls: Use after free in client key_share extension",
|
||||
"Description": "A flaw was found in gnutls. A use after free issue in client sending key_share extension may lead to memory corruption and other consequences.",
|
||||
"Severity": "CRITICAL",
|
||||
"CweIDs": [
|
||||
"CWE-416"
|
||||
],
|
||||
"CVSS": {
|
||||
"nvd": {
|
||||
"V2Vector": "AV:N/AC:L/Au:N/C:P/I:P/A:P",
|
||||
"V3Vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
|
||||
"V2Score": 7.5,
|
||||
"V3Score": 9.8
|
||||
},
|
||||
"redhat": {
|
||||
"V3Vector": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L",
|
||||
"V3Score": 3.7
|
||||
}
|
||||
},
|
||||
"References": [
|
||||
"https://bugzilla.redhat.com/show_bug.cgi?id=1922276"
|
||||
],
|
||||
"PublishedDate": "2021-03-12T19:15:00Z",
|
||||
"LastModifiedDate": "2021-06-01T14:07:00Z"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Target": "Ruby",
|
||||
"Class": "lang-pkgs",
|
||||
"Type": "gemspec",
|
||||
"Packages": [
|
||||
{
|
||||
"Name": "activesupport",
|
||||
"Version": "6.0.2.1",
|
||||
"License": "MIT",
|
||||
"Layer": {
|
||||
"Digest": "sha256:a8877cad19f14a7044524a145ce33170085441a7922458017db1631dcd5f7602",
|
||||
"DiffID": "sha256:75e43d55939745950bc3f8fad56c5834617c4339f0f54755e69a0dd5372624e9"
|
||||
},
|
||||
"FilePath": "var/lib/gems/2.5.0/specifications/activesupport-6.0.2.1.gemspec"
|
||||
}
|
||||
],
|
||||
"Vulnerabilities": [
|
||||
{
|
||||
"VulnerabilityID": "CVE-2020-8165",
|
||||
"PkgName": "activesupport",
|
||||
"PkgPath": "var/lib/gems/2.5.0/specifications/activesupport-6.0.2.1.gemspec",
|
||||
"InstalledVersion": "6.0.2.1",
|
||||
"FixedVersion": "6.0.3.1, 5.2.4.3",
|
||||
"Layer": {
|
||||
"Digest": "sha256:a8877cad19f14a7044524a145ce33170085441a7922458017db1631dcd5f7602",
|
||||
"DiffID": "sha256:75e43d55939745950bc3f8fad56c5834617c4339f0f54755e69a0dd5372624e9"
|
||||
},
|
||||
"SeveritySource": "nvd",
|
||||
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2020-8165",
|
||||
"Title": "rubygem-activesupport: potentially unintended unmarshalling of user-provided objects in MemCacheStore and RedisCacheStore",
|
||||
"Description": "A deserialization of untrusted data vulnernerability exists in rails \u003c 5.2.4.3, rails \u003c 6.0.3.1 that can allow an attacker to unmarshal user-provided objects in MemCacheStore and RedisCacheStore potentially resulting in an RCE.",
|
||||
"Severity": "CRITICAL",
|
||||
"CweIDs": [
|
||||
"CWE-502"
|
||||
],
|
||||
"CVSS": {
|
||||
"nvd": {
|
||||
"V2Vector": "AV:N/AC:L/Au:N/C:P/I:P/A:P",
|
||||
"V3Vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
|
||||
"V2Score": 7.5,
|
||||
"V3Score": 9.8
|
||||
},
|
||||
"redhat": {
|
||||
"V3Vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
|
||||
"V3Score": 9.8
|
||||
}
|
||||
},
|
||||
"References": [
|
||||
"https://www.debian.org/security/2020/dsa-4766"
|
||||
],
|
||||
"PublishedDate": "2020-06-19T18:15:00Z",
|
||||
"LastModifiedDate": "2020-10-17T12:15:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}`)
|
||||
|
||||
var osAndLibSR = &models.ScanResult{
|
||||
JSONVersion: 4,
|
||||
ServerName: "quay.io/fluentd_elasticsearch/fluentd:v2.9.0 (debian 10.2)",
|
||||
Family: "debian",
|
||||
ScannedBy: "trivy",
|
||||
ScannedVia: "trivy",
|
||||
ScannedCves: models.VulnInfos{
|
||||
"CVE-2021-20231": {
|
||||
CveID: "CVE-2021-20231",
|
||||
Confidences: models.Confidences{
|
||||
models.Confidence{
|
||||
Score: 100,
|
||||
DetectionMethod: "TrivyMatch",
|
||||
},
|
||||
},
|
||||
AffectedPackages: models.PackageFixStatuses{
|
||||
models.PackageFixStatus{
|
||||
Name: "libgnutls30",
|
||||
NotFixedYet: false,
|
||||
FixState: "",
|
||||
FixedIn: "3.6.7-4+deb10u7",
|
||||
}},
|
||||
CveContents: models.CveContents{
|
||||
"trivy": []models.CveContent{{
|
||||
Title: "gnutls: Use after free in client key_share extension",
|
||||
Summary: "A flaw was found in gnutls. A use after free issue in client sending key_share extension may lead to memory corruption and other consequences.",
|
||||
Cvss3Severity: "CRITICAL",
|
||||
References: models.References{
|
||||
{Source: "trivy", Link: "https://bugzilla.redhat.com/show_bug.cgi?id=1922276"},
|
||||
},
|
||||
}},
|
||||
},
|
||||
LibraryFixedIns: models.LibraryFixedIns{},
|
||||
},
|
||||
"CVE-2020-8165": {
|
||||
CveID: "CVE-2020-8165",
|
||||
Confidences: models.Confidences{
|
||||
models.Confidence{
|
||||
Score: 100,
|
||||
DetectionMethod: "TrivyMatch",
|
||||
},
|
||||
},
|
||||
AffectedPackages: models.PackageFixStatuses{},
|
||||
CveContents: models.CveContents{
|
||||
"trivy": []models.CveContent{{
|
||||
Title: "rubygem-activesupport: potentially unintended unmarshalling of user-provided objects in MemCacheStore and RedisCacheStore",
|
||||
Summary: "A deserialization of untrusted data vulnernerability exists in rails \u003c 5.2.4.3, rails \u003c 6.0.3.1 that can allow an attacker to unmarshal user-provided objects in MemCacheStore and RedisCacheStore potentially resulting in an RCE.",
|
||||
Cvss3Severity: "CRITICAL",
|
||||
References: models.References{
|
||||
{Source: "trivy", Link: "https://www.debian.org/security/2020/dsa-4766"},
|
||||
},
|
||||
}},
|
||||
},
|
||||
LibraryFixedIns: models.LibraryFixedIns{
|
||||
models.LibraryFixedIn{
|
||||
Key: "gemspec",
|
||||
Name: "activesupport",
|
||||
FixedIn: "6.0.3.1, 5.2.4.3",
|
||||
Path: "Ruby",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
LibraryScanners: models.LibraryScanners{
|
||||
models.LibraryScanner{
|
||||
Type: "gemspec",
|
||||
LockfilePath: "Ruby",
|
||||
Libs: []models.Library{
|
||||
{
|
||||
Name: "activesupport",
|
||||
Version: "6.0.2.1",
|
||||
FilePath: "var/lib/gems/2.5.0/specifications/activesupport-6.0.2.1.gemspec",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Packages: models.Packages{
|
||||
"libgnutls30": models.Package{
|
||||
Name: "libgnutls30",
|
||||
Version: "3.6.7-4",
|
||||
},
|
||||
},
|
||||
SrcPackages: models.SrcPackages{
|
||||
"gnutls28": models.SrcPackage{
|
||||
Name: "gnutls28",
|
||||
Version: "3.6.7-4",
|
||||
BinaryNames: []string{"libgnutls30"},
|
||||
},
|
||||
},
|
||||
Optional: map[string]interface{}{
|
||||
"trivy-target": "quay.io/fluentd_elasticsearch/fluentd:v2.9.0 (debian 10.2)",
|
||||
},
|
||||
}
|
||||
226
contrib/trivy/pkg/converter.go
Normal file
226
contrib/trivy/pkg/converter.go
Normal file
@@ -0,0 +1,226 @@
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
ftypes "github.com/aquasecurity/fanal/types"
|
||||
|
||||
"github.com/aquasecurity/fanal/analyzer/os"
|
||||
"github.com/aquasecurity/trivy/pkg/report"
|
||||
|
||||
"github.com/future-architect/vuls/models"
|
||||
)
|
||||
|
||||
// Convert :
|
||||
func Convert(results report.Results) (result *models.ScanResult, err error) {
|
||||
scanResult := &models.ScanResult{
|
||||
JSONVersion: models.JSONVersion,
|
||||
ScannedCves: models.VulnInfos{},
|
||||
}
|
||||
|
||||
pkgs := models.Packages{}
|
||||
srcPkgs := models.SrcPackages{}
|
||||
vulnInfos := models.VulnInfos{}
|
||||
uniqueLibraryScannerPaths := map[string]models.LibraryScanner{}
|
||||
for _, trivyResult := range results {
|
||||
for _, vuln := range trivyResult.Vulnerabilities {
|
||||
if _, ok := vulnInfos[vuln.VulnerabilityID]; !ok {
|
||||
vulnInfos[vuln.VulnerabilityID] = models.VulnInfo{
|
||||
CveID: vuln.VulnerabilityID,
|
||||
Confidences: models.Confidences{
|
||||
{
|
||||
Score: 100,
|
||||
DetectionMethod: models.TrivyMatchStr,
|
||||
},
|
||||
},
|
||||
AffectedPackages: models.PackageFixStatuses{},
|
||||
CveContents: models.CveContents{},
|
||||
LibraryFixedIns: models.LibraryFixedIns{},
|
||||
// VulnType : "",
|
||||
}
|
||||
}
|
||||
vulnInfo := vulnInfos[vuln.VulnerabilityID]
|
||||
var notFixedYet bool
|
||||
fixState := ""
|
||||
if len(vuln.FixedVersion) == 0 {
|
||||
notFixedYet = true
|
||||
fixState = "Affected"
|
||||
}
|
||||
var references models.References
|
||||
for _, reference := range vuln.References {
|
||||
references = append(references, models.Reference{
|
||||
Source: "trivy",
|
||||
Link: reference,
|
||||
})
|
||||
}
|
||||
|
||||
sort.Slice(references, func(i, j int) bool {
|
||||
return references[i].Link < references[j].Link
|
||||
})
|
||||
|
||||
var published time.Time
|
||||
if vuln.PublishedDate != nil {
|
||||
published = *vuln.PublishedDate
|
||||
}
|
||||
|
||||
var lastModified time.Time
|
||||
if vuln.LastModifiedDate != nil {
|
||||
lastModified = *vuln.LastModifiedDate
|
||||
}
|
||||
|
||||
vulnInfo.CveContents = models.CveContents{
|
||||
models.Trivy: []models.CveContent{{
|
||||
Cvss3Severity: vuln.Severity,
|
||||
References: references,
|
||||
Title: vuln.Title,
|
||||
Summary: vuln.Description,
|
||||
Published: published,
|
||||
LastModified: lastModified,
|
||||
}},
|
||||
}
|
||||
// do onlyIif image type is Vuln
|
||||
if IsTrivySupportedOS(trivyResult.Type) {
|
||||
pkgs[vuln.PkgName] = models.Package{
|
||||
Name: vuln.PkgName,
|
||||
Version: vuln.InstalledVersion,
|
||||
}
|
||||
vulnInfo.AffectedPackages = append(vulnInfo.AffectedPackages, models.PackageFixStatus{
|
||||
Name: vuln.PkgName,
|
||||
NotFixedYet: notFixedYet,
|
||||
FixState: fixState,
|
||||
FixedIn: vuln.FixedVersion,
|
||||
})
|
||||
} else {
|
||||
vulnInfo.LibraryFixedIns = append(vulnInfo.LibraryFixedIns, models.LibraryFixedIn{
|
||||
Key: trivyResult.Type,
|
||||
Name: vuln.PkgName,
|
||||
Path: trivyResult.Target,
|
||||
FixedIn: vuln.FixedVersion,
|
||||
})
|
||||
libScanner := uniqueLibraryScannerPaths[trivyResult.Target]
|
||||
libScanner.Type = trivyResult.Type
|
||||
libScanner.Libs = append(libScanner.Libs, models.Library{
|
||||
Name: vuln.PkgName,
|
||||
Version: vuln.InstalledVersion,
|
||||
FilePath: vuln.PkgPath,
|
||||
})
|
||||
uniqueLibraryScannerPaths[trivyResult.Target] = libScanner
|
||||
}
|
||||
vulnInfos[vuln.VulnerabilityID] = vulnInfo
|
||||
}
|
||||
|
||||
// --list-all-pkgs flg of trivy will output all installed packages, so collect them.
|
||||
if trivyResult.Class == report.ClassOSPkg {
|
||||
for _, p := range trivyResult.Packages {
|
||||
pkgs[p.Name] = models.Package{
|
||||
Name: p.Name,
|
||||
Version: p.Version,
|
||||
}
|
||||
if p.Name != p.SrcName {
|
||||
if v, ok := srcPkgs[p.SrcName]; !ok {
|
||||
srcPkgs[p.SrcName] = models.SrcPackage{
|
||||
Name: p.SrcName,
|
||||
Version: p.SrcVersion,
|
||||
BinaryNames: []string{p.Name},
|
||||
}
|
||||
} else {
|
||||
v.AddBinaryName(p.Name)
|
||||
srcPkgs[p.SrcName] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if trivyResult.Class == report.ClassLangPkg {
|
||||
libScanner := uniqueLibraryScannerPaths[trivyResult.Target]
|
||||
libScanner.Type = trivyResult.Type
|
||||
for _, p := range trivyResult.Packages {
|
||||
libScanner.Libs = append(libScanner.Libs, models.Library{
|
||||
Name: p.Name,
|
||||
Version: p.Version,
|
||||
FilePath: p.FilePath,
|
||||
})
|
||||
}
|
||||
uniqueLibraryScannerPaths[trivyResult.Target] = libScanner
|
||||
}
|
||||
}
|
||||
|
||||
// flatten and unique libraries
|
||||
libraryScanners := make([]models.LibraryScanner, 0, len(uniqueLibraryScannerPaths))
|
||||
for path, v := range uniqueLibraryScannerPaths {
|
||||
uniqueLibrary := map[string]models.Library{}
|
||||
for _, lib := range v.Libs {
|
||||
uniqueLibrary[lib.Name+lib.Version] = lib
|
||||
}
|
||||
|
||||
var libraries []models.Library
|
||||
for _, library := range uniqueLibrary {
|
||||
libraries = append(libraries, library)
|
||||
}
|
||||
|
||||
sort.Slice(libraries, func(i, j int) bool {
|
||||
return libraries[i].Name < libraries[j].Name
|
||||
})
|
||||
|
||||
libscanner := models.LibraryScanner{
|
||||
Type: v.Type,
|
||||
LockfilePath: path,
|
||||
Libs: libraries,
|
||||
}
|
||||
libraryScanners = append(libraryScanners, libscanner)
|
||||
}
|
||||
sort.Slice(libraryScanners, func(i, j int) bool {
|
||||
return libraryScanners[i].LockfilePath < libraryScanners[j].LockfilePath
|
||||
})
|
||||
scanResult.ScannedCves = vulnInfos
|
||||
scanResult.Packages = pkgs
|
||||
scanResult.SrcPackages = srcPkgs
|
||||
scanResult.LibraryScanners = libraryScanners
|
||||
return scanResult, nil
|
||||
}
|
||||
|
||||
// IsTrivySupportedOS :
|
||||
func IsTrivySupportedOS(family string) bool {
|
||||
supportedFamilies := map[string]interface{}{
|
||||
os.RedHat: struct{}{},
|
||||
os.Debian: struct{}{},
|
||||
os.Ubuntu: struct{}{},
|
||||
os.CentOS: struct{}{},
|
||||
os.Rocky: struct{}{},
|
||||
os.Alma: struct{}{},
|
||||
os.Fedora: struct{}{},
|
||||
os.Amazon: struct{}{},
|
||||
os.Oracle: struct{}{},
|
||||
os.Windows: struct{}{},
|
||||
os.OpenSUSE: struct{}{},
|
||||
os.OpenSUSELeap: struct{}{},
|
||||
os.OpenSUSETumbleweed: struct{}{},
|
||||
os.SLES: struct{}{},
|
||||
os.Photon: struct{}{},
|
||||
os.Alpine: struct{}{},
|
||||
}
|
||||
_, ok := supportedFamilies[family]
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsTrivySupportedLib :
|
||||
func IsTrivySupportedLib(typestr string) bool {
|
||||
supportedLibs := map[string]interface{}{
|
||||
ftypes.Bundler: struct{}{},
|
||||
ftypes.GemSpec: struct{}{},
|
||||
ftypes.Cargo: struct{}{},
|
||||
ftypes.Composer: struct{}{},
|
||||
ftypes.Npm: struct{}{},
|
||||
ftypes.NuGet: struct{}{},
|
||||
ftypes.Pip: struct{}{},
|
||||
ftypes.Pipenv: struct{}{},
|
||||
ftypes.Poetry: struct{}{},
|
||||
ftypes.PythonPkg: struct{}{},
|
||||
ftypes.NodePkg: struct{}{},
|
||||
ftypes.Yarn: struct{}{},
|
||||
ftypes.Jar: struct{}{},
|
||||
ftypes.GoBinary: struct{}{},
|
||||
ftypes.GoMod: struct{}{},
|
||||
}
|
||||
_, ok := supportedLibs[typestr]
|
||||
return ok
|
||||
}
|
||||
@@ -16,9 +16,9 @@ import (
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/logging"
|
||||
"github.com/future-architect/vuls/util"
|
||||
cvedb "github.com/kotakanbe/go-cve-dictionary/db"
|
||||
cvelog "github.com/kotakanbe/go-cve-dictionary/log"
|
||||
cvemodels "github.com/kotakanbe/go-cve-dictionary/models"
|
||||
cvedb "github.com/vulsio/go-cve-dictionary/db"
|
||||
cvelog "github.com/vulsio/go-cve-dictionary/log"
|
||||
cvemodels "github.com/vulsio/go-cve-dictionary/models"
|
||||
)
|
||||
|
||||
type goCveDictClient struct {
|
||||
@@ -27,7 +27,7 @@ type goCveDictClient struct {
|
||||
}
|
||||
|
||||
func newGoCveDictClient(cnf config.VulnDictInterface, o logging.LogOpts) (*goCveDictClient, error) {
|
||||
if err := cvelog.SetLogger(o.LogToFile, o.LogDir, o.Debug, false); err != nil {
|
||||
if err := cvelog.SetLogger(o.LogToFile, o.LogDir, o.Debug, o.LogJSON); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -44,21 +44,18 @@ func (api goCveDictClient) closeDB() error {
|
||||
if api.driver == nil {
|
||||
return nil
|
||||
}
|
||||
if err := api.driver.CloseDB(); err != nil {
|
||||
return xerrors.Errorf("Failed to close DB: %+v", err)
|
||||
}
|
||||
return nil
|
||||
return api.driver.CloseDB()
|
||||
}
|
||||
|
||||
func (api goCveDictClient) fetchCveDetails(cveIDs []string) (cveDetails []cvemodels.CveDetail, err error) {
|
||||
for _, cveID := range cveIDs {
|
||||
cveDetail, err := api.driver.Get(cveID)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("Failed to fetch CVE. err: %w", err)
|
||||
}
|
||||
cveDetails = append(cveDetails, *cveDetail)
|
||||
m, err := api.driver.GetMulti(cveIDs)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("Failed to GetMulti. err: %w", err)
|
||||
}
|
||||
return
|
||||
for _, v := range m {
|
||||
cveDetails = append(cveDetails, v)
|
||||
}
|
||||
return cveDetails, nil
|
||||
}
|
||||
|
||||
type response struct {
|
||||
@@ -156,7 +153,7 @@ func (api goCveDictClient) detectCveByCpeURI(cpeURI string, useJVN bool) (cves [
|
||||
|
||||
query := map[string]string{"name": cpeURI}
|
||||
logging.Log.Debugf("HTTP Request to %s, query: %#v", url, query)
|
||||
if cves, err = api.httpPost(cpeURI, url, query); err != nil {
|
||||
if cves, err = api.httpPost(url, query); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
@@ -180,7 +177,7 @@ func (api goCveDictClient) detectCveByCpeURI(cpeURI string, useJVN bool) (cves [
|
||||
return nvdCves, nil
|
||||
}
|
||||
|
||||
func (api goCveDictClient) httpPost(key, url string, query map[string]string) ([]cvemodels.CveDetail, error) {
|
||||
func (api goCveDictClient) httpPost(url string, query map[string]string) ([]cvemodels.CveDetail, error) {
|
||||
var body string
|
||||
var errs []error
|
||||
var resp *http.Response
|
||||
@@ -219,7 +216,7 @@ func newCveDB(cnf config.VulnDictInterface) (driver cvedb.DB, locked bool, err e
|
||||
if cnf.GetType() == "sqlite3" {
|
||||
path = cnf.GetSQLite3Path()
|
||||
}
|
||||
driver, locked, err = cvedb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL())
|
||||
driver, locked, err = cvedb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), cvedb.Option{})
|
||||
if err != nil {
|
||||
err = xerrors.Errorf("Failed to init CVE DB. err: %w, path: %s", err, path)
|
||||
return nil, locked, err
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
"github.com/future-architect/vuls/oval"
|
||||
"github.com/future-architect/vuls/reporter"
|
||||
"github.com/future-architect/vuls/util"
|
||||
cvemodels "github.com/kotakanbe/go-cve-dictionary/models"
|
||||
cvemodels "github.com/vulsio/go-cve-dictionary/models"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
@@ -145,9 +145,23 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
|
||||
}
|
||||
|
||||
for i, r := range rs {
|
||||
r.ScannedCves = r.ScannedCves.FilterByCvssOver(config.Conf.CvssScoreOver)
|
||||
r.ScannedCves = r.ScannedCves.FilterUnfixed(config.Conf.IgnoreUnfixed)
|
||||
r.ScannedCves = r.ScannedCves.FilterByConfidenceOver(config.Conf.ConfidenceScoreOver)
|
||||
nFiltered := 0
|
||||
logging.Log.Infof("%s: total %d CVEs detected", r.FormatServerName(), len(r.ScannedCves))
|
||||
|
||||
if 0 < config.Conf.CvssScoreOver {
|
||||
r.ScannedCves, nFiltered = r.ScannedCves.FilterByCvssOver(config.Conf.CvssScoreOver)
|
||||
logging.Log.Infof("%s: %d CVEs filtered by --cvss-over=%g", r.FormatServerName(), nFiltered, config.Conf.CvssScoreOver)
|
||||
}
|
||||
|
||||
if config.Conf.IgnoreUnfixed {
|
||||
r.ScannedCves, nFiltered = r.ScannedCves.FilterUnfixed(config.Conf.IgnoreUnfixed)
|
||||
logging.Log.Infof("%s: %d CVEs filtered by --ignore-unfixed", r.FormatServerName(), nFiltered)
|
||||
}
|
||||
|
||||
if 0 < config.Conf.ConfidenceScoreOver {
|
||||
r.ScannedCves, nFiltered = r.ScannedCves.FilterByConfidenceOver(config.Conf.ConfidenceScoreOver)
|
||||
logging.Log.Infof("%s: %d CVEs filtered by --confidence-over=%d", r.FormatServerName(), nFiltered, config.Conf.ConfidenceScoreOver)
|
||||
}
|
||||
|
||||
// IgnoreCves
|
||||
ignoreCves := []string{}
|
||||
@@ -156,7 +170,10 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
|
||||
} else if con, ok := config.Conf.Servers[r.ServerName].Containers[r.Container.Name]; ok {
|
||||
ignoreCves = con.IgnoreCves
|
||||
}
|
||||
r.ScannedCves = r.ScannedCves.FilterIgnoreCves(ignoreCves)
|
||||
if 0 < len(ignoreCves) {
|
||||
r.ScannedCves, nFiltered = r.ScannedCves.FilterIgnoreCves(ignoreCves)
|
||||
logging.Log.Infof("%s: %d CVEs filtered by ignoreCves=%s", r.FormatServerName(), nFiltered, ignoreCves)
|
||||
}
|
||||
|
||||
// ignorePkgs
|
||||
ignorePkgsRegexps := []string{}
|
||||
@@ -165,11 +182,15 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
|
||||
} else if s, ok := config.Conf.Servers[r.ServerName].Containers[r.Container.Name]; ok {
|
||||
ignorePkgsRegexps = s.IgnorePkgsRegexp
|
||||
}
|
||||
r.ScannedCves = r.ScannedCves.FilterIgnorePkgs(ignorePkgsRegexps)
|
||||
if 0 < len(ignorePkgsRegexps) {
|
||||
r.ScannedCves, nFiltered = r.ScannedCves.FilterIgnorePkgs(ignorePkgsRegexps)
|
||||
logging.Log.Infof("%s: %d CVEs filtered by ignorePkgsRegexp=%s", r.FormatServerName(), nFiltered, ignorePkgsRegexps)
|
||||
}
|
||||
|
||||
// IgnoreUnscored
|
||||
if config.Conf.IgnoreUnscoredCves {
|
||||
r.ScannedCves = r.ScannedCves.FindScoredVulns()
|
||||
r.ScannedCves, nFiltered = r.ScannedCves.FindScoredVulns()
|
||||
logging.Log.Infof("%s: %d CVEs filtered by --ignore-unscored-cves", r.FormatServerName(), nFiltered)
|
||||
}
|
||||
|
||||
r.FilterInactiveWordPressLibs(config.Conf.WpScan.DetectInactive)
|
||||
@@ -202,7 +223,7 @@ func DetectPkgCves(r *models.ScanResult, ovalCnf config.GovalDictConf, gostCnf c
|
||||
} else if r.Family == constant.ServerTypePseudo {
|
||||
logging.Log.Infof("pseudo type. Skip OVAL and gost detection")
|
||||
} else {
|
||||
return xerrors.Errorf("Failed to fill CVEs. r.Release is empty")
|
||||
logging.Log.Infof("r.Release is empty. detect as pseudo type. Skip OVAL and gost detection")
|
||||
}
|
||||
|
||||
for i, v := range r.ScannedCves {
|
||||
@@ -307,9 +328,23 @@ func FillCvesWithNvdJvn(r *models.ScanResult, cnf config.GoCveDictConf, logOpts
|
||||
if vinfo.CveContents == nil {
|
||||
vinfo.CveContents = models.CveContents{}
|
||||
}
|
||||
for _, con := range append(nvds, jvns...) {
|
||||
for _, con := range nvds {
|
||||
if !con.Empty() {
|
||||
vinfo.CveContents[con.Type] = append(vinfo.CveContents[con.Type], con)
|
||||
vinfo.CveContents[con.Type] = []models.CveContent{con}
|
||||
}
|
||||
}
|
||||
for _, con := range jvns {
|
||||
if !con.Empty() {
|
||||
found := false
|
||||
for _, cveCont := range vinfo.CveContents[con.Type] {
|
||||
if con.SourceLink == cveCont.SourceLink {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
vinfo.CveContents[con.Type] = append(vinfo.CveContents[con.Type], con)
|
||||
}
|
||||
}
|
||||
}
|
||||
vinfo.AlertDict = alerts
|
||||
@@ -364,11 +399,11 @@ func detectPkgsCvesWithOval(cnf config.GovalDictConf, r *models.ScanResult) erro
|
||||
}
|
||||
if !ok {
|
||||
if r.Family == constant.Debian {
|
||||
logging.Log.Debug("Skip OVAL and Scan with gost alone.")
|
||||
logging.Log.Infof("Skip OVAL and Scan with gost alone.")
|
||||
logging.Log.Infof("%s: %d CVEs are detected with OVAL", r.FormatServerName(), 0)
|
||||
return nil
|
||||
}
|
||||
return xerrors.Errorf("OVAL entries of %s %s are not found. Fetch OVAL before reporting. For details, see `https://github.com/kotakanbe/goval-dictionary#usage`", r.Family, r.Release)
|
||||
return xerrors.Errorf("OVAL entries of %s %s are not found. Fetch OVAL before reporting. For details, see `https://github.com/vulsio/goval-dictionary#usage`", r.Family, r.Release)
|
||||
}
|
||||
|
||||
logging.Log.Debugf("Check if oval fresh: %s %s", r.Family, r.Release)
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/future-architect/vuls/models"
|
||||
cvemodels "github.com/kotakanbe/go-cve-dictionary/models"
|
||||
cvemodels "github.com/vulsio/go-cve-dictionary/models"
|
||||
)
|
||||
|
||||
func Test_getMaxConfidence(t *testing.T) {
|
||||
|
||||
@@ -28,7 +28,7 @@ func FillWithExploit(r *models.ScanResult, cnf config.ExploitConf) (nExploitCve
|
||||
cveIDs = append(cveIDs, cveID)
|
||||
}
|
||||
prefix, _ := util.URLPathJoin(cnf.GetURL(), "cves")
|
||||
responses, err := getCvesViaHTTP(cveIDs, prefix)
|
||||
responses, err := getExploitsViaHTTP(cveIDs, prefix)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -37,7 +37,7 @@ func FillWithExploit(r *models.ScanResult, cnf config.ExploitConf) (nExploitCve
|
||||
if err := json.Unmarshal([]byte(res.json), &exps); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
exploits := ConvertToModels(exps)
|
||||
exploits := ConvertToModelsExploit(exps)
|
||||
v, ok := r.ScannedCves[res.request.cveID]
|
||||
if ok {
|
||||
v.Exploits = exploits
|
||||
@@ -46,7 +46,6 @@ func FillWithExploit(r *models.ScanResult, cnf config.ExploitConf) (nExploitCve
|
||||
nExploitCve++
|
||||
}
|
||||
} else {
|
||||
|
||||
driver, locked, err := newExploitDB(&cnf)
|
||||
if locked {
|
||||
return 0, xerrors.Errorf("SQLite3 is locked: %s", cnf.GetSQLite3Path())
|
||||
@@ -63,11 +62,14 @@ func FillWithExploit(r *models.ScanResult, cnf config.ExploitConf) (nExploitCve
|
||||
if cveID == "" {
|
||||
continue
|
||||
}
|
||||
es := driver.GetExploitByCveID(cveID)
|
||||
es, err := driver.GetExploitByCveID(cveID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(es) == 0 {
|
||||
continue
|
||||
}
|
||||
exploits := ConvertToModels(es)
|
||||
exploits := ConvertToModelsExploit(es)
|
||||
vuln.Exploits = exploits
|
||||
r.ScannedCves[cveID] = vuln
|
||||
nExploitCve++
|
||||
@@ -76,8 +78,8 @@ func FillWithExploit(r *models.ScanResult, cnf config.ExploitConf) (nExploitCve
|
||||
return nExploitCve, nil
|
||||
}
|
||||
|
||||
// ConvertToModels converts gost model to vuls model
|
||||
func ConvertToModels(es []exploitmodels.Exploit) (exploits []models.Exploit) {
|
||||
// ConvertToModelsExploit converts exploit model to vuls model
|
||||
func ConvertToModelsExploit(es []exploitmodels.Exploit) (exploits []models.Exploit) {
|
||||
for _, e := range es {
|
||||
var documentURL, shellURL *string
|
||||
if e.OffensiveSecurity != nil {
|
||||
@@ -103,14 +105,14 @@ func ConvertToModels(es []exploitmodels.Exploit) (exploits []models.Exploit) {
|
||||
}
|
||||
|
||||
type exploitResponse struct {
|
||||
request request
|
||||
request exploitRequest
|
||||
json string
|
||||
}
|
||||
|
||||
func getCvesViaHTTP(cveIDs []string, urlPrefix string) (
|
||||
func getExploitsViaHTTP(cveIDs []string, urlPrefix string) (
|
||||
responses []exploitResponse, err error) {
|
||||
nReq := len(cveIDs)
|
||||
reqChan := make(chan request, nReq)
|
||||
reqChan := make(chan exploitRequest, nReq)
|
||||
resChan := make(chan exploitResponse, nReq)
|
||||
errChan := make(chan error, nReq)
|
||||
defer close(reqChan)
|
||||
@@ -119,7 +121,7 @@ func getCvesViaHTTP(cveIDs []string, urlPrefix string) (
|
||||
|
||||
go func() {
|
||||
for _, cveID := range cveIDs {
|
||||
reqChan <- request{
|
||||
reqChan <- exploitRequest{
|
||||
cveID: cveID,
|
||||
}
|
||||
}
|
||||
@@ -129,18 +131,16 @@ func getCvesViaHTTP(cveIDs []string, urlPrefix string) (
|
||||
tasks := util.GenWorkers(concurrency)
|
||||
for i := 0; i < nReq; i++ {
|
||||
tasks <- func() {
|
||||
select {
|
||||
case req := <-reqChan:
|
||||
url, err := util.URLPathJoin(
|
||||
urlPrefix,
|
||||
req.cveID,
|
||||
)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
} else {
|
||||
logging.Log.Debugf("HTTP Request to %s", url)
|
||||
httpGet(url, req, resChan, errChan)
|
||||
}
|
||||
req := <-reqChan
|
||||
url, err := util.URLPathJoin(
|
||||
urlPrefix,
|
||||
req.cveID,
|
||||
)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
} else {
|
||||
logging.Log.Debugf("HTTP Request to %s", url)
|
||||
httpGetExploit(url, req, resChan, errChan)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -154,23 +154,20 @@ func getCvesViaHTTP(cveIDs []string, urlPrefix string) (
|
||||
case err := <-errChan:
|
||||
errs = append(errs, err)
|
||||
case <-timeout:
|
||||
return nil, xerrors.New("Timeout Fetching OVAL")
|
||||
return nil, xerrors.New("Timeout Fetching Exploit")
|
||||
}
|
||||
}
|
||||
if len(errs) != 0 {
|
||||
return nil, xerrors.Errorf("Failed to fetch OVAL. err: %w", errs)
|
||||
return nil, xerrors.Errorf("Failed to fetch Exploit. err: %w", errs)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type request struct {
|
||||
osMajorVersion string
|
||||
packName string
|
||||
isSrcPack bool
|
||||
cveID string
|
||||
type exploitRequest struct {
|
||||
cveID string
|
||||
}
|
||||
|
||||
func httpGet(url string, req request, resChan chan<- exploitResponse, errChan chan<- error) {
|
||||
func httpGetExploit(url string, req exploitRequest, resChan chan<- exploitResponse, errChan chan<- error) {
|
||||
var body string
|
||||
var errs []error
|
||||
var resp *http.Response
|
||||
@@ -214,7 +211,7 @@ func newExploitDB(cnf config.VulnDictInterface) (driver exploitdb.DB, locked boo
|
||||
if cnf.GetType() == "sqlite3" {
|
||||
path = cnf.GetSQLite3Path()
|
||||
}
|
||||
if driver, locked, err = exploitdb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL()); err != nil {
|
||||
if driver, locked, err = exploitdb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), exploitdb.Option{}); err != nil {
|
||||
if locked {
|
||||
return nil, true, xerrors.Errorf("exploitDB is locked. err: %w", err)
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ func DetectGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string,
|
||||
|
||||
if val, ok := r.ScannedCves[cveID]; ok {
|
||||
val.GitHubSecurityAlerts = val.GitHubSecurityAlerts.Add(m)
|
||||
val.CveContents[models.GitHub] = append(val.CveContents[models.GitHub], cveContent)
|
||||
val.CveContents[models.GitHub] = []models.CveContent{cveContent}
|
||||
r.ScannedCves[cveID] = val
|
||||
} else {
|
||||
v := models.VulnInfo{
|
||||
|
||||
193
detector/msf.go
193
detector/msf.go
@@ -4,47 +4,182 @@
|
||||
package detector
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff"
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/logging"
|
||||
"github.com/future-architect/vuls/models"
|
||||
metasploitdb "github.com/takuzoo3868/go-msfdb/db"
|
||||
metasploitmodels "github.com/takuzoo3868/go-msfdb/models"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"github.com/parnurzeal/gorequest"
|
||||
metasploitdb "github.com/vulsio/go-msfdb/db"
|
||||
metasploitmodels "github.com/vulsio/go-msfdb/models"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// FillWithMetasploit fills metasploit module information that has in module
|
||||
func FillWithMetasploit(r *models.ScanResult, cnf config.MetasploitConf) (nMetasploitCve int, err error) {
|
||||
if cnf.IsFetchViaHTTP() {
|
||||
var cveIDs []string
|
||||
for cveID := range r.ScannedCves {
|
||||
cveIDs = append(cveIDs, cveID)
|
||||
}
|
||||
prefix, err := util.URLPathJoin(cnf.GetURL(), "cves")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
responses, err := getMetasploitsViaHTTP(cveIDs, prefix)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for _, res := range responses {
|
||||
msfs := []metasploitmodels.Metasploit{}
|
||||
if err := json.Unmarshal([]byte(res.json), &msfs); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
metasploits := ConvertToModelsMsf(msfs)
|
||||
v, ok := r.ScannedCves[res.request.cveID]
|
||||
if ok {
|
||||
v.Metasploits = metasploits
|
||||
}
|
||||
r.ScannedCves[res.request.cveID] = v
|
||||
nMetasploitCve++
|
||||
}
|
||||
} else {
|
||||
driver, locked, err := newMetasploitDB(&cnf)
|
||||
if locked {
|
||||
return 0, xerrors.Errorf("SQLite3 is locked: %s", cnf.GetSQLite3Path())
|
||||
} else if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer func() {
|
||||
if err := driver.CloseDB(); err != nil {
|
||||
logging.Log.Errorf("Failed to close DB. err: %+v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
driver, locked, err := newMetasploitDB(&cnf)
|
||||
if locked {
|
||||
return 0, xerrors.Errorf("SQLite3 is locked: %s", cnf.GetSQLite3Path())
|
||||
} else if err != nil {
|
||||
return 0, err
|
||||
for cveID, vuln := range r.ScannedCves {
|
||||
if cveID == "" {
|
||||
continue
|
||||
}
|
||||
ms, err := driver.GetModuleByCveID(cveID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(ms) == 0 {
|
||||
continue
|
||||
}
|
||||
modules := ConvertToModelsMsf(ms)
|
||||
vuln.Metasploits = modules
|
||||
r.ScannedCves[cveID] = vuln
|
||||
nMetasploitCve++
|
||||
}
|
||||
}
|
||||
defer func() {
|
||||
if err := driver.CloseDB(); err != nil {
|
||||
logging.Log.Errorf("Failed to close DB. err: %+v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
for cveID, vuln := range r.ScannedCves {
|
||||
if cveID == "" {
|
||||
continue
|
||||
}
|
||||
ms := driver.GetModuleByCveID(cveID)
|
||||
if len(ms) == 0 {
|
||||
continue
|
||||
}
|
||||
modules := ConvertToModelsMsf(ms)
|
||||
vuln.Metasploits = modules
|
||||
r.ScannedCves[cveID] = vuln
|
||||
nMetasploitCve++
|
||||
}
|
||||
|
||||
return nMetasploitCve, nil
|
||||
}
|
||||
|
||||
// ConvertToModelsMsf converts gost model to vuls model
|
||||
type metasploitResponse struct {
|
||||
request metasploitRequest
|
||||
json string
|
||||
}
|
||||
|
||||
func getMetasploitsViaHTTP(cveIDs []string, urlPrefix string) (
|
||||
responses []metasploitResponse, err error) {
|
||||
nReq := len(cveIDs)
|
||||
reqChan := make(chan metasploitRequest, nReq)
|
||||
resChan := make(chan metasploitResponse, nReq)
|
||||
errChan := make(chan error, nReq)
|
||||
defer close(reqChan)
|
||||
defer close(resChan)
|
||||
defer close(errChan)
|
||||
|
||||
go func() {
|
||||
for _, cveID := range cveIDs {
|
||||
reqChan <- metasploitRequest{
|
||||
cveID: cveID,
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
concurrency := 10
|
||||
tasks := util.GenWorkers(concurrency)
|
||||
for i := 0; i < nReq; i++ {
|
||||
tasks <- func() {
|
||||
req := <-reqChan
|
||||
url, err := util.URLPathJoin(
|
||||
urlPrefix,
|
||||
req.cveID,
|
||||
)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
} else {
|
||||
logging.Log.Debugf("HTTP Request to %s", url)
|
||||
httpGetMetasploit(url, req, resChan, errChan)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
timeout := time.After(2 * 60 * time.Second)
|
||||
var errs []error
|
||||
for i := 0; i < nReq; i++ {
|
||||
select {
|
||||
case res := <-resChan:
|
||||
responses = append(responses, res)
|
||||
case err := <-errChan:
|
||||
errs = append(errs, err)
|
||||
case <-timeout:
|
||||
return nil, xerrors.New("Timeout Fetching Metasploit")
|
||||
}
|
||||
}
|
||||
if len(errs) != 0 {
|
||||
return nil, xerrors.Errorf("Failed to fetch Metasploit. err: %w", errs)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type metasploitRequest struct {
|
||||
cveID string
|
||||
}
|
||||
|
||||
func httpGetMetasploit(url string, req metasploitRequest, resChan chan<- metasploitResponse, errChan chan<- error) {
|
||||
var body string
|
||||
var errs []error
|
||||
var resp *http.Response
|
||||
count, retryMax := 0, 3
|
||||
f := func() (err error) {
|
||||
// resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
|
||||
resp, body, errs = gorequest.New().Timeout(10 * time.Second).Get(url).End()
|
||||
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
|
||||
count++
|
||||
if count == retryMax {
|
||||
return nil
|
||||
}
|
||||
return xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %+v", url, resp, errs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
notify := func(err error, t time.Duration) {
|
||||
logging.Log.Warnf("Failed to HTTP GET. retrying in %s seconds. err: %+v", t, err)
|
||||
}
|
||||
err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
|
||||
if err != nil {
|
||||
errChan <- xerrors.Errorf("HTTP Error %w", err)
|
||||
return
|
||||
}
|
||||
if count == retryMax {
|
||||
errChan <- xerrors.New("Retry count exceeded")
|
||||
return
|
||||
}
|
||||
|
||||
resChan <- metasploitResponse{
|
||||
request: req,
|
||||
json: body,
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertToModelsMsf converts metasploit model to vuls model
|
||||
func ConvertToModelsMsf(ms []metasploitmodels.Metasploit) (modules []models.Metasploit) {
|
||||
for _, m := range ms {
|
||||
var links []string
|
||||
@@ -72,7 +207,7 @@ func newMetasploitDB(cnf config.VulnDictInterface) (driver metasploitdb.DB, lock
|
||||
if cnf.GetType() == "sqlite3" {
|
||||
path = cnf.GetSQLite3Path()
|
||||
}
|
||||
if driver, locked, err = metasploitdb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL()); err != nil {
|
||||
if driver, locked, err = metasploitdb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), metasploitdb.Option{}); err != nil {
|
||||
if locked {
|
||||
return nil, true, xerrors.Errorf("metasploitDB is locked. err: %w", err)
|
||||
}
|
||||
|
||||
@@ -68,10 +68,9 @@ func loadPrevious(currs models.ScanResults, resultsDir string) (prevs models.Sca
|
||||
prevs = append(prevs, *r)
|
||||
logging.Log.Infof("Previous json found: %s", path)
|
||||
break
|
||||
} else {
|
||||
logging.Log.Infof("Previous json is different family.Release: %s, pre: %s.%s cur: %s.%s",
|
||||
path, r.Family, r.Release, result.Family, result.Release)
|
||||
}
|
||||
logging.Log.Infof("Previous json is different family.Release: %s, pre: %s.%s cur: %s.%s",
|
||||
path, r.Family, r.Release, result.Family, result.Release)
|
||||
}
|
||||
}
|
||||
return prevs, nil
|
||||
@@ -145,7 +144,7 @@ func getPlusDiffCves(previous, current models.ScanResult) models.VulnInfos {
|
||||
|
||||
// TODO commented out because a bug of diff logic when multiple oval defs found for a certain CVE-ID and same updated_at
|
||||
// if these OVAL defs have different affected packages, this logic detects as updated.
|
||||
// This logic will be uncomented after integration with gost https://github.com/knqyf263/gost
|
||||
// This logic will be uncomented after integration with gost https://github.com/vulsio/gost
|
||||
// } else if isCveFixed(v, previous) {
|
||||
// updated[v.CveID] = v
|
||||
// logging.Log.Debugf("fixed: %s", v.CveID)
|
||||
|
||||
94
go.mod
94
go.mod
@@ -4,44 +4,40 @@ go 1.17
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-sdk-for-go v50.2.0+incompatible
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
github.com/BurntSushi/toml v0.4.1
|
||||
github.com/Ullaakut/nmap/v2 v2.1.2-0.20210406060955-59a52fe80a4f
|
||||
github.com/VividCortex/ewma v1.2.0 // indirect
|
||||
github.com/aquasecurity/fanal v0.0.0-20210719144537-c73c1e9f21bf
|
||||
github.com/aquasecurity/trivy v0.19.2
|
||||
github.com/aquasecurity/trivy-db v0.0.0-20210531102723-aaab62dec6ee
|
||||
github.com/aquasecurity/fanal v0.0.0-20211005172059-69527b46560c
|
||||
github.com/aquasecurity/trivy v0.20.0
|
||||
github.com/aquasecurity/trivy-db v0.0.0-20210916043317-726b7b72a47b
|
||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
|
||||
github.com/aws/aws-sdk-go v1.37.0
|
||||
github.com/aws/aws-sdk-go v1.40.49
|
||||
github.com/boltdb/bolt v1.3.1
|
||||
github.com/briandowns/spinner v1.16.0 // indirect
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/cheggaaa/pb/v3 v3.0.8 // indirect
|
||||
github.com/cheggaaa/pb v1.0.27
|
||||
github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b
|
||||
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21
|
||||
github.com/emersion/go-smtp v0.14.0
|
||||
github.com/fatih/color v1.12.0 // indirect
|
||||
github.com/fatih/color v1.13.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
||||
github.com/go-redis/redis/v8 v8.11.3 // indirect
|
||||
github.com/go-redis/redis/v8 v8.11.4 // indirect
|
||||
github.com/go-stack/stack v1.8.1 // indirect
|
||||
github.com/google/subcommands v1.2.0
|
||||
github.com/gosuri/uitable v0.0.4
|
||||
github.com/hashicorp/go-uuid v1.0.2
|
||||
github.com/hashicorp/go-version v1.3.0
|
||||
github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c
|
||||
github.com/jackc/pgx/v4 v4.13.0 // indirect
|
||||
github.com/jesseduffield/gocui v0.3.0
|
||||
github.com/k0kubun/pp v3.0.1+incompatible
|
||||
github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f
|
||||
github.com/knqyf263/go-cpe v0.0.0-20201213041631-54f6ab28673f
|
||||
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d
|
||||
github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936
|
||||
github.com/knqyf263/gost v0.2.0
|
||||
github.com/kotakanbe/go-cve-dictionary v0.7.2-0.20210907024016-69922490c76a
|
||||
github.com/kotakanbe/go-pingscanner v0.1.0
|
||||
github.com/kotakanbe/goval-dictionary v0.3.6-0.20210625044258-9be85404d7dd
|
||||
github.com/kotakanbe/logrus-prefixed-formatter v0.0.0-20180123152602-928f7356cb96
|
||||
github.com/mattn/go-isatty v0.0.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
@@ -49,57 +45,69 @@ require (
|
||||
github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/parnurzeal/gorequest v0.2.16
|
||||
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
|
||||
github.com/sirupsen/logrus v1.8.0
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/spf13/afero v1.6.0
|
||||
github.com/spf13/cast v1.4.1 // indirect
|
||||
github.com/spf13/cobra v1.2.1
|
||||
github.com/takuzoo3868/go-msfdb v0.1.6
|
||||
github.com/vulsio/go-exploitdb v0.2.0
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
|
||||
golang.org/x/net v0.0.0-20210902165921-8d991716f632 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602
|
||||
github.com/vulsio/go-cve-dictionary v0.8.2-0.20211028094424-0a854f8e8f85
|
||||
github.com/vulsio/go-exploitdb v0.4.2-0.20211028071949-1ebf9c4f6c4d
|
||||
github.com/vulsio/go-msfdb v0.2.1-0.20211028071756-4a9759bd9f14
|
||||
github.com/vulsio/gost v0.4.1-0.20211028071837-7ad032a6ffa8
|
||||
github.com/vulsio/goval-dictionary v0.6.1-0.20211028072035-e85e14b91ccc
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
|
||||
golang.org/x/net v0.0.0-20211019232329-c6ed85c7a12d // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
|
||||
gopkg.in/ini.v1 v1.63.2 // indirect
|
||||
gorm.io/driver/mysql v1.1.2 // indirect
|
||||
gorm.io/gorm v1.21.14 // indirect
|
||||
gorm.io/driver/postgres v1.1.2 // indirect
|
||||
gorm.io/driver/sqlite v1.1.6 // indirect
|
||||
k8s.io/utils v0.0.0-20210111153108-fddb29f9d009
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/go-autorest/autorest v0.10.2 // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.3 // indirect
|
||||
github.com/Azure/go-autorest/autorest/date v0.2.0 // indirect
|
||||
github.com/Azure/go-autorest/logger v0.1.0 // indirect
|
||||
github.com/Azure/go-autorest/tracing v0.5.0 // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.11.1 // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.5 // indirect
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
|
||||
github.com/Azure/go-autorest/logger v0.2.0 // indirect
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver v1.5.0 // indirect
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible // indirect
|
||||
github.com/aquasecurity/go-dep-parser v0.0.0-20210520015931-0dd56983cc62 // indirect
|
||||
github.com/PuerkitoBio/goquery v1.7.1 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.1 // indirect
|
||||
github.com/aquasecurity/go-dep-parser v0.0.0-20210919151457-76db061b9305 // indirect
|
||||
github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce // indirect
|
||||
github.com/aquasecurity/go-npm-version v0.0.0-20201110091526-0b796d180798 // indirect
|
||||
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46 // indirect
|
||||
github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492 // indirect
|
||||
github.com/caarlos0/env/v6 v6.0.0 // indirect
|
||||
github.com/cheggaaa/pb/v3 v3.0.8 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/go-redis/redis v6.15.9+incompatible // indirect
|
||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible // indirect
|
||||
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/go-containerregistry v0.6.0 // indirect
|
||||
github.com/google/go-github/v33 v33.0.0 // indirect
|
||||
github.com/google/go-querystring v1.0.0 // indirect
|
||||
github.com/google/uuid v1.2.0 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/google/wire v0.4.0 // indirect
|
||||
github.com/gorilla/websocket v1.4.2 // indirect
|
||||
github.com/grokify/html-strip-tags-go v0.0.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/htcat/htcat v1.0.2 // indirect
|
||||
github.com/huandu/xstrings v1.3.2 // indirect
|
||||
github.com/imdario/mergo v0.3.9 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/inconshreveable/log15 v0.0.0-20201112154412-8562bdadbbac // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||
@@ -109,45 +117,39 @@ require (
|
||||
github.com/jackc/pgproto3/v2 v2.1.1 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
|
||||
github.com/jackc/pgtype v1.8.1 // indirect
|
||||
github.com/jackc/pgx/v4 v4.13.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.2 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/labstack/gommon v0.3.0 // indirect
|
||||
github.com/magefile/mage v1.11.0 // indirect
|
||||
github.com/magiconair/properties v1.8.5 // indirect
|
||||
github.com/masahiro331/go-mvn-version v0.0.0-20210429150710-d3157d602a08 // indirect
|
||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
|
||||
github.com/mattn/go-colorable v0.1.11 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.9 // indirect
|
||||
github.com/mitchellh/copystructure v1.1.1 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.2 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.1 // indirect
|
||||
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/satori/go.uuid v1.2.0 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.8.1 // indirect
|
||||
github.com/spf13/viper v1.9.0 // indirect
|
||||
github.com/stretchr/objx v0.3.0 // indirect
|
||||
github.com/stretchr/testify v1.7.0 // indirect
|
||||
github.com/subosito/gotenv v1.2.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.1 // indirect
|
||||
github.com/ymomoi/goval-parser v0.0.0-20170813122243-0a0be1dd9d08 // indirect
|
||||
go.etcd.io/bbolt v1.3.5 // indirect
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.17.0 // indirect
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect
|
||||
go.uber.org/zap v1.19.1 // indirect
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 // indirect
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.26.0 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect
|
||||
gopkg.in/ini.v1 v1.63.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
gorm.io/driver/postgres v1.1.0 // indirect
|
||||
gorm.io/driver/sqlite v1.1.4 // indirect
|
||||
gorm.io/gorm v1.21.16 // indirect
|
||||
moul.io/http2curl v1.0.0 // indirect
|
||||
)
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/util"
|
||||
debver "github.com/knqyf263/go-deb-version"
|
||||
gostmodels "github.com/knqyf263/gost/models"
|
||||
gostmodels "github.com/vulsio/gost/models"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
@@ -31,6 +31,7 @@ func (deb Debian) supported(major string) bool {
|
||||
"8": "jessie",
|
||||
"9": "stretch",
|
||||
"10": "buster",
|
||||
"11": "bullseye",
|
||||
}[major]
|
||||
return ok
|
||||
}
|
||||
@@ -112,7 +113,10 @@ func (deb Debian) detectCVEsWithFixState(r *models.ScanResult, fixStatus string)
|
||||
return 0, nil
|
||||
}
|
||||
for _, pack := range r.Packages {
|
||||
cves, fixes := deb.getCvesDebianWithfixStatus(fixStatus, major(r.Release), pack.Name)
|
||||
cves, fixes, err := deb.getCvesDebianWithfixStatus(fixStatus, major(r.Release), pack.Name)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
packCvesList = append(packCvesList, packCves{
|
||||
packName: pack.Name,
|
||||
isSrcPack: false,
|
||||
@@ -123,7 +127,10 @@ func (deb Debian) detectCVEsWithFixState(r *models.ScanResult, fixStatus string)
|
||||
|
||||
// SrcPack
|
||||
for _, pack := range r.SrcPackages {
|
||||
cves, fixes := deb.getCvesDebianWithfixStatus(fixStatus, major(r.Release), pack.Name)
|
||||
cves, fixes, err := deb.getCvesDebianWithfixStatus(fixStatus, major(r.Release), pack.Name)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
packCvesList = append(packCvesList, packCves{
|
||||
packName: pack.Name,
|
||||
isSrcPack: true,
|
||||
@@ -142,7 +149,7 @@ func (deb Debian) detectCVEsWithFixState(r *models.ScanResult, fixStatus string)
|
||||
if v.CveContents == nil {
|
||||
v.CveContents = models.NewCveContents(cve)
|
||||
} else {
|
||||
v.CveContents[models.DebianSecurityTracker] = append(v.CveContents[models.DebianSecurityTracker], cve)
|
||||
v.CveContents[models.DebianSecurityTracker] = []models.CveContent{cve}
|
||||
v.Confidences = models.Confidences{models.DebianSecurityTrackerMatch}
|
||||
}
|
||||
} else {
|
||||
@@ -232,21 +239,25 @@ func isGostDefAffected(versionRelease, gostVersion string) (affected bool, err e
|
||||
return vera.LessThan(verb), nil
|
||||
}
|
||||
|
||||
func (deb Debian) getCvesDebianWithfixStatus(fixStatus, release, pkgName string) (cves []models.CveContent, fixes []models.PackageFixStatus) {
|
||||
var f func(string, string) map[string]gostmodels.DebianCVE
|
||||
|
||||
func (deb Debian) getCvesDebianWithfixStatus(fixStatus, release, pkgName string) ([]models.CveContent, []models.PackageFixStatus, error) {
|
||||
var f func(string, string) (map[string]gostmodels.DebianCVE, error)
|
||||
if fixStatus == "resolved" {
|
||||
f = deb.DBDriver.DB.GetFixedCvesDebian
|
||||
} else {
|
||||
f = deb.DBDriver.DB.GetUnfixedCvesDebian
|
||||
}
|
||||
|
||||
for _, cveDeb := range f(release, pkgName) {
|
||||
cves = append(cves, *deb.ConvertToModel(&cveDeb))
|
||||
fixes = append(fixes, checkPackageFixStatus(&cveDeb)...)
|
||||
debCves, err := f(release, pkgName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return
|
||||
cves := []models.CveContent{}
|
||||
fixes := []models.PackageFixStatus{}
|
||||
for _, devbCve := range debCves {
|
||||
cves = append(cves, *deb.ConvertToModel(&devbCve))
|
||||
fixes = append(fixes, checkPackageFixStatus(&devbCve)...)
|
||||
}
|
||||
return cves, fixes, nil
|
||||
}
|
||||
|
||||
// ConvertToModel converts gost model to vuls model
|
||||
|
||||
@@ -39,10 +39,17 @@ func TestDebian_Supported(t *testing.T) {
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "11 is not supported yet",
|
||||
name: "11 is supported",
|
||||
args: args{
|
||||
major: "11",
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "12 is not supported yet",
|
||||
args: args{
|
||||
major: "12",
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/logging"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/knqyf263/gost/db"
|
||||
"github.com/vulsio/gost/db"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/future-architect/vuls/constant"
|
||||
@@ -88,7 +88,7 @@ func newGostDB(cnf config.GostConf) (driver db.DB, locked bool, err error) {
|
||||
if cnf.GetType() == "sqlite3" {
|
||||
path = cnf.GetSQLite3Path()
|
||||
}
|
||||
if driver, locked, err = db.NewDB(cnf.GetType(), path, cnf.GetDebugSQL()); err != nil {
|
||||
if driver, locked, err = db.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), db.Option{}); err != nil {
|
||||
if locked {
|
||||
return nil, true, xerrors.Errorf("gostDB is locked. err: %w", err)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/future-architect/vuls/models"
|
||||
gostmodels "github.com/knqyf263/gost/models"
|
||||
gostmodels "github.com/vulsio/gost/models"
|
||||
)
|
||||
|
||||
func TestSetPackageStates(t *testing.T) {
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/future-architect/vuls/models"
|
||||
gostmodels "github.com/knqyf263/gost/models"
|
||||
gostmodels "github.com/vulsio/gost/models"
|
||||
)
|
||||
|
||||
// Microsoft is Gost client for windows
|
||||
@@ -25,7 +25,11 @@ func (ms Microsoft) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err err
|
||||
for cveID := range r.ScannedCves {
|
||||
cveIDs = append(cveIDs, cveID)
|
||||
}
|
||||
for cveID, msCve := range ms.DBDriver.DB.GetMicrosoftMulti(cveIDs) {
|
||||
msCves, err := ms.DBDriver.DB.GetMicrosoftMulti(cveIDs)
|
||||
if err != nil {
|
||||
return 0, nil
|
||||
}
|
||||
for cveID, msCve := range msCves {
|
||||
if _, ok := r.ScannedCves[cveID]; !ok {
|
||||
continue
|
||||
}
|
||||
@@ -34,7 +38,7 @@ func (ms Microsoft) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err err
|
||||
if v.CveContents == nil {
|
||||
v.CveContents = models.CveContents{}
|
||||
}
|
||||
v.CveContents[models.Microsoft] = append(v.CveContents[models.Microsoft], *cveCont)
|
||||
v.CveContents[models.Microsoft] = []models.CveContent{*cveCont}
|
||||
v.Mitigations = append(v.Mitigations, mitigations...)
|
||||
r.ScannedCves[cveID] = v
|
||||
}
|
||||
|
||||
@@ -13,6 +13,6 @@ type Pseudo struct {
|
||||
}
|
||||
|
||||
// DetectCVEs fills cve information that has in Gost
|
||||
func (pse Pseudo) DetectCVEs(r *models.ScanResult, _ bool) (int, error) {
|
||||
func (pse Pseudo) DetectCVEs(_ *models.ScanResult, _ bool) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/util"
|
||||
gostmodels "github.com/knqyf263/gost/models"
|
||||
gostmodels "github.com/vulsio/gost/models"
|
||||
)
|
||||
|
||||
// RedHat is Gost client for RedHat family linux
|
||||
@@ -45,7 +45,10 @@ func (red RedHat) DetectCVEs(r *models.ScanResult, ignoreWillNotFix bool) (nCVEs
|
||||
}
|
||||
for _, pack := range r.Packages {
|
||||
// CVE-ID: RedhatCVE
|
||||
cves := red.DBDriver.DB.GetUnfixedCvesRedhat(major(r.Release), pack.Name, ignoreWillNotFix)
|
||||
cves, err := red.DBDriver.DB.GetUnfixedCvesRedhat(major(r.Release), pack.Name, ignoreWillNotFix)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for _, cve := range cves {
|
||||
if newly := red.setUnfixedCveToScanResult(&cve, r); newly {
|
||||
nCVEs++
|
||||
@@ -85,7 +88,11 @@ func (red RedHat) fillCvesWithRedHatAPI(r *models.ScanResult) error {
|
||||
if red.DBDriver.DB == nil {
|
||||
return nil
|
||||
}
|
||||
for _, redCve := range red.DBDriver.DB.GetRedhatMulti(cveIDs) {
|
||||
redCves, err := red.DBDriver.DB.GetRedhatMulti(cveIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, redCve := range redCves {
|
||||
if len(redCve.Name) == 0 {
|
||||
continue
|
||||
}
|
||||
@@ -103,7 +110,7 @@ func (red RedHat) setFixedCveToScanResult(cve *gostmodels.RedhatCVE, r *models.S
|
||||
if v.CveContents == nil {
|
||||
v.CveContents = models.NewCveContents(*cveCont)
|
||||
} else {
|
||||
v.CveContents[models.RedHatAPI] = append(v.CveContents[models.RedHatAPI], *cveCont)
|
||||
v.CveContents[models.RedHatAPI] = []models.CveContent{*cveCont}
|
||||
}
|
||||
} else {
|
||||
v = models.VulnInfo{
|
||||
@@ -123,7 +130,7 @@ func (red RedHat) setUnfixedCveToScanResult(cve *gostmodels.RedhatCVE, r *models
|
||||
if v.CveContents == nil {
|
||||
v.CveContents = models.NewCveContents(*cveCont)
|
||||
} else {
|
||||
v.CveContents[models.RedHatAPI] = append(v.CveContents[models.RedHatAPI], *cveCont)
|
||||
v.CveContents[models.RedHatAPI] = []models.CveContent{*cveCont}
|
||||
}
|
||||
} else {
|
||||
v = models.VulnInfo{
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/future-architect/vuls/logging"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/util"
|
||||
gostmodels "github.com/knqyf263/gost/models"
|
||||
gostmodels "github.com/vulsio/gost/models"
|
||||
)
|
||||
|
||||
// Ubuntu is Gost client for Ubuntu
|
||||
@@ -80,7 +80,10 @@ func (ubu Ubuntu) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error
|
||||
return 0, nil
|
||||
}
|
||||
for _, pack := range r.Packages {
|
||||
ubuCves := ubu.DBDriver.DB.GetUnfixedCvesUbuntu(ubuReleaseVer, pack.Name)
|
||||
ubuCves, err := ubu.DBDriver.DB.GetUnfixedCvesUbuntu(ubuReleaseVer, pack.Name)
|
||||
if err != nil {
|
||||
return 0, nil
|
||||
}
|
||||
cves := []models.CveContent{}
|
||||
for _, ubucve := range ubuCves {
|
||||
cves = append(cves, *ubu.ConvertToModel(&ubucve))
|
||||
@@ -94,7 +97,10 @@ func (ubu Ubuntu) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error
|
||||
|
||||
// SrcPack
|
||||
for _, pack := range r.SrcPackages {
|
||||
ubuCves := ubu.DBDriver.DB.GetUnfixedCvesUbuntu(ubuReleaseVer, pack.Name)
|
||||
ubuCves, err := ubu.DBDriver.DB.GetUnfixedCvesUbuntu(ubuReleaseVer, pack.Name)
|
||||
if err != nil {
|
||||
return 0, nil
|
||||
}
|
||||
cves := []models.CveContent{}
|
||||
for _, ubucve := range ubuCves {
|
||||
cves = append(cves, *ubu.ConvertToModel(&ubucve))
|
||||
@@ -116,7 +122,7 @@ func (ubu Ubuntu) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error
|
||||
if v.CveContents == nil {
|
||||
v.CveContents = models.NewCveContents(cve)
|
||||
} else {
|
||||
v.CveContents[models.UbuntuAPI] = append(v.CveContents[models.UbuntuAPI], cve)
|
||||
v.CveContents[models.UbuntuAPI] = []models.CveContent{cve}
|
||||
}
|
||||
} else {
|
||||
v = models.VulnInfo{
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/future-architect/vuls/models"
|
||||
gostmodels "github.com/knqyf263/gost/models"
|
||||
gostmodels "github.com/vulsio/gost/models"
|
||||
)
|
||||
|
||||
func TestUbuntu_Supported(t *testing.T) {
|
||||
|
||||
BIN
img/sponsor/tines.png
Normal file
BIN
img/sponsor/tines.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
@@ -6469,13 +6469,13 @@
|
||||
"cveDict": {
|
||||
"Name": "cveDict",
|
||||
"Type": "sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/go-cve-dictionary/cve.sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-cve-dictionary/cve.sqlite3",
|
||||
"DebugSQL": false
|
||||
},
|
||||
"ovalDict": {
|
||||
"Name": "ovalDict",
|
||||
"Type": "sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/goval-dictionary/oval.sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/goval-dictionary/oval.sqlite3",
|
||||
"DebugSQL": false
|
||||
},
|
||||
"gost": {
|
||||
@@ -6493,7 +6493,7 @@
|
||||
"metasploit": {
|
||||
"Name": "metasploit",
|
||||
"Type": "sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/takuzoo3868/go-msfdb/go-msfdb.sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-msfdb/go-msfdb.sqlite3",
|
||||
"DebugSQL": false
|
||||
}
|
||||
},
|
||||
|
||||
@@ -5569,13 +5569,13 @@
|
||||
"cveDict": {
|
||||
"Name": "cveDict",
|
||||
"Type": "sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/go-cve-dictionary/cve.sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-cve-dictionary/cve.sqlite3",
|
||||
"DebugSQL": false
|
||||
},
|
||||
"ovalDict": {
|
||||
"Name": "ovalDict",
|
||||
"Type": "sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/goval-dictionary/oval.sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/goval-dictionary/oval.sqlite3",
|
||||
"DebugSQL": false
|
||||
},
|
||||
"gost": {
|
||||
@@ -5593,7 +5593,7 @@
|
||||
"metasploit": {
|
||||
"Name": "metasploit",
|
||||
"Type": "sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/takuzoo3868/go-msfdb/go-msfdb.sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-msfdb/go-msfdb.sqlite3",
|
||||
"DebugSQL": false
|
||||
}
|
||||
},
|
||||
|
||||
@@ -5566,13 +5566,13 @@
|
||||
"cveDict": {
|
||||
"Name": "cveDict",
|
||||
"Type": "sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/go-cve-dictionary/cve.sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-cve-dictionary/cve.sqlite3",
|
||||
"DebugSQL": false
|
||||
},
|
||||
"ovalDict": {
|
||||
"Name": "ovalDict",
|
||||
"Type": "sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/goval-dictionary/oval.sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/goval-dictionary/oval.sqlite3",
|
||||
"DebugSQL": false
|
||||
},
|
||||
"gost": {
|
||||
@@ -5590,7 +5590,7 @@
|
||||
"metasploit": {
|
||||
"Name": "metasploit",
|
||||
"Type": "sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/takuzoo3868/go-msfdb/go-msfdb.sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-msfdb/go-msfdb.sqlite3",
|
||||
"DebugSQL": false
|
||||
}
|
||||
},
|
||||
|
||||
@@ -5093,13 +5093,13 @@
|
||||
"cveDict": {
|
||||
"Name": "cveDict",
|
||||
"Type": "sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/go-cve-dictionary/cve.sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-cve-dictionary/cve.sqlite3",
|
||||
"DebugSQL": false
|
||||
},
|
||||
"ovalDict": {
|
||||
"Name": "ovalDict",
|
||||
"Type": "sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/goval-dictionary/oval.sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/goval-dictionary/oval.sqlite3",
|
||||
"DebugSQL": false
|
||||
},
|
||||
"gost": {
|
||||
@@ -5117,7 +5117,7 @@
|
||||
"metasploit": {
|
||||
"Name": "metasploit",
|
||||
"Type": "sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/takuzoo3868/go-msfdb/go-msfdb.sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-msfdb/go-msfdb.sqlite3",
|
||||
"DebugSQL": false
|
||||
}
|
||||
},
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"version": "",
|
||||
"rebootRequired": false
|
||||
},
|
||||
"enabledDnfModules": ["nodejs:12"],
|
||||
"packages": {
|
||||
"NetworkManager": {
|
||||
"name": "NetworkManager",
|
||||
@@ -5906,6 +5907,15 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"nodejs": {
|
||||
"name": "nodejs",
|
||||
"version": "1:12.14.1",
|
||||
"release": "1.module+el8.1.0+5466+30f75628",
|
||||
"newVersion": "1:12.14.1",
|
||||
"newRelease": "1.module+el8.1.0+5466+30f75629",
|
||||
"arch": "x86_64",
|
||||
"repository": "rhel-8-appstream-rhui-rpms"
|
||||
},
|
||||
"sed": {
|
||||
"name": "sed",
|
||||
"version": "4.5",
|
||||
@@ -6861,13 +6871,13 @@
|
||||
"cveDict": {
|
||||
"Name": "cveDict",
|
||||
"Type": "sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/go-cve-dictionary/cve.sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-cve-dictionary/cve.sqlite3",
|
||||
"DebugSQL": false
|
||||
},
|
||||
"ovalDict": {
|
||||
"Name": "ovalDict",
|
||||
"Type": "sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/goval-dictionary/oval.sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/goval-dictionary/oval.sqlite3",
|
||||
"DebugSQL": false
|
||||
},
|
||||
"gost": {
|
||||
@@ -6885,7 +6895,7 @@
|
||||
"metasploit": {
|
||||
"Name": "metasploit",
|
||||
"Type": "sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/takuzoo3868/go-msfdb/go-msfdb.sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-msfdb/go-msfdb.sqlite3",
|
||||
"DebugSQL": false
|
||||
}
|
||||
},
|
||||
|
||||
@@ -8544,13 +8544,13 @@
|
||||
"cveDict": {
|
||||
"Name": "cveDict",
|
||||
"Type": "sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/go-cve-dictionary/cve.sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-cve-dictionary/cve.sqlite3",
|
||||
"DebugSQL": false
|
||||
},
|
||||
"ovalDict": {
|
||||
"Name": "ovalDict",
|
||||
"Type": "sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/goval-dictionary/oval.sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/goval-dictionary/oval.sqlite3",
|
||||
"DebugSQL": false
|
||||
},
|
||||
"gost": {
|
||||
@@ -8568,7 +8568,7 @@
|
||||
"metasploit": {
|
||||
"Name": "metasploit",
|
||||
"Type": "sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/takuzoo3868/go-msfdb/go-msfdb.sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-msfdb/go-msfdb.sqlite3",
|
||||
"DebugSQL": false
|
||||
}
|
||||
},
|
||||
|
||||
@@ -8494,13 +8494,13 @@
|
||||
"cveDict": {
|
||||
"Name": "cveDict",
|
||||
"Type": "sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/go-cve-dictionary/cve.sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-cve-dictionary/cve.sqlite3",
|
||||
"DebugSQL": false
|
||||
},
|
||||
"ovalDict": {
|
||||
"Name": "ovalDict",
|
||||
"Type": "sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/goval-dictionary/oval.sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/goval-dictionary/oval.sqlite3",
|
||||
"DebugSQL": false
|
||||
},
|
||||
"gost": {
|
||||
@@ -8518,7 +8518,7 @@
|
||||
"metasploit": {
|
||||
"Name": "metasploit",
|
||||
"Type": "sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/takuzoo3868/go-msfdb/go-msfdb.sqlite3",
|
||||
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-msfdb/go-msfdb.sqlite3",
|
||||
"DebugSQL": false
|
||||
}
|
||||
},
|
||||
|
||||
@@ -22,6 +22,7 @@ type LogOpts struct {
|
||||
DebugSQL bool `json:"debugSQL,omitempty"`
|
||||
LogToFile bool `json:"logToFile,omitempty"`
|
||||
LogDir string `json:"logDir,omitempty"`
|
||||
LogJSON bool `json:"logJSON"`
|
||||
Quiet bool `json:"quiet,omitempty"`
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,20 @@ type CveContents map[CveContentType][]CveContent
|
||||
func NewCveContents(conts ...CveContent) CveContents {
|
||||
m := CveContents{}
|
||||
for _, cont := range conts {
|
||||
m[cont.Type] = append(m[cont.Type], cont)
|
||||
if cont.Type == Jvn {
|
||||
found := false
|
||||
for _, cveCont := range m[cont.Type] {
|
||||
if cont.SourceLink == cveCont.SourceLink {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
m[cont.Type] = append(m[cont.Type], cont)
|
||||
}
|
||||
} else {
|
||||
m[cont.Type] = []CveContent{cont}
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
@@ -229,6 +242,7 @@ func (v CveContents) UniqCweIDs(myFamily string) (values []CveContentStr) {
|
||||
return values
|
||||
}
|
||||
|
||||
// Sort elements for integration-testing
|
||||
func (v CveContents) Sort() {
|
||||
for contType, contents := range v {
|
||||
// CVSS3 desc, CVSS2 desc, SourceLink asc
|
||||
|
||||
@@ -10,19 +10,18 @@ import (
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
"golang.org/x/xerrors"
|
||||
// "github.com/aquasecurity/go-dep-parser/pkg/types"
|
||||
)
|
||||
|
||||
// LibraryScanners is an array of LibraryScanner
|
||||
type LibraryScanners []LibraryScanner
|
||||
|
||||
// Find : find by name
|
||||
func (lss LibraryScanners) Find(path, name string) map[string]types.Library {
|
||||
filtered := map[string]types.Library{}
|
||||
func (lss LibraryScanners) Find(path, name string) map[string]Library {
|
||||
filtered := map[string]Library{}
|
||||
for _, ls := range lss {
|
||||
for _, lib := range ls.Libs {
|
||||
if ls.Path == path && lib.Name == name {
|
||||
filtered[ls.Path] = lib
|
||||
if ls.LockfilePath == path && lib.Name == name {
|
||||
filtered[ls.LockfilePath] = lib
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -41,8 +40,20 @@ func (lss LibraryScanners) Total() (total int) {
|
||||
// LibraryScanner has libraries information
|
||||
type LibraryScanner struct {
|
||||
Type string
|
||||
Path string
|
||||
Libs []types.Library
|
||||
Libs []Library
|
||||
|
||||
// The path to the Lockfile is stored.
|
||||
LockfilePath string `json:"path,omitempty"`
|
||||
}
|
||||
|
||||
// Library holds the attribute of a package library
|
||||
type Library struct {
|
||||
Name string
|
||||
Version string
|
||||
|
||||
// The Path to the library in the container image. Empty string when Lockfile scan.
|
||||
// This field is used to convert the result JSON of a `trivy image` using trivy-to-vuls.
|
||||
FilePath string
|
||||
}
|
||||
|
||||
// Scan : scan target library
|
||||
@@ -93,7 +104,7 @@ func (s LibraryScanner) getVulnDetail(tvuln types.DetectedVulnerability) (vinfo
|
||||
Key: s.GetLibraryKey(),
|
||||
Name: tvuln.PkgName,
|
||||
FixedIn: tvuln.FixedVersion,
|
||||
Path: s.Path,
|
||||
Path: s.LockfilePath,
|
||||
},
|
||||
}
|
||||
return vinfo, nil
|
||||
@@ -106,33 +117,39 @@ func getCveContents(cveID string, vul trivyDBTypes.Vulnerability) (contents map[
|
||||
refs = append(refs, Reference{Source: "trivy", Link: refURL})
|
||||
}
|
||||
|
||||
content := CveContent{
|
||||
Type: Trivy,
|
||||
CveID: cveID,
|
||||
Title: vul.Title,
|
||||
Summary: vul.Description,
|
||||
Cvss3Severity: string(vul.Severity),
|
||||
References: refs,
|
||||
contents[Trivy] = []CveContent{
|
||||
{
|
||||
Type: Trivy,
|
||||
CveID: cveID,
|
||||
Title: vul.Title,
|
||||
Summary: vul.Description,
|
||||
Cvss3Severity: string(vul.Severity),
|
||||
References: refs,
|
||||
},
|
||||
}
|
||||
contents[Trivy] = append(contents[Trivy], content)
|
||||
return contents
|
||||
}
|
||||
|
||||
// LibraryMap is filename and library type
|
||||
var LibraryMap = map[string]string{
|
||||
"package-lock.json": "node",
|
||||
"yarn.lock": "node",
|
||||
"Gemfile.lock": "ruby",
|
||||
"Cargo.lock": "rust",
|
||||
"composer.lock": "php",
|
||||
"Pipfile.lock": "python",
|
||||
"poetry.lock": "python",
|
||||
"go.sum": "gomod",
|
||||
"package-lock.json": "node",
|
||||
"yarn.lock": "node",
|
||||
"Gemfile.lock": "ruby",
|
||||
"Cargo.lock": "rust",
|
||||
"composer.lock": "php",
|
||||
"Pipfile.lock": "python",
|
||||
"poetry.lock": "python",
|
||||
"packages.lock.json": ".net",
|
||||
"go.sum": "gomod",
|
||||
}
|
||||
|
||||
// GetLibraryKey returns target library key
|
||||
func (s LibraryScanner) GetLibraryKey() string {
|
||||
fileName := filepath.Base(s.Path)
|
||||
fileName := filepath.Base(s.LockfilePath)
|
||||
switch s.Type {
|
||||
case "jar", "war", "ear":
|
||||
return "java"
|
||||
}
|
||||
return LibraryMap[fileName]
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,6 @@ package models
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
)
|
||||
|
||||
func TestLibraryScanners_Find(t *testing.T) {
|
||||
@@ -16,14 +14,14 @@ func TestLibraryScanners_Find(t *testing.T) {
|
||||
name string
|
||||
lss LibraryScanners
|
||||
args args
|
||||
want map[string]types.Library
|
||||
want map[string]Library
|
||||
}{
|
||||
{
|
||||
name: "single file",
|
||||
lss: LibraryScanners{
|
||||
{
|
||||
Path: "/pathA",
|
||||
Libs: []types.Library{
|
||||
LockfilePath: "/pathA",
|
||||
Libs: []Library{
|
||||
{
|
||||
Name: "libA",
|
||||
Version: "1.0.0",
|
||||
@@ -32,7 +30,7 @@ func TestLibraryScanners_Find(t *testing.T) {
|
||||
},
|
||||
},
|
||||
args: args{"/pathA", "libA"},
|
||||
want: map[string]types.Library{
|
||||
want: map[string]Library{
|
||||
"/pathA": {
|
||||
Name: "libA",
|
||||
Version: "1.0.0",
|
||||
@@ -43,8 +41,8 @@ func TestLibraryScanners_Find(t *testing.T) {
|
||||
name: "multi file",
|
||||
lss: LibraryScanners{
|
||||
{
|
||||
Path: "/pathA",
|
||||
Libs: []types.Library{
|
||||
LockfilePath: "/pathA",
|
||||
Libs: []Library{
|
||||
{
|
||||
Name: "libA",
|
||||
Version: "1.0.0",
|
||||
@@ -52,8 +50,8 @@ func TestLibraryScanners_Find(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
Path: "/pathB",
|
||||
Libs: []types.Library{
|
||||
LockfilePath: "/pathB",
|
||||
Libs: []Library{
|
||||
{
|
||||
Name: "libA",
|
||||
Version: "1.0.5",
|
||||
@@ -62,7 +60,7 @@ func TestLibraryScanners_Find(t *testing.T) {
|
||||
},
|
||||
},
|
||||
args: args{"/pathA", "libA"},
|
||||
want: map[string]types.Library{
|
||||
want: map[string]Library{
|
||||
"/pathA": {
|
||||
Name: "libA",
|
||||
Version: "1.0.0",
|
||||
@@ -73,8 +71,8 @@ func TestLibraryScanners_Find(t *testing.T) {
|
||||
name: "miss",
|
||||
lss: LibraryScanners{
|
||||
{
|
||||
Path: "/pathA",
|
||||
Libs: []types.Library{
|
||||
LockfilePath: "/pathA",
|
||||
Libs: []Library{
|
||||
{
|
||||
Name: "libA",
|
||||
Version: "1.0.0",
|
||||
@@ -83,7 +81,7 @@ func TestLibraryScanners_Find(t *testing.T) {
|
||||
},
|
||||
},
|
||||
args: args{"/pathA", "libB"},
|
||||
want: map[string]types.Library{},
|
||||
want: map[string]Library{},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
|
||||
@@ -72,6 +72,31 @@ func (ps Packages) FindByFQPN(nameVerRel string) (*Package, error) {
|
||||
return nil, xerrors.Errorf("Failed to find the package: %s", nameVerRel)
|
||||
}
|
||||
|
||||
// ResolveReverseDepsRecursively resolve and set reverse dependencies for each packages recursively
|
||||
func (ps Packages) ResolveReverseDepsRecursively() {
|
||||
for name, pkg := range ps {
|
||||
depsmap := ps.resolveReverseDeps(pkg, map[string]struct{}{})
|
||||
delete(depsmap, pkg.Name)
|
||||
for depname := range depsmap {
|
||||
pkg.ReverseDependenciesRecursively = append(pkg.ReverseDependenciesRecursively, depname)
|
||||
}
|
||||
ps[name] = pkg
|
||||
}
|
||||
}
|
||||
|
||||
func (ps Packages) resolveReverseDeps(pkg Package, acc map[string]struct{}) map[string]struct{} {
|
||||
acc[pkg.Name] = struct{}{}
|
||||
for _, name := range pkg.ReverseDependencies {
|
||||
if _, ok := acc[name]; ok {
|
||||
continue
|
||||
}
|
||||
if p, ok := ps[name]; ok {
|
||||
acc = ps.resolveReverseDeps(p, acc)
|
||||
}
|
||||
}
|
||||
return acc
|
||||
}
|
||||
|
||||
// Package has installed binary packages.
|
||||
type Package struct {
|
||||
Name string `json:"name"`
|
||||
@@ -84,6 +109,18 @@ type Package struct {
|
||||
Changelog *Changelog `json:"changelog,omitempty"`
|
||||
AffectedProcs []AffectedProcess `json:",omitempty"`
|
||||
NeedRestartProcs []NeedRestartProcess `json:",omitempty"`
|
||||
|
||||
// Dependencies are dependent packages
|
||||
Dependencies []string `json:",omitempty"`
|
||||
|
||||
// ReverseDependencies are packages which depend on this package
|
||||
ReverseDependencies []string `json:",omitempty"`
|
||||
|
||||
// ReverseDependencies are packages which depend on this package
|
||||
ReverseDependenciesRecursively []string `json:",omitempty"`
|
||||
|
||||
// DependenciesForUpdate are packages that needs to be updated together
|
||||
DependenciesForUpdate []string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// FQPN returns Fully-Qualified-Package-Name
|
||||
|
||||
@@ -428,3 +428,166 @@ func Test_NewPortStat(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPackages_resolveReverseDeps(t *testing.T) {
|
||||
type args struct {
|
||||
pkg Package
|
||||
acc map[string]struct{}
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
ps Packages
|
||||
args args
|
||||
want map[string]struct{}
|
||||
}{
|
||||
{
|
||||
name: "",
|
||||
ps: map[string]Package{
|
||||
"pkgA": {
|
||||
Name: "pkgA",
|
||||
ReverseDependencies: []string{"pkgB"},
|
||||
},
|
||||
"pkgB": {
|
||||
Name: "pkgB",
|
||||
ReverseDependencies: []string{"pkgC"},
|
||||
},
|
||||
"pkgC": {
|
||||
Name: "pkgC",
|
||||
ReverseDependencies: []string{""},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
pkg: Package{
|
||||
Name: "pkgA",
|
||||
ReverseDependencies: []string{"pkgB"},
|
||||
},
|
||||
acc: map[string]struct{}{},
|
||||
},
|
||||
want: map[string]struct{}{
|
||||
"pkgA": {},
|
||||
"pkgB": {},
|
||||
"pkgC": {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
ps: map[string]Package{
|
||||
"pkgA": {
|
||||
Name: "pkgA",
|
||||
ReverseDependencies: []string{"pkgB"},
|
||||
},
|
||||
"pkgB": {
|
||||
Name: "pkgB",
|
||||
ReverseDependencies: []string{"pkgA", "pkgC"},
|
||||
},
|
||||
"pkgC": {
|
||||
Name: "pkgC",
|
||||
ReverseDependencies: []string{"pkgB"},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
pkg: Package{
|
||||
Name: "pkgA",
|
||||
ReverseDependencies: []string{"pkgB"},
|
||||
},
|
||||
acc: map[string]struct{}{},
|
||||
},
|
||||
want: map[string]struct{}{
|
||||
"pkgA": {},
|
||||
"pkgB": {},
|
||||
"pkgC": {},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.ps.resolveReverseDeps(tt.args.pkg, tt.args.acc); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Packages.resolveReverseDeps() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPackages_resolveReverseDepsRecursively(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ps Packages
|
||||
want Packages
|
||||
}{
|
||||
{
|
||||
name: "",
|
||||
ps: map[string]Package{
|
||||
"pkgA": {
|
||||
Name: "pkgA",
|
||||
ReverseDependencies: []string{"pkgB"},
|
||||
},
|
||||
"pkgB": {
|
||||
Name: "pkgB",
|
||||
ReverseDependencies: []string{"pkgC"},
|
||||
},
|
||||
"pkgC": {
|
||||
Name: "pkgC",
|
||||
ReverseDependencies: []string{""},
|
||||
},
|
||||
},
|
||||
want: map[string]Package{
|
||||
"pkgA": {
|
||||
Name: "pkgA",
|
||||
ReverseDependencies: []string{"pkgB"},
|
||||
ReverseDependenciesRecursively: []string{"pkgB", "pkgC"},
|
||||
},
|
||||
"pkgB": {
|
||||
Name: "pkgB",
|
||||
ReverseDependencies: []string{"pkgC"},
|
||||
ReverseDependenciesRecursively: []string{"pkgC"},
|
||||
},
|
||||
"pkgC": {
|
||||
Name: "pkgC",
|
||||
ReverseDependencies: []string{""},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
ps: map[string]Package{
|
||||
"pkgA": {
|
||||
Name: "pkgA",
|
||||
ReverseDependencies: []string{"pkgB"},
|
||||
},
|
||||
"pkgB": {
|
||||
Name: "pkgB",
|
||||
ReverseDependencies: []string{"pkgA", "pkgC"},
|
||||
},
|
||||
"pkgC": {
|
||||
Name: "pkgC",
|
||||
ReverseDependencies: []string{"pkgB"},
|
||||
},
|
||||
},
|
||||
want: map[string]Package{
|
||||
"pkgA": {
|
||||
Name: "pkgA",
|
||||
ReverseDependencies: []string{"pkgB"},
|
||||
ReverseDependenciesRecursively: []string{"pkgB", "pkgC"},
|
||||
},
|
||||
"pkgB": {
|
||||
Name: "pkgB",
|
||||
ReverseDependencies: []string{"pkgA", "pkgC"},
|
||||
ReverseDependenciesRecursively: []string{"pkgA", "pkgC"},
|
||||
},
|
||||
"pkgC": {
|
||||
Name: "pkgC",
|
||||
ReverseDependencies: []string{"pkgB"},
|
||||
ReverseDependenciesRecursively: []string{"pkgA", "pkgB"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.ps.ResolveReverseDepsRecursively()
|
||||
if !reflect.DeepEqual(tt.ps, tt.want) {
|
||||
t.Errorf("Packages.resolveReverseDepsRecursively() = \n%+v, want \n%+v", tt.ps, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ package models
|
||||
import (
|
||||
"strings"
|
||||
|
||||
cvedict "github.com/kotakanbe/go-cve-dictionary/models"
|
||||
cvedict "github.com/vulsio/go-cve-dictionary/models"
|
||||
)
|
||||
|
||||
// ConvertJvnToModel convert JVN to CveContent
|
||||
|
||||
@@ -28,43 +28,46 @@ func (v VulnInfos) Find(f func(VulnInfo) bool) VulnInfos {
|
||||
}
|
||||
|
||||
// FilterByCvssOver return scored vulnerabilities
|
||||
func (v VulnInfos) FilterByCvssOver(over float64) VulnInfos {
|
||||
func (v VulnInfos) FilterByCvssOver(over float64) (_ VulnInfos, nFiltered int) {
|
||||
return v.Find(func(v VulnInfo) bool {
|
||||
if over <= v.MaxCvssScore().Value.Score {
|
||||
return true
|
||||
}
|
||||
nFiltered++
|
||||
return false
|
||||
})
|
||||
}), nFiltered
|
||||
}
|
||||
|
||||
// FilterByConfidenceOver scored vulnerabilities
|
||||
func (v VulnInfos) FilterByConfidenceOver(over int) VulnInfos {
|
||||
func (v VulnInfos) FilterByConfidenceOver(over int) (_ VulnInfos, nFiltered int) {
|
||||
return v.Find(func(v VulnInfo) bool {
|
||||
for _, c := range v.Confidences {
|
||||
if over <= c.Score {
|
||||
return true
|
||||
}
|
||||
}
|
||||
nFiltered++
|
||||
return false
|
||||
})
|
||||
}), nFiltered
|
||||
}
|
||||
|
||||
// FilterIgnoreCves filter function.
|
||||
func (v VulnInfos) FilterIgnoreCves(ignoreCveIDs []string) VulnInfos {
|
||||
func (v VulnInfos) FilterIgnoreCves(ignoreCveIDs []string) (_ VulnInfos, nFiltered int) {
|
||||
return v.Find(func(v VulnInfo) bool {
|
||||
for _, c := range ignoreCveIDs {
|
||||
if v.CveID == c {
|
||||
nFiltered++
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}), nFiltered
|
||||
}
|
||||
|
||||
// FilterUnfixed filter unfixed CVE-IDs
|
||||
func (v VulnInfos) FilterUnfixed(ignoreUnfixed bool) VulnInfos {
|
||||
func (v VulnInfos) FilterUnfixed(ignoreUnfixed bool) (_ VulnInfos, nFiltered int) {
|
||||
if !ignoreUnfixed {
|
||||
return v
|
||||
return v, 0
|
||||
}
|
||||
return v.Find(func(v VulnInfo) bool {
|
||||
// Report cves detected by CPE because Vuls can't know 'fixed' or 'unfixed'
|
||||
@@ -75,24 +78,26 @@ func (v VulnInfos) FilterUnfixed(ignoreUnfixed bool) VulnInfos {
|
||||
for _, p := range v.AffectedPackages {
|
||||
NotFixedAll = NotFixedAll && p.NotFixedYet
|
||||
}
|
||||
if NotFixedAll {
|
||||
nFiltered++
|
||||
}
|
||||
return !NotFixedAll
|
||||
})
|
||||
}), nFiltered
|
||||
}
|
||||
|
||||
// FilterIgnorePkgs is filter function.
|
||||
func (v VulnInfos) FilterIgnorePkgs(ignorePkgsRegexps []string) VulnInfos {
|
||||
func (v VulnInfos) FilterIgnorePkgs(ignorePkgsRegexps []string) (_ VulnInfos, nFiltered int) {
|
||||
regexps := []*regexp.Regexp{}
|
||||
for _, pkgRegexp := range ignorePkgsRegexps {
|
||||
re, err := regexp.Compile(pkgRegexp)
|
||||
if err != nil {
|
||||
logging.Log.Warnf("Failed to parse %s. err: %+v", pkgRegexp, err)
|
||||
continue
|
||||
} else {
|
||||
regexps = append(regexps, re)
|
||||
}
|
||||
regexps = append(regexps, re)
|
||||
}
|
||||
if len(regexps) == 0 {
|
||||
return v
|
||||
return v, 0
|
||||
}
|
||||
|
||||
return v.Find(func(v VulnInfo) bool {
|
||||
@@ -110,19 +115,21 @@ func (v VulnInfos) FilterIgnorePkgs(ignorePkgsRegexps []string) VulnInfos {
|
||||
return true
|
||||
}
|
||||
}
|
||||
nFiltered++
|
||||
return false
|
||||
})
|
||||
}), nFiltered
|
||||
}
|
||||
|
||||
// FindScoredVulns return scored vulnerabilities
|
||||
func (v VulnInfos) FindScoredVulns() VulnInfos {
|
||||
func (v VulnInfos) FindScoredVulns() (_ VulnInfos, nFiltered int) {
|
||||
return v.Find(func(vv VulnInfo) bool {
|
||||
if 0 < vv.MaxCvss2Score().Value.Score ||
|
||||
0 < vv.MaxCvss3Score().Value.Score {
|
||||
return true
|
||||
}
|
||||
nFiltered++
|
||||
return false
|
||||
})
|
||||
}), nFiltered
|
||||
}
|
||||
|
||||
// ToSortedSlice returns slice of VulnInfos that is sorted by Score, CVE-ID
|
||||
@@ -893,7 +900,7 @@ const (
|
||||
// ChangelogExactMatchStr :
|
||||
ChangelogExactMatchStr = "ChangelogExactMatch"
|
||||
|
||||
// ChangelogRoughMatch :
|
||||
// ChangelogRoughMatchStr :
|
||||
ChangelogRoughMatchStr = "ChangelogRoughMatch"
|
||||
|
||||
// GitHubMatchStr :
|
||||
|
||||
@@ -1247,10 +1247,11 @@ func TestVulnInfos_FilterByCvssOver(t *testing.T) {
|
||||
over float64
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
v VulnInfos
|
||||
args args
|
||||
want VulnInfos
|
||||
name string
|
||||
v VulnInfos
|
||||
args args
|
||||
want VulnInfos
|
||||
nwant int
|
||||
}{
|
||||
{
|
||||
name: "over 7.0",
|
||||
@@ -1296,6 +1297,7 @@ func TestVulnInfos_FilterByCvssOver(t *testing.T) {
|
||||
),
|
||||
},
|
||||
},
|
||||
nwant: 1,
|
||||
want: VulnInfos{
|
||||
"CVE-2017-0001": {
|
||||
CveID: "CVE-2017-0001",
|
||||
@@ -1404,9 +1406,13 @@ func TestVulnInfos_FilterByCvssOver(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.v.FilterByCvssOver(tt.args.over); !reflect.DeepEqual(got, tt.want) {
|
||||
got, ngot := tt.v.FilterByCvssOver(tt.args.over)
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("VulnInfos.FindByCvssOver() = %v, want %v", got, tt.want)
|
||||
}
|
||||
if ngot != tt.nwant {
|
||||
t.Errorf("VulnInfos.FindByCvssOver() = %d, want %d", ngot, tt.nwant)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1416,10 +1422,11 @@ func TestVulnInfos_FilterIgnoreCves(t *testing.T) {
|
||||
ignoreCveIDs []string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
v VulnInfos
|
||||
args args
|
||||
want VulnInfos
|
||||
name string
|
||||
v VulnInfos
|
||||
args args
|
||||
want VulnInfos
|
||||
nwant int
|
||||
}{
|
||||
{
|
||||
name: "filter ignored",
|
||||
@@ -1435,6 +1442,7 @@ func TestVulnInfos_FilterIgnoreCves(t *testing.T) {
|
||||
CveID: "CVE-2017-0003",
|
||||
},
|
||||
},
|
||||
nwant: 1,
|
||||
want: VulnInfos{
|
||||
"CVE-2017-0001": {
|
||||
CveID: "CVE-2017-0001",
|
||||
@@ -1447,9 +1455,13 @@ func TestVulnInfos_FilterIgnoreCves(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.v.FilterIgnoreCves(tt.args.ignoreCveIDs); !reflect.DeepEqual(got, tt.want) {
|
||||
got, ngot := tt.v.FilterIgnoreCves(tt.args.ignoreCveIDs)
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("VulnInfos.FindIgnoreCves() = %v, want %v", got, tt.want)
|
||||
}
|
||||
if ngot != tt.nwant {
|
||||
t.Errorf("VulnInfos.FindByCvssOver() = %d, want %d", ngot, tt.nwant)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1459,10 +1471,11 @@ func TestVulnInfos_FilterUnfixed(t *testing.T) {
|
||||
ignoreUnfixed bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
v VulnInfos
|
||||
args args
|
||||
want VulnInfos
|
||||
name string
|
||||
v VulnInfos
|
||||
args args
|
||||
want VulnInfos
|
||||
nwant int
|
||||
}{
|
||||
{
|
||||
name: "filter ok",
|
||||
@@ -1500,6 +1513,7 @@ func TestVulnInfos_FilterUnfixed(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
nwant: 1,
|
||||
want: VulnInfos{
|
||||
"CVE-2017-0002": {
|
||||
CveID: "CVE-2017-0002",
|
||||
@@ -1528,9 +1542,13 @@ func TestVulnInfos_FilterUnfixed(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.v.FilterUnfixed(tt.args.ignoreUnfixed); !reflect.DeepEqual(got, tt.want) {
|
||||
got, ngot := tt.v.FilterUnfixed(tt.args.ignoreUnfixed)
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("VulnInfos.FilterUnfixed() = %v, want %v", got, tt.want)
|
||||
}
|
||||
if ngot != tt.nwant {
|
||||
t.Errorf("VulnInfos.FindByCvssOver() = %d, want %d", ngot, tt.nwant)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1540,10 +1558,11 @@ func TestVulnInfos_FilterIgnorePkgs(t *testing.T) {
|
||||
ignorePkgsRegexps []string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
v VulnInfos
|
||||
args args
|
||||
want VulnInfos
|
||||
name string
|
||||
v VulnInfos
|
||||
args args
|
||||
want VulnInfos
|
||||
nwant int
|
||||
}{
|
||||
{
|
||||
name: "filter pkgs 1",
|
||||
@@ -1559,6 +1578,7 @@ func TestVulnInfos_FilterIgnorePkgs(t *testing.T) {
|
||||
CveID: "CVE-2017-0002",
|
||||
},
|
||||
},
|
||||
nwant: 1,
|
||||
want: VulnInfos{
|
||||
"CVE-2017-0002": {
|
||||
CveID: "CVE-2017-0002",
|
||||
@@ -1577,6 +1597,7 @@ func TestVulnInfos_FilterIgnorePkgs(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
nwant: 0,
|
||||
want: VulnInfos{
|
||||
"CVE-2017-0001": {
|
||||
CveID: "CVE-2017-0001",
|
||||
@@ -1599,14 +1620,19 @@ func TestVulnInfos_FilterIgnorePkgs(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
want: VulnInfos{},
|
||||
nwant: 1,
|
||||
want: VulnInfos{},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.v.FilterIgnorePkgs(tt.args.ignorePkgsRegexps); !reflect.DeepEqual(got, tt.want) {
|
||||
got, ngot := tt.v.FilterIgnorePkgs(tt.args.ignorePkgsRegexps)
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("VulnInfos.FilterIgnorePkgs() = %v, want %v", got, tt.want)
|
||||
}
|
||||
if ngot != tt.nwant {
|
||||
t.Errorf("VulnInfos.FilterIgnorePkgs() = %d, want %d", ngot, tt.nwant)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1616,10 +1642,11 @@ func TestVulnInfos_FilterByConfidenceOver(t *testing.T) {
|
||||
over int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
v VulnInfos
|
||||
args args
|
||||
want VulnInfos
|
||||
name string
|
||||
v VulnInfos
|
||||
args args
|
||||
want VulnInfos
|
||||
nwant int
|
||||
}{
|
||||
{
|
||||
name: "over 0",
|
||||
@@ -1650,7 +1677,8 @@ func TestVulnInfos_FilterByConfidenceOver(t *testing.T) {
|
||||
args: args{
|
||||
over: 20,
|
||||
},
|
||||
want: map[string]VulnInfo{},
|
||||
nwant: 1,
|
||||
want: map[string]VulnInfo{},
|
||||
},
|
||||
{
|
||||
name: "over 100",
|
||||
@@ -1679,9 +1707,13 @@ func TestVulnInfos_FilterByConfidenceOver(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.v.FilterByConfidenceOver(tt.args.over); !reflect.DeepEqual(got, tt.want) {
|
||||
got, ngot := tt.v.FilterByConfidenceOver(tt.args.over)
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("VulnInfos.FilterByConfidenceOver() = %v, want %v", got, tt.want)
|
||||
}
|
||||
if ngot != tt.nwant {
|
||||
t.Errorf("VulnInfos.FilterByConfidenceOver() = %d, want %d", ngot, tt.nwant)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ func (o Alpine) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
driver, err := newOvalDB(o.Cnf, r.Family)
|
||||
driver, err := newOvalDB(o.Cnf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -54,8 +54,8 @@ func (o Alpine) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
|
||||
return len(relatedDefs.entries), nil
|
||||
}
|
||||
|
||||
func (o Alpine) update(r *models.ScanResult, defPacks defPacks) {
|
||||
cveID := defPacks.def.Advisory.Cves[0].CveID
|
||||
func (o Alpine) update(r *models.ScanResult, defpacks defPacks) {
|
||||
cveID := defpacks.def.Advisory.Cves[0].CveID
|
||||
vinfo, ok := r.ScannedCves[cveID]
|
||||
if !ok {
|
||||
logging.Log.Debugf("%s is newly detected by OVAL", cveID)
|
||||
@@ -65,7 +65,7 @@ func (o Alpine) update(r *models.ScanResult, defPacks defPacks) {
|
||||
}
|
||||
}
|
||||
|
||||
vinfo.AffectedPackages = defPacks.toPackStatuses()
|
||||
vinfo.AffectedPackages = defpacks.toPackStatuses()
|
||||
vinfo.AffectedPackages.Sort()
|
||||
r.ScannedCves[cveID] = vinfo
|
||||
}
|
||||
|
||||
147
oval/debian.go
147
oval/debian.go
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/future-architect/vuls/logging"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/util"
|
||||
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
|
||||
ovalmodels "github.com/vulsio/goval-dictionary/models"
|
||||
)
|
||||
|
||||
// DebianBase is the base struct of Debian and Ubuntu
|
||||
@@ -20,73 +20,75 @@ type DebianBase struct {
|
||||
Base
|
||||
}
|
||||
|
||||
func (o DebianBase) update(r *models.ScanResult, defPacks defPacks) {
|
||||
ovalContent := *o.convertToModel(&defPacks.def)
|
||||
ovalContent.Type = models.NewCveContentType(o.family)
|
||||
vinfo, ok := r.ScannedCves[defPacks.def.Debian.CveID]
|
||||
if !ok {
|
||||
logging.Log.Debugf("%s is newly detected by OVAL", defPacks.def.Debian.CveID)
|
||||
vinfo = models.VulnInfo{
|
||||
CveID: defPacks.def.Debian.CveID,
|
||||
Confidences: []models.Confidence{models.OvalMatch},
|
||||
CveContents: models.NewCveContents(ovalContent),
|
||||
func (o DebianBase) update(r *models.ScanResult, defpacks defPacks) {
|
||||
for _, cve := range defpacks.def.Advisory.Cves {
|
||||
ovalContent := o.convertToModel(cve.CveID, &defpacks.def)
|
||||
if ovalContent == nil {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
cveContents := vinfo.CveContents
|
||||
ctype := models.NewCveContentType(o.family)
|
||||
if _, ok := vinfo.CveContents[ctype]; ok {
|
||||
logging.Log.Debugf("%s OVAL will be overwritten",
|
||||
defPacks.def.Debian.CveID)
|
||||
vinfo, ok := r.ScannedCves[cve.CveID]
|
||||
if !ok {
|
||||
logging.Log.Debugf("%s is newly detected by OVAL", cve.CveID)
|
||||
vinfo = models.VulnInfo{
|
||||
CveID: cve.CveID,
|
||||
Confidences: []models.Confidence{models.OvalMatch},
|
||||
CveContents: models.NewCveContents(*ovalContent),
|
||||
}
|
||||
} else {
|
||||
logging.Log.Debugf("%s is also detected by OVAL",
|
||||
defPacks.def.Debian.CveID)
|
||||
cveContents = models.CveContents{}
|
||||
}
|
||||
if r.Family != constant.Raspbian {
|
||||
cveContents := vinfo.CveContents
|
||||
if _, ok := vinfo.CveContents[ovalContent.Type]; ok {
|
||||
logging.Log.Debugf("%s OVAL will be overwritten", cve.CveID)
|
||||
} else {
|
||||
logging.Log.Debugf("%s is also detected by OVAL", cve.CveID)
|
||||
cveContents = models.CveContents{}
|
||||
}
|
||||
vinfo.Confidences.AppendIfMissing(models.OvalMatch)
|
||||
} else {
|
||||
if len(vinfo.Confidences) == 0 {
|
||||
vinfo.Confidences.AppendIfMissing(models.OvalMatch)
|
||||
cveContents[ovalContent.Type] = []models.CveContent{*ovalContent}
|
||||
vinfo.CveContents = cveContents
|
||||
}
|
||||
|
||||
// uniq(vinfo.AffectedPackages[].Name + defPacks.binpkgFixstat(map[string(=package name)]fixStat{}))
|
||||
collectBinpkgFixstat := defPacks{
|
||||
binpkgFixstat: map[string]fixStat{},
|
||||
}
|
||||
for packName, fixStatus := range defpacks.binpkgFixstat {
|
||||
collectBinpkgFixstat.binpkgFixstat[packName] = fixStatus
|
||||
}
|
||||
|
||||
for _, pack := range vinfo.AffectedPackages {
|
||||
collectBinpkgFixstat.binpkgFixstat[pack.Name] = fixStat{
|
||||
notFixedYet: pack.NotFixedYet,
|
||||
fixedIn: pack.FixedIn,
|
||||
isSrcPack: false,
|
||||
}
|
||||
}
|
||||
cveContents[ctype] = append(cveContents[ctype], ovalContent)
|
||||
vinfo.CveContents = cveContents
|
||||
}
|
||||
|
||||
// uniq(vinfo.PackNames + defPacks.binpkgStat)
|
||||
for _, pack := range vinfo.AffectedPackages {
|
||||
defPacks.binpkgFixstat[pack.Name] = fixStat{
|
||||
notFixedYet: pack.NotFixedYet,
|
||||
fixedIn: pack.FixedIn,
|
||||
isSrcPack: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Update package status of source packages.
|
||||
// In the case of Debian based Linux, sometimes source package name is defined as affected package in OVAL.
|
||||
// To display binary package name showed in apt-get, need to convert source name to binary name.
|
||||
for binName := range defPacks.binpkgFixstat {
|
||||
if srcPack, ok := r.SrcPackages.FindByBinName(binName); ok {
|
||||
for _, p := range defPacks.def.AffectedPacks {
|
||||
if p.Name == srcPack.Name {
|
||||
defPacks.binpkgFixstat[binName] = fixStat{
|
||||
notFixedYet: p.NotFixedYet,
|
||||
fixedIn: p.Version,
|
||||
isSrcPack: true,
|
||||
srcPackName: srcPack.Name,
|
||||
// Update package status of source packages.
|
||||
// In the case of Debian based Linux, sometimes source package name is defined as affected package in OVAL.
|
||||
// To display binary package name showed in apt-get, need to convert source name to binary name.
|
||||
for binName := range defpacks.binpkgFixstat {
|
||||
if srcPack, ok := r.SrcPackages.FindByBinName(binName); ok {
|
||||
for _, p := range defpacks.def.AffectedPacks {
|
||||
if p.Name == srcPack.Name {
|
||||
collectBinpkgFixstat.binpkgFixstat[binName] = fixStat{
|
||||
notFixedYet: p.NotFixedYet,
|
||||
fixedIn: p.Version,
|
||||
isSrcPack: true,
|
||||
srcPackName: srcPack.Name,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vinfo.AffectedPackages = defPacks.toPackStatuses()
|
||||
vinfo.AffectedPackages.Sort()
|
||||
r.ScannedCves[defPacks.def.Debian.CveID] = vinfo
|
||||
vinfo.AffectedPackages = collectBinpkgFixstat.toPackStatuses()
|
||||
vinfo.AffectedPackages.Sort()
|
||||
r.ScannedCves[cve.CveID] = vinfo
|
||||
}
|
||||
}
|
||||
|
||||
func (o DebianBase) convertToModel(def *ovalmodels.Definition) *models.CveContent {
|
||||
refs := []models.Reference{}
|
||||
func (o DebianBase) convertToModel(cveID string, def *ovalmodels.Definition) *models.CveContent {
|
||||
refs := make([]models.Reference, 0, len(def.References))
|
||||
for _, r := range def.References {
|
||||
refs = append(refs, models.Reference{
|
||||
Link: r.RefURL,
|
||||
@@ -95,14 +97,23 @@ func (o DebianBase) convertToModel(def *ovalmodels.Definition) *models.CveConten
|
||||
})
|
||||
}
|
||||
|
||||
return &models.CveContent{
|
||||
CveID: def.Debian.CveID,
|
||||
Title: def.Title,
|
||||
Summary: def.Description,
|
||||
Cvss2Severity: def.Advisory.Severity,
|
||||
Cvss3Severity: def.Advisory.Severity,
|
||||
References: refs,
|
||||
for _, cve := range def.Advisory.Cves {
|
||||
if cve.CveID != cveID {
|
||||
continue
|
||||
}
|
||||
|
||||
return &models.CveContent{
|
||||
Type: models.NewCveContentType(o.family),
|
||||
CveID: cve.CveID,
|
||||
Title: def.Title,
|
||||
Summary: def.Description,
|
||||
Cvss2Severity: def.Advisory.Severity,
|
||||
Cvss3Severity: def.Advisory.Severity,
|
||||
References: refs,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Debian is the interface for Debian OVAL
|
||||
@@ -147,7 +158,7 @@ func (o Debian) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
driver, err := newOvalDB(o.Cnf, r.Family)
|
||||
driver, err := newOvalDB(o.Cnf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -183,9 +194,9 @@ func (o Debian) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
|
||||
|
||||
for _, vuln := range r.ScannedCves {
|
||||
if conts, ok := vuln.CveContents[models.Debian]; ok {
|
||||
for _, cont := range conts {
|
||||
for i, cont := range conts {
|
||||
cont.SourceLink = "https://security-tracker.debian.org/tracker/" + cont.CveID
|
||||
vuln.CveContents[models.Debian] = append(vuln.CveContents[models.Debian], cont)
|
||||
vuln.CveContents[models.Debian][i] = cont
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -461,7 +472,7 @@ func (o Ubuntu) fillWithOval(r *models.ScanResult, kernelNamesInOval []string) (
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
driver, err := newOvalDB(o.Cnf, r.Family)
|
||||
driver, err := newOvalDB(o.Cnf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -502,9 +513,9 @@ func (o Ubuntu) fillWithOval(r *models.ScanResult, kernelNamesInOval []string) (
|
||||
|
||||
for _, vuln := range r.ScannedCves {
|
||||
if conts, ok := vuln.CveContents[models.Ubuntu]; ok {
|
||||
for _, cont := range conts {
|
||||
for i, cont := range conts {
|
||||
cont.SourceLink = "http://people.ubuntu.com/~ubuntu-security/cve/" + cont.CveID
|
||||
vuln.CveContents[models.Ubuntu] = append(vuln.CveContents[models.Ubuntu], cont)
|
||||
vuln.CveContents[models.Ubuntu][i] = cont
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/future-architect/vuls/models"
|
||||
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
|
||||
ovalmodels "github.com/vulsio/goval-dictionary/models"
|
||||
)
|
||||
|
||||
func TestPackNamesOfUpdateDebian(t *testing.T) {
|
||||
@@ -30,8 +30,8 @@ func TestPackNamesOfUpdateDebian(t *testing.T) {
|
||||
},
|
||||
defPacks: defPacks{
|
||||
def: ovalmodels.Definition{
|
||||
Debian: ovalmodels.Debian{
|
||||
CveID: "CVE-2000-1000",
|
||||
Advisory: ovalmodels.Advisory{
|
||||
Cves: []ovalmodels.Cve{{CveID: "CVE-2000-1000"}},
|
||||
},
|
||||
},
|
||||
binpkgFixstat: map[string]fixStat{
|
||||
@@ -53,15 +53,68 @@ func TestPackNamesOfUpdateDebian(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
in: models.ScanResult{
|
||||
ScannedCves: models.VulnInfos{
|
||||
"CVE-2000-1000": models.VulnInfo{
|
||||
AffectedPackages: models.PackageFixStatuses{
|
||||
{Name: "packA"},
|
||||
},
|
||||
},
|
||||
"CVE-2000-1001": models.VulnInfo{
|
||||
AffectedPackages: models.PackageFixStatuses{
|
||||
{Name: "packC"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
defPacks: defPacks{
|
||||
def: ovalmodels.Definition{
|
||||
Advisory: ovalmodels.Advisory{
|
||||
Cves: []ovalmodels.Cve{
|
||||
{
|
||||
CveID: "CVE-2000-1000",
|
||||
},
|
||||
{
|
||||
CveID: "CVE-2000-1001",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
binpkgFixstat: map[string]fixStat{
|
||||
"packB": {
|
||||
notFixedYet: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
out: models.ScanResult{
|
||||
ScannedCves: models.VulnInfos{
|
||||
"CVE-2000-1000": models.VulnInfo{
|
||||
AffectedPackages: models.PackageFixStatuses{
|
||||
{Name: "packA"},
|
||||
{Name: "packB", NotFixedYet: false},
|
||||
},
|
||||
},
|
||||
"CVE-2000-1001": models.VulnInfo{
|
||||
AffectedPackages: models.PackageFixStatuses{
|
||||
{Name: "packB", NotFixedYet: false},
|
||||
{Name: "packC"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// util.Log = util.NewCustomLogger()
|
||||
for i, tt := range tests {
|
||||
Debian{}.update(&tt.in, tt.defPacks)
|
||||
e := tt.out.ScannedCves["CVE-2000-1000"].AffectedPackages
|
||||
a := tt.in.ScannedCves["CVE-2000-1000"].AffectedPackages
|
||||
if !reflect.DeepEqual(a, e) {
|
||||
t.Errorf("[%d] expected: %#v\n actual: %#v\n", i, e, a)
|
||||
for cveid := range tt.out.ScannedCves {
|
||||
e := tt.out.ScannedCves[cveid].AffectedPackages
|
||||
a := tt.in.ScannedCves[cveid].AffectedPackages
|
||||
if !reflect.DeepEqual(a, e) {
|
||||
t.Errorf("[%d] expected: %v\n actual: %v\n", i, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
17
oval/oval.go
17
oval/oval.go
@@ -11,8 +11,8 @@ import (
|
||||
"github.com/future-architect/vuls/logging"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"github.com/kotakanbe/goval-dictionary/db"
|
||||
"github.com/parnurzeal/gorequest"
|
||||
"github.com/vulsio/goval-dictionary/db"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
@@ -36,7 +36,7 @@ func (b Base) CheckIfOvalFetched(osFamily, release string) (fetched bool, err er
|
||||
return false, err
|
||||
}
|
||||
if !b.Cnf.IsFetchViaHTTP() {
|
||||
driver, err := newOvalDB(b.Cnf, ovalFamily)
|
||||
driver, err := newOvalDB(b.Cnf)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -75,7 +75,7 @@ func (b Base) CheckIfOvalFresh(osFamily, release string) (ok bool, err error) {
|
||||
}
|
||||
var lastModified time.Time
|
||||
if !b.Cnf.IsFetchViaHTTP() {
|
||||
driver, err := newOvalDB(b.Cnf, ovalFamily)
|
||||
driver, err := newOvalDB(b.Cnf)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -103,7 +103,7 @@ func (b Base) CheckIfOvalFresh(osFamily, release string) (ok bool, err error) {
|
||||
since := time.Now()
|
||||
since = since.AddDate(0, 0, -3)
|
||||
if lastModified.Before(since) {
|
||||
logging.Log.Warnf("OVAL for %s %s is old, last modified is %s. It's recommended to update OVAL to improve scanning accuracy. How to update OVAL database, see https://github.com/kotakanbe/goval-dictionary#usage",
|
||||
logging.Log.Warnf("OVAL for %s %s is old, last modified is %s. It's recommended to update OVAL to improve scanning accuracy. How to update OVAL database, see https://github.com/vulsio/goval-dictionary#usage",
|
||||
osFamily, release, lastModified)
|
||||
return false, nil
|
||||
}
|
||||
@@ -112,7 +112,7 @@ func (b Base) CheckIfOvalFresh(osFamily, release string) (ok bool, err error) {
|
||||
}
|
||||
|
||||
// NewOvalDB returns oval db client
|
||||
func newOvalDB(cnf config.VulnDictInterface, familyInScanResult string) (driver db.DB, err error) {
|
||||
func newOvalDB(cnf config.VulnDictInterface) (driver db.DB, err error) {
|
||||
if cnf.IsFetchViaHTTP() {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -122,12 +122,7 @@ func newOvalDB(cnf config.VulnDictInterface, familyInScanResult string) (driver
|
||||
path = cnf.GetSQLite3Path()
|
||||
}
|
||||
|
||||
ovalFamily, err := GetFamilyInOval(familyInScanResult)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
driver, locked, err := db.NewDB(ovalFamily, cnf.GetType(), path, cnf.GetDebugSQL())
|
||||
driver, locked, err := db.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), db.Option{})
|
||||
if err != nil {
|
||||
if locked {
|
||||
err = xerrors.Errorf("SQLite3: %s is locked. err: %w", cnf.GetSQLite3Path(), err)
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/future-architect/vuls/constant"
|
||||
"github.com/future-architect/vuls/logging"
|
||||
"github.com/future-architect/vuls/models"
|
||||
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
|
||||
ovalmodels "github.com/vulsio/goval-dictionary/models"
|
||||
)
|
||||
|
||||
// RedHatBase is the base struct for RedHat, CentOS, Alma and Rocky
|
||||
@@ -28,7 +28,7 @@ func (o RedHatBase) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
driver, err := newOvalDB(o.Cnf, r.Family)
|
||||
driver, err := newOvalDB(o.Cnf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -52,16 +52,29 @@ func (o RedHatBase) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
|
||||
switch models.NewCveContentType(o.family) {
|
||||
case models.RedHat:
|
||||
if conts, ok := vuln.CveContents[models.RedHat]; ok {
|
||||
for _, cont := range conts {
|
||||
for i, cont := range conts {
|
||||
cont.SourceLink = "https://access.redhat.com/security/cve/" + cont.CveID
|
||||
vuln.CveContents[models.RedHat] = append(vuln.CveContents[models.RedHat], cont)
|
||||
vuln.CveContents[models.RedHat][i] = cont
|
||||
}
|
||||
}
|
||||
case models.Oracle:
|
||||
if conts, ok := vuln.CveContents[models.Oracle]; ok {
|
||||
for _, cont := range conts {
|
||||
for i, cont := range conts {
|
||||
cont.SourceLink = fmt.Sprintf("https://linux.oracle.com/cve/%s.html", cont.CveID)
|
||||
vuln.CveContents[models.Oracle] = append(vuln.CveContents[models.Oracle], cont)
|
||||
vuln.CveContents[models.Oracle][i] = cont
|
||||
}
|
||||
}
|
||||
case models.Amazon:
|
||||
for _, d := range vuln.DistroAdvisories {
|
||||
if conts, ok := vuln.CveContents[models.Amazon]; ok {
|
||||
for i, cont := range conts {
|
||||
if strings.HasPrefix(d.AdvisoryID, "ALAS2-") {
|
||||
cont.SourceLink = fmt.Sprintf("https://alas.aws.amazon.com/AL2/%s.html", strings.ReplaceAll(d.AdvisoryID, "ALAS2", "ALAS"))
|
||||
} else if strings.HasPrefix(d.AdvisoryID, "ALAS-") {
|
||||
cont.SourceLink = fmt.Sprintf("https://alas.aws.amazon.com/%s.html", d.AdvisoryID)
|
||||
}
|
||||
vuln.CveContents[models.Amazon][i] = cont
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -102,57 +115,66 @@ var kernelRelatedPackNames = map[string]bool{
|
||||
"python-perf": true,
|
||||
}
|
||||
|
||||
func (o RedHatBase) update(r *models.ScanResult, defPacks defPacks) (nCVEs int) {
|
||||
ctype := models.NewCveContentType(o.family)
|
||||
for _, cve := range defPacks.def.Advisory.Cves {
|
||||
ovalContent := *o.convertToModel(cve.CveID, &defPacks.def)
|
||||
func (o RedHatBase) update(r *models.ScanResult, defpacks defPacks) (nCVEs int) {
|
||||
for _, cve := range defpacks.def.Advisory.Cves {
|
||||
ovalContent := o.convertToModel(cve.CveID, &defpacks.def)
|
||||
if ovalContent == nil {
|
||||
continue
|
||||
}
|
||||
vinfo, ok := r.ScannedCves[cve.CveID]
|
||||
if !ok {
|
||||
logging.Log.Debugf("%s is newly detected by OVAL: DefID: %s", cve.CveID, defPacks.def.DefinitionID)
|
||||
logging.Log.Debugf("%s is newly detected by OVAL: DefID: %s", cve.CveID, defpacks.def.DefinitionID)
|
||||
vinfo = models.VulnInfo{
|
||||
CveID: cve.CveID,
|
||||
Confidences: models.Confidences{models.OvalMatch},
|
||||
CveContents: models.NewCveContents(ovalContent),
|
||||
CveContents: models.NewCveContents(*ovalContent),
|
||||
}
|
||||
nCVEs++
|
||||
} else {
|
||||
cveContents := vinfo.CveContents
|
||||
if v, ok := vinfo.CveContents[ctype]; ok {
|
||||
if v, ok := vinfo.CveContents[ovalContent.Type]; ok {
|
||||
for _, vv := range v {
|
||||
if vv.LastModified.After(ovalContent.LastModified) {
|
||||
logging.Log.Debugf("%s ignored. DefID: %s ", cve.CveID, defPacks.def.DefinitionID)
|
||||
logging.Log.Debugf("%s ignored. DefID: %s ", cve.CveID, defpacks.def.DefinitionID)
|
||||
} else {
|
||||
logging.Log.Debugf("%s OVAL will be overwritten. DefID: %s", cve.CveID, defPacks.def.DefinitionID)
|
||||
logging.Log.Debugf("%s OVAL will be overwritten. DefID: %s", cve.CveID, defpacks.def.DefinitionID)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logging.Log.Debugf("%s also detected by OVAL. DefID: %s", cve.CveID, defPacks.def.DefinitionID)
|
||||
logging.Log.Debugf("%s also detected by OVAL. DefID: %s", cve.CveID, defpacks.def.DefinitionID)
|
||||
cveContents = models.CveContents{}
|
||||
}
|
||||
|
||||
vinfo.Confidences.AppendIfMissing(models.OvalMatch)
|
||||
cveContents[ctype] = append(cveContents[ctype], ovalContent)
|
||||
cveContents[ovalContent.Type] = []models.CveContent{*ovalContent}
|
||||
vinfo.CveContents = cveContents
|
||||
}
|
||||
|
||||
vinfo.DistroAdvisories.AppendIfMissing(
|
||||
o.convertToDistroAdvisory(&defPacks.def))
|
||||
o.convertToDistroAdvisory(&defpacks.def))
|
||||
|
||||
// uniq(vinfo.AffectedPackages[].Name + defPacks.binpkgFixstat(map[string(=package name)]fixStat{}))
|
||||
collectBinpkgFixstat := defPacks{
|
||||
binpkgFixstat: map[string]fixStat{},
|
||||
}
|
||||
for packName, fixStatus := range defpacks.binpkgFixstat {
|
||||
collectBinpkgFixstat.binpkgFixstat[packName] = fixStatus
|
||||
}
|
||||
|
||||
// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
|
||||
for _, pack := range vinfo.AffectedPackages {
|
||||
if stat, ok := defPacks.binpkgFixstat[pack.Name]; !ok {
|
||||
defPacks.binpkgFixstat[pack.Name] = fixStat{
|
||||
if stat, ok := collectBinpkgFixstat.binpkgFixstat[pack.Name]; !ok {
|
||||
collectBinpkgFixstat.binpkgFixstat[pack.Name] = fixStat{
|
||||
notFixedYet: pack.NotFixedYet,
|
||||
fixedIn: pack.FixedIn,
|
||||
}
|
||||
} else if stat.notFixedYet {
|
||||
defPacks.binpkgFixstat[pack.Name] = fixStat{
|
||||
collectBinpkgFixstat.binpkgFixstat[pack.Name] = fixStat{
|
||||
notFixedYet: true,
|
||||
fixedIn: pack.FixedIn,
|
||||
}
|
||||
}
|
||||
}
|
||||
vinfo.AffectedPackages = defPacks.toPackStatuses()
|
||||
vinfo.AffectedPackages = collectBinpkgFixstat.toPackStatuses()
|
||||
vinfo.AffectedPackages.Sort()
|
||||
r.ScannedCves[cve.CveID] = vinfo
|
||||
}
|
||||
@@ -178,18 +200,19 @@ func (o RedHatBase) convertToDistroAdvisory(def *ovalmodels.Definition) *models.
|
||||
}
|
||||
|
||||
func (o RedHatBase) convertToModel(cveID string, def *ovalmodels.Definition) *models.CveContent {
|
||||
refs := make([]models.Reference, 0, len(def.References))
|
||||
for _, r := range def.References {
|
||||
refs = append(refs, models.Reference{
|
||||
Link: r.RefURL,
|
||||
Source: r.Source,
|
||||
RefID: r.RefID,
|
||||
})
|
||||
}
|
||||
|
||||
for _, cve := range def.Advisory.Cves {
|
||||
if cve.CveID != cveID {
|
||||
continue
|
||||
}
|
||||
var refs []models.Reference
|
||||
for _, r := range def.References {
|
||||
refs = append(refs, models.Reference{
|
||||
Link: r.RefURL,
|
||||
Source: r.Source,
|
||||
RefID: r.RefID,
|
||||
})
|
||||
}
|
||||
|
||||
score2, vec2 := o.parseCvss2(cve.Cvss2)
|
||||
score3, vec3 := o.parseCvss3(cve.Cvss3)
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/future-architect/vuls/models"
|
||||
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
|
||||
ovalmodels "github.com/vulsio/goval-dictionary/models"
|
||||
)
|
||||
|
||||
func TestParseCvss2(t *testing.T) {
|
||||
@@ -129,15 +129,68 @@ func TestPackNamesOfUpdate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
in: models.ScanResult{
|
||||
ScannedCves: models.VulnInfos{
|
||||
"CVE-2000-1000": models.VulnInfo{
|
||||
AffectedPackages: models.PackageFixStatuses{
|
||||
{Name: "packA"},
|
||||
},
|
||||
},
|
||||
"CVE-2000-1001": models.VulnInfo{
|
||||
AffectedPackages: models.PackageFixStatuses{
|
||||
{Name: "packC"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
defPacks: defPacks{
|
||||
def: ovalmodels.Definition{
|
||||
Advisory: ovalmodels.Advisory{
|
||||
Cves: []ovalmodels.Cve{
|
||||
{
|
||||
CveID: "CVE-2000-1000",
|
||||
},
|
||||
{
|
||||
CveID: "CVE-2000-1001",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
binpkgFixstat: map[string]fixStat{
|
||||
"packB": {
|
||||
notFixedYet: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
out: models.ScanResult{
|
||||
ScannedCves: models.VulnInfos{
|
||||
"CVE-2000-1000": models.VulnInfo{
|
||||
AffectedPackages: models.PackageFixStatuses{
|
||||
{Name: "packA"},
|
||||
{Name: "packB", NotFixedYet: false},
|
||||
},
|
||||
},
|
||||
"CVE-2000-1001": models.VulnInfo{
|
||||
AffectedPackages: models.PackageFixStatuses{
|
||||
{Name: "packB", NotFixedYet: false},
|
||||
{Name: "packC"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// util.Log = util.Logger{}.NewCustomLogger()
|
||||
for i, tt := range tests {
|
||||
RedHat{}.update(&tt.in, tt.defPacks)
|
||||
e := tt.out.ScannedCves["CVE-2000-1000"].AffectedPackages
|
||||
a := tt.in.ScannedCves["CVE-2000-1000"].AffectedPackages
|
||||
if !reflect.DeepEqual(a, e) {
|
||||
t.Errorf("[%d] expected: %v\n actual: %v\n", i, e, a)
|
||||
for cveid := range tt.out.ScannedCves {
|
||||
e := tt.out.ScannedCves[cveid].AffectedPackages
|
||||
a := tt.in.ScannedCves[cveid].AffectedPackages
|
||||
if !reflect.DeepEqual(a, e) {
|
||||
t.Errorf("[%d] expected: %v\n actual: %v\n", i, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
39
oval/suse.go
39
oval/suse.go
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/future-architect/vuls/constant"
|
||||
"github.com/future-architect/vuls/logging"
|
||||
"github.com/future-architect/vuls/models"
|
||||
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
|
||||
ovalmodels "github.com/vulsio/goval-dictionary/models"
|
||||
)
|
||||
|
||||
// SUSE is the struct of SUSE Linux
|
||||
@@ -35,7 +35,7 @@ func (o SUSE) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
driver, err := newOvalDB(o.Cnf, r.Family)
|
||||
driver, err := newOvalDB(o.Cnf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -55,23 +55,23 @@ func (o SUSE) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
|
||||
|
||||
for _, vuln := range r.ScannedCves {
|
||||
if conts, ok := vuln.CveContents[models.SUSE]; ok {
|
||||
for _, cont := range conts {
|
||||
for i, cont := range conts {
|
||||
cont.SourceLink = "https://security-tracker.debian.org/tracker/" + cont.CveID
|
||||
vuln.CveContents[models.SUSE] = append(vuln.CveContents[models.SUSE], cont)
|
||||
vuln.CveContents[models.SUSE][i] = cont
|
||||
}
|
||||
}
|
||||
}
|
||||
return len(relatedDefs.entries), nil
|
||||
}
|
||||
|
||||
func (o SUSE) update(r *models.ScanResult, defPacks defPacks) {
|
||||
ovalContent := *o.convertToModel(&defPacks.def)
|
||||
func (o SUSE) update(r *models.ScanResult, defpacks defPacks) {
|
||||
ovalContent := *o.convertToModel(&defpacks.def)
|
||||
ovalContent.Type = models.NewCveContentType(o.family)
|
||||
vinfo, ok := r.ScannedCves[defPacks.def.Title]
|
||||
vinfo, ok := r.ScannedCves[defpacks.def.Title]
|
||||
if !ok {
|
||||
logging.Log.Debugf("%s is newly detected by OVAL", defPacks.def.Title)
|
||||
logging.Log.Debugf("%s is newly detected by OVAL", defpacks.def.Title)
|
||||
vinfo = models.VulnInfo{
|
||||
CveID: defPacks.def.Title,
|
||||
CveID: defpacks.def.Title,
|
||||
Confidences: models.Confidences{models.OvalMatch},
|
||||
CveContents: models.NewCveContents(ovalContent),
|
||||
}
|
||||
@@ -79,26 +79,33 @@ func (o SUSE) update(r *models.ScanResult, defPacks defPacks) {
|
||||
cveContents := vinfo.CveContents
|
||||
ctype := models.NewCveContentType(o.family)
|
||||
if _, ok := vinfo.CveContents[ctype]; ok {
|
||||
logging.Log.Debugf("%s OVAL will be overwritten", defPacks.def.Title)
|
||||
logging.Log.Debugf("%s OVAL will be overwritten", defpacks.def.Title)
|
||||
} else {
|
||||
logging.Log.Debugf("%s is also detected by OVAL", defPacks.def.Title)
|
||||
logging.Log.Debugf("%s is also detected by OVAL", defpacks.def.Title)
|
||||
cveContents = models.CveContents{}
|
||||
}
|
||||
vinfo.Confidences.AppendIfMissing(models.OvalMatch)
|
||||
cveContents[ctype] = append(cveContents[ctype], ovalContent)
|
||||
cveContents[ctype] = []models.CveContent{ovalContent}
|
||||
vinfo.CveContents = cveContents
|
||||
}
|
||||
|
||||
// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
|
||||
// uniq(vinfo.AffectedPackages[].Name + defPacks.binpkgFixstat(map[string(=package name)]fixStat{}))
|
||||
collectBinpkgFixstat := defPacks{
|
||||
binpkgFixstat: map[string]fixStat{},
|
||||
}
|
||||
for packName, fixStatus := range defpacks.binpkgFixstat {
|
||||
collectBinpkgFixstat.binpkgFixstat[packName] = fixStatus
|
||||
}
|
||||
|
||||
for _, pack := range vinfo.AffectedPackages {
|
||||
defPacks.binpkgFixstat[pack.Name] = fixStat{
|
||||
collectBinpkgFixstat.binpkgFixstat[pack.Name] = fixStat{
|
||||
notFixedYet: pack.NotFixedYet,
|
||||
fixedIn: pack.FixedIn,
|
||||
}
|
||||
}
|
||||
vinfo.AffectedPackages = defPacks.toPackStatuses()
|
||||
vinfo.AffectedPackages = collectBinpkgFixstat.toPackStatuses()
|
||||
vinfo.AffectedPackages.Sort()
|
||||
r.ScannedCves[defPacks.def.Title] = vinfo
|
||||
r.ScannedCves[defpacks.def.Title] = vinfo
|
||||
}
|
||||
|
||||
func (o SUSE) convertToModel(def *ovalmodels.Definition) *models.CveContent {
|
||||
|
||||
@@ -20,9 +20,9 @@ import (
|
||||
apkver "github.com/knqyf263/go-apk-version"
|
||||
debver "github.com/knqyf263/go-deb-version"
|
||||
rpmver "github.com/knqyf263/go-rpm-version"
|
||||
"github.com/kotakanbe/goval-dictionary/db"
|
||||
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
|
||||
"github.com/parnurzeal/gorequest"
|
||||
"github.com/vulsio/goval-dictionary/db"
|
||||
ovalmodels "github.com/vulsio/goval-dictionary/models"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
"github.com/future-architect/vuls/constant"
|
||||
"github.com/future-architect/vuls/models"
|
||||
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
|
||||
ovalmodels "github.com/vulsio/goval-dictionary/models"
|
||||
)
|
||||
|
||||
func TestUpsert(t *testing.T) {
|
||||
|
||||
@@ -621,7 +621,7 @@ func getPlusDiffCves(previous, current models.ScanResult) models.VulnInfos {
|
||||
|
||||
// TODO commented out because a bug of diff logic when multiple oval defs found for a certain CVE-ID and same updated_at
|
||||
// if these OVAL defs have different affected packages, this logic detects as updated.
|
||||
// This logic will be uncomented after integration with gost https://github.com/knqyf263/gost
|
||||
// This logic will be uncomented after integration with gost https://github.com/vulsio/gost
|
||||
// } else if isCveFixed(v, previous) {
|
||||
// updated[v.CveID] = v
|
||||
// logging.Log.Debugf("fixed: %s", v.CveID)
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
// EnsureUUIDs generate a new UUID of the scan target server if UUID is not assigned yet.
|
||||
// And then set the generated UUID to config.toml and scan results.
|
||||
func EnsureUUIDs(servers map[string]config.ServerInfo, path string, scanResults models.ScanResults) (err error) {
|
||||
needsOverwrite, err := ensure(servers, path, scanResults, uuid.GenerateUUID)
|
||||
needsOverwrite, err := ensure(servers, scanResults, uuid.GenerateUUID)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to ensure UUIDs. err: %w", err)
|
||||
}
|
||||
@@ -30,7 +30,7 @@ func EnsureUUIDs(servers map[string]config.ServerInfo, path string, scanResults
|
||||
return writeToFile(config.Conf, path)
|
||||
}
|
||||
|
||||
func ensure(servers map[string]config.ServerInfo, path string, scanResults models.ScanResults, generateFunc func() (string, error)) (needsOverwrite bool, err error) {
|
||||
func ensure(servers map[string]config.ServerInfo, scanResults models.ScanResults, generateFunc func() (string, error)) (needsOverwrite bool, err error) {
|
||||
for i, r := range scanResults {
|
||||
serverInfo := servers[r.ServerName]
|
||||
if serverInfo.UUIDs == nil {
|
||||
|
||||
@@ -377,7 +377,7 @@ func Test_ensure(t *testing.T) {
|
||||
}
|
||||
for i, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotNeedsOverwrite, err := ensure(tt.args.servers, tt.args.path, tt.args.scanResults, tt.args.generateFunc)
|
||||
gotNeedsOverwrite, err := ensure(tt.args.servers, tt.args.scanResults, tt.args.generateFunc)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ensure() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
|
||||
@@ -26,14 +26,22 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
// Import library scanner
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/bundler"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/cargo"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/composer"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/gomod"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/npm"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/pipenv"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/poetry"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/yarn"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/language/dotnet/nuget"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/language/golang/binary"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/language/golang/mod"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/language/java/jar"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/language/nodejs/npm"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/language/nodejs/yarn"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/language/php/composer"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/language/python/pip"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/language/python/pipenv"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/language/python/poetry"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/language/ruby/bundler"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/language/rust/cargo"
|
||||
|
||||
// _ "github.com/aquasecurity/fanal/analyzer/language/ruby/gemspec"
|
||||
// _ "github.com/aquasecurity/fanal/analyzer/language/nodejs/pkg"
|
||||
// _ "github.com/aquasecurity/fanal/analyzer/language/python/packaging"
|
||||
|
||||
nmap "github.com/Ullaakut/nmap/v2"
|
||||
)
|
||||
|
||||
@@ -4,13 +4,18 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/bundler"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/cargo"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/composer"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/npm"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/pipenv"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/poetry"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/yarn"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/language/dotnet/nuget"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/language/golang/binary"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/language/golang/mod"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/language/java/jar"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/language/nodejs/npm"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/language/nodejs/yarn"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/language/php/composer"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/language/python/pip"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/language/python/pipenv"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/language/python/poetry"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/language/ruby/bundler"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/language/rust/cargo"
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
)
|
||||
|
||||
@@ -3,24 +3,23 @@ package scanner
|
||||
import (
|
||||
"github.com/aquasecurity/fanal/types"
|
||||
"github.com/future-architect/vuls/models"
|
||||
|
||||
trivyTypes "github.com/aquasecurity/trivy/pkg/types"
|
||||
)
|
||||
|
||||
func convertLibWithScanner(apps []types.Application) ([]models.LibraryScanner, error) {
|
||||
scanners := []models.LibraryScanner{}
|
||||
for _, app := range apps {
|
||||
libs := []trivyTypes.Library{}
|
||||
libs := []models.Library{}
|
||||
for _, lib := range app.Libraries {
|
||||
libs = append(libs, trivyTypes.Library{
|
||||
Name: lib.Library.Name,
|
||||
Version: lib.Library.Version,
|
||||
libs = append(libs, models.Library{
|
||||
Name: lib.Name,
|
||||
Version: lib.Version,
|
||||
FilePath: lib.FilePath,
|
||||
})
|
||||
}
|
||||
scanners = append(scanners, models.LibraryScanner{
|
||||
Type: app.Type,
|
||||
Path: app.FilePath,
|
||||
Libs: libs,
|
||||
Type: app.Type,
|
||||
LockfilePath: app.FilePath,
|
||||
Libs: libs,
|
||||
})
|
||||
}
|
||||
return scanners, nil
|
||||
|
||||
@@ -4,8 +4,10 @@ import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/cheggaaa/pb"
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/constant"
|
||||
"github.com/future-architect/vuls/logging"
|
||||
@@ -218,6 +220,10 @@ func (o *redhatBase) execCheckDeps(packNames []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *redhatBase) isDnf() bool {
|
||||
return o.exec(util.PrependProxyEnv(`repoquery --version | grep dnf`), o.sudo.repoquery()).isSuccess()
|
||||
}
|
||||
|
||||
func (o *redhatBase) preCure() error {
|
||||
if err := o.detectIPAddr(); err != nil {
|
||||
o.log.Warnf("Failed to detect IP addresses: %s", err)
|
||||
@@ -230,8 +236,7 @@ func (o *redhatBase) preCure() error {
|
||||
func (o *redhatBase) postScan() error {
|
||||
if o.isExecYumPS() {
|
||||
if err := o.pkgPs(o.getOwnerPkgs); err != nil {
|
||||
err = xerrors.Errorf("Failed to execute yum-ps: %w", err)
|
||||
o.log.Warnf("err: %+v", err)
|
||||
o.log.Warnf("err: %+v", xerrors.Errorf("Failed to execute yum-ps: %w", err))
|
||||
o.warns = append(o.warns, err)
|
||||
// Only warning this error
|
||||
}
|
||||
@@ -239,8 +244,7 @@ func (o *redhatBase) postScan() error {
|
||||
|
||||
if o.isExecNeedsRestarting() {
|
||||
if err := o.needsRestarting(); err != nil {
|
||||
err = xerrors.Errorf("Failed to execute need-restarting: %w", err)
|
||||
o.log.Warnf("err: %+v", err)
|
||||
o.log.Warnf("err: %+v", xerrors.Errorf("Failed to execute need-restarting: %w", err))
|
||||
o.warns = append(o.warns, err)
|
||||
// Only warning this error
|
||||
}
|
||||
@@ -260,15 +264,10 @@ func (o *redhatBase) scanPackages() (err error) {
|
||||
return xerrors.Errorf("Failed to scan installed packages: %w", err)
|
||||
}
|
||||
|
||||
if o.EnabledDnfModules, err = o.detectEnabledDnfModules(); err != nil {
|
||||
return xerrors.Errorf("Failed to detect installed dnf modules: %w", err)
|
||||
}
|
||||
|
||||
fn := func(pkgName string) execResult { return o.exec(fmt.Sprintf("rpm -q --last %s", pkgName), noSudo) }
|
||||
o.Kernel.RebootRequired, err = o.rebootRequired(fn)
|
||||
if err != nil {
|
||||
err = xerrors.Errorf("Failed to detect the kernel reboot required: %w", err)
|
||||
o.log.Warnf("err: %+v", err)
|
||||
o.log.Warnf("err: %+v", xerrors.Errorf("Failed to detect the kernel reboot required: %w", err))
|
||||
o.warns = append(o.warns, err)
|
||||
// Only warning this error
|
||||
}
|
||||
@@ -281,15 +280,31 @@ func (o *redhatBase) scanPackages() (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
if err := o.yumMakeCache(); err != nil {
|
||||
return xerrors.Errorf("Failed to `yum makecache`: %w", err)
|
||||
}
|
||||
|
||||
// `dnf module list` needs yum cache
|
||||
if o.EnabledDnfModules, err = o.detectEnabledDnfModules(); err != nil {
|
||||
return xerrors.Errorf("Failed to detect installed dnf modules: %w", err)
|
||||
}
|
||||
|
||||
updatable, err := o.scanUpdatablePackages()
|
||||
if err != nil {
|
||||
err = xerrors.Errorf("Failed to scan updatable packages: %w", err)
|
||||
o.log.Warnf("err: %+v", err)
|
||||
o.log.Warnf("err: %+v", xerrors.Errorf("Failed to scan updatable packages: %w", err))
|
||||
o.warns = append(o.warns, err)
|
||||
// Only warning this error
|
||||
} else {
|
||||
o.Packages.MergeNewVersion(updatable)
|
||||
}
|
||||
|
||||
if o.getServerInfo().Mode.IsDeep() {
|
||||
resolver := yumDependentResolver{redhat: o, isDnf: o.isDnf()}
|
||||
resolver.detectDependenciesForUpdate()
|
||||
resolver.detectReverseDependencies()
|
||||
o.Packages.ResolveReverseDepsRecursively()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -420,13 +435,8 @@ func (o *redhatBase) yumMakeCache() error {
|
||||
}
|
||||
|
||||
func (o *redhatBase) scanUpdatablePackages() (models.Packages, error) {
|
||||
if err := o.yumMakeCache(); err != nil {
|
||||
return nil, xerrors.Errorf("Failed to `yum makecache`: %w", err)
|
||||
}
|
||||
|
||||
isDnf := o.exec(util.PrependProxyEnv(`repoquery --version | grep dnf`), o.sudo.repoquery()).isSuccess()
|
||||
cmd := `repoquery --all --pkgnarrow=updates --qf='%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{REPO}'`
|
||||
if isDnf {
|
||||
if o.isDnf() {
|
||||
cmd = `repoquery --upgrades --qf='%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{REPONAME}' -q`
|
||||
}
|
||||
for _, repo := range o.getServerInfo().Enablerepo {
|
||||
@@ -700,9 +710,6 @@ func (o *redhatBase) detectEnabledDnfModules() ([]string, error) {
|
||||
cmd := `dnf --nogpgcheck --cacheonly --color=never --quiet module list --enabled`
|
||||
r := o.exec(util.PrependProxyEnv(cmd), noSudo)
|
||||
if !r.isSuccess() {
|
||||
if strings.Contains(r.Stdout, "Cache-only enabled but no cache") {
|
||||
return nil, xerrors.Errorf("sudo yum check-update to make local cache before scanning: %s", r)
|
||||
}
|
||||
return nil, xerrors.Errorf("Failed to dnf module list: %s", r)
|
||||
}
|
||||
return o.parseDnfModuleList(r.Stdout)
|
||||
@@ -723,3 +730,122 @@ func (o *redhatBase) parseDnfModuleList(stdout string) (labels []string, err err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type yumDependentResolver struct {
|
||||
redhat *redhatBase
|
||||
isDnf bool
|
||||
}
|
||||
|
||||
func (o *yumDependentResolver) detectDependenciesForUpdate() {
|
||||
o.redhat.log.Infof("Detecting the dependencies for each packages when yum updating")
|
||||
o.redhat.log.Infof("If it is too slow, `yum clean all` may make it faster")
|
||||
bar := pb.StartNew(len(o.redhat.Packages))
|
||||
for name, pkg := range o.redhat.Packages {
|
||||
bar.Increment()
|
||||
if pkg.Version == pkg.NewVersion && pkg.Release == pkg.NewRelease {
|
||||
// only updatable pkgs
|
||||
continue
|
||||
}
|
||||
names, err := o.scanUpdatablePkgDeps(name)
|
||||
if err != nil {
|
||||
o.redhat.log.Warnf("err: %+v", xerrors.Errorf("Failed to scan dependent packages for update: %w", err))
|
||||
o.redhat.warns = append(o.redhat.warns, err)
|
||||
// Only warning this error
|
||||
}
|
||||
sort.Strings(names)
|
||||
pkg.DependenciesForUpdate = names
|
||||
o.redhat.Packages[name] = pkg
|
||||
}
|
||||
bar.Finish()
|
||||
return
|
||||
}
|
||||
|
||||
func (o *yumDependentResolver) scanUpdatablePkgDeps(name string) (depsPkgNames []string, err error) {
|
||||
cmd := fmt.Sprintf("LANGUAGE=en_US.UTF-8 yum install --assumeno --cacheonly %s", name)
|
||||
r := o.redhat.exec(cmd, true)
|
||||
if !r.isSuccess(0, 1) {
|
||||
return nil, xerrors.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
names := o.parseYumInstall(r.Stdout)
|
||||
for _, n := range names {
|
||||
if _, ok := o.redhat.Packages[n]; ok {
|
||||
depsPkgNames = append(depsPkgNames, n)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (o *yumDependentResolver) parseYumInstall(stdout string) []string {
|
||||
names, inDepsLines := []string{}, false
|
||||
scanner := bufio.NewScanner(strings.NewReader(stdout))
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if strings.HasPrefix(line, "Updating for dependencies:") {
|
||||
inDepsLines = true
|
||||
continue
|
||||
}
|
||||
if inDepsLines {
|
||||
ss := strings.Fields(line)
|
||||
if len(ss) == 0 {
|
||||
break
|
||||
}
|
||||
names = append(names, ss[0])
|
||||
}
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
func (o *yumDependentResolver) detectReverseDependencies() {
|
||||
o.redhat.log.Infof("Detecting the reverse dependencies for each packages")
|
||||
bar := pb.StartNew(len(o.redhat.Packages))
|
||||
for name, pkg := range o.redhat.Packages {
|
||||
bar.Increment()
|
||||
// if pkg.Version == pkg.NewVersion && pkg.Release == pkg.NewRelease {
|
||||
// continue
|
||||
// }
|
||||
names, err := o.repoqueryWhatRequires(name)
|
||||
if err != nil {
|
||||
o.redhat.log.Warnf("err: %+v", xerrors.Errorf("Failed to scan reverse dependent packages: %w", err))
|
||||
o.redhat.warns = append(o.redhat.warns, err)
|
||||
// Only warning this error
|
||||
}
|
||||
sort.Strings(names)
|
||||
pkg.ReverseDependencies = names
|
||||
o.redhat.Packages[name] = pkg
|
||||
}
|
||||
bar.Finish()
|
||||
return
|
||||
}
|
||||
|
||||
func (o *yumDependentResolver) repoqueryWhatRequires(pkgName string) (depsPkgNames []string, err error) {
|
||||
cmd := `LANGUAGE=en_US.UTF-8 repoquery --cache --resolve --pkgnarrow=installed --qf "%{name}" --whatrequires ` + pkgName
|
||||
if o.isDnf {
|
||||
cmd = `LANGUAGE=en_US.UTF-8 repoquery --cache --installed --qf "%{name}" --whatrequires ` + pkgName
|
||||
}
|
||||
r := o.redhat.exec(cmd, true)
|
||||
if !r.isSuccess() {
|
||||
return nil, xerrors.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
names := o.parseRepoqueryWhatRequires(r.Stdout)
|
||||
for _, n := range names {
|
||||
if pkgName == n {
|
||||
continue
|
||||
}
|
||||
if _, ok := o.redhat.Packages[n]; ok {
|
||||
depsPkgNames = append(depsPkgNames, n)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (o *yumDependentResolver) parseRepoqueryWhatRequires(stdout string) []string {
|
||||
names := []string{}
|
||||
scanner := bufio.NewScanner(strings.NewReader(stdout))
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if line != "" {
|
||||
names = append(names, line)
|
||||
}
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
@@ -10,11 +10,6 @@ import (
|
||||
"github.com/k0kubun/pp"
|
||||
)
|
||||
|
||||
// func unixtimeNoerr(s string) time.Time {
|
||||
// t, _ := unixtime(s)
|
||||
// return t
|
||||
// }
|
||||
|
||||
func TestParseInstalledPackagesLinesRedhat(t *testing.T) {
|
||||
r := newRHEL(config.ServerInfo{})
|
||||
r.Distro = config.Distro{Family: constant.RedHat}
|
||||
@@ -641,3 +636,125 @@ kernel-3.10.0-1062.12.1.el7.x86_64 Sat 29 Feb 2020 12:09:00 PM UTC`,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_redhatBase_parseYumInstall(t *testing.T) {
|
||||
type fields struct {
|
||||
base base
|
||||
sudo rootPriv
|
||||
}
|
||||
type args struct {
|
||||
stdout string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
wantPkgNames []string
|
||||
}{
|
||||
{
|
||||
name: "ok",
|
||||
fields: fields{
|
||||
base: base{},
|
||||
},
|
||||
args: args{
|
||||
stdout: `===========================================================================================================================================================
|
||||
Package Arch Version Repository Size
|
||||
===========================================================================================================================================================
|
||||
Updating:
|
||||
glibc x86_64 2.17-325.el7_9 updates 3.6 M
|
||||
Updating for dependencies:
|
||||
glibc-common x86_64 2.17-325.el7_9 updates 12 M
|
||||
glibc-devel x86_64 2.17-325.el7_9 updates 1.1 M
|
||||
glibc-headers x86_64 2.17-325.el7_9 updates 691 k
|
||||
|
||||
Transaction Summary
|
||||
===========================================================================================================================================================
|
||||
Upgrade 1 Package (+3 Dependent packages)
|
||||
|
||||
Exiting on user command
|
||||
Your transaction was saved, rerun it with:
|
||||
yum load-transaction /tmp/yum_save_tx.2021-10-25.08-24.1EXcRc.yumtx`,
|
||||
},
|
||||
wantPkgNames: []string{
|
||||
"glibc-common",
|
||||
"glibc-devel",
|
||||
"glibc-headers",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no deps",
|
||||
fields: fields{
|
||||
base: base{},
|
||||
},
|
||||
args: args{
|
||||
stdout: `Package zlib-1.2.7-19.el7_9.x86_64 already installed and latest version`,
|
||||
},
|
||||
wantPkgNames: []string{},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
o := &redhatBase{
|
||||
base: tt.fields.base,
|
||||
}
|
||||
resolver := yumDependentResolver{redhat: o}
|
||||
if gotPkgNames := resolver.parseYumInstall(tt.args.stdout); !reflect.DeepEqual(gotPkgNames, tt.wantPkgNames) {
|
||||
t.Errorf("redhatBase.parseYumInstall() = %v, want %v", gotPkgNames, tt.wantPkgNames)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_yumDependentResolver_parseRepoqueryWhatRequires(t *testing.T) {
|
||||
type fields struct {
|
||||
redhat *redhatBase
|
||||
}
|
||||
type args struct {
|
||||
stdout string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "no deps",
|
||||
fields: fields{redhat: &redhatBase{}},
|
||||
args: args{
|
||||
stdout: "",
|
||||
},
|
||||
want: []string{},
|
||||
},
|
||||
{
|
||||
name: "no deps",
|
||||
fields: fields{redhat: &redhatBase{}},
|
||||
args: args{
|
||||
stdout: `which
|
||||
whois
|
||||
wpa_supplicant
|
||||
xcb-util
|
||||
xfsprogs
|
||||
xinetd`,
|
||||
},
|
||||
want: []string{
|
||||
"which",
|
||||
"whois",
|
||||
"wpa_supplicant",
|
||||
"xcb-util",
|
||||
"xfsprogs",
|
||||
"xinetd",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
o := &yumDependentResolver{
|
||||
redhat: tt.fields.redhat,
|
||||
}
|
||||
if got := o.parseRepoqueryWhatRequires(tt.args.stdout); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("yumDependentResolver.parseRepoqueryWhatRequires() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ func (*DiscoverCmd) Usage() string {
|
||||
}
|
||||
|
||||
// SetFlags set flag
|
||||
func (p *DiscoverCmd) SetFlags(f *flag.FlagSet) {
|
||||
func (p *DiscoverCmd) SetFlags(_ *flag.FlagSet) {
|
||||
}
|
||||
|
||||
// Execute execute
|
||||
|
||||
@@ -43,7 +43,7 @@ func (p *HistoryCmd) SetFlags(f *flag.FlagSet) {
|
||||
}
|
||||
|
||||
// Execute execute
|
||||
func (p *HistoryCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
func (p *HistoryCmd) Execute(_ context.Context, _ *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
dirs, err := reporter.ListValidJSONDirs(config.Conf.ResultsDir)
|
||||
if err != nil {
|
||||
return subcommands.ExitFailure
|
||||
|
||||
@@ -90,7 +90,7 @@ func (p *ServerCmd) SetFlags(f *flag.FlagSet) {
|
||||
}
|
||||
|
||||
// Execute execute
|
||||
func (p *ServerCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
func (p *ServerCmd) Execute(_ context.Context, _ *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
logging.Log = logging.NewCustomLogger(config.Conf.Debug, config.Conf.Quiet, config.Conf.LogToFile, config.Conf.LogDir, "", "")
|
||||
logging.Log.Infof("vuls-%s-%s", config.Version, config.Revision)
|
||||
if err := config.Load(p.configPath, ""); err != nil {
|
||||
|
||||
@@ -301,10 +301,7 @@ func cursorMoveMiddle(g *gocui.Gui, v *gocui.View) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := onMovingCursorRedrawView(g, v); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return onMovingCursorRedrawView(g, v)
|
||||
}
|
||||
|
||||
func cursorPageDown(g *gocui.Gui, v *gocui.View) error {
|
||||
@@ -505,7 +502,7 @@ func showMsg(g *gocui.Gui, v *gocui.View) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func delMsg(g *gocui.Gui, v *gocui.View) error {
|
||||
func delMsg(g *gocui.Gui, _ *gocui.View) error {
|
||||
if err := g.DeleteView("msg"); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -513,7 +510,7 @@ func delMsg(g *gocui.Gui, v *gocui.View) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func quit(g *gocui.Gui, v *gocui.View) error {
|
||||
func quit(_ *gocui.Gui, _ *gocui.View) error {
|
||||
return gocui.ErrQuit
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user