Compare commits
	
		
			17 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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=%d", 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=%d", 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=%s", r.FormatServerName(), nFiltered, config.Conf.IgnoreUnscoredCves)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		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
 | 
			
		||||
 
 | 
			
		||||
@@ -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{
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										191
									
								
								detector/msf.go
									
									
									
									
									
								
							
							
						
						
									
										191
									
								
								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
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										89
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										89
									
								
								go.mod
									
									
									
									
									
								
							@@ -4,14 +4,14 @@ 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
 | 
			
		||||
@@ -20,28 +20,24 @@ require (
 | 
			
		||||
	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,68 @@ 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.20211013020338-ec22aa70ffdb
 | 
			
		||||
	github.com/vulsio/go-exploitdb v0.4.2-0.20210930235136-c10d2716b7e2
 | 
			
		||||
	github.com/vulsio/go-msfdb v0.2.1-0.20210928020521-9b56a938f544
 | 
			
		||||
	github.com/vulsio/gost v0.4.1-0.20210928234623-3e6372ba2821
 | 
			
		||||
	github.com/vulsio/goval-dictionary v0.6.1
 | 
			
		||||
	golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
 | 
			
		||||
	golang.org/x/net v0.0.0-20211011170408-caeb26a5c8c0 // 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/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 +116,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-colorable v0.1.11 // indirect
 | 
			
		||||
	github.com/mattn/go-sqlite3 v2.0.3+incompatible // 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-20211007075335-d3039528d8ac // 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"
 | 
			
		||||
 
 | 
			
		||||
@@ -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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										143
									
								
								oval/debian.go
									
									
									
									
									
								
							
							
						
						
									
										143
									
								
								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
 | 
			
		||||
@@ -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
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -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)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -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
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -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)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										37
									
								
								oval/suse.go
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								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
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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