Compare commits
	
		
			46 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					0c6a892893 | ||
| 
						 | 
					89d94ad85a | ||
| 
						 | 
					ffdb78962f | ||
| 
						 | 
					321dae37ce | ||
| 
						 | 
					a31797af0b | ||
| 
						 | 
					32999cf432 | ||
| 
						 | 
					88218f5d92 | ||
| 
						 | 
					15761933ac | ||
| 
						 | 
					0b62842f0e | ||
| 
						 | 
					6bceddeeda | ||
| 
						 | 
					2dcbff8cd5 | ||
| 
						 | 
					8659668177 | ||
| 
						 | 
					e07b6a9160 | ||
| 
						 | 
					aac5ef1438 | ||
| 
						 | 
					d780a73297 | ||
| 
						 | 
					9ef8cee36e | ||
| 
						 | 
					77808a2c05 | ||
| 
						 | 
					177e553d12 | ||
| 
						 | 
					40f8272a28 | ||
| 
						 | 
					a7eb1141ae | ||
| 
						 | 
					c73ed7f32f | ||
| 
						 | 
					f047a6fe0c | ||
| 
						 | 
					7f15a86d6a | ||
| 
						 | 
					da1e515253 | ||
| 
						 | 
					591786fde6 | ||
| 
						 | 
					47e6ea249d | ||
| 
						 | 
					4a72295de7 | ||
| 
						 | 
					9ed5f2cac5 | ||
| 
						 | 
					3e67f04fe4 | ||
| 
						 | 
					b9416ae062 | ||
| 
						 | 
					b4e49e093e | ||
| 
						 | 
					020f6ac609 | ||
| 
						 | 
					7e71cbdd46 | ||
| 
						 | 
					1003f62212 | ||
| 
						 | 
					9b18e1f9f0 | ||
| 
						 | 
					24f790f474 | ||
| 
						 | 
					fb8749fc5e | ||
| 
						 | 
					96c3592db1 | ||
| 
						 | 
					d65421cf46 | ||
| 
						 | 
					c52ba448cd | ||
| 
						 | 
					21adce463b | ||
| 
						 | 
					f24240bf90 | ||
| 
						 | 
					ff83cadd6e | ||
| 
						 | 
					e8c09282d9 | ||
| 
						 | 
					5f4d68cde4 | ||
| 
						 | 
					9077a83ea8 | 
							
								
								
									
										45
									
								
								.github/workflows/docker-publish.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								.github/workflows/docker-publish.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
name: Publish Docker image
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches:
 | 
			
		||||
      - 'master'
 | 
			
		||||
    tags:
 | 
			
		||||
      - '*'
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  docker:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@v2
 | 
			
		||||
 | 
			
		||||
      - name: Set up QEMU
 | 
			
		||||
        uses: docker/setup-qemu-action@v1
 | 
			
		||||
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v1
 | 
			
		||||
 | 
			
		||||
      - name: Docker meta
 | 
			
		||||
        id: meta
 | 
			
		||||
        uses: docker/metadata-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          images: vuls/vuls
 | 
			
		||||
          tags: |
 | 
			
		||||
            type=ref,event=tag
 | 
			
		||||
 | 
			
		||||
      - name: Login to DockerHub
 | 
			
		||||
        uses: docker/login-action@v1
 | 
			
		||||
        with:
 | 
			
		||||
          username: ${{ secrets.DOCKERHUB_USERNAME }}
 | 
			
		||||
          password: ${{ secrets.DOCKERHUB_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Build and push
 | 
			
		||||
        uses: docker/build-push-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: |
 | 
			
		||||
            vuls/vuls:latest
 | 
			
		||||
            ${{ steps.meta.outputs.tags }}
 | 
			
		||||
          secrets: |
 | 
			
		||||
            "github_token=${{ secrets.GITHUB_TOKEN }}"
 | 
			
		||||
							
								
								
									
										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]
 | 
			
		||||
@@ -10,10 +10,7 @@ ENV REPOSITORY github.com/future-architect/vuls
 | 
			
		||||
COPY . $GOPATH/src/$REPOSITORY
 | 
			
		||||
RUN cd $GOPATH/src/$REPOSITORY && make install
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FROM alpine:3.13
 | 
			
		||||
 | 
			
		||||
LABEL maintainer hikachan sadayuki-matsuno
 | 
			
		||||
FROM alpine:3.14
 | 
			
		||||
 | 
			
		||||
ENV LOGDIR /var/log/vuls
 | 
			
		||||
ENV WORKDIR /vuls
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								GNUmakefile
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								GNUmakefile
									
									
									
									
									
								
							@@ -17,14 +17,13 @@ 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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
all: build
 | 
			
		||||
all: b
 | 
			
		||||
 | 
			
		||||
build: ./cmd/vuls/main.go pretest fmt
 | 
			
		||||
	$(GO) build -a -ldflags "$(LDFLAGS)" -o vuls ./cmd/vuls
 | 
			
		||||
@@ -42,12 +41,15 @@ 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;
 | 
			
		||||
 | 
			
		||||
golangci:
 | 
			
		||||
	golangci-lint run
 | 
			
		||||
 | 
			
		||||
fmt:
 | 
			
		||||
	gofmt -s -w $(SRCS)
 | 
			
		||||
 | 
			
		||||
@@ -57,7 +59,7 @@ mlint:
 | 
			
		||||
fmtcheck:
 | 
			
		||||
	$(foreach file,$(SRCS),gofmt -s -d $(file);)
 | 
			
		||||
 | 
			
		||||
pretest: lint vet fmtcheck
 | 
			
		||||
pretest: lint vet fmtcheck golangci
 | 
			
		||||
 | 
			
		||||
test: 
 | 
			
		||||
	$(GO) test -cover -v ./... || exit;
 | 
			
		||||
@@ -75,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
 | 
			
		||||
@@ -89,7 +91,7 @@ NOW=$(shell date --iso-8601=seconds)
 | 
			
		||||
NOW_JSON_DIR := '${BASE_DIR}/$(NOW)'
 | 
			
		||||
ONE_SEC_AFTER=$(shell date -d '+1 second' --iso-8601=seconds)
 | 
			
		||||
ONE_SEC_AFTER_JSON_DIR := '${BASE_DIR}/$(ONE_SEC_AFTER)'
 | 
			
		||||
LIBS := 'gemfile' 'pipfile' 'poetry' 'composer' 'packagelock' 'yarn' 'cargo' 'gomod' 'rails' 'cpe_vendor_product_match'
 | 
			
		||||
LIBS := 'gemfile' 'pipfile' 'poetry' 'composer' 'packagelock' 'yarn' 'cargo' 'gomod' 'nvd_exact' 'nvd_rough' 'nvd_vendor_product' 'nvd_match_no_jvn' 'jvn_vendor_product' 'jvn_vendor_product_nover'
 | 
			
		||||
 | 
			
		||||
diff:
 | 
			
		||||
	# git clone git@github.com:vulsio/vulsctl.git
 | 
			
		||||
@@ -240,4 +242,4 @@ define count-cve
 | 
			
		||||
	for jsonfile in ${ONE_SEC_AFTER_JSON_DIR}/*.json ;  do \
 | 
			
		||||
		echo $$jsonfile; cat $$jsonfile | jq ".scannedCves | length" ; \
 | 
			
		||||
	done
 | 
			
		||||
endef
 | 
			
		||||
endef
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								README.md
									
									
									
									
									
								
							@@ -50,7 +50,7 @@ Vuls is a tool created to solve the problems listed above. It has the following
 | 
			
		||||
 | 
			
		||||
[Supports major Linux/FreeBSD](https://vuls.io/docs/en/supported-os.html)
 | 
			
		||||
 | 
			
		||||
- Alpine, Amazon Linux, CentOS, Rocky Linux, Debian, Oracle Linux, Raspbian, RHEL, SUSE Enterprise Linux, and Ubuntu
 | 
			
		||||
- Alpine, Amazon Linux, CentOS, Alma Linux, Rocky Linux, Debian, Oracle Linux, Raspbian, RHEL, SUSE Enterprise Linux, and Ubuntu
 | 
			
		||||
- FreeBSD
 | 
			
		||||
- Cloud, on-premise, Running Docker Container
 | 
			
		||||
 | 
			
		||||
@@ -80,11 +80,16 @@ Vuls is a tool created to solve the problems listed above. It has the following
 | 
			
		||||
- PoC, Exploit
 | 
			
		||||
  - [Exploit Database](https://www.exploit-db.com/)
 | 
			
		||||
  - [Metasploit-Framework modules](https://www.rapid7.com/db/?q=&type=metasploit)
 | 
			
		||||
  - [qazbnm456/awesome-cve-poc](https://github.com/qazbnm456/awesome-cve-poc)
 | 
			
		||||
  - [nomi-sec/PoC-in-GitHub](https://github.com/nomi-sec/PoC-in-GitHub)
 | 
			
		||||
 | 
			
		||||
- CERT
 | 
			
		||||
  - [US-CERT](https://www.us-cert.gov/ncas/alerts)
 | 
			
		||||
  - [JPCERT](http://www.jpcert.or.jp/at/2019.html)
 | 
			
		||||
 | 
			
		||||
- CISA(Cybersecurity & Infrastructure Security Agency)
 | 
			
		||||
  - [Known Exploited Vulnerabilities Catalog](https://www.cisa.gov/known-exploited-vulnerabilities-catalog)
 | 
			
		||||
 | 
			
		||||
- Libraries
 | 
			
		||||
  - [Node.js Security Working Group](https://github.com/nodejs/security-wg)
 | 
			
		||||
  - [Ruby Advisory Database](https://github.com/rubysec/ruby-advisory-db)
 | 
			
		||||
@@ -101,15 +106,15 @@ Vuls is a tool created to solve the problems listed above. It has the following
 | 
			
		||||
 | 
			
		||||
- Scan without root privilege, no dependencies
 | 
			
		||||
- Almost no load on the scan target server
 | 
			
		||||
- Offline mode scan with no internet access. (CentOS, Rocky Linux, Debian, Oracle Linux, Red Hat, and Ubuntu)
 | 
			
		||||
- Offline mode scan with no internet access. (CentOS, Alma Linux, Rocky Linux, Debian, Oracle Linux, Red Hat, and Ubuntu)
 | 
			
		||||
 | 
			
		||||
[Fast Root Scan](https://vuls.io/docs/en/architecture-fast-root-scan.html)
 | 
			
		||||
 | 
			
		||||
- Scan with root privilege
 | 
			
		||||
- Almost no load on the scan target server
 | 
			
		||||
- Detect processes affected by update using yum-ps (Amazon Linux, CentOS, Rocky Linux, Oracle Linux, and RedHat)
 | 
			
		||||
- Detect processes affected by update using yum-ps (Amazon Linux, CentOS, Alma Linux, Rocky Linux, Oracle Linux, and RedHat)
 | 
			
		||||
- Detect processes which updated before but not restarting yet using checkrestart of debian-goodies (Debian and Ubuntu)
 | 
			
		||||
- Offline mode scan with no internet access. (CentOS, Rocky Linux, Debian, Oracle Linux, Red Hat, and Ubuntu)
 | 
			
		||||
- Offline mode scan with no internet access. (CentOS, Alma Linux, Rocky Linux, Debian, Oracle Linux, Red Hat, and Ubuntu)
 | 
			
		||||
 | 
			
		||||
### [Remote, Local scan mode, Server mode](https://vuls.io/docs/en/architecture-remote-local.html)
 | 
			
		||||
 | 
			
		||||
@@ -184,11 +189,14 @@ see [vulsdoc](https://vuls.io/docs/en/how-to-contribute.html)
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
## Stargazers over time
 | 
			
		||||
## Sponsors
 | 
			
		||||
 | 
			
		||||
[](https://starcharts.herokuapp.com/future-architect/vuls)
 | 
			
		||||
|  |  |
 | 
			
		||||
| ------------- | ------------- |
 | 
			
		||||
| <a href="https://www.tines.com/?utm_source=oss&utm_medium=sponsorship&utm_campaign=vuls"><img src="img/sponsor/tines.png" align="left" width="600px" ></a> | Tines is no-code automation for security teams. Build powerful, reliable workflows without a development team. |
 | 
			
		||||
| <a href="https://www.sakura.ad.jp/"><img src="https://vuls.io/img/icons/sakura.svg" align="left" width="600px" ></a> | SAKURA internet Inc. is an Internet company founded in 1996. We provide cloud computing services such as "Sakura's Shared Server", "Sakura's VPS", and "Sakura's Cloud" to meet the needs of a wide range of customers, from individuals and corporations to the education and public sectors, using its own data centers in Japan. Based on the philosophy of "changing what you want to do into what you can do," we offer DX solutions for all fields.  |
 | 
			
		||||
 | 
			
		||||
-----;
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
## License
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -41,6 +41,7 @@ type Config struct {
 | 
			
		||||
	Gost       GostConf       `json:"gost,omitempty"`
 | 
			
		||||
	Exploit    ExploitConf    `json:"exploit,omitempty"`
 | 
			
		||||
	Metasploit MetasploitConf `json:"metasploit,omitempty"`
 | 
			
		||||
	KEVuln     KEVulnConf     `json:"kevuln,omitempty"`
 | 
			
		||||
 | 
			
		||||
	Slack      SlackConf      `json:"-"`
 | 
			
		||||
	EMail      SMTPConf       `json:"-"`
 | 
			
		||||
@@ -69,17 +70,17 @@ type ScanOpts struct {
 | 
			
		||||
 | 
			
		||||
// ReportOpts is options for report
 | 
			
		||||
type ReportOpts struct {
 | 
			
		||||
	// refactored
 | 
			
		||||
	CvssScoreOver      float64 `json:"cvssScoreOver,omitempty"`
 | 
			
		||||
	TrivyCacheDBDir    string  `json:"trivyCacheDBDir,omitempty"`
 | 
			
		||||
	NoProgress         bool    `json:"noProgress,omitempty"`
 | 
			
		||||
	RefreshCve         bool    `json:"refreshCve,omitempty"`
 | 
			
		||||
	IgnoreUnfixed      bool    `json:"ignoreUnfixed,omitempty"`
 | 
			
		||||
	IgnoreUnscoredCves bool    `json:"ignoreUnscoredCves,omitempty"`
 | 
			
		||||
	DiffPlus           bool    `json:"diffPlus,omitempty"`
 | 
			
		||||
	DiffMinus          bool    `json:"diffMinus,omitempty"`
 | 
			
		||||
	Diff               bool    `json:"diff,omitempty"`
 | 
			
		||||
	Lang               string  `json:"lang,omitempty"`
 | 
			
		||||
	CvssScoreOver       float64 `json:"cvssScoreOver,omitempty"`
 | 
			
		||||
	ConfidenceScoreOver int     `json:"confidenceScoreOver,omitempty"`
 | 
			
		||||
	TrivyCacheDBDir     string  `json:"trivyCacheDBDir,omitempty"`
 | 
			
		||||
	NoProgress          bool    `json:"noProgress,omitempty"`
 | 
			
		||||
	RefreshCve          bool    `json:"refreshCve,omitempty"`
 | 
			
		||||
	IgnoreUnfixed       bool    `json:"ignoreUnfixed,omitempty"`
 | 
			
		||||
	IgnoreUnscoredCves  bool    `json:"ignoreUnscoredCves,omitempty"`
 | 
			
		||||
	DiffPlus            bool    `json:"diffPlus,omitempty"`
 | 
			
		||||
	DiffMinus           bool    `json:"diffMinus,omitempty"`
 | 
			
		||||
	Diff                bool    `json:"diff,omitempty"`
 | 
			
		||||
	Lang                string  `json:"lang,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ValidateOnConfigtest validates
 | 
			
		||||
@@ -176,6 +177,7 @@ func (c *Config) ValidateOnReport() bool {
 | 
			
		||||
		&Conf.Gost,
 | 
			
		||||
		&Conf.Exploit,
 | 
			
		||||
		&Conf.Metasploit,
 | 
			
		||||
		&Conf.KEVuln,
 | 
			
		||||
	} {
 | 
			
		||||
		if err := cnf.Validate(); err != nil {
 | 
			
		||||
			errs = append(errs, xerrors.Errorf("Failed to validate %s: %+v", cnf.GetName(), err))
 | 
			
		||||
@@ -230,18 +232,19 @@ type ServerInfo struct {
 | 
			
		||||
	GitHubRepos        map[string]GitHubConf       `toml:"githubs" json:"githubs,omitempty"` // key: owner/repo
 | 
			
		||||
	UUIDs              map[string]string           `toml:"uuids,omitempty" json:"uuids,omitempty"`
 | 
			
		||||
	Memo               string                      `toml:"memo,omitempty" json:"memo,omitempty"`
 | 
			
		||||
	Enablerepo         []string                    `toml:"enablerepo,omitempty" json:"enablerepo,omitempty"` // For CentOS, Rocky, RHEL, Amazon
 | 
			
		||||
	Enablerepo         []string                    `toml:"enablerepo,omitempty" json:"enablerepo,omitempty"` // For CentOS, Alma, Rocky, RHEL, Amazon
 | 
			
		||||
	Optional           map[string]interface{}      `toml:"optional,omitempty" json:"optional,omitempty"`     // Optional key-value set that will be outputted to JSON
 | 
			
		||||
	Lockfiles          []string                    `toml:"lockfiles,omitempty" json:"lockfiles,omitempty"`   // ie) path/to/package-lock.json
 | 
			
		||||
	FindLock           bool                        `toml:"findLock,omitempty" json:"findLock,omitempty"`
 | 
			
		||||
	Type               string                      `toml:"type,omitempty" json:"type,omitempty"` // "pseudo" or ""
 | 
			
		||||
	IgnoredJSONKeys    []string                    `toml:"ignoredJSONKeys,omitempty" json:"ignoredJSONKeys,omitempty"`
 | 
			
		||||
	IPv4Addrs          []string                    `toml:"-" json:"ipv4Addrs,omitempty"`
 | 
			
		||||
	IPv6Addrs          []string                    `toml:"-" json:"ipv6Addrs,omitempty"`
 | 
			
		||||
	IPSIdentifiers     map[string]string           `toml:"-" json:"ipsIdentifiers,omitempty"`
 | 
			
		||||
	WordPress          *WordPressConf              `toml:"wordpress,omitempty" json:"wordpress,omitempty"`
 | 
			
		||||
	PortScan           *PortScanConf               `toml:"portscan,omitempty" json:"portscan,omitempty"`
 | 
			
		||||
 | 
			
		||||
	IPv4Addrs      []string          `toml:"-" json:"ipv4Addrs,omitempty"`
 | 
			
		||||
	IPv6Addrs      []string          `toml:"-" json:"ipv6Addrs,omitempty"`
 | 
			
		||||
	IPSIdentifiers map[string]string `toml:"-" json:"ipsIdentifiers,omitempty"`
 | 
			
		||||
 | 
			
		||||
	// internal use
 | 
			
		||||
	LogMsgAnsiColor string     `toml:"-" json:"-"` // DebugLog Color
 | 
			
		||||
	Container       Container  `toml:"-" json:"-"`
 | 
			
		||||
 
 | 
			
		||||
@@ -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")
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -75,6 +75,10 @@ func GetEOL(family, release string) (eol EOL, found bool) {
 | 
			
		||||
			"7": {StandardSupportUntil: time.Date(2024, 6, 30, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"8": {StandardSupportUntil: time.Date(2021, 12, 31, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
		}[major(release)]
 | 
			
		||||
	case constant.Alma:
 | 
			
		||||
		eol, found = map[string]EOL{
 | 
			
		||||
			"8": {StandardSupportUntil: time.Date(2029, 12, 31, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
		}[major(release)]
 | 
			
		||||
	case constant.Rocky:
 | 
			
		||||
		eol, found = map[string]EOL{
 | 
			
		||||
			"8": {StandardSupportUntil: time.Date(2029, 5, 31, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
@@ -106,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
 | 
			
		||||
 
 | 
			
		||||
@@ -111,6 +111,31 @@ func TestEOL_IsStandardSupportEnded(t *testing.T) {
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    false,
 | 
			
		||||
		},
 | 
			
		||||
		// Alma
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Alma Linux 8 supported",
 | 
			
		||||
			fields:   fields{family: Alma, release: "8"},
 | 
			
		||||
			now:      time.Date(2021, 7, 2, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Alma Linux 8 EOL",
 | 
			
		||||
			fields:   fields{family: Alma, release: "8"},
 | 
			
		||||
			now:      time.Date(2029, 2, 1, 0, 0, 0, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Alma Linux 9 Not Found",
 | 
			
		||||
			fields:   fields{family: Alma, release: "9"},
 | 
			
		||||
			now:      time.Date(2021, 7, 2, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    false,
 | 
			
		||||
		},
 | 
			
		||||
		// Rocky
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Rocky Linux 8 supported",
 | 
			
		||||
@@ -265,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
 | 
			
		||||
@@ -27,6 +27,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
 | 
			
		||||
		&Conf.Gost,
 | 
			
		||||
		&Conf.Exploit,
 | 
			
		||||
		&Conf.Metasploit,
 | 
			
		||||
		&Conf.KEVuln,
 | 
			
		||||
	} {
 | 
			
		||||
		cnf.Init()
 | 
			
		||||
	}
 | 
			
		||||
@@ -34,11 +35,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 +138,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")
 | 
			
		||||
 
 | 
			
		||||
@@ -248,7 +248,7 @@ func (cnf *GostConf) Init() {
 | 
			
		||||
	cnf.DebugSQL = Conf.DebugSQL
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MetasploitConf is gost go-metasploitdb
 | 
			
		||||
// MetasploitConf is go-msfdb config
 | 
			
		||||
type MetasploitConf struct {
 | 
			
		||||
	VulnDict
 | 
			
		||||
}
 | 
			
		||||
@@ -274,3 +274,30 @@ func (cnf *MetasploitConf) Init() {
 | 
			
		||||
	cnf.setDefault("go-msfdb.sqlite3")
 | 
			
		||||
	cnf.DebugSQL = Conf.DebugSQL
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// KEVulnConf is go-kev config
 | 
			
		||||
type KEVulnConf struct {
 | 
			
		||||
	VulnDict
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const kevulnDBType = "KEVULN_TYPE"
 | 
			
		||||
const kevulnDBURL = "KEVULN_URL"
 | 
			
		||||
const kevulnDBPATH = "KEVULN_SQLITE3_PATH"
 | 
			
		||||
 | 
			
		||||
// Init set options with the following priority.
 | 
			
		||||
// 1. Environment variable
 | 
			
		||||
// 2. config.toml
 | 
			
		||||
func (cnf *KEVulnConf) Init() {
 | 
			
		||||
	cnf.Name = "kevuln"
 | 
			
		||||
	if os.Getenv(kevulnDBType) != "" {
 | 
			
		||||
		cnf.Type = os.Getenv(kevulnDBType)
 | 
			
		||||
	}
 | 
			
		||||
	if os.Getenv(kevulnDBURL) != "" {
 | 
			
		||||
		cnf.URL = os.Getenv(kevulnDBURL)
 | 
			
		||||
	}
 | 
			
		||||
	if os.Getenv(kevulnDBPATH) != "" {
 | 
			
		||||
		cnf.SQLite3Path = os.Getenv(kevulnDBPATH)
 | 
			
		||||
	}
 | 
			
		||||
	cnf.setDefault("go-kev.sqlite3")
 | 
			
		||||
	cnf.DebugSQL = Conf.DebugSQL
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,9 @@ const (
 | 
			
		||||
	// CentOS is
 | 
			
		||||
	CentOS = "centos"
 | 
			
		||||
 | 
			
		||||
	// Alma is
 | 
			
		||||
	Alma = "alma"
 | 
			
		||||
 | 
			
		||||
	// Rocky is
 | 
			
		||||
	Rocky = "rocky"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
}
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package detector
 | 
			
		||||
@@ -15,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 {
 | 
			
		||||
@@ -26,7 +27,9 @@ type goCveDictClient struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newGoCveDictClient(cnf config.VulnDictInterface, o logging.LogOpts) (*goCveDictClient, error) {
 | 
			
		||||
	cvelog.SetLogger(o.Debug, o.Quiet, false, o.LogToFile, o.LogDir)
 | 
			
		||||
	if err := cvelog.SetLogger(o.LogToFile, o.LogDir, o.Debug, o.LogJSON); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	driver, locked, err := newCveDB(cnf)
 | 
			
		||||
	if locked {
 | 
			
		||||
@@ -41,25 +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)
 | 
			
		||||
		}
 | 
			
		||||
		if len(cveDetail.CveID) == 0 {
 | 
			
		||||
			cveDetails = append(cveDetails, cvemodels.CveDetail{CveID: cveID})
 | 
			
		||||
		} else {
 | 
			
		||||
			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 {
 | 
			
		||||
@@ -103,13 +99,7 @@ func (api goCveDictClient) fetchCveDetailsViaHTTP(cveIDs []string) (cveDetails [
 | 
			
		||||
	for range cveIDs {
 | 
			
		||||
		select {
 | 
			
		||||
		case res := <-resChan:
 | 
			
		||||
			if len(res.CveDetail.CveID) == 0 {
 | 
			
		||||
				cveDetails = append(cveDetails, cvemodels.CveDetail{
 | 
			
		||||
					CveID: res.Key,
 | 
			
		||||
				})
 | 
			
		||||
			} else {
 | 
			
		||||
				cveDetails = append(cveDetails, res.CveDetail)
 | 
			
		||||
			}
 | 
			
		||||
			cveDetails = append(cveDetails, res.CveDetail)
 | 
			
		||||
		case err := <-errChan:
 | 
			
		||||
			errs = append(errs, err)
 | 
			
		||||
		case <-timeout:
 | 
			
		||||
@@ -154,21 +144,40 @@ func (api goCveDictClient) httpGet(key, url string, resChan chan<- response, err
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api goCveDictClient) fetchCveDetailsByCpeName(cpeName string) ([]cvemodels.CveDetail, error) {
 | 
			
		||||
func (api goCveDictClient) detectCveByCpeURI(cpeURI string, useJVN bool) (cves []cvemodels.CveDetail, err error) {
 | 
			
		||||
	if api.cnf.IsFetchViaHTTP() {
 | 
			
		||||
		url, err := util.URLPathJoin(api.cnf.GetURL(), "cpes")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		query := map[string]string{"name": cpeName}
 | 
			
		||||
		query := map[string]string{"name": cpeURI}
 | 
			
		||||
		logging.Log.Debugf("HTTP Request to %s, query: %#v", url, query)
 | 
			
		||||
		return api.httpPost(cpeName, url, query)
 | 
			
		||||
		if cves, err = api.httpPost(url, query); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if cves, err = api.driver.GetByCpeURI(cpeURI); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return api.driver.GetByCpeURI(cpeName)
 | 
			
		||||
 | 
			
		||||
	if useJVN {
 | 
			
		||||
		return cves, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	nvdCves := []cvemodels.CveDetail{}
 | 
			
		||||
	for _, cve := range cves {
 | 
			
		||||
		if !cve.HasNvd() {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		cve.Jvns = []cvemodels.Jvn{}
 | 
			
		||||
		nvdCves = append(nvdCves, cve)
 | 
			
		||||
	}
 | 
			
		||||
	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
 | 
			
		||||
@@ -207,7 +216,7 @@ func newCveDB(cnf config.VulnDictInterface) (driver cvedb.DB, locked bool, err e
 | 
			
		||||
	if cnf.GetType() == "sqlite3" {
 | 
			
		||||
		path = cnf.GetSQLite3Path()
 | 
			
		||||
	}
 | 
			
		||||
	driver, locked, err = cvedb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL())
 | 
			
		||||
	driver, locked, err = cvedb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), cvedb.Option{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		err = xerrors.Errorf("Failed to init CVE DB. err: %w, path: %s", err, path)
 | 
			
		||||
		return nil, locked, err
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package detector
 | 
			
		||||
@@ -17,10 +18,16 @@ 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"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Cpe :
 | 
			
		||||
type Cpe struct {
 | 
			
		||||
	CpeURI string
 | 
			
		||||
	UseJVN bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Detect vulns and fill CVE detailed information
 | 
			
		||||
func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
 | 
			
		||||
 | 
			
		||||
@@ -36,7 +43,16 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
 | 
			
		||||
			r.ScannedCves = models.VulnInfos{}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := DetectLibsCves(&r, config.Conf.TrivyCacheDBDir, config.Conf.NoProgress); err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to fill with Library dependency: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := DetectPkgCves(&r, config.Conf.OvalDict, config.Conf.Gost); err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to detect Pkg CVE: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cpeURIs, owaspDCXMLPath := []string{}, ""
 | 
			
		||||
		cpes := []Cpe{}
 | 
			
		||||
		if len(r.Container.ContainerID) == 0 {
 | 
			
		||||
			cpeURIs = config.Conf.Servers[r.ServerName].CpeNames
 | 
			
		||||
			owaspDCXMLPath = config.Conf.Servers[r.ServerName].OwaspDCXMLPath
 | 
			
		||||
@@ -56,16 +72,13 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
 | 
			
		||||
			}
 | 
			
		||||
			cpeURIs = append(cpeURIs, cpes...)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := DetectLibsCves(&r, config.Conf.TrivyCacheDBDir, config.Conf.NoProgress); err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to fill with Library dependency: %w", err)
 | 
			
		||||
		for _, uri := range cpeURIs {
 | 
			
		||||
			cpes = append(cpes, Cpe{
 | 
			
		||||
				CpeURI: uri,
 | 
			
		||||
				UseJVN: true,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := DetectPkgCves(&r, config.Conf.OvalDict, config.Conf.Gost); err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to detect Pkg CVE: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := DetectCpeURIsCves(&r, cpeURIs, config.Conf.CveDict, config.Conf.LogOpts); err != nil {
 | 
			
		||||
		if err := DetectCpeURIsCves(&r, cpes, config.Conf.CveDict, config.Conf.LogOpts); err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to detect CVE of `%s`: %w", cpeURIs, err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -98,6 +111,10 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
 | 
			
		||||
		}
 | 
			
		||||
		logging.Log.Infof("%s: %d exploits are detected", r.FormatServerName(), nMetasploitCve)
 | 
			
		||||
 | 
			
		||||
		if err := FillWithKEVuln(&r, config.Conf.KEVuln); err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to fill with Known Exploited Vulnerabilities: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		FillCweDict(&r)
 | 
			
		||||
 | 
			
		||||
		r.ReportedBy, _ = os.Hostname()
 | 
			
		||||
@@ -132,8 +149,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)
 | 
			
		||||
		nFiltered := 0
 | 
			
		||||
		logging.Log.Infof("%s: total %d CVEs detected", r.FormatServerName(), len(r.ScannedCves))
 | 
			
		||||
 | 
			
		||||
		if 0 < config.Conf.CvssScoreOver {
 | 
			
		||||
			r.ScannedCves, nFiltered = r.ScannedCves.FilterByCvssOver(config.Conf.CvssScoreOver)
 | 
			
		||||
			logging.Log.Infof("%s: %d CVEs filtered by --cvss-over=%g", r.FormatServerName(), nFiltered, config.Conf.CvssScoreOver)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if config.Conf.IgnoreUnfixed {
 | 
			
		||||
			r.ScannedCves, nFiltered = r.ScannedCves.FilterUnfixed(config.Conf.IgnoreUnfixed)
 | 
			
		||||
			logging.Log.Infof("%s: %d CVEs filtered by --ignore-unfixed", r.FormatServerName(), nFiltered)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if 0 < config.Conf.ConfidenceScoreOver {
 | 
			
		||||
			r.ScannedCves, nFiltered = r.ScannedCves.FilterByConfidenceOver(config.Conf.ConfidenceScoreOver)
 | 
			
		||||
			logging.Log.Infof("%s: %d CVEs filtered by --confidence-over=%d", r.FormatServerName(), nFiltered, config.Conf.ConfidenceScoreOver)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// IgnoreCves
 | 
			
		||||
		ignoreCves := []string{}
 | 
			
		||||
@@ -142,7 +174,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{}
 | 
			
		||||
@@ -151,11 +186,15 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
 | 
			
		||||
		} else if s, ok := config.Conf.Servers[r.ServerName].Containers[r.Container.Name]; ok {
 | 
			
		||||
			ignorePkgsRegexps = s.IgnorePkgsRegexp
 | 
			
		||||
		}
 | 
			
		||||
		r.ScannedCves = r.ScannedCves.FilterIgnorePkgs(ignorePkgsRegexps)
 | 
			
		||||
		if 0 < len(ignorePkgsRegexps) {
 | 
			
		||||
			r.ScannedCves, nFiltered = r.ScannedCves.FilterIgnorePkgs(ignorePkgsRegexps)
 | 
			
		||||
			logging.Log.Infof("%s: %d CVEs filtered by ignorePkgsRegexp=%s", r.FormatServerName(), nFiltered, ignorePkgsRegexps)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// IgnoreUnscored
 | 
			
		||||
		if config.Conf.IgnoreUnscoredCves {
 | 
			
		||||
			r.ScannedCves = r.ScannedCves.FindScoredVulns()
 | 
			
		||||
			r.ScannedCves, nFiltered = r.ScannedCves.FindScoredVulns()
 | 
			
		||||
			logging.Log.Infof("%s: %d CVEs filtered by --ignore-unscored-cves", r.FormatServerName(), nFiltered)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		r.FilterInactiveWordPressLibs(config.Conf.WpScan.DetectInactive)
 | 
			
		||||
@@ -188,7 +227,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 {
 | 
			
		||||
@@ -284,8 +323,8 @@ func FillCvesWithNvdJvn(r *models.ScanResult, cnf config.GoCveDictConf, logOpts
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, d := range ds {
 | 
			
		||||
		nvd, exploits, mitigations := models.ConvertNvdJSONToModel(d.CveID, d.NvdJSON)
 | 
			
		||||
		jvn := models.ConvertJvnToModel(d.CveID, d.Jvn)
 | 
			
		||||
		nvds, exploits, mitigations := models.ConvertNvdToModel(d.CveID, d.Nvds)
 | 
			
		||||
		jvns := models.ConvertJvnToModel(d.CveID, d.Jvns)
 | 
			
		||||
 | 
			
		||||
		alerts := fillCertAlerts(&d)
 | 
			
		||||
		for cveID, vinfo := range r.ScannedCves {
 | 
			
		||||
@@ -293,9 +332,23 @@ func FillCvesWithNvdJvn(r *models.ScanResult, cnf config.GoCveDictConf, logOpts
 | 
			
		||||
				if vinfo.CveContents == nil {
 | 
			
		||||
					vinfo.CveContents = models.CveContents{}
 | 
			
		||||
				}
 | 
			
		||||
				for _, con := range []*models.CveContent{nvd, jvn} {
 | 
			
		||||
					if con != nil && !con.Empty() {
 | 
			
		||||
						vinfo.CveContents[con.Type] = *con
 | 
			
		||||
				for _, con := range nvds {
 | 
			
		||||
					if !con.Empty() {
 | 
			
		||||
						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
 | 
			
		||||
@@ -310,24 +363,26 @@ func FillCvesWithNvdJvn(r *models.ScanResult, cnf config.GoCveDictConf, logOpts
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fillCertAlerts(cvedetail *cvemodels.CveDetail) (dict models.AlertDict) {
 | 
			
		||||
	if cvedetail.NvdJSON != nil {
 | 
			
		||||
		for _, cert := range cvedetail.NvdJSON.Certs {
 | 
			
		||||
			dict.En = append(dict.En, models.Alert{
 | 
			
		||||
	for _, nvd := range cvedetail.Nvds {
 | 
			
		||||
		for _, cert := range nvd.Certs {
 | 
			
		||||
			dict.USCERT = append(dict.USCERT, models.Alert{
 | 
			
		||||
				URL:   cert.Link,
 | 
			
		||||
				Title: cert.Title,
 | 
			
		||||
				Team:  "us",
 | 
			
		||||
				Team:  "uscert",
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if cvedetail.Jvn != nil {
 | 
			
		||||
		for _, cert := range cvedetail.Jvn.Certs {
 | 
			
		||||
			dict.Ja = append(dict.Ja, models.Alert{
 | 
			
		||||
 | 
			
		||||
	for _, jvn := range cvedetail.Jvns {
 | 
			
		||||
		for _, cert := range jvn.Certs {
 | 
			
		||||
			dict.JPCERT = append(dict.JPCERT, models.Alert{
 | 
			
		||||
				URL:   cert.Link,
 | 
			
		||||
				Title: cert.Title,
 | 
			
		||||
				Team:  "jp",
 | 
			
		||||
				Team:  "jpcert",
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return dict
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -348,11 +403,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)
 | 
			
		||||
@@ -402,7 +457,7 @@ func detectPkgsCvesWithGost(cnf config.GostConf, r *models.ScanResult) error {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DetectCpeURIsCves detects CVEs of given CPE-URIs
 | 
			
		||||
func DetectCpeURIsCves(r *models.ScanResult, cpeURIs []string, cnf config.GoCveDictConf, logOpts logging.LogOpts) error {
 | 
			
		||||
func DetectCpeURIsCves(r *models.ScanResult, cpes []Cpe, cnf config.GoCveDictConf, logOpts logging.LogOpts) error {
 | 
			
		||||
	client, err := newGoCveDictClient(&cnf, logOpts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
@@ -414,28 +469,34 @@ func DetectCpeURIsCves(r *models.ScanResult, cpeURIs []string, cnf config.GoCveD
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	nCVEs := 0
 | 
			
		||||
	for _, name := range cpeURIs {
 | 
			
		||||
		details, err := client.fetchCveDetailsByCpeName(name)
 | 
			
		||||
	for _, cpe := range cpes {
 | 
			
		||||
		details, err := client.detectCveByCpeURI(cpe.CpeURI, cpe.UseJVN)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, detail := range details {
 | 
			
		||||
			confidence := models.CpeVersionMatch
 | 
			
		||||
			if detail.HasJvn() && !detail.HasNvd() {
 | 
			
		||||
				// In the case of CpeVendorProduct-match, only the JVN is set(Nvd is not set).
 | 
			
		||||
				confidence = models.CpeVendorProductMatch
 | 
			
		||||
			advisories := []models.DistroAdvisory{}
 | 
			
		||||
			if !detail.HasNvd() && detail.HasJvn() {
 | 
			
		||||
				for _, jvn := range detail.Jvns {
 | 
			
		||||
					advisories = append(advisories, models.DistroAdvisory{
 | 
			
		||||
						AdvisoryID: jvn.JvnID,
 | 
			
		||||
					})
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			maxConfidence := getMaxConfidence(detail)
 | 
			
		||||
 | 
			
		||||
			if val, ok := r.ScannedCves[detail.CveID]; ok {
 | 
			
		||||
				val.CpeURIs = util.AppendIfMissing(val.CpeURIs, name)
 | 
			
		||||
				val.Confidences.AppendIfMissing(confidence)
 | 
			
		||||
				val.CpeURIs = util.AppendIfMissing(val.CpeURIs, cpe.CpeURI)
 | 
			
		||||
				val.Confidences.AppendIfMissing(maxConfidence)
 | 
			
		||||
				val.DistroAdvisories = advisories
 | 
			
		||||
				r.ScannedCves[detail.CveID] = val
 | 
			
		||||
			} else {
 | 
			
		||||
				v := models.VulnInfo{
 | 
			
		||||
					CveID:       detail.CveID,
 | 
			
		||||
					CpeURIs:     []string{name},
 | 
			
		||||
					Confidences: models.Confidences{confidence},
 | 
			
		||||
					CveID:            detail.CveID,
 | 
			
		||||
					CpeURIs:          []string{cpe.CpeURI},
 | 
			
		||||
					Confidences:      models.Confidences{maxConfidence},
 | 
			
		||||
					DistroAdvisories: advisories,
 | 
			
		||||
				}
 | 
			
		||||
				r.ScannedCves[detail.CveID] = v
 | 
			
		||||
				nCVEs++
 | 
			
		||||
@@ -446,15 +507,39 @@ func DetectCpeURIsCves(r *models.ScanResult, cpeURIs []string, cnf config.GoCveD
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getMaxConfidence(detail cvemodels.CveDetail) (max models.Confidence) {
 | 
			
		||||
	if !detail.HasNvd() && detail.HasJvn() {
 | 
			
		||||
		return models.JvnVendorProductMatch
 | 
			
		||||
	} else if detail.HasNvd() {
 | 
			
		||||
		for _, nvd := range detail.Nvds {
 | 
			
		||||
			confidence := models.Confidence{}
 | 
			
		||||
			switch nvd.DetectionMethod {
 | 
			
		||||
			case cvemodels.NvdExactVersionMatch:
 | 
			
		||||
				confidence = models.NvdExactVersionMatch
 | 
			
		||||
			case cvemodels.NvdRoughVersionMatch:
 | 
			
		||||
				confidence = models.NvdRoughVersionMatch
 | 
			
		||||
			case cvemodels.NvdVendorProductMatch:
 | 
			
		||||
				confidence = models.NvdVendorProductMatch
 | 
			
		||||
			}
 | 
			
		||||
			if max.Score < confidence.Score {
 | 
			
		||||
				max = confidence
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return max
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillCweDict fills CWE
 | 
			
		||||
func FillCweDict(r *models.ScanResult) {
 | 
			
		||||
	uniqCweIDMap := map[string]bool{}
 | 
			
		||||
	for _, vinfo := range r.ScannedCves {
 | 
			
		||||
		for _, cont := range vinfo.CveContents {
 | 
			
		||||
			for _, id := range cont.CweIDs {
 | 
			
		||||
				if strings.HasPrefix(id, "CWE-") {
 | 
			
		||||
					id = strings.TrimPrefix(id, "CWE-")
 | 
			
		||||
					uniqCweIDMap[id] = true
 | 
			
		||||
		for _, conts := range vinfo.CveContents {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
				for _, id := range cont.CweIDs {
 | 
			
		||||
					if strings.HasPrefix(id, "CWE-") {
 | 
			
		||||
						id = strings.TrimPrefix(id, "CWE-")
 | 
			
		||||
						uniqCweIDMap[id] = true
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										90
									
								
								detector/detector_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								detector/detector_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,90 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package detector
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	cvemodels "github.com/vulsio/go-cve-dictionary/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func Test_getMaxConfidence(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		detail cvemodels.CveDetail
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name    string
 | 
			
		||||
		args    args
 | 
			
		||||
		wantMax models.Confidence
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "JvnVendorProductMatch",
 | 
			
		||||
			args: args{
 | 
			
		||||
				detail: cvemodels.CveDetail{
 | 
			
		||||
					Nvds: []cvemodels.Nvd{},
 | 
			
		||||
					Jvns: []cvemodels.Jvn{{}},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantMax: models.JvnVendorProductMatch,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "NvdExactVersionMatch",
 | 
			
		||||
			args: args{
 | 
			
		||||
				detail: cvemodels.CveDetail{
 | 
			
		||||
					Nvds: []cvemodels.Nvd{
 | 
			
		||||
						{DetectionMethod: cvemodels.NvdRoughVersionMatch},
 | 
			
		||||
						{DetectionMethod: cvemodels.NvdVendorProductMatch},
 | 
			
		||||
						{DetectionMethod: cvemodels.NvdExactVersionMatch},
 | 
			
		||||
					},
 | 
			
		||||
					Jvns: []cvemodels.Jvn{{DetectionMethod: cvemodels.JvnVendorProductMatch}},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantMax: models.NvdExactVersionMatch,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "NvdRoughVersionMatch",
 | 
			
		||||
			args: args{
 | 
			
		||||
				detail: cvemodels.CveDetail{
 | 
			
		||||
					Nvds: []cvemodels.Nvd{
 | 
			
		||||
						{DetectionMethod: cvemodels.NvdRoughVersionMatch},
 | 
			
		||||
						{DetectionMethod: cvemodels.NvdVendorProductMatch},
 | 
			
		||||
					},
 | 
			
		||||
					Jvns: []cvemodels.Jvn{},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantMax: models.NvdRoughVersionMatch,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "NvdVendorProductMatch",
 | 
			
		||||
			args: args{
 | 
			
		||||
				detail: cvemodels.CveDetail{
 | 
			
		||||
					Nvds: []cvemodels.Nvd{
 | 
			
		||||
						{DetectionMethod: cvemodels.NvdVendorProductMatch},
 | 
			
		||||
					},
 | 
			
		||||
					Jvns: []cvemodels.Jvn{{DetectionMethod: cvemodels.JvnVendorProductMatch}},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantMax: models.NvdVendorProductMatch,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "empty",
 | 
			
		||||
			args: args{
 | 
			
		||||
				detail: cvemodels.CveDetail{
 | 
			
		||||
					Nvds: []cvemodels.Nvd{},
 | 
			
		||||
					Jvns: []cvemodels.Jvn{},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantMax: models.Confidence{},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			if gotMax := getMaxConfidence(tt.args.detail); !reflect.DeepEqual(gotMax, tt.wantMax) {
 | 
			
		||||
				t.Errorf("getMaxConfidence() = %v, want %v", gotMax, tt.wantMax)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package detector
 | 
			
		||||
@@ -27,16 +28,16 @@ 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
 | 
			
		||||
		}
 | 
			
		||||
		for _, res := range responses {
 | 
			
		||||
			exps := []*exploitmodels.Exploit{}
 | 
			
		||||
			exps := []exploitmodels.Exploit{}
 | 
			
		||||
			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
 | 
			
		||||
@@ -45,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())
 | 
			
		||||
@@ -62,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++
 | 
			
		||||
@@ -75,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 {
 | 
			
		||||
@@ -102,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)
 | 
			
		||||
@@ -118,7 +121,7 @@ func getCvesViaHTTP(cveIDs []string, urlPrefix string) (
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		for _, cveID := range cveIDs {
 | 
			
		||||
			reqChan <- request{
 | 
			
		||||
			reqChan <- exploitRequest{
 | 
			
		||||
				cveID: cveID,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -128,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)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -153,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
 | 
			
		||||
@@ -213,7 +211,7 @@ func newExploitDB(cnf config.VulnDictInterface) (driver exploitdb.DB, locked boo
 | 
			
		||||
	if cnf.GetType() == "sqlite3" {
 | 
			
		||||
		path = cnf.GetSQLite3Path()
 | 
			
		||||
	}
 | 
			
		||||
	if driver, locked, err = exploitdb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL()); err != nil {
 | 
			
		||||
	if driver, locked, err = exploitdb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), exploitdb.Option{}); err != nil {
 | 
			
		||||
		if locked {
 | 
			
		||||
			return nil, true, xerrors.Errorf("exploitDB is locked. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package detector
 | 
			
		||||
@@ -125,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] = cveContent
 | 
			
		||||
					val.CveContents[models.GitHub] = []models.CveContent{cveContent}
 | 
			
		||||
					r.ScannedCves[cveID] = val
 | 
			
		||||
				} else {
 | 
			
		||||
					v := models.VulnInfo{
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										214
									
								
								detector/kevuln.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								detector/kevuln.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,214 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
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"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	kevulndb "github.com/vulsio/go-kev/db"
 | 
			
		||||
	kevulnmodels "github.com/vulsio/go-kev/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// FillWithKEVuln :
 | 
			
		||||
func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf) 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 err
 | 
			
		||||
		}
 | 
			
		||||
		responses, err := getKEVulnsViaHTTP(cveIDs, prefix)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		for _, res := range responses {
 | 
			
		||||
			kevulns := []kevulnmodels.KEVuln{}
 | 
			
		||||
			if err := json.Unmarshal([]byte(res.json), &kevulns); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			alerts := []models.Alert{}
 | 
			
		||||
			if len(kevulns) > 0 {
 | 
			
		||||
				alerts = append(alerts, models.Alert{
 | 
			
		||||
					Title: "Known Exploited Vulnerabilities Catalog",
 | 
			
		||||
					URL:   "https://www.cisa.gov/known-exploited-vulnerabilities-catalog",
 | 
			
		||||
					Team:  "cisa",
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			v, ok := r.ScannedCves[res.request.cveID]
 | 
			
		||||
			if ok {
 | 
			
		||||
				v.AlertDict.CISA = alerts
 | 
			
		||||
			}
 | 
			
		||||
			r.ScannedCves[res.request.cveID] = v
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		driver, locked, err := newKEVulnDB(&cnf)
 | 
			
		||||
		if locked {
 | 
			
		||||
			return xerrors.Errorf("SQLite3 is locked: %s", cnf.GetSQLite3Path())
 | 
			
		||||
		} else if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		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
 | 
			
		||||
			}
 | 
			
		||||
			kevulns, err := driver.GetKEVulnByCveID(cveID)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			if len(kevulns) == 0 {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			alerts := []models.Alert{}
 | 
			
		||||
			if len(kevulns) > 0 {
 | 
			
		||||
				alerts = append(alerts, models.Alert{
 | 
			
		||||
					Title: "Known Exploited Vulnerabilities Catalog",
 | 
			
		||||
					URL:   "https://www.cisa.gov/known-exploited-vulnerabilities-catalog",
 | 
			
		||||
					Team:  "cisa",
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			vuln.AlertDict.CISA = alerts
 | 
			
		||||
			r.ScannedCves[cveID] = vuln
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type kevulnResponse struct {
 | 
			
		||||
	request kevulnRequest
 | 
			
		||||
	json    string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getKEVulnsViaHTTP(cveIDs []string, urlPrefix string) (
 | 
			
		||||
	responses []kevulnResponse, err error) {
 | 
			
		||||
	nReq := len(cveIDs)
 | 
			
		||||
	reqChan := make(chan kevulnRequest, nReq)
 | 
			
		||||
	resChan := make(chan kevulnResponse, nReq)
 | 
			
		||||
	errChan := make(chan error, nReq)
 | 
			
		||||
	defer close(reqChan)
 | 
			
		||||
	defer close(resChan)
 | 
			
		||||
	defer close(errChan)
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		for _, cveID := range cveIDs {
 | 
			
		||||
			reqChan <- kevulnRequest{
 | 
			
		||||
				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)
 | 
			
		||||
				httpGetKEVuln(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 KEVuln")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(errs) != 0 {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to fetch KEVuln. err: %w", errs)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type kevulnRequest struct {
 | 
			
		||||
	cveID string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func httpGetKEVuln(url string, req kevulnRequest, resChan chan<- kevulnResponse, 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 <- kevulnResponse{
 | 
			
		||||
		request: req,
 | 
			
		||||
		json:    body,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newKEVulnDB(cnf config.VulnDictInterface) (driver kevulndb.DB, locked bool, err error) {
 | 
			
		||||
	if cnf.IsFetchViaHTTP() {
 | 
			
		||||
		return nil, false, nil
 | 
			
		||||
	}
 | 
			
		||||
	path := cnf.GetURL()
 | 
			
		||||
	if cnf.GetType() == "sqlite3" {
 | 
			
		||||
		path = cnf.GetSQLite3Path()
 | 
			
		||||
	}
 | 
			
		||||
	if driver, locked, err = kevulndb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), kevulndb.Option{}); err != nil {
 | 
			
		||||
		if locked {
 | 
			
		||||
			return nil, true, xerrors.Errorf("kevulnDB is locked. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		return nil, false, err
 | 
			
		||||
	}
 | 
			
		||||
	return driver, false, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package detector
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										196
									
								
								detector/msf.go
									
									
									
									
									
								
							
							
						
						
									
										196
									
								
								detector/msf.go
									
									
									
									
									
								
							@@ -1,50 +1,186 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
func ConvertToModelsMsf(ms []*metasploitmodels.Metasploit) (modules []models.Metasploit) {
 | 
			
		||||
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
 | 
			
		||||
		if 0 < len(m.References) {
 | 
			
		||||
@@ -71,7 +207,7 @@ func newMetasploitDB(cnf config.VulnDictInterface) (driver metasploitdb.DB, lock
 | 
			
		||||
	if cnf.GetType() == "sqlite3" {
 | 
			
		||||
		path = cnf.GetSQLite3Path()
 | 
			
		||||
	}
 | 
			
		||||
	if driver, locked, err = metasploitdb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), false); err != nil {
 | 
			
		||||
	if driver, locked, err = metasploitdb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), metasploitdb.Option{}); err != nil {
 | 
			
		||||
		if locked {
 | 
			
		||||
			return nil, true, xerrors.Errorf("metasploitDB is locked. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package detector
 | 
			
		||||
@@ -8,6 +9,7 @@ import (
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"time"
 | 
			
		||||
@@ -66,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
 | 
			
		||||
@@ -143,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)
 | 
			
		||||
@@ -196,30 +197,34 @@ func isCveInfoUpdated(cveID string, previous, current models.ScanResult) bool {
 | 
			
		||||
		models.NewCveContentType(current.Family),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	prevLastModified := map[models.CveContentType]time.Time{}
 | 
			
		||||
	prevLastModified := map[models.CveContentType][]time.Time{}
 | 
			
		||||
	preVinfo, ok := previous.ScannedCves[cveID]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	for _, cType := range cTypes {
 | 
			
		||||
		if content, ok := preVinfo.CveContents[cType]; ok {
 | 
			
		||||
			prevLastModified[cType] = content.LastModified
 | 
			
		||||
		if conts, ok := preVinfo.CveContents[cType]; ok {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
				prevLastModified[cType] = append(prevLastModified[cType], cont.LastModified)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	curLastModified := map[models.CveContentType]time.Time{}
 | 
			
		||||
	curLastModified := map[models.CveContentType][]time.Time{}
 | 
			
		||||
	curVinfo, ok := current.ScannedCves[cveID]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	for _, cType := range cTypes {
 | 
			
		||||
		if content, ok := curVinfo.CveContents[cType]; ok {
 | 
			
		||||
			curLastModified[cType] = content.LastModified
 | 
			
		||||
		if conts, ok := curVinfo.CveContents[cType]; ok {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
				curLastModified[cType] = append(curLastModified[cType], cont.LastModified)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, t := range cTypes {
 | 
			
		||||
		if !curLastModified[t].Equal(prevLastModified[t]) {
 | 
			
		||||
		if !reflect.DeepEqual(curLastModified[t], prevLastModified[t]) {
 | 
			
		||||
			logging.Log.Debugf("%s LastModified not equal: \n%s\n%s",
 | 
			
		||||
				cveID, curLastModified[t], prevLastModified[t])
 | 
			
		||||
			return true
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package detector
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package detector
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										139
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										139
									
								
								go.mod
									
									
									
									
									
								
							@@ -1,45 +1,43 @@
 | 
			
		||||
module github.com/future-architect/vuls
 | 
			
		||||
 | 
			
		||||
go 1.16
 | 
			
		||||
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-20210520034323-54c5a82e861f
 | 
			
		||||
	github.com/aquasecurity/trivy v0.18.3
 | 
			
		||||
	github.com/aquasecurity/trivy-db v0.0.0-20210429114658-ae22941a55d0
 | 
			
		||||
	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.36.31
 | 
			
		||||
	github.com/aws/aws-sdk-go v1.40.49
 | 
			
		||||
	github.com/boltdb/bolt v1.3.1
 | 
			
		||||
	github.com/briandowns/spinner v1.16.0 // indirect
 | 
			
		||||
	github.com/cenkalti/backoff v2.2.1+incompatible
 | 
			
		||||
	github.com/cespare/xxhash/v2 v2.1.2 // indirect
 | 
			
		||||
	github.com/cheggaaa/pb/v3 v3.0.8 // indirect
 | 
			
		||||
	github.com/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/go-redis/redis/v8 v8.11.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.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/pgproto3/v2 v2.1.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.6.2
 | 
			
		||||
	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/lib/pq v1.10.2 // indirect
 | 
			
		||||
	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
 | 
			
		||||
@@ -47,20 +45,111 @@ 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/cobra v1.1.3
 | 
			
		||||
	github.com/spf13/viper v1.8.1 // indirect
 | 
			
		||||
	github.com/takuzoo3868/go-msfdb v0.1.5
 | 
			
		||||
	github.com/vulsio/go-exploitdb v0.1.8-0.20210625021845-e5081ca67229
 | 
			
		||||
	golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
 | 
			
		||||
	golang.org/x/net v0.0.0-20210716203947-853a461950ff // indirect
 | 
			
		||||
	golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602
 | 
			
		||||
	github.com/spf13/cast v1.4.1 // indirect
 | 
			
		||||
	github.com/spf13/cobra v1.2.1
 | 
			
		||||
	github.com/vulsio/go-cve-dictionary v0.8.2-0.20211028094424-0a854f8e8f85
 | 
			
		||||
	github.com/vulsio/go-exploitdb v0.4.2-0.20211028071949-1ebf9c4f6c4d
 | 
			
		||||
	github.com/vulsio/go-kev v0.0.1
 | 
			
		||||
	github.com/vulsio/go-msfdb v0.2.1-0.20211028071756-4a9759bd9f14
 | 
			
		||||
	github.com/vulsio/gost v0.4.1-0.20211028071837-7ad032a6ffa8
 | 
			
		||||
	github.com/vulsio/goval-dictionary v0.6.1-0.20211028072035-e85e14b91ccc
 | 
			
		||||
	golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
 | 
			
		||||
	golang.org/x/net v0.0.0-20211019232329-c6ed85c7a12d // indirect
 | 
			
		||||
	golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f
 | 
			
		||||
	golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
 | 
			
		||||
	golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
 | 
			
		||||
	golang.org/x/text v0.3.7 // indirect
 | 
			
		||||
	golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
 | 
			
		||||
	gorm.io/driver/mysql v1.1.1 // indirect
 | 
			
		||||
	gorm.io/gorm v1.21.11 // indirect
 | 
			
		||||
	gopkg.in/ini.v1 v1.64.0 // indirect
 | 
			
		||||
	gorm.io/driver/mysql v1.2.0 // indirect
 | 
			
		||||
	gorm.io/driver/postgres v1.2.2 // indirect
 | 
			
		||||
	gorm.io/driver/sqlite v1.2.4 // indirect
 | 
			
		||||
	k8s.io/utils v0.0.0-20210111153108-fddb29f9d009
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	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/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/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // 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.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.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
 | 
			
		||||
	github.com/jackc/pgconn v1.10.0 // indirect
 | 
			
		||||
	github.com/jackc/pgio v1.0.0 // indirect
 | 
			
		||||
	github.com/jackc/pgpassfile v1.0.0 // indirect
 | 
			
		||||
	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.3 // indirect
 | 
			
		||||
	github.com/jmespath/go-jmespath v0.4.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.11 // indirect
 | 
			
		||||
	github.com/mattn/go-sqlite3 v1.14.9 // indirect
 | 
			
		||||
	github.com/mitchellh/copystructure v1.1.1 // indirect
 | 
			
		||||
	github.com/mitchellh/mapstructure v1.4.2 // indirect
 | 
			
		||||
	github.com/mitchellh/reflectwalk v1.0.1 // 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.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/ymomoi/goval-parser v0.0.0-20170813122243-0a0be1dd9d08 // 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.19.1 // indirect
 | 
			
		||||
	golang.org/x/sys v0.0.0-20211103235746-7861aae1554b // indirect
 | 
			
		||||
	golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
 | 
			
		||||
	google.golang.org/appengine v1.6.7 // indirect
 | 
			
		||||
	google.golang.org/protobuf v1.27.1 // indirect
 | 
			
		||||
	gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect
 | 
			
		||||
	gopkg.in/yaml.v2 v2.4.0 // indirect
 | 
			
		||||
	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
 | 
			
		||||
	gorm.io/gorm v1.22.3 // indirect
 | 
			
		||||
	moul.io/http2curl v1.0.0 // indirect
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package gost
 | 
			
		||||
@@ -9,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"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -30,6 +31,7 @@ func (deb Debian) supported(major string) bool {
 | 
			
		||||
		"8":  "jessie",
 | 
			
		||||
		"9":  "stretch",
 | 
			
		||||
		"10": "buster",
 | 
			
		||||
		"11": "bullseye",
 | 
			
		||||
	}[major]
 | 
			
		||||
	return ok
 | 
			
		||||
}
 | 
			
		||||
@@ -111,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,
 | 
			
		||||
@@ -122,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,
 | 
			
		||||
@@ -141,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] = cve
 | 
			
		||||
					v.CveContents[models.DebianSecurityTracker] = []models.CveContent{cve}
 | 
			
		||||
					v.Confidences = models.Confidences{models.DebianSecurityTrackerMatch}
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
@@ -231,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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package gost
 | 
			
		||||
@@ -38,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,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package gost
 | 
			
		||||
@@ -6,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"
 | 
			
		||||
@@ -65,7 +66,7 @@ func NewClient(cnf config.GostConf, family string) (Client, error) {
 | 
			
		||||
	driver := DBDriver{DB: db, Cnf: &cnf}
 | 
			
		||||
 | 
			
		||||
	switch family {
 | 
			
		||||
	case constant.RedHat, constant.CentOS, constant.Rocky:
 | 
			
		||||
	case constant.RedHat, constant.CentOS, constant.Rocky, constant.Alma:
 | 
			
		||||
		return RedHat{Base{DBDriver: driver}}, nil
 | 
			
		||||
	case constant.Debian, constant.Raspbian:
 | 
			
		||||
		return Debian{Base{DBDriver: driver}}, nil
 | 
			
		||||
@@ -87,7 +88,7 @@ func newGostDB(cnf config.GostConf) (driver db.DB, locked bool, err error) {
 | 
			
		||||
	if cnf.GetType() == "sqlite3" {
 | 
			
		||||
		path = cnf.GetSQLite3Path()
 | 
			
		||||
	}
 | 
			
		||||
	if driver, locked, err = db.NewDB(cnf.GetType(), path, cnf.GetDebugSQL()); err != nil {
 | 
			
		||||
	if driver, locked, err = db.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), db.Option{}); err != nil {
 | 
			
		||||
		if locked {
 | 
			
		||||
			return nil, true, xerrors.Errorf("gostDB is locked. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package gost
 | 
			
		||||
@@ -7,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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,14 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"sort"
 | 
			
		||||
	"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
 | 
			
		||||
@@ -23,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
 | 
			
		||||
		}
 | 
			
		||||
@@ -32,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] = *cveCont
 | 
			
		||||
		v.CveContents[models.Microsoft] = []models.CveContent{*cveCont}
 | 
			
		||||
		v.Mitigations = append(v.Mitigations, mitigations...)
 | 
			
		||||
		r.ScannedCves[cveID] = v
 | 
			
		||||
	}
 | 
			
		||||
@@ -41,6 +47,9 @@ func (ms Microsoft) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err err
 | 
			
		||||
 | 
			
		||||
// ConvertToModel converts gost model to vuls model
 | 
			
		||||
func (ms Microsoft) ConvertToModel(cve *gostmodels.MicrosoftCVE) (*models.CveContent, []models.Mitigation) {
 | 
			
		||||
	sort.Slice(cve.ScoreSets, func(i, j int) bool {
 | 
			
		||||
		return cve.ScoreSets[i].Vector < cve.ScoreSets[j].Vector
 | 
			
		||||
	})
 | 
			
		||||
	v3score := 0.0
 | 
			
		||||
	var v3Vector string
 | 
			
		||||
	for _, scoreSet := range cve.ScoreSets {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package gost
 | 
			
		||||
@@ -12,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
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package gost
 | 
			
		||||
@@ -10,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
 | 
			
		||||
@@ -44,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++
 | 
			
		||||
@@ -84,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
 | 
			
		||||
			}
 | 
			
		||||
@@ -102,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] = *cveCont
 | 
			
		||||
			v.CveContents[models.RedHatAPI] = []models.CveContent{*cveCont}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		v = models.VulnInfo{
 | 
			
		||||
@@ -122,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] = *cveCont
 | 
			
		||||
			v.CveContents[models.RedHatAPI] = []models.CveContent{*cveCont}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		v = models.VulnInfo{
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package gost
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package gost
 | 
			
		||||
@@ -9,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
 | 
			
		||||
@@ -53,7 +54,7 @@ func (ubu Ubuntu) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error
 | 
			
		||||
 | 
			
		||||
	packCvesList := []packCves{}
 | 
			
		||||
	if ubu.DBDriver.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		url, _ := util.URLPathJoin(ubu.DBDriver.Cnf.GetURL(), "ubuntu", ubuReleaseVer, "pkg")
 | 
			
		||||
		url, _ := util.URLPathJoin(ubu.DBDriver.Cnf.GetURL(), "ubuntu", ubuReleaseVer, "pkgs")
 | 
			
		||||
		responses, err := getAllUnfixedCvesViaHTTP(r, url)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
@@ -79,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))
 | 
			
		||||
@@ -93,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))
 | 
			
		||||
@@ -115,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] = 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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package gost
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								img/sponsor/tines.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								img/sponsor/tines.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 34 KiB  | 
@@ -6469,13 +6469,13 @@
 | 
			
		||||
            "cveDict": {
 | 
			
		||||
                "Name": "cveDict",
 | 
			
		||||
                "Type": "sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/go-cve-dictionary/cve.sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-cve-dictionary/cve.sqlite3",
 | 
			
		||||
                "DebugSQL": false
 | 
			
		||||
            },
 | 
			
		||||
            "ovalDict": {
 | 
			
		||||
                "Name": "ovalDict",
 | 
			
		||||
                "Type": "sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/goval-dictionary/oval.sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/goval-dictionary/oval.sqlite3",
 | 
			
		||||
                "DebugSQL": false
 | 
			
		||||
            },
 | 
			
		||||
            "gost": {
 | 
			
		||||
@@ -6493,7 +6493,7 @@
 | 
			
		||||
            "metasploit": {
 | 
			
		||||
                "Name": "metasploit",
 | 
			
		||||
                "Type": "sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/takuzoo3868/go-msfdb/go-msfdb.sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-msfdb/go-msfdb.sqlite3",
 | 
			
		||||
                "DebugSQL": false
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
@@ -5569,13 +5569,13 @@
 | 
			
		||||
            "cveDict": {
 | 
			
		||||
                "Name": "cveDict",
 | 
			
		||||
                "Type": "sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/go-cve-dictionary/cve.sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-cve-dictionary/cve.sqlite3",
 | 
			
		||||
                "DebugSQL": false
 | 
			
		||||
            },
 | 
			
		||||
            "ovalDict": {
 | 
			
		||||
                "Name": "ovalDict",
 | 
			
		||||
                "Type": "sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/goval-dictionary/oval.sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/goval-dictionary/oval.sqlite3",
 | 
			
		||||
                "DebugSQL": false
 | 
			
		||||
            },
 | 
			
		||||
            "gost": {
 | 
			
		||||
@@ -5593,7 +5593,7 @@
 | 
			
		||||
            "metasploit": {
 | 
			
		||||
                "Name": "metasploit",
 | 
			
		||||
                "Type": "sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/takuzoo3868/go-msfdb/go-msfdb.sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-msfdb/go-msfdb.sqlite3",
 | 
			
		||||
                "DebugSQL": false
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
@@ -5566,13 +5566,13 @@
 | 
			
		||||
            "cveDict": {
 | 
			
		||||
                "Name": "cveDict",
 | 
			
		||||
                "Type": "sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/go-cve-dictionary/cve.sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-cve-dictionary/cve.sqlite3",
 | 
			
		||||
                "DebugSQL": false
 | 
			
		||||
            },
 | 
			
		||||
            "ovalDict": {
 | 
			
		||||
                "Name": "ovalDict",
 | 
			
		||||
                "Type": "sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/goval-dictionary/oval.sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/goval-dictionary/oval.sqlite3",
 | 
			
		||||
                "DebugSQL": false
 | 
			
		||||
            },
 | 
			
		||||
            "gost": {
 | 
			
		||||
@@ -5590,7 +5590,7 @@
 | 
			
		||||
            "metasploit": {
 | 
			
		||||
                "Name": "metasploit",
 | 
			
		||||
                "Type": "sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/takuzoo3868/go-msfdb/go-msfdb.sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-msfdb/go-msfdb.sqlite3",
 | 
			
		||||
                "DebugSQL": false
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
@@ -5093,13 +5093,13 @@
 | 
			
		||||
            "cveDict": {
 | 
			
		||||
                "Name": "cveDict",
 | 
			
		||||
                "Type": "sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/go-cve-dictionary/cve.sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-cve-dictionary/cve.sqlite3",
 | 
			
		||||
                "DebugSQL": false
 | 
			
		||||
            },
 | 
			
		||||
            "ovalDict": {
 | 
			
		||||
                "Name": "ovalDict",
 | 
			
		||||
                "Type": "sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/goval-dictionary/oval.sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/goval-dictionary/oval.sqlite3",
 | 
			
		||||
                "DebugSQL": false
 | 
			
		||||
            },
 | 
			
		||||
            "gost": {
 | 
			
		||||
@@ -5117,7 +5117,7 @@
 | 
			
		||||
            "metasploit": {
 | 
			
		||||
                "Name": "metasploit",
 | 
			
		||||
                "Type": "sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/takuzoo3868/go-msfdb/go-msfdb.sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-msfdb/go-msfdb.sqlite3",
 | 
			
		||||
                "DebugSQL": false
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
@@ -44,6 +44,7 @@
 | 
			
		||||
        "version": "",
 | 
			
		||||
        "rebootRequired": false
 | 
			
		||||
    },
 | 
			
		||||
    "enabledDnfModules": ["nodejs:12"],
 | 
			
		||||
    "packages": {
 | 
			
		||||
        "NetworkManager": {
 | 
			
		||||
            "name": "NetworkManager",
 | 
			
		||||
@@ -5906,6 +5907,15 @@
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        "nodejs": {
 | 
			
		||||
            "name": "nodejs",
 | 
			
		||||
            "version": "1:12.14.1",
 | 
			
		||||
            "release": "1.module+el8.1.0+5466+30f75628",
 | 
			
		||||
            "newVersion": "1:12.14.1",
 | 
			
		||||
            "newRelease": "1.module+el8.1.0+5466+30f75629",
 | 
			
		||||
            "arch": "x86_64",
 | 
			
		||||
            "repository": "rhel-8-appstream-rhui-rpms"
 | 
			
		||||
        },
 | 
			
		||||
        "sed": {
 | 
			
		||||
            "name": "sed",
 | 
			
		||||
            "version": "4.5",
 | 
			
		||||
@@ -6861,13 +6871,13 @@
 | 
			
		||||
            "cveDict": {
 | 
			
		||||
                "Name": "cveDict",
 | 
			
		||||
                "Type": "sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/go-cve-dictionary/cve.sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-cve-dictionary/cve.sqlite3",
 | 
			
		||||
                "DebugSQL": false
 | 
			
		||||
            },
 | 
			
		||||
            "ovalDict": {
 | 
			
		||||
                "Name": "ovalDict",
 | 
			
		||||
                "Type": "sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/goval-dictionary/oval.sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/goval-dictionary/oval.sqlite3",
 | 
			
		||||
                "DebugSQL": false
 | 
			
		||||
            },
 | 
			
		||||
            "gost": {
 | 
			
		||||
@@ -6885,7 +6895,7 @@
 | 
			
		||||
            "metasploit": {
 | 
			
		||||
                "Name": "metasploit",
 | 
			
		||||
                "Type": "sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/takuzoo3868/go-msfdb/go-msfdb.sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-msfdb/go-msfdb.sqlite3",
 | 
			
		||||
                "DebugSQL": false
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
@@ -8544,13 +8544,13 @@
 | 
			
		||||
            "cveDict": {
 | 
			
		||||
                "Name": "cveDict",
 | 
			
		||||
                "Type": "sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/go-cve-dictionary/cve.sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-cve-dictionary/cve.sqlite3",
 | 
			
		||||
                "DebugSQL": false
 | 
			
		||||
            },
 | 
			
		||||
            "ovalDict": {
 | 
			
		||||
                "Name": "ovalDict",
 | 
			
		||||
                "Type": "sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/goval-dictionary/oval.sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/goval-dictionary/oval.sqlite3",
 | 
			
		||||
                "DebugSQL": false
 | 
			
		||||
            },
 | 
			
		||||
            "gost": {
 | 
			
		||||
@@ -8568,7 +8568,7 @@
 | 
			
		||||
            "metasploit": {
 | 
			
		||||
                "Name": "metasploit",
 | 
			
		||||
                "Type": "sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/takuzoo3868/go-msfdb/go-msfdb.sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-msfdb/go-msfdb.sqlite3",
 | 
			
		||||
                "DebugSQL": false
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
@@ -8494,13 +8494,13 @@
 | 
			
		||||
            "cveDict": {
 | 
			
		||||
                "Name": "cveDict",
 | 
			
		||||
                "Type": "sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/go-cve-dictionary/cve.sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-cve-dictionary/cve.sqlite3",
 | 
			
		||||
                "DebugSQL": false
 | 
			
		||||
            },
 | 
			
		||||
            "ovalDict": {
 | 
			
		||||
                "Name": "ovalDict",
 | 
			
		||||
                "Type": "sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/goval-dictionary/oval.sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/goval-dictionary/oval.sqlite3",
 | 
			
		||||
                "DebugSQL": false
 | 
			
		||||
            },
 | 
			
		||||
            "gost": {
 | 
			
		||||
@@ -8518,7 +8518,7 @@
 | 
			
		||||
            "metasploit": {
 | 
			
		||||
                "Name": "metasploit",
 | 
			
		||||
                "Type": "sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/takuzoo3868/go-msfdb/go-msfdb.sqlite3",
 | 
			
		||||
                "SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-msfdb/go-msfdb.sqlite3",
 | 
			
		||||
                "DebugSQL": false
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
@@ -1,34 +1,50 @@
 | 
			
		||||
[cveDict]
 | 
			
		||||
  Type = "sqlite3"
 | 
			
		||||
  SQLite3Path = "/home/ubuntu/vulsctl/docker/cve.sqlite3"
 | 
			
		||||
  SQLite3Path = "/data/vulsctl/docker/cve.sqlite3"
 | 
			
		||||
 | 
			
		||||
[ovalDict]
 | 
			
		||||
  Type = "sqlite3"
 | 
			
		||||
  SQLite3Path = "/home/ubuntu/vulsctl/docker/oval.sqlite3"
 | 
			
		||||
  SQLite3Path = "/data/vulsctl/docker/oval.sqlite3"
 | 
			
		||||
 | 
			
		||||
[gost]
 | 
			
		||||
  Type = "sqlite3"
 | 
			
		||||
  SQLite3Path = "/home/ubuntu/vulsctl/docker/gost.sqlite3"
 | 
			
		||||
  SQLite3Path = "/data/vulsctl/docker/gost.sqlite3"
 | 
			
		||||
 | 
			
		||||
[exploit]
 | 
			
		||||
  Type = "sqlite3"
 | 
			
		||||
  SQLite3Path = "/home/ubuntu/vulsctl/docker/go-exploitdb.sqlite3"
 | 
			
		||||
  SQLite3Path = "/data/vulsctl/docker/go-exploitdb.sqlite3"
 | 
			
		||||
 | 
			
		||||
[metasploit]
 | 
			
		||||
  type = "sqlite3"
 | 
			
		||||
  SQLite3Path = "/home/ubuntu/vulsctl/docker/go-msfdb.sqlite3"
 | 
			
		||||
  SQLite3Path = "/data/vulsctl/docker/go-msfdb.sqlite3"
 | 
			
		||||
 | 
			
		||||
[default]
 | 
			
		||||
 | 
			
		||||
[servers]
 | 
			
		||||
 | 
			
		||||
[servers.rails]
 | 
			
		||||
[servers.nvd_exact]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
cpeNames = [ "cpe:/a:rubyonrails:rails:3.0.1" ]
 | 
			
		||||
 | 
			
		||||
[servers.cpe_vendor_product_match]
 | 
			
		||||
[servers.nvd_rough]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
cpeNames = ["cpe:/a:hitachi_abb_power_grids:afs660"]
 | 
			
		||||
cpeNames = [ "cpe:/a:openssl:openssl:1.1.1" ]
 | 
			
		||||
 | 
			
		||||
[servers.nvd_vendor_product]
 | 
			
		||||
 type = "pseudo"
 | 
			
		||||
 cpeNames = [ "cpe:/a:djangoproject:django" ]
 | 
			
		||||
 | 
			
		||||
[servers.nvd_match_no_jvn]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
cpeNames = [ "cpe:/a:apache:tomcat:7.0.27" ]
 | 
			
		||||
 | 
			
		||||
[servers.jvn_vendor_product]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
cpeNames = [ "cpe:/a:hitachi_abb_power_grids:afs660:1.0.0" ]
 | 
			
		||||
 | 
			
		||||
[servers.jvn_vendor_product_nover]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
cpeNames = [ "cpe:/o:nec:aterm_wg2600hp2_firmware"]
 | 
			
		||||
 | 
			
		||||
[servers.gemfile]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
 
 | 
			
		||||
@@ -22,13 +22,30 @@ Url = "redis://127.0.0.1/3"
 | 
			
		||||
 | 
			
		||||
[servers]
 | 
			
		||||
 | 
			
		||||
[servers.rails]
 | 
			
		||||
[servers.nvd_exact]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
cpeNames = [ "cpe:/a:rubyonrails:rails:3.0.1" ]
 | 
			
		||||
#cpeNames = [ "cpe:/a:rubyonrails:rails:4.0.0" ]
 | 
			
		||||
 | 
			
		||||
[servers.cpe_vendor_product_match]
 | 
			
		||||
[servers.nvd_rough]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
cpeNames = ["cpe:/a:hitachi_abb_power_grids:afs660"]
 | 
			
		||||
cpeNames = [ "cpe:/a:openssl:openssl:1.1.1" ]
 | 
			
		||||
 | 
			
		||||
[servers.nvd_vendor_product]
 | 
			
		||||
 type = "pseudo"
 | 
			
		||||
 cpeNames = [ "cpe:/a:djangoproject:django" ]
 | 
			
		||||
 | 
			
		||||
[servers.nvd_match_no_jvn]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
cpeNames = [ "cpe:/a:apache:tomcat:7.0.27" ]
 | 
			
		||||
 | 
			
		||||
[servers.jvn_vendor_product]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
cpeNames = [ "cpe:/a:hitachi_abb_power_grids:afs660:1.0.0" ]
 | 
			
		||||
 | 
			
		||||
[servers.jvn_vendor_product_nover]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
cpeNames = [ "cpe:/o:nec:aterm_wg2600hp2_firmware"]
 | 
			
		||||
 | 
			
		||||
[servers.gemfile]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
 
 | 
			
		||||
@@ -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"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -100,7 +101,7 @@ func NewCustomLogger(debug, quiet, logToFile bool, logDir, logMsgAnsiColor, serv
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else if quiet {
 | 
			
		||||
		log.Out = io.Discard
 | 
			
		||||
		log.Out = ioutil.Discard
 | 
			
		||||
	} else {
 | 
			
		||||
		log.Out = os.Stderr
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
@@ -8,13 +9,26 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CveContents has CveContent
 | 
			
		||||
type CveContents map[CveContentType]CveContent
 | 
			
		||||
type CveContents map[CveContentType][]CveContent
 | 
			
		||||
 | 
			
		||||
// NewCveContents create CveContents
 | 
			
		||||
func NewCveContents(conts ...CveContent) CveContents {
 | 
			
		||||
	m := CveContents{}
 | 
			
		||||
	for _, cont := range conts {
 | 
			
		||||
		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
 | 
			
		||||
}
 | 
			
		||||
@@ -44,16 +58,18 @@ func (v CveContents) Except(exceptCtypes ...CveContentType) (values CveContents)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PrimarySrcURLs returns link of source
 | 
			
		||||
func (v CveContents) PrimarySrcURLs(lang, myFamily, cveID string) (values []CveContentStr) {
 | 
			
		||||
func (v CveContents) PrimarySrcURLs(lang, myFamily, cveID string, confidences Confidences) (values []CveContentStr) {
 | 
			
		||||
	if cveID == "" {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if cont, found := v[Nvd]; found {
 | 
			
		||||
		for _, r := range cont.References {
 | 
			
		||||
			for _, t := range r.Tags {
 | 
			
		||||
				if t == "Vendor Advisory" {
 | 
			
		||||
					values = append(values, CveContentStr{Nvd, r.Link})
 | 
			
		||||
	if conts, found := v[Nvd]; found {
 | 
			
		||||
		for _, cont := range conts {
 | 
			
		||||
			for _, r := range cont.References {
 | 
			
		||||
				for _, t := range r.Tags {
 | 
			
		||||
					if t == "Vendor Advisory" {
 | 
			
		||||
						values = append(values, CveContentStr{Nvd, r.Link})
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -61,17 +77,31 @@ func (v CveContents) PrimarySrcURLs(lang, myFamily, cveID string) (values []CveC
 | 
			
		||||
 | 
			
		||||
	order := CveContentTypes{Nvd, NewCveContentType(myFamily), GitHub}
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if cont, found := v[ctype]; found {
 | 
			
		||||
			if cont.SourceLink == "" {
 | 
			
		||||
				continue
 | 
			
		||||
		if conts, found := v[ctype]; found {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
				if cont.SourceLink == "" {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				values = append(values, CveContentStr{ctype, cont.SourceLink})
 | 
			
		||||
			}
 | 
			
		||||
			values = append(values, CveContentStr{ctype, cont.SourceLink})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if lang == "ja" {
 | 
			
		||||
		if cont, found := v[Jvn]; found && 0 < len(cont.SourceLink) {
 | 
			
		||||
			values = append(values, CveContentStr{Jvn, cont.SourceLink})
 | 
			
		||||
	jvnMatch := false
 | 
			
		||||
	for _, confidence := range confidences {
 | 
			
		||||
		if confidence.DetectionMethod == JvnVendorProductMatchStr {
 | 
			
		||||
			jvnMatch = true
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if lang == "ja" || jvnMatch {
 | 
			
		||||
		if conts, found := v[Jvn]; found {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
				if 0 < len(cont.SourceLink) {
 | 
			
		||||
					values = append(values, CveContentStr{Jvn, cont.SourceLink})
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -86,14 +116,17 @@ func (v CveContents) PrimarySrcURLs(lang, myFamily, cveID string) (values []CveC
 | 
			
		||||
 | 
			
		||||
// PatchURLs returns link of patch
 | 
			
		||||
func (v CveContents) PatchURLs() (urls []string) {
 | 
			
		||||
	cont, found := v[Nvd]
 | 
			
		||||
	conts, found := v[Nvd]
 | 
			
		||||
	if !found {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	for _, r := range cont.References {
 | 
			
		||||
		for _, t := range r.Tags {
 | 
			
		||||
			if t == "Patch" {
 | 
			
		||||
				urls = append(urls, r.Link)
 | 
			
		||||
 | 
			
		||||
	for _, cont := range conts {
 | 
			
		||||
		for _, r := range cont.References {
 | 
			
		||||
			for _, t := range r.Tags {
 | 
			
		||||
				if t == "Patch" {
 | 
			
		||||
					urls = append(urls, r.Link)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -130,11 +163,15 @@ func (v CveContents) Cpes(myFamily string) (values []CveContentCpes) {
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(order...)...)
 | 
			
		||||
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if cont, found := v[ctype]; found && 0 < len(cont.Cpes) {
 | 
			
		||||
			values = append(values, CveContentCpes{
 | 
			
		||||
				Type:  ctype,
 | 
			
		||||
				Value: cont.Cpes,
 | 
			
		||||
			})
 | 
			
		||||
		if conts, found := v[ctype]; found {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
				if 0 < len(cont.Cpes) {
 | 
			
		||||
					values = append(values, CveContentCpes{
 | 
			
		||||
						Type:  ctype,
 | 
			
		||||
						Value: cont.Cpes,
 | 
			
		||||
					})
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
@@ -152,11 +189,15 @@ func (v CveContents) References(myFamily string) (values []CveContentRefs) {
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(order...)...)
 | 
			
		||||
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if cont, found := v[ctype]; found && 0 < len(cont.References) {
 | 
			
		||||
			values = append(values, CveContentRefs{
 | 
			
		||||
				Type:  ctype,
 | 
			
		||||
				Value: cont.References,
 | 
			
		||||
			})
 | 
			
		||||
		if conts, found := v[ctype]; found {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
				if 0 < len(cont.References) {
 | 
			
		||||
					values = append(values, CveContentRefs{
 | 
			
		||||
						Type:  ctype,
 | 
			
		||||
						Value: cont.References,
 | 
			
		||||
					})
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -168,17 +209,21 @@ func (v CveContents) CweIDs(myFamily string) (values []CveContentStr) {
 | 
			
		||||
	order := CveContentTypes{NewCveContentType(myFamily)}
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(order...)...)
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if cont, found := v[ctype]; found && 0 < len(cont.CweIDs) {
 | 
			
		||||
			for _, cweID := range cont.CweIDs {
 | 
			
		||||
				for _, val := range values {
 | 
			
		||||
					if val.Value == cweID {
 | 
			
		||||
						continue
 | 
			
		||||
		if conts, found := v[ctype]; found {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
				if 0 < len(cont.CweIDs) {
 | 
			
		||||
					for _, cweID := range cont.CweIDs {
 | 
			
		||||
						for _, val := range values {
 | 
			
		||||
							if val.Value == cweID {
 | 
			
		||||
								continue
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
						values = append(values, CveContentStr{
 | 
			
		||||
							Type:  ctype,
 | 
			
		||||
							Value: cweID,
 | 
			
		||||
						})
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				values = append(values, CveContentStr{
 | 
			
		||||
					Type:  ctype,
 | 
			
		||||
					Value: cweID,
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -197,6 +242,47 @@ 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
 | 
			
		||||
		sort.Slice(contents, func(i, j int) bool {
 | 
			
		||||
			if contents[i].Cvss3Score > contents[j].Cvss3Score {
 | 
			
		||||
				return true
 | 
			
		||||
			} else if contents[i].Cvss3Score == contents[i].Cvss3Score {
 | 
			
		||||
				if contents[i].Cvss2Score > contents[j].Cvss2Score {
 | 
			
		||||
					return true
 | 
			
		||||
				} else if contents[i].Cvss2Score == contents[i].Cvss2Score {
 | 
			
		||||
					if contents[i].SourceLink < contents[j].SourceLink {
 | 
			
		||||
						return true
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return false
 | 
			
		||||
		})
 | 
			
		||||
		v[contType] = contents
 | 
			
		||||
	}
 | 
			
		||||
	for contType, contents := range v {
 | 
			
		||||
		for cveID, cont := range contents {
 | 
			
		||||
			sort.Slice(cont.References, func(i, j int) bool {
 | 
			
		||||
				return cont.References[i].Link < cont.References[j].Link
 | 
			
		||||
			})
 | 
			
		||||
			sort.Slice(cont.CweIDs, func(i, j int) bool {
 | 
			
		||||
				return cont.CweIDs[i] < cont.CweIDs[j]
 | 
			
		||||
			})
 | 
			
		||||
			for i, ref := range cont.References {
 | 
			
		||||
				// sort v.CveContents[].References[].Tags
 | 
			
		||||
				sort.Slice(ref.Tags, func(j, k int) bool {
 | 
			
		||||
					return ref.Tags[j] < ref.Tags[k]
 | 
			
		||||
				})
 | 
			
		||||
				cont.References[i] = ref
 | 
			
		||||
			}
 | 
			
		||||
			contents[cveID] = cont
 | 
			
		||||
		}
 | 
			
		||||
		v[contType] = contents
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CveContent has abstraction of various vulnerability information
 | 
			
		||||
type CveContent struct {
 | 
			
		||||
	Type          CveContentType    `json:"type"`
 | 
			
		||||
@@ -233,7 +319,7 @@ func NewCveContentType(name string) CveContentType {
 | 
			
		||||
		return Nvd
 | 
			
		||||
	case "jvn":
 | 
			
		||||
		return Jvn
 | 
			
		||||
	case "redhat", "centos", "rocky":
 | 
			
		||||
	case "redhat", "centos", "alma", "rocky":
 | 
			
		||||
		return RedHat
 | 
			
		||||
	case "oracle":
 | 
			
		||||
		return Oracle
 | 
			
		||||
 
 | 
			
		||||
@@ -11,12 +11,12 @@ func TestExcept(t *testing.T) {
 | 
			
		||||
		out CveContents
 | 
			
		||||
	}{{
 | 
			
		||||
		in: CveContents{
 | 
			
		||||
			RedHat: {Type: RedHat},
 | 
			
		||||
			Ubuntu: {Type: Ubuntu},
 | 
			
		||||
			Debian: {Type: Debian},
 | 
			
		||||
			RedHat: []CveContent{{Type: RedHat}},
 | 
			
		||||
			Ubuntu: []CveContent{{Type: Ubuntu}},
 | 
			
		||||
			Debian: []CveContent{{Type: Debian}},
 | 
			
		||||
		},
 | 
			
		||||
		out: CveContents{
 | 
			
		||||
			RedHat: {Type: RedHat},
 | 
			
		||||
			RedHat: []CveContent{{Type: RedHat}},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	}
 | 
			
		||||
@@ -30,9 +30,10 @@ func TestExcept(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
func TestSourceLinks(t *testing.T) {
 | 
			
		||||
	type in struct {
 | 
			
		||||
		lang  string
 | 
			
		||||
		cveID string
 | 
			
		||||
		cont  CveContents
 | 
			
		||||
		lang        string
 | 
			
		||||
		cveID       string
 | 
			
		||||
		cont        CveContents
 | 
			
		||||
		confidences Confidences
 | 
			
		||||
	}
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in  in
 | 
			
		||||
@@ -44,15 +45,15 @@ func TestSourceLinks(t *testing.T) {
 | 
			
		||||
				lang:  "ja",
 | 
			
		||||
				cveID: "CVE-2017-6074",
 | 
			
		||||
				cont: CveContents{
 | 
			
		||||
					Jvn: {
 | 
			
		||||
					Jvn: []CveContent{{
 | 
			
		||||
						Type:       Jvn,
 | 
			
		||||
						SourceLink: "https://jvn.jp/vu/JVNVU93610402/",
 | 
			
		||||
					},
 | 
			
		||||
					RedHat: {
 | 
			
		||||
					}},
 | 
			
		||||
					RedHat: []CveContent{{
 | 
			
		||||
						Type:       RedHat,
 | 
			
		||||
						SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
 | 
			
		||||
					},
 | 
			
		||||
					Nvd: {
 | 
			
		||||
					}},
 | 
			
		||||
					Nvd: []CveContent{{
 | 
			
		||||
						Type: Nvd,
 | 
			
		||||
						References: []Reference{
 | 
			
		||||
							{
 | 
			
		||||
@@ -69,7 +70,7 @@ func TestSourceLinks(t *testing.T) {
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
 | 
			
		||||
					},
 | 
			
		||||
					}},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: []CveContentStr{
 | 
			
		||||
@@ -97,14 +98,14 @@ func TestSourceLinks(t *testing.T) {
 | 
			
		||||
				lang:  "en",
 | 
			
		||||
				cveID: "CVE-2017-6074",
 | 
			
		||||
				cont: CveContents{
 | 
			
		||||
					Jvn: {
 | 
			
		||||
					Jvn: []CveContent{{
 | 
			
		||||
						Type:       Jvn,
 | 
			
		||||
						SourceLink: "https://jvn.jp/vu/JVNVU93610402/",
 | 
			
		||||
					},
 | 
			
		||||
					RedHat: {
 | 
			
		||||
					}},
 | 
			
		||||
					RedHat: []CveContent{{
 | 
			
		||||
						Type:       RedHat,
 | 
			
		||||
						SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
 | 
			
		||||
					},
 | 
			
		||||
					}},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: []CveContentStr{
 | 
			
		||||
@@ -128,11 +129,123 @@ func TestSourceLinks(t *testing.T) {
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		// Confidence: JvnVendorProductMatch
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				lang:  "en",
 | 
			
		||||
				cveID: "CVE-2017-6074",
 | 
			
		||||
				cont: CveContents{
 | 
			
		||||
					Jvn: []CveContent{{
 | 
			
		||||
						Type:       Jvn,
 | 
			
		||||
						SourceLink: "https://jvn.jp/vu/JVNVU93610402/",
 | 
			
		||||
					}},
 | 
			
		||||
				},
 | 
			
		||||
				confidences: Confidences{
 | 
			
		||||
					Confidence{DetectionMethod: JvnVendorProductMatchStr},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: []CveContentStr{
 | 
			
		||||
				{
 | 
			
		||||
					Type:  Jvn,
 | 
			
		||||
					Value: "https://jvn.jp/vu/JVNVU93610402/",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for i, tt := range tests {
 | 
			
		||||
		actual := tt.in.cont.PrimarySrcURLs(tt.in.lang, "redhat", tt.in.cveID)
 | 
			
		||||
		actual := tt.in.cont.PrimarySrcURLs(tt.in.lang, "redhat", tt.in.cveID, tt.in.confidences)
 | 
			
		||||
		if !reflect.DeepEqual(tt.out, actual) {
 | 
			
		||||
			t.Errorf("\n[%d] expected: %v\n  actual: %v\n", i, tt.out, actual)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCveContents_Sort(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name string
 | 
			
		||||
		v    CveContents
 | 
			
		||||
		want CveContents
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "sorted",
 | 
			
		||||
			v: map[CveContentType][]CveContent{
 | 
			
		||||
				"jvn": {
 | 
			
		||||
					{Cvss3Score: 3},
 | 
			
		||||
					{Cvss3Score: 10},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			want: map[CveContentType][]CveContent{
 | 
			
		||||
				"jvn": {
 | 
			
		||||
					{Cvss3Score: 10},
 | 
			
		||||
					{Cvss3Score: 3},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "sort JVN by cvss3, cvss2, sourceLink",
 | 
			
		||||
			v: map[CveContentType][]CveContent{
 | 
			
		||||
				"jvn": {
 | 
			
		||||
					{
 | 
			
		||||
						Cvss3Score: 3,
 | 
			
		||||
						Cvss2Score: 3,
 | 
			
		||||
						SourceLink: "https://jvndb.jvn.jp/ja/contents/2023/JVNDB-2023-001210.html",
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						Cvss3Score: 3,
 | 
			
		||||
						Cvss2Score: 3,
 | 
			
		||||
						SourceLink: "https://jvndb.jvn.jp/ja/contents/2021/JVNDB-2021-001210.html",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			want: map[CveContentType][]CveContent{
 | 
			
		||||
				"jvn": {
 | 
			
		||||
					{
 | 
			
		||||
						Cvss3Score: 3,
 | 
			
		||||
						Cvss2Score: 3,
 | 
			
		||||
						SourceLink: "https://jvndb.jvn.jp/ja/contents/2021/JVNDB-2021-001210.html",
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						Cvss3Score: 3,
 | 
			
		||||
						Cvss2Score: 3,
 | 
			
		||||
						SourceLink: "https://jvndb.jvn.jp/ja/contents/2023/JVNDB-2023-001210.html",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "sort JVN by cvss3, cvss2",
 | 
			
		||||
			v: map[CveContentType][]CveContent{
 | 
			
		||||
				"jvn": {
 | 
			
		||||
					{
 | 
			
		||||
						Cvss3Score: 3,
 | 
			
		||||
						Cvss2Score: 1,
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						Cvss3Score: 3,
 | 
			
		||||
						Cvss2Score: 10,
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			want: map[CveContentType][]CveContent{
 | 
			
		||||
				"jvn": {
 | 
			
		||||
					{
 | 
			
		||||
						Cvss3Score: 3,
 | 
			
		||||
						Cvss2Score: 10,
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						Cvss3Score: 3,
 | 
			
		||||
						Cvss2Score: 1,
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			tt.v.Sort()
 | 
			
		||||
			if !reflect.DeepEqual(tt.v, tt.want) {
 | 
			
		||||
				t.Errorf("\n[%s] expected: %v\n  actual: %v\n", tt.name, tt.want, tt.v)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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,46 +104,52 @@ 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getCveContents(cveID string, vul trivyDBTypes.Vulnerability) (contents map[CveContentType]CveContent) {
 | 
			
		||||
	contents = map[CveContentType]CveContent{}
 | 
			
		||||
func getCveContents(cveID string, vul trivyDBTypes.Vulnerability) (contents map[CveContentType][]CveContent) {
 | 
			
		||||
	contents = map[CveContentType][]CveContent{}
 | 
			
		||||
	refs := []Reference{}
 | 
			
		||||
	for _, refURL := range vul.References {
 | 
			
		||||
		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] = 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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -105,13 +105,12 @@ func (r *ScanResult) FilterInactiveWordPressLibs(detectInactive bool) {
 | 
			
		||||
		return false
 | 
			
		||||
	})
 | 
			
		||||
	r.ScannedCves = filtered
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReportFileName returns the filename on localhost without extension
 | 
			
		||||
func (r ScanResult) ReportFileName() (name string) {
 | 
			
		||||
	if r.Container.ContainerID == "" {
 | 
			
		||||
		return fmt.Sprintf("%s", r.ServerName)
 | 
			
		||||
		return r.ServerName
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%s@%s", r.Container.Name, r.ServerName)
 | 
			
		||||
}
 | 
			
		||||
@@ -246,17 +245,21 @@ func (r ScanResult) FormatMetasploitCveSummary() string {
 | 
			
		||||
 | 
			
		||||
// FormatAlertSummary returns a summary of CERT alerts
 | 
			
		||||
func (r ScanResult) FormatAlertSummary() string {
 | 
			
		||||
	jaCnt := 0
 | 
			
		||||
	enCnt := 0
 | 
			
		||||
	cisaCnt := 0
 | 
			
		||||
	uscertCnt := 0
 | 
			
		||||
	jpcertCnt := 0
 | 
			
		||||
	for _, vuln := range r.ScannedCves {
 | 
			
		||||
		if len(vuln.AlertDict.En) > 0 {
 | 
			
		||||
			enCnt += len(vuln.AlertDict.En)
 | 
			
		||||
		if len(vuln.AlertDict.CISA) > 0 {
 | 
			
		||||
			cisaCnt += len(vuln.AlertDict.CISA)
 | 
			
		||||
		}
 | 
			
		||||
		if len(vuln.AlertDict.Ja) > 0 {
 | 
			
		||||
			jaCnt += len(vuln.AlertDict.Ja)
 | 
			
		||||
		if len(vuln.AlertDict.USCERT) > 0 {
 | 
			
		||||
			uscertCnt += len(vuln.AlertDict.USCERT)
 | 
			
		||||
		}
 | 
			
		||||
		if len(vuln.AlertDict.JPCERT) > 0 {
 | 
			
		||||
			jpcertCnt += len(vuln.AlertDict.JPCERT)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("en: %d, ja: %d alerts", enCnt, jaCnt)
 | 
			
		||||
	return fmt.Sprintf("cisa: %d, uscert: %d, jpcert: %d alerts", cisaCnt, uscertCnt, jpcertCnt)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r ScanResult) isDisplayUpdatableNum(mode config.ScanMode) bool {
 | 
			
		||||
@@ -415,27 +418,17 @@ func (r *ScanResult) SortForJSONOutput() {
 | 
			
		||||
		sort.Slice(v.Mitigations, func(i, j int) bool {
 | 
			
		||||
			return v.Mitigations[i].URL < v.Mitigations[j].URL
 | 
			
		||||
		})
 | 
			
		||||
		for kk, vv := range v.CveContents {
 | 
			
		||||
			sort.Slice(vv.References, func(i, j int) bool {
 | 
			
		||||
				return vv.References[i].Link < vv.References[j].Link
 | 
			
		||||
			})
 | 
			
		||||
			sort.Slice(vv.CweIDs, func(i, j int) bool {
 | 
			
		||||
				return vv.CweIDs[i] < vv.CweIDs[j]
 | 
			
		||||
			})
 | 
			
		||||
			for kkk, vvv := range vv.References {
 | 
			
		||||
				// sort v.CveContents[].References[].Tags
 | 
			
		||||
				sort.Slice(vvv.Tags, func(i, j int) bool {
 | 
			
		||||
					return vvv.Tags[i] < vvv.Tags[j]
 | 
			
		||||
				})
 | 
			
		||||
				vv.References[kkk] = vvv
 | 
			
		||||
			}
 | 
			
		||||
			v.CveContents[kk] = vv
 | 
			
		||||
		}
 | 
			
		||||
		sort.Slice(v.AlertDict.En, func(i, j int) bool {
 | 
			
		||||
			return v.AlertDict.En[i].Title < v.AlertDict.En[j].Title
 | 
			
		||||
 | 
			
		||||
		v.CveContents.Sort()
 | 
			
		||||
 | 
			
		||||
		sort.Slice(v.AlertDict.USCERT, func(i, j int) bool {
 | 
			
		||||
			return v.AlertDict.USCERT[i].Title < v.AlertDict.USCERT[j].Title
 | 
			
		||||
		})
 | 
			
		||||
		sort.Slice(v.AlertDict.Ja, func(i, j int) bool {
 | 
			
		||||
			return v.AlertDict.Ja[i].Title < v.AlertDict.Ja[j].Title
 | 
			
		||||
		sort.Slice(v.AlertDict.JPCERT, func(i, j int) bool {
 | 
			
		||||
			return v.AlertDict.JPCERT[i].Title < v.AlertDict.JPCERT[j].Title
 | 
			
		||||
		})
 | 
			
		||||
		sort.Slice(v.AlertDict.CISA, func(i, j int) bool {
 | 
			
		||||
			return v.AlertDict.CISA[i].Title < v.AlertDict.CISA[j].Title
 | 
			
		||||
		})
 | 
			
		||||
		r.ScannedCves[k] = v
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -56,6 +56,11 @@ func TestIsDisplayUpdatableNum(t *testing.T) {
 | 
			
		||||
			family:   constant.CentOS,
 | 
			
		||||
			expected: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			mode:     []byte{config.Fast},
 | 
			
		||||
			family:   constant.Alma,
 | 
			
		||||
			expected: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			mode:     []byte{config.Fast},
 | 
			
		||||
			family:   constant.Rocky,
 | 
			
		||||
@@ -193,25 +198,29 @@ func TestScanResult_Sort(t *testing.T) {
 | 
			
		||||
							{Name: "b"},
 | 
			
		||||
						},
 | 
			
		||||
						CveContents: CveContents{
 | 
			
		||||
							"nvd": CveContent{
 | 
			
		||||
							"nvd": []CveContent{{
 | 
			
		||||
								References: References{
 | 
			
		||||
									Reference{Link: "a"},
 | 
			
		||||
									Reference{Link: "b"},
 | 
			
		||||
								},
 | 
			
		||||
								}},
 | 
			
		||||
							},
 | 
			
		||||
							"jvn": CveContent{
 | 
			
		||||
							"jvn": []CveContent{{
 | 
			
		||||
								References: References{
 | 
			
		||||
									Reference{Link: "a"},
 | 
			
		||||
									Reference{Link: "b"},
 | 
			
		||||
								},
 | 
			
		||||
								}},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AlertDict: AlertDict{
 | 
			
		||||
							En: []Alert{
 | 
			
		||||
							USCERT: []Alert{
 | 
			
		||||
								{Title: "a"},
 | 
			
		||||
								{Title: "b"},
 | 
			
		||||
							},
 | 
			
		||||
							Ja: []Alert{
 | 
			
		||||
							JPCERT: []Alert{
 | 
			
		||||
								{Title: "a"},
 | 
			
		||||
								{Title: "b"},
 | 
			
		||||
							},
 | 
			
		||||
							CISA: []Alert{
 | 
			
		||||
								{Title: "a"},
 | 
			
		||||
								{Title: "b"},
 | 
			
		||||
							},
 | 
			
		||||
@@ -252,25 +261,29 @@ func TestScanResult_Sort(t *testing.T) {
 | 
			
		||||
							{Name: "b"},
 | 
			
		||||
						},
 | 
			
		||||
						CveContents: CveContents{
 | 
			
		||||
							"nvd": CveContent{
 | 
			
		||||
							"nvd": []CveContent{{
 | 
			
		||||
								References: References{
 | 
			
		||||
									Reference{Link: "a"},
 | 
			
		||||
									Reference{Link: "b"},
 | 
			
		||||
								},
 | 
			
		||||
								}},
 | 
			
		||||
							},
 | 
			
		||||
							"jvn": CveContent{
 | 
			
		||||
							"jvn": []CveContent{{
 | 
			
		||||
								References: References{
 | 
			
		||||
									Reference{Link: "a"},
 | 
			
		||||
									Reference{Link: "b"},
 | 
			
		||||
								},
 | 
			
		||||
								}},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AlertDict: AlertDict{
 | 
			
		||||
							En: []Alert{
 | 
			
		||||
							USCERT: []Alert{
 | 
			
		||||
								{Title: "a"},
 | 
			
		||||
								{Title: "b"},
 | 
			
		||||
							},
 | 
			
		||||
							Ja: []Alert{
 | 
			
		||||
							JPCERT: []Alert{
 | 
			
		||||
								{Title: "a"},
 | 
			
		||||
								{Title: "b"},
 | 
			
		||||
							},
 | 
			
		||||
							CISA: []Alert{
 | 
			
		||||
								{Title: "a"},
 | 
			
		||||
								{Title: "b"},
 | 
			
		||||
							},
 | 
			
		||||
@@ -314,25 +327,29 @@ func TestScanResult_Sort(t *testing.T) {
 | 
			
		||||
							{Name: "a"},
 | 
			
		||||
						},
 | 
			
		||||
						CveContents: CveContents{
 | 
			
		||||
							"nvd": CveContent{
 | 
			
		||||
							"nvd": []CveContent{{
 | 
			
		||||
								References: References{
 | 
			
		||||
									Reference{Link: "b"},
 | 
			
		||||
									Reference{Link: "a"},
 | 
			
		||||
								},
 | 
			
		||||
								}},
 | 
			
		||||
							},
 | 
			
		||||
							"jvn": CveContent{
 | 
			
		||||
							"jvn": []CveContent{{
 | 
			
		||||
								References: References{
 | 
			
		||||
									Reference{Link: "b"},
 | 
			
		||||
									Reference{Link: "a"},
 | 
			
		||||
								},
 | 
			
		||||
								}},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AlertDict: AlertDict{
 | 
			
		||||
							En: []Alert{
 | 
			
		||||
							USCERT: []Alert{
 | 
			
		||||
								{Title: "b"},
 | 
			
		||||
								{Title: "a"},
 | 
			
		||||
							},
 | 
			
		||||
							Ja: []Alert{
 | 
			
		||||
							JPCERT: []Alert{
 | 
			
		||||
								{Title: "b"},
 | 
			
		||||
								{Title: "a"},
 | 
			
		||||
							},
 | 
			
		||||
							CISA: []Alert{
 | 
			
		||||
								{Title: "b"},
 | 
			
		||||
								{Title: "a"},
 | 
			
		||||
							},
 | 
			
		||||
@@ -373,28 +390,141 @@ func TestScanResult_Sort(t *testing.T) {
 | 
			
		||||
							{Name: "b"},
 | 
			
		||||
						},
 | 
			
		||||
						CveContents: CveContents{
 | 
			
		||||
							"nvd": CveContent{
 | 
			
		||||
							"nvd": []CveContent{{
 | 
			
		||||
								References: References{
 | 
			
		||||
									Reference{Link: "a"},
 | 
			
		||||
									Reference{Link: "b"},
 | 
			
		||||
								},
 | 
			
		||||
								}},
 | 
			
		||||
							},
 | 
			
		||||
							"jvn": CveContent{
 | 
			
		||||
							"jvn": []CveContent{{
 | 
			
		||||
								References: References{
 | 
			
		||||
									Reference{Link: "a"},
 | 
			
		||||
									Reference{Link: "b"},
 | 
			
		||||
								},
 | 
			
		||||
								}},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AlertDict: AlertDict{
 | 
			
		||||
							En: []Alert{
 | 
			
		||||
							USCERT: []Alert{
 | 
			
		||||
								{Title: "a"},
 | 
			
		||||
								{Title: "b"},
 | 
			
		||||
							},
 | 
			
		||||
							Ja: []Alert{
 | 
			
		||||
							JPCERT: []Alert{
 | 
			
		||||
								{Title: "a"},
 | 
			
		||||
								{Title: "b"},
 | 
			
		||||
							},
 | 
			
		||||
							CISA: []Alert{
 | 
			
		||||
								{Title: "a"},
 | 
			
		||||
								{Title: "b"},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "sort JVN by cvss v3",
 | 
			
		||||
			fields: fields{
 | 
			
		||||
				ScannedCves: VulnInfos{
 | 
			
		||||
					"CVE-2014-3591": VulnInfo{
 | 
			
		||||
						CveContents: CveContents{
 | 
			
		||||
							"jvn": []CveContent{
 | 
			
		||||
								{Cvss3Score: 3},
 | 
			
		||||
								{Cvss3Score: 10},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expected: fields{
 | 
			
		||||
				ScannedCves: VulnInfos{
 | 
			
		||||
					"CVE-2014-3591": VulnInfo{
 | 
			
		||||
						CveContents: CveContents{
 | 
			
		||||
							"jvn": []CveContent{
 | 
			
		||||
								{Cvss3Score: 10},
 | 
			
		||||
								{Cvss3Score: 3},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "sort JVN by cvss3, cvss2, sourceLink",
 | 
			
		||||
			fields: fields{
 | 
			
		||||
				ScannedCves: VulnInfos{
 | 
			
		||||
					"CVE-2014-3591": VulnInfo{
 | 
			
		||||
						CveContents: CveContents{
 | 
			
		||||
							"jvn": []CveContent{
 | 
			
		||||
								{
 | 
			
		||||
									Cvss3Score: 3,
 | 
			
		||||
									Cvss2Score: 3,
 | 
			
		||||
									SourceLink: "https://jvndb.jvn.jp/ja/contents/2023/JVNDB-2023-001210.html",
 | 
			
		||||
								},
 | 
			
		||||
								{
 | 
			
		||||
									Cvss3Score: 3,
 | 
			
		||||
									Cvss2Score: 3,
 | 
			
		||||
									SourceLink: "https://jvndb.jvn.jp/ja/contents/2021/JVNDB-2021-001210.html",
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expected: fields{
 | 
			
		||||
				ScannedCves: VulnInfos{
 | 
			
		||||
					"CVE-2014-3591": VulnInfo{
 | 
			
		||||
						CveContents: CveContents{
 | 
			
		||||
							"jvn": []CveContent{
 | 
			
		||||
								{
 | 
			
		||||
									Cvss3Score: 3,
 | 
			
		||||
									Cvss2Score: 3,
 | 
			
		||||
									SourceLink: "https://jvndb.jvn.jp/ja/contents/2021/JVNDB-2021-001210.html",
 | 
			
		||||
								},
 | 
			
		||||
								{
 | 
			
		||||
									Cvss3Score: 3,
 | 
			
		||||
									Cvss2Score: 3,
 | 
			
		||||
									SourceLink: "https://jvndb.jvn.jp/ja/contents/2023/JVNDB-2023-001210.html",
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "sort JVN by cvss3, cvss2",
 | 
			
		||||
			fields: fields{
 | 
			
		||||
				ScannedCves: VulnInfos{
 | 
			
		||||
					"CVE-2014-3591": VulnInfo{
 | 
			
		||||
						CveContents: CveContents{
 | 
			
		||||
							"jvn": []CveContent{
 | 
			
		||||
								{
 | 
			
		||||
									Cvss3Score: 3,
 | 
			
		||||
									Cvss2Score: 1,
 | 
			
		||||
								},
 | 
			
		||||
								{
 | 
			
		||||
									Cvss3Score: 3,
 | 
			
		||||
									Cvss2Score: 10,
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expected: fields{
 | 
			
		||||
				ScannedCves: VulnInfos{
 | 
			
		||||
					"CVE-2014-3591": VulnInfo{
 | 
			
		||||
						CveContents: CveContents{
 | 
			
		||||
							"jvn": []CveContent{
 | 
			
		||||
								{
 | 
			
		||||
									Cvss3Score: 3,
 | 
			
		||||
									Cvss2Score: 10,
 | 
			
		||||
								},
 | 
			
		||||
								{
 | 
			
		||||
									Cvss3Score: 3,
 | 
			
		||||
									Cvss2Score: 1,
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										195
									
								
								models/utils.go
									
									
									
									
									
								
							
							
						
						
									
										195
									
								
								models/utils.go
									
									
									
									
									
								
							@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package models
 | 
			
		||||
@@ -5,116 +6,120 @@ 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
 | 
			
		||||
func ConvertJvnToModel(cveID string, jvn *cvedict.Jvn) *CveContent {
 | 
			
		||||
	if jvn == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	// var cpes = []Cpe{}
 | 
			
		||||
	// for _, c := range jvn.Cpes {
 | 
			
		||||
	// 	cpes = append(cpes, Cpe{
 | 
			
		||||
	// 		FormattedString: c.FormattedString,
 | 
			
		||||
	// 		URI:             c.URI,
 | 
			
		||||
	// 	})
 | 
			
		||||
	// }
 | 
			
		||||
func ConvertJvnToModel(cveID string, jvns []cvedict.Jvn) []CveContent {
 | 
			
		||||
	cves := []CveContent{}
 | 
			
		||||
	for _, jvn := range jvns {
 | 
			
		||||
		// cpes := []Cpe{}
 | 
			
		||||
		// for _, c := range jvn.Cpes {
 | 
			
		||||
		// 	cpes = append(cpes, Cpe{
 | 
			
		||||
		// 		FormattedString: c.FormattedString,
 | 
			
		||||
		// 		URI:             c.URI,
 | 
			
		||||
		// 	})
 | 
			
		||||
		// }
 | 
			
		||||
 | 
			
		||||
	refs := []Reference{}
 | 
			
		||||
	for _, r := range jvn.References {
 | 
			
		||||
		refs = append(refs, Reference{
 | 
			
		||||
			Link:   r.Link,
 | 
			
		||||
			Source: r.Source,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
		refs := []Reference{}
 | 
			
		||||
		for _, r := range jvn.References {
 | 
			
		||||
			refs = append(refs, Reference{
 | 
			
		||||
				Link:   r.Link,
 | 
			
		||||
				Source: r.Source,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	return &CveContent{
 | 
			
		||||
		Type:          Jvn,
 | 
			
		||||
		CveID:         cveID,
 | 
			
		||||
		Title:         jvn.Title,
 | 
			
		||||
		Summary:       jvn.Summary,
 | 
			
		||||
		Cvss2Score:    jvn.Cvss2.BaseScore,
 | 
			
		||||
		Cvss2Vector:   jvn.Cvss2.VectorString,
 | 
			
		||||
		Cvss2Severity: jvn.Cvss2.Severity,
 | 
			
		||||
		Cvss3Score:    jvn.Cvss3.BaseScore,
 | 
			
		||||
		Cvss3Vector:   jvn.Cvss3.VectorString,
 | 
			
		||||
		Cvss3Severity: jvn.Cvss3.BaseSeverity,
 | 
			
		||||
		SourceLink:    jvn.JvnLink,
 | 
			
		||||
		// Cpes:          cpes,
 | 
			
		||||
		References:   refs,
 | 
			
		||||
		Published:    jvn.PublishedDate,
 | 
			
		||||
		LastModified: jvn.LastModifiedDate,
 | 
			
		||||
		cve := CveContent{
 | 
			
		||||
			Type:          Jvn,
 | 
			
		||||
			CveID:         cveID,
 | 
			
		||||
			Title:         jvn.Title,
 | 
			
		||||
			Summary:       jvn.Summary,
 | 
			
		||||
			Cvss2Score:    jvn.Cvss2.BaseScore,
 | 
			
		||||
			Cvss2Vector:   jvn.Cvss2.VectorString,
 | 
			
		||||
			Cvss2Severity: jvn.Cvss2.Severity,
 | 
			
		||||
			Cvss3Score:    jvn.Cvss3.BaseScore,
 | 
			
		||||
			Cvss3Vector:   jvn.Cvss3.VectorString,
 | 
			
		||||
			Cvss3Severity: jvn.Cvss3.BaseSeverity,
 | 
			
		||||
			SourceLink:    jvn.JvnLink,
 | 
			
		||||
			// Cpes:          cpes,
 | 
			
		||||
			References:   refs,
 | 
			
		||||
			Published:    jvn.PublishedDate,
 | 
			
		||||
			LastModified: jvn.LastModifiedDate,
 | 
			
		||||
		}
 | 
			
		||||
		cves = append(cves, cve)
 | 
			
		||||
	}
 | 
			
		||||
	return cves
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConvertNvdJSONToModel convert NVD to CveContent
 | 
			
		||||
func ConvertNvdJSONToModel(cveID string, nvd *cvedict.NvdJSON) (*CveContent, []Exploit, []Mitigation) {
 | 
			
		||||
	if nvd == nil {
 | 
			
		||||
		return nil, nil, nil
 | 
			
		||||
	}
 | 
			
		||||
	// var cpes = []Cpe{}
 | 
			
		||||
	// for _, c := range nvd.Cpes {
 | 
			
		||||
	// 	cpes = append(cpes, Cpe{
 | 
			
		||||
	// 		FormattedString: c.FormattedString,
 | 
			
		||||
	// 		URI:             c.URI,
 | 
			
		||||
	// 	})
 | 
			
		||||
	// }
 | 
			
		||||
 | 
			
		||||
// ConvertNvdToModel convert NVD to CveContent
 | 
			
		||||
func ConvertNvdToModel(cveID string, nvds []cvedict.Nvd) ([]CveContent, []Exploit, []Mitigation) {
 | 
			
		||||
	cves := []CveContent{}
 | 
			
		||||
	refs := []Reference{}
 | 
			
		||||
	exploits := []Exploit{}
 | 
			
		||||
	mitigations := []Mitigation{}
 | 
			
		||||
	for _, r := range nvd.References {
 | 
			
		||||
		var tags []string
 | 
			
		||||
		if 0 < len(r.Tags) {
 | 
			
		||||
			tags = strings.Split(r.Tags, ",")
 | 
			
		||||
		}
 | 
			
		||||
		refs = append(refs, Reference{
 | 
			
		||||
			Link:   r.Link,
 | 
			
		||||
			Source: r.Source,
 | 
			
		||||
			Tags:   tags,
 | 
			
		||||
		})
 | 
			
		||||
		if strings.Contains(r.Tags, "Exploit") {
 | 
			
		||||
			exploits = append(exploits, Exploit{
 | 
			
		||||
				//TODO Add const to here
 | 
			
		||||
				// https://github.com/vulsio/go-exploitdb/blob/master/models/exploit.go#L13-L18
 | 
			
		||||
				ExploitType: "nvd",
 | 
			
		||||
				URL:         r.Link,
 | 
			
		||||
	for _, nvd := range nvds {
 | 
			
		||||
		// cpes := []Cpe{}
 | 
			
		||||
		// for _, c := range nvd.Cpes {
 | 
			
		||||
		// 	cpes = append(cpes, Cpe{
 | 
			
		||||
		// 		FormattedString: c.FormattedString,
 | 
			
		||||
		// 		URI:             c.URI,
 | 
			
		||||
		// 	})
 | 
			
		||||
		// }
 | 
			
		||||
 | 
			
		||||
		for _, r := range nvd.References {
 | 
			
		||||
			var tags []string
 | 
			
		||||
			if 0 < len(r.Tags) {
 | 
			
		||||
				tags = strings.Split(r.Tags, ",")
 | 
			
		||||
			}
 | 
			
		||||
			refs = append(refs, Reference{
 | 
			
		||||
				Link:   r.Link,
 | 
			
		||||
				Source: r.Source,
 | 
			
		||||
				Tags:   tags,
 | 
			
		||||
			})
 | 
			
		||||
			if strings.Contains(r.Tags, "Exploit") {
 | 
			
		||||
				exploits = append(exploits, Exploit{
 | 
			
		||||
					//TODO Add const to here
 | 
			
		||||
					// https://github.com/vulsio/go-exploitdb/blob/master/models/exploit.go#L13-L18
 | 
			
		||||
					ExploitType: "nvd",
 | 
			
		||||
					URL:         r.Link,
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
			if strings.Contains(r.Tags, "Mitigation") {
 | 
			
		||||
				mitigations = append(mitigations, Mitigation{
 | 
			
		||||
					CveContentType: Nvd,
 | 
			
		||||
					URL:            r.Link,
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if strings.Contains(r.Tags, "Mitigation") {
 | 
			
		||||
			mitigations = append(mitigations, Mitigation{
 | 
			
		||||
				CveContentType: Nvd,
 | 
			
		||||
				URL:            r.Link,
 | 
			
		||||
			})
 | 
			
		||||
 | 
			
		||||
		cweIDs := []string{}
 | 
			
		||||
		for _, cid := range nvd.Cwes {
 | 
			
		||||
			cweIDs = append(cweIDs, cid.CweID)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cweIDs := []string{}
 | 
			
		||||
	for _, cid := range nvd.Cwes {
 | 
			
		||||
		cweIDs = append(cweIDs, cid.CweID)
 | 
			
		||||
	}
 | 
			
		||||
		desc := []string{}
 | 
			
		||||
		for _, d := range nvd.Descriptions {
 | 
			
		||||
			desc = append(desc, d.Value)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	desc := []string{}
 | 
			
		||||
	for _, d := range nvd.Descriptions {
 | 
			
		||||
		desc = append(desc, d.Value)
 | 
			
		||||
		cve := CveContent{
 | 
			
		||||
			Type:          Nvd,
 | 
			
		||||
			CveID:         cveID,
 | 
			
		||||
			Summary:       strings.Join(desc, "\n"),
 | 
			
		||||
			Cvss2Score:    nvd.Cvss2.BaseScore,
 | 
			
		||||
			Cvss2Vector:   nvd.Cvss2.VectorString,
 | 
			
		||||
			Cvss2Severity: nvd.Cvss2.Severity,
 | 
			
		||||
			Cvss3Score:    nvd.Cvss3.BaseScore,
 | 
			
		||||
			Cvss3Vector:   nvd.Cvss3.VectorString,
 | 
			
		||||
			Cvss3Severity: nvd.Cvss3.BaseSeverity,
 | 
			
		||||
			SourceLink:    "https://nvd.nist.gov/vuln/detail/" + cveID,
 | 
			
		||||
			// Cpes:          cpes,
 | 
			
		||||
			CweIDs:       cweIDs,
 | 
			
		||||
			References:   refs,
 | 
			
		||||
			Published:    nvd.PublishedDate,
 | 
			
		||||
			LastModified: nvd.LastModifiedDate,
 | 
			
		||||
		}
 | 
			
		||||
		cves = append(cves, cve)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &CveContent{
 | 
			
		||||
		Type:          Nvd,
 | 
			
		||||
		CveID:         cveID,
 | 
			
		||||
		Summary:       strings.Join(desc, "\n"),
 | 
			
		||||
		Cvss2Score:    nvd.Cvss2.BaseScore,
 | 
			
		||||
		Cvss2Vector:   nvd.Cvss2.VectorString,
 | 
			
		||||
		Cvss2Severity: nvd.Cvss2.Severity,
 | 
			
		||||
		Cvss3Score:    nvd.Cvss3.BaseScore,
 | 
			
		||||
		Cvss3Vector:   nvd.Cvss3.VectorString,
 | 
			
		||||
		Cvss3Severity: nvd.Cvss3.BaseSeverity,
 | 
			
		||||
		SourceLink:    "https://nvd.nist.gov/vuln/detail/" + cveID,
 | 
			
		||||
		// Cpes:          cpes,
 | 
			
		||||
		CweIDs:       cweIDs,
 | 
			
		||||
		References:   refs,
 | 
			
		||||
		Published:    nvd.PublishedDate,
 | 
			
		||||
		LastModified: nvd.LastModifiedDate,
 | 
			
		||||
	}, exploits, mitigations
 | 
			
		||||
	return cves, exploits, mitigations
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -28,31 +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, 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'
 | 
			
		||||
@@ -63,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 {
 | 
			
		||||
@@ -98,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
 | 
			
		||||
@@ -222,7 +241,6 @@ func (ps PackageFixStatuses) Sort() {
 | 
			
		||||
	sort.Slice(ps, func(i, j int) bool {
 | 
			
		||||
		return ps[i].Name < ps[j].Name
 | 
			
		||||
	})
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PackageFixStatus has name and other status about the package
 | 
			
		||||
@@ -341,36 +359,52 @@ func (v VulnInfo) CveIDDiffFormat() string {
 | 
			
		||||
	if v.DiffStatus != "" {
 | 
			
		||||
		return fmt.Sprintf("%s %s", v.DiffStatus, v.CveID)
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%s", v.CveID)
 | 
			
		||||
	return v.CveID
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Titles returns title (TUI)
 | 
			
		||||
func (v VulnInfo) Titles(lang, myFamily string) (values []CveContentStr) {
 | 
			
		||||
	if lang == "ja" {
 | 
			
		||||
		if cont, found := v.CveContents[Jvn]; found && cont.Title != "" {
 | 
			
		||||
			values = append(values, CveContentStr{Jvn, cont.Title})
 | 
			
		||||
		if conts, found := v.CveContents[Jvn]; found {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
				if cont.Title != "" {
 | 
			
		||||
					values = append(values, CveContentStr{Jvn, cont.Title})
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// RedHat API has one line title.
 | 
			
		||||
	if cont, found := v.CveContents[RedHatAPI]; found && cont.Title != "" {
 | 
			
		||||
		values = append(values, CveContentStr{RedHatAPI, cont.Title})
 | 
			
		||||
	if conts, found := v.CveContents[RedHatAPI]; found {
 | 
			
		||||
		for _, cont := range conts {
 | 
			
		||||
			if cont.Title != "" {
 | 
			
		||||
				values = append(values, CveContentStr{RedHatAPI, cont.Title})
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// GitHub security alerts has a title.
 | 
			
		||||
	if cont, found := v.CveContents[GitHub]; found && cont.Title != "" {
 | 
			
		||||
		values = append(values, CveContentStr{GitHub, cont.Title})
 | 
			
		||||
	if conts, found := v.CveContents[GitHub]; found {
 | 
			
		||||
		for _, cont := range conts {
 | 
			
		||||
			if cont.Title != "" {
 | 
			
		||||
				values = append(values, CveContentStr{GitHub, cont.Title})
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	order := CveContentTypes{Trivy, Nvd, NewCveContentType(myFamily)}
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(append(order, Jvn)...)...)
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if cont, found := v.CveContents[ctype]; found && cont.Summary != "" {
 | 
			
		||||
			summary := strings.Replace(cont.Summary, "\n", " ", -1)
 | 
			
		||||
			values = append(values, CveContentStr{
 | 
			
		||||
				Type:  ctype,
 | 
			
		||||
				Value: summary,
 | 
			
		||||
			})
 | 
			
		||||
		if conts, found := v.CveContents[ctype]; found {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
				if cont.Summary != "" {
 | 
			
		||||
					summary := strings.Replace(cont.Summary, "\n", " ", -1)
 | 
			
		||||
					values = append(values, CveContentStr{
 | 
			
		||||
						Type:  ctype,
 | 
			
		||||
						Value: summary,
 | 
			
		||||
					})
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -393,23 +427,31 @@ func (v VulnInfo) Titles(lang, myFamily string) (values []CveContentStr) {
 | 
			
		||||
// Summaries returns summaries
 | 
			
		||||
func (v VulnInfo) Summaries(lang, myFamily string) (values []CveContentStr) {
 | 
			
		||||
	if lang == "ja" {
 | 
			
		||||
		if cont, found := v.CveContents[Jvn]; found && cont.Summary != "" {
 | 
			
		||||
			summary := cont.Title
 | 
			
		||||
			summary += "\n" + strings.Replace(
 | 
			
		||||
				strings.Replace(cont.Summary, "\n", " ", -1), "\r", " ", -1)
 | 
			
		||||
			values = append(values, CveContentStr{Jvn, summary})
 | 
			
		||||
		if conts, found := v.CveContents[Jvn]; found {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
				if cont.Summary != "" {
 | 
			
		||||
					summary := cont.Title
 | 
			
		||||
					summary += "\n" + strings.Replace(
 | 
			
		||||
						strings.Replace(cont.Summary, "\n", " ", -1), "\r", " ", -1)
 | 
			
		||||
					values = append(values, CveContentStr{Jvn, summary})
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	order := CveContentTypes{Trivy, NewCveContentType(myFamily), Nvd, GitHub}
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(append(order, Jvn)...)...)
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if cont, found := v.CveContents[ctype]; found && cont.Summary != "" {
 | 
			
		||||
			summary := strings.Replace(cont.Summary, "\n", " ", -1)
 | 
			
		||||
			values = append(values, CveContentStr{
 | 
			
		||||
				Type:  ctype,
 | 
			
		||||
				Value: summary,
 | 
			
		||||
			})
 | 
			
		||||
		if conts, found := v.CveContents[ctype]; found {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
				if cont.Summary != "" {
 | 
			
		||||
					summary := strings.Replace(cont.Summary, "\n", " ", -1)
 | 
			
		||||
					values = append(values, CveContentStr{
 | 
			
		||||
						Type:  ctype,
 | 
			
		||||
						Value: summary,
 | 
			
		||||
					})
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -420,11 +462,15 @@ func (v VulnInfo) Summaries(lang, myFamily string) (values []CveContentStr) {
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v, ok := v.CveContents[WpScan]; ok {
 | 
			
		||||
		values = append(values, CveContentStr{
 | 
			
		||||
			Type:  WpScan,
 | 
			
		||||
			Value: v.Title,
 | 
			
		||||
		})
 | 
			
		||||
	if conts, ok := v.CveContents[WpScan]; ok {
 | 
			
		||||
		for _, cont := range conts {
 | 
			
		||||
			if cont.Title != "" {
 | 
			
		||||
				values = append(values, CveContentStr{
 | 
			
		||||
					Type:  WpScan,
 | 
			
		||||
					Value: cont.Title,
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(values) == 0 {
 | 
			
		||||
@@ -441,20 +487,22 @@ func (v VulnInfo) Summaries(lang, myFamily string) (values []CveContentStr) {
 | 
			
		||||
func (v VulnInfo) Cvss2Scores() (values []CveContentCvss) {
 | 
			
		||||
	order := []CveContentType{RedHatAPI, RedHat, Nvd, Jvn}
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if cont, found := v.CveContents[ctype]; found {
 | 
			
		||||
			if cont.Cvss2Score == 0 && cont.Cvss2Severity == "" {
 | 
			
		||||
				continue
 | 
			
		||||
		if conts, found := v.CveContents[ctype]; found {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
				if cont.Cvss2Score == 0 && cont.Cvss2Severity == "" {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				// https://nvd.nist.gov/vuln-metrics/cvss
 | 
			
		||||
				values = append(values, CveContentCvss{
 | 
			
		||||
					Type: ctype,
 | 
			
		||||
					Value: Cvss{
 | 
			
		||||
						Type:     CVSS2,
 | 
			
		||||
						Score:    cont.Cvss2Score,
 | 
			
		||||
						Vector:   cont.Cvss2Vector,
 | 
			
		||||
						Severity: strings.ToUpper(cont.Cvss2Severity),
 | 
			
		||||
					},
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
			// https://nvd.nist.gov/vuln-metrics/cvss
 | 
			
		||||
			values = append(values, CveContentCvss{
 | 
			
		||||
				Type: ctype,
 | 
			
		||||
				Value: Cvss{
 | 
			
		||||
					Type:     CVSS2,
 | 
			
		||||
					Score:    cont.Cvss2Score,
 | 
			
		||||
					Vector:   cont.Cvss2Vector,
 | 
			
		||||
					Severity: strings.ToUpper(cont.Cvss2Severity),
 | 
			
		||||
				},
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
@@ -464,34 +512,40 @@ func (v VulnInfo) Cvss2Scores() (values []CveContentCvss) {
 | 
			
		||||
func (v VulnInfo) Cvss3Scores() (values []CveContentCvss) {
 | 
			
		||||
	order := []CveContentType{RedHatAPI, RedHat, Nvd, Jvn}
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if cont, found := v.CveContents[ctype]; found {
 | 
			
		||||
			if cont.Cvss3Score == 0 && cont.Cvss3Severity == "" {
 | 
			
		||||
				continue
 | 
			
		||||
		if conts, found := v.CveContents[ctype]; found {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
				if cont.Cvss3Score == 0 && cont.Cvss3Severity == "" {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				// https://nvd.nist.gov/vuln-metrics/cvss
 | 
			
		||||
				values = append(values, CveContentCvss{
 | 
			
		||||
					Type: ctype,
 | 
			
		||||
					Value: Cvss{
 | 
			
		||||
						Type:     CVSS3,
 | 
			
		||||
						Score:    cont.Cvss3Score,
 | 
			
		||||
						Vector:   cont.Cvss3Vector,
 | 
			
		||||
						Severity: strings.ToUpper(cont.Cvss3Severity),
 | 
			
		||||
					},
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
			// https://nvd.nist.gov/vuln-metrics/cvss
 | 
			
		||||
			values = append(values, CveContentCvss{
 | 
			
		||||
				Type: ctype,
 | 
			
		||||
				Value: Cvss{
 | 
			
		||||
					Type:     CVSS3,
 | 
			
		||||
					Score:    cont.Cvss3Score,
 | 
			
		||||
					Vector:   cont.Cvss3Vector,
 | 
			
		||||
					Severity: strings.ToUpper(cont.Cvss3Severity),
 | 
			
		||||
				},
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, ctype := range []CveContentType{Debian, DebianSecurityTracker, Ubuntu, Amazon, Trivy, GitHub, WpScan} {
 | 
			
		||||
		if cont, found := v.CveContents[ctype]; found && cont.Cvss3Severity != "" {
 | 
			
		||||
			values = append(values, CveContentCvss{
 | 
			
		||||
				Type: ctype,
 | 
			
		||||
				Value: Cvss{
 | 
			
		||||
					Type:                 CVSS3,
 | 
			
		||||
					Score:                severityToCvssScoreRoughly(cont.Cvss3Severity),
 | 
			
		||||
					CalculatedBySeverity: true,
 | 
			
		||||
					Severity:             strings.ToUpper(cont.Cvss3Severity),
 | 
			
		||||
				},
 | 
			
		||||
			})
 | 
			
		||||
		if conts, found := v.CveContents[ctype]; found {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
				if cont.Cvss3Severity != "" {
 | 
			
		||||
					values = append(values, CveContentCvss{
 | 
			
		||||
						Type: ctype,
 | 
			
		||||
						Value: Cvss{
 | 
			
		||||
							Type:                 CVSS3,
 | 
			
		||||
							Score:                severityToCvssScoreRoughly(cont.Cvss3Severity),
 | 
			
		||||
							CalculatedBySeverity: true,
 | 
			
		||||
							Severity:             strings.ToUpper(cont.Cvss3Severity),
 | 
			
		||||
						},
 | 
			
		||||
					})
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -553,24 +607,28 @@ func (v VulnInfo) MaxCvss2Score() CveContentCvss {
 | 
			
		||||
 | 
			
		||||
// AttackVector returns attack vector string
 | 
			
		||||
func (v VulnInfo) AttackVector() string {
 | 
			
		||||
	for _, cnt := range v.CveContents {
 | 
			
		||||
		if strings.HasPrefix(cnt.Cvss2Vector, "AV:N") ||
 | 
			
		||||
			strings.Contains(cnt.Cvss3Vector, "AV:N") {
 | 
			
		||||
			return "AV:N"
 | 
			
		||||
		} else if strings.HasPrefix(cnt.Cvss2Vector, "AV:A") ||
 | 
			
		||||
			strings.Contains(cnt.Cvss3Vector, "AV:A") {
 | 
			
		||||
			return "AV:A"
 | 
			
		||||
		} else if strings.HasPrefix(cnt.Cvss2Vector, "AV:L") ||
 | 
			
		||||
			strings.Contains(cnt.Cvss3Vector, "AV:L") {
 | 
			
		||||
			return "AV:L"
 | 
			
		||||
		} else if strings.Contains(cnt.Cvss3Vector, "AV:P") {
 | 
			
		||||
			// no AV:P in CVSS v2
 | 
			
		||||
			return "AV:P"
 | 
			
		||||
	for _, conts := range v.CveContents {
 | 
			
		||||
		for _, cont := range conts {
 | 
			
		||||
			if strings.HasPrefix(cont.Cvss2Vector, "AV:N") ||
 | 
			
		||||
				strings.Contains(cont.Cvss3Vector, "AV:N") {
 | 
			
		||||
				return "AV:N"
 | 
			
		||||
			} else if strings.HasPrefix(cont.Cvss2Vector, "AV:A") ||
 | 
			
		||||
				strings.Contains(cont.Cvss3Vector, "AV:A") {
 | 
			
		||||
				return "AV:A"
 | 
			
		||||
			} else if strings.HasPrefix(cont.Cvss2Vector, "AV:L") ||
 | 
			
		||||
				strings.Contains(cont.Cvss3Vector, "AV:L") {
 | 
			
		||||
				return "AV:L"
 | 
			
		||||
			} else if strings.Contains(cont.Cvss3Vector, "AV:P") {
 | 
			
		||||
				// no AV:P in CVSS v2
 | 
			
		||||
				return "AV:P"
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if cont, found := v.CveContents[DebianSecurityTracker]; found {
 | 
			
		||||
		if attackRange, found := cont.Optional["attack range"]; found {
 | 
			
		||||
			return attackRange
 | 
			
		||||
	if conts, found := v.CveContents[DebianSecurityTracker]; found {
 | 
			
		||||
		for _, cont := range conts {
 | 
			
		||||
			if attackRange, found := cont.Optional["attack range"]; found {
 | 
			
		||||
				return attackRange
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
@@ -755,18 +813,28 @@ type Mitigation struct {
 | 
			
		||||
	URL            string         `json:"url,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AlertDict has target cve JPCERT and USCERT alert data
 | 
			
		||||
// AlertDict has target cve JPCERT, USCERT and CISA alert data
 | 
			
		||||
type AlertDict struct {
 | 
			
		||||
	Ja []Alert `json:"ja"`
 | 
			
		||||
	En []Alert `json:"en"`
 | 
			
		||||
	CISA   []Alert `json:"cisa"`
 | 
			
		||||
	JPCERT []Alert `json:"jpcert"`
 | 
			
		||||
	USCERT []Alert `json:"uscert"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsEmpty checks if the content of AlertDict is empty
 | 
			
		||||
func (a AlertDict) IsEmpty() bool {
 | 
			
		||||
	return len(a.CISA) == 0 && len(a.JPCERT) == 0 && len(a.USCERT) == 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FormatSource returns which source has this alert
 | 
			
		||||
func (a AlertDict) FormatSource() string {
 | 
			
		||||
	if len(a.En) != 0 || len(a.Ja) != 0 {
 | 
			
		||||
		return "CERT"
 | 
			
		||||
	var s []string
 | 
			
		||||
	if len(a.CISA) != 0 {
 | 
			
		||||
		s = append(s, "CISA")
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
	if len(a.USCERT) != 0 || len(a.JPCERT) != 0 {
 | 
			
		||||
		s = append(s, "CERT")
 | 
			
		||||
	}
 | 
			
		||||
	return strings.Join(s, "/")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Confidences is a list of Confidence
 | 
			
		||||
@@ -808,59 +876,56 @@ func (c Confidence) String() string {
 | 
			
		||||
type DetectionMethod string
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// CpeVersionMatchStr is a String representation of CpeNameMatch
 | 
			
		||||
	CpeVersionMatchStr = "CpeVersionMatch"
 | 
			
		||||
	// NvdExactVersionMatchStr :
 | 
			
		||||
	NvdExactVersionMatchStr = "NvdExactVersionMatch"
 | 
			
		||||
 | 
			
		||||
	// CpeVendorProductMatchStr is a String representation of CpeNameMatch
 | 
			
		||||
	CpeVendorProductMatchStr = "CpeVendorProductMatch"
 | 
			
		||||
	// NvdRoughVersionMatchStr :
 | 
			
		||||
	NvdRoughVersionMatchStr = "NvdRoughVersionMatch"
 | 
			
		||||
 | 
			
		||||
	// YumUpdateSecurityMatchStr is a String representation of YumUpdateSecurityMatch
 | 
			
		||||
	YumUpdateSecurityMatchStr = "YumUpdateSecurityMatch"
 | 
			
		||||
	// NvdVendorProductMatchStr :
 | 
			
		||||
	NvdVendorProductMatchStr = "NvdVendorProductMatch"
 | 
			
		||||
 | 
			
		||||
	// PkgAuditMatchStr is a String representation of PkgAuditMatch
 | 
			
		||||
	// JvnVendorProductMatchStr :
 | 
			
		||||
	JvnVendorProductMatchStr = "JvnVendorProductMatch"
 | 
			
		||||
 | 
			
		||||
	// PkgAuditMatchStr :
 | 
			
		||||
	PkgAuditMatchStr = "PkgAuditMatch"
 | 
			
		||||
 | 
			
		||||
	// OvalMatchStr is a String representation of OvalMatch
 | 
			
		||||
	// OvalMatchStr :
 | 
			
		||||
	OvalMatchStr = "OvalMatch"
 | 
			
		||||
 | 
			
		||||
	// RedHatAPIStr is a String representation of RedHatAPIMatch
 | 
			
		||||
	// RedHatAPIStr is :
 | 
			
		||||
	RedHatAPIStr = "RedHatAPIMatch"
 | 
			
		||||
 | 
			
		||||
	// DebianSecurityTrackerMatchStr is a String representation of DebianSecurityTrackerMatch
 | 
			
		||||
	// DebianSecurityTrackerMatchStr :
 | 
			
		||||
	DebianSecurityTrackerMatchStr = "DebianSecurityTrackerMatch"
 | 
			
		||||
 | 
			
		||||
	// UbuntuAPIMatchStr is a String representation of UbuntuAPIMatch
 | 
			
		||||
	// UbuntuAPIMatchStr :
 | 
			
		||||
	UbuntuAPIMatchStr = "UbuntuAPIMatch"
 | 
			
		||||
 | 
			
		||||
	// TrivyMatchStr is a String representation of Trivy
 | 
			
		||||
	// TrivyMatchStr :
 | 
			
		||||
	TrivyMatchStr = "TrivyMatch"
 | 
			
		||||
 | 
			
		||||
	// ChangelogExactMatchStr is a String representation of ChangelogExactMatch
 | 
			
		||||
	// ChangelogExactMatchStr :
 | 
			
		||||
	ChangelogExactMatchStr = "ChangelogExactMatch"
 | 
			
		||||
 | 
			
		||||
	// ChangelogLenientMatchStr is a String representation of ChangelogLenientMatch
 | 
			
		||||
	ChangelogLenientMatchStr = "ChangelogLenientMatch"
 | 
			
		||||
	// ChangelogRoughMatchStr :
 | 
			
		||||
	ChangelogRoughMatchStr = "ChangelogRoughMatch"
 | 
			
		||||
 | 
			
		||||
	// GitHubMatchStr is a String representation of GitHubMatch
 | 
			
		||||
	// GitHubMatchStr :
 | 
			
		||||
	GitHubMatchStr = "GitHubMatch"
 | 
			
		||||
 | 
			
		||||
	// WpScanMatchStr is a String representation of WordPress VulnDB scanning
 | 
			
		||||
	// WpScanMatchStr :
 | 
			
		||||
	WpScanMatchStr = "WpScanMatch"
 | 
			
		||||
 | 
			
		||||
	// FailedToGetChangelog is a String representation of FailedToGetChangelog
 | 
			
		||||
	// FailedToGetChangelog :
 | 
			
		||||
	FailedToGetChangelog = "FailedToGetChangelog"
 | 
			
		||||
 | 
			
		||||
	// FailedToFindVersionInChangelog is a String representation of FailedToFindVersionInChangelog
 | 
			
		||||
	// FailedToFindVersionInChangelog :
 | 
			
		||||
	FailedToFindVersionInChangelog = "FailedToFindVersionInChangelog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// CpeVersionMatch is a ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	CpeVersionMatch = Confidence{100, CpeVersionMatchStr, 1}
 | 
			
		||||
 | 
			
		||||
	// YumUpdateSecurityMatch is a ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	YumUpdateSecurityMatch = Confidence{100, YumUpdateSecurityMatchStr, 2}
 | 
			
		||||
 | 
			
		||||
	// PkgAuditMatch is a ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	PkgAuditMatch = Confidence{100, PkgAuditMatchStr, 2}
 | 
			
		||||
 | 
			
		||||
@@ -882,15 +947,24 @@ var (
 | 
			
		||||
	// ChangelogExactMatch is a ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	ChangelogExactMatch = Confidence{95, ChangelogExactMatchStr, 3}
 | 
			
		||||
 | 
			
		||||
	// ChangelogLenientMatch is a ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	ChangelogLenientMatch = Confidence{50, ChangelogLenientMatchStr, 4}
 | 
			
		||||
	// ChangelogRoughMatch is a ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	ChangelogRoughMatch = Confidence{50, ChangelogRoughMatchStr, 4}
 | 
			
		||||
 | 
			
		||||
	// GitHubMatch is a ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	GitHubMatch = Confidence{97, GitHubMatchStr, 2}
 | 
			
		||||
	GitHubMatch = Confidence{100, GitHubMatchStr, 2}
 | 
			
		||||
 | 
			
		||||
	// WpScanMatch is a ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	WpScanMatch = Confidence{100, WpScanMatchStr, 0}
 | 
			
		||||
 | 
			
		||||
	// CpeVendorProductMatch is a ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	CpeVendorProductMatch = Confidence{10, CpeVendorProductMatchStr, 9}
 | 
			
		||||
	// NvdExactVersionMatch is a ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	NvdExactVersionMatch = Confidence{100, NvdExactVersionMatchStr, 1}
 | 
			
		||||
 | 
			
		||||
	// NvdRoughVersionMatch NvdExactVersionMatch is a ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	NvdRoughVersionMatch = Confidence{80, NvdRoughVersionMatchStr, 1}
 | 
			
		||||
 | 
			
		||||
	// NvdVendorProductMatch is a ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	NvdVendorProductMatch = Confidence{10, NvdVendorProductMatchStr, 9}
 | 
			
		||||
 | 
			
		||||
	// JvnVendorProductMatch is a ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	JvnVendorProductMatch = Confidence{10, JvnVendorProductMatchStr, 10}
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -21,19 +21,19 @@ func TestTitles(t *testing.T) {
 | 
			
		||||
				lang: "ja",
 | 
			
		||||
				cont: VulnInfo{
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						Jvn: {
 | 
			
		||||
						Jvn: []CveContent{{
 | 
			
		||||
							Type:  Jvn,
 | 
			
		||||
							Title: "Title1",
 | 
			
		||||
						},
 | 
			
		||||
						RedHat: {
 | 
			
		||||
						}},
 | 
			
		||||
						RedHat: []CveContent{{
 | 
			
		||||
							Type:    RedHat,
 | 
			
		||||
							Summary: "Summary RedHat",
 | 
			
		||||
						},
 | 
			
		||||
						Nvd: {
 | 
			
		||||
						}},
 | 
			
		||||
						Nvd: []CveContent{{
 | 
			
		||||
							Type:    Nvd,
 | 
			
		||||
							Summary: "Summary NVD",
 | 
			
		||||
							// Severity is NOT included in NVD
 | 
			
		||||
						},
 | 
			
		||||
						}},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
@@ -58,19 +58,19 @@ func TestTitles(t *testing.T) {
 | 
			
		||||
				lang: "en",
 | 
			
		||||
				cont: VulnInfo{
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						Jvn: {
 | 
			
		||||
						Jvn: []CveContent{{
 | 
			
		||||
							Type:  Jvn,
 | 
			
		||||
							Title: "Title1",
 | 
			
		||||
						},
 | 
			
		||||
						RedHat: {
 | 
			
		||||
						}},
 | 
			
		||||
						RedHat: []CveContent{{
 | 
			
		||||
							Type:    RedHat,
 | 
			
		||||
							Summary: "Summary RedHat",
 | 
			
		||||
						},
 | 
			
		||||
						Nvd: {
 | 
			
		||||
						}},
 | 
			
		||||
						Nvd: []CveContent{{
 | 
			
		||||
							Type:    Nvd,
 | 
			
		||||
							Summary: "Summary NVD",
 | 
			
		||||
							// Severity is NOT included in NVD
 | 
			
		||||
						},
 | 
			
		||||
						}},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
@@ -122,20 +122,20 @@ func TestSummaries(t *testing.T) {
 | 
			
		||||
				lang: "ja",
 | 
			
		||||
				cont: VulnInfo{
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						Jvn: {
 | 
			
		||||
						Jvn: []CveContent{{
 | 
			
		||||
							Type:    Jvn,
 | 
			
		||||
							Title:   "Title JVN",
 | 
			
		||||
							Summary: "Summary JVN",
 | 
			
		||||
						},
 | 
			
		||||
						RedHat: {
 | 
			
		||||
						}},
 | 
			
		||||
						RedHat: []CveContent{{
 | 
			
		||||
							Type:    RedHat,
 | 
			
		||||
							Summary: "Summary RedHat",
 | 
			
		||||
						},
 | 
			
		||||
						Nvd: {
 | 
			
		||||
						}},
 | 
			
		||||
						Nvd: []CveContent{{
 | 
			
		||||
							Type:    Nvd,
 | 
			
		||||
							Summary: "Summary NVD",
 | 
			
		||||
							// Severity is NOT included in NVD
 | 
			
		||||
						},
 | 
			
		||||
						}},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
@@ -160,20 +160,20 @@ func TestSummaries(t *testing.T) {
 | 
			
		||||
				lang: "en",
 | 
			
		||||
				cont: VulnInfo{
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						Jvn: {
 | 
			
		||||
						Jvn: []CveContent{{
 | 
			
		||||
							Type:    Jvn,
 | 
			
		||||
							Title:   "Title JVN",
 | 
			
		||||
							Summary: "Summary JVN",
 | 
			
		||||
						},
 | 
			
		||||
						RedHat: {
 | 
			
		||||
						}},
 | 
			
		||||
						RedHat: []CveContent{{
 | 
			
		||||
							Type:    RedHat,
 | 
			
		||||
							Summary: "Summary RedHat",
 | 
			
		||||
						},
 | 
			
		||||
						Nvd: {
 | 
			
		||||
						}},
 | 
			
		||||
						Nvd: []CveContent{{
 | 
			
		||||
							Type:    Nvd,
 | 
			
		||||
							Summary: "Summary NVD",
 | 
			
		||||
							// Severity is NOT included in NVD
 | 
			
		||||
						},
 | 
			
		||||
						}},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
@@ -220,32 +220,32 @@ func TestCountGroupBySeverity(t *testing.T) {
 | 
			
		||||
				"CVE-2017-0002": {
 | 
			
		||||
					CveID: "CVE-2017-0002",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						Nvd: {
 | 
			
		||||
						Nvd: []CveContent{{
 | 
			
		||||
							Type:       Nvd,
 | 
			
		||||
							Cvss3Score: 6.0,
 | 
			
		||||
						},
 | 
			
		||||
						RedHat: {
 | 
			
		||||
						}},
 | 
			
		||||
						RedHat: []CveContent{{
 | 
			
		||||
							Type:       RedHat,
 | 
			
		||||
							Cvss3Score: 7.0,
 | 
			
		||||
						},
 | 
			
		||||
						}},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				"CVE-2017-0003": {
 | 
			
		||||
					CveID: "CVE-2017-0003",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						Nvd: {
 | 
			
		||||
						Nvd: []CveContent{{
 | 
			
		||||
							Type:       Nvd,
 | 
			
		||||
							Cvss3Score: 2.0,
 | 
			
		||||
						},
 | 
			
		||||
						}},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				"CVE-2017-0004": {
 | 
			
		||||
					CveID: "CVE-2017-0004",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						Nvd: {
 | 
			
		||||
						Nvd: []CveContent{{
 | 
			
		||||
							Type:       Nvd,
 | 
			
		||||
							Cvss3Score: 5.0,
 | 
			
		||||
						},
 | 
			
		||||
						}},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				"CVE-2017-0005": {
 | 
			
		||||
@@ -254,10 +254,10 @@ func TestCountGroupBySeverity(t *testing.T) {
 | 
			
		||||
				"CVE-2017-0006": {
 | 
			
		||||
					CveID: "CVE-2017-0005",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						Nvd: {
 | 
			
		||||
						Nvd: []CveContent{{
 | 
			
		||||
							Type:       Nvd,
 | 
			
		||||
							Cvss3Score: 10.0,
 | 
			
		||||
						},
 | 
			
		||||
						}},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
@@ -274,32 +274,32 @@ func TestCountGroupBySeverity(t *testing.T) {
 | 
			
		||||
				"CVE-2017-0002": {
 | 
			
		||||
					CveID: "CVE-2017-0002",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						Nvd: {
 | 
			
		||||
						Nvd: []CveContent{{
 | 
			
		||||
							Type:       Nvd,
 | 
			
		||||
							Cvss2Score: 1.0,
 | 
			
		||||
						},
 | 
			
		||||
						RedHat: {
 | 
			
		||||
						}},
 | 
			
		||||
						RedHat: []CveContent{{
 | 
			
		||||
							Type:       RedHat,
 | 
			
		||||
							Cvss3Score: 7.0,
 | 
			
		||||
						},
 | 
			
		||||
						}},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				"CVE-2017-0003": {
 | 
			
		||||
					CveID: "CVE-2017-0003",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						Nvd: {
 | 
			
		||||
						Nvd: []CveContent{{
 | 
			
		||||
							Type:       Nvd,
 | 
			
		||||
							Cvss2Score: 2.0,
 | 
			
		||||
						},
 | 
			
		||||
						}},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				"CVE-2017-0004": {
 | 
			
		||||
					CveID: "CVE-2017-0004",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						Nvd: {
 | 
			
		||||
						Nvd: []CveContent{{
 | 
			
		||||
							Type:       Nvd,
 | 
			
		||||
							Cvss2Score: 5.0,
 | 
			
		||||
						},
 | 
			
		||||
						}},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				"CVE-2017-0005": {
 | 
			
		||||
@@ -308,10 +308,10 @@ func TestCountGroupBySeverity(t *testing.T) {
 | 
			
		||||
				"CVE-2017-0006": {
 | 
			
		||||
					CveID: "CVE-2017-0005",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						Nvd: {
 | 
			
		||||
						Nvd: []CveContent{{
 | 
			
		||||
							Type:       Nvd,
 | 
			
		||||
							Cvss2Score: 10.0,
 | 
			
		||||
						},
 | 
			
		||||
						}},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
@@ -346,27 +346,27 @@ func TestToSortedSlice(t *testing.T) {
 | 
			
		||||
				"CVE-2017-0002": {
 | 
			
		||||
					CveID: "CVE-2017-0002",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						Nvd: {
 | 
			
		||||
						Nvd: []CveContent{{
 | 
			
		||||
							Type:       Nvd,
 | 
			
		||||
							Cvss2Score: 6.0,
 | 
			
		||||
						},
 | 
			
		||||
						RedHat: {
 | 
			
		||||
						}},
 | 
			
		||||
						RedHat: []CveContent{{
 | 
			
		||||
							Type:       RedHat,
 | 
			
		||||
							Cvss3Score: 7.0,
 | 
			
		||||
						},
 | 
			
		||||
						}},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				"CVE-2017-0001": {
 | 
			
		||||
					CveID: "CVE-2017-0001",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						Nvd: {
 | 
			
		||||
						Nvd: []CveContent{{
 | 
			
		||||
							Type:       Nvd,
 | 
			
		||||
							Cvss2Score: 7.0,
 | 
			
		||||
						},
 | 
			
		||||
						RedHat: {
 | 
			
		||||
						}},
 | 
			
		||||
						RedHat: []CveContent{{
 | 
			
		||||
							Type:       RedHat,
 | 
			
		||||
							Cvss3Score: 8.0,
 | 
			
		||||
						},
 | 
			
		||||
						}},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
@@ -374,27 +374,27 @@ func TestToSortedSlice(t *testing.T) {
 | 
			
		||||
				{
 | 
			
		||||
					CveID: "CVE-2017-0001",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						Nvd: {
 | 
			
		||||
						Nvd: []CveContent{{
 | 
			
		||||
							Type:       Nvd,
 | 
			
		||||
							Cvss2Score: 7.0,
 | 
			
		||||
						},
 | 
			
		||||
						RedHat: {
 | 
			
		||||
						}},
 | 
			
		||||
						RedHat: []CveContent{{
 | 
			
		||||
							Type:       RedHat,
 | 
			
		||||
							Cvss3Score: 8.0,
 | 
			
		||||
						},
 | 
			
		||||
						}},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					CveID: "CVE-2017-0002",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						Nvd: {
 | 
			
		||||
						Nvd: []CveContent{{
 | 
			
		||||
							Type:       Nvd,
 | 
			
		||||
							Cvss2Score: 6.0,
 | 
			
		||||
						},
 | 
			
		||||
						RedHat: {
 | 
			
		||||
						}},
 | 
			
		||||
						RedHat: []CveContent{{
 | 
			
		||||
							Type:       RedHat,
 | 
			
		||||
							Cvss3Score: 7.0,
 | 
			
		||||
						},
 | 
			
		||||
						}},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
@@ -405,23 +405,23 @@ func TestToSortedSlice(t *testing.T) {
 | 
			
		||||
				"CVE-2017-0002": {
 | 
			
		||||
					CveID: "CVE-2017-0002",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						Nvd: {
 | 
			
		||||
						Nvd: []CveContent{{
 | 
			
		||||
							Type:       Nvd,
 | 
			
		||||
							Cvss2Score: 6.0,
 | 
			
		||||
						},
 | 
			
		||||
						RedHat: {
 | 
			
		||||
						}},
 | 
			
		||||
						RedHat: []CveContent{{
 | 
			
		||||
							Type:       RedHat,
 | 
			
		||||
							Cvss3Score: 7.0,
 | 
			
		||||
						},
 | 
			
		||||
						}},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				"CVE-2017-0001": {
 | 
			
		||||
					CveID: "CVE-2017-0001",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						RedHat: {
 | 
			
		||||
						RedHat: []CveContent{{
 | 
			
		||||
							Type:       RedHat,
 | 
			
		||||
							Cvss3Score: 7.0,
 | 
			
		||||
						},
 | 
			
		||||
						}},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
@@ -429,23 +429,23 @@ func TestToSortedSlice(t *testing.T) {
 | 
			
		||||
				{
 | 
			
		||||
					CveID: "CVE-2017-0001",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						RedHat: {
 | 
			
		||||
						RedHat: []CveContent{{
 | 
			
		||||
							Type:       RedHat,
 | 
			
		||||
							Cvss3Score: 7.0,
 | 
			
		||||
						},
 | 
			
		||||
						}},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					CveID: "CVE-2017-0002",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						Nvd: {
 | 
			
		||||
						Nvd: []CveContent{{
 | 
			
		||||
							Type:       Nvd,
 | 
			
		||||
							Cvss2Score: 6.0,
 | 
			
		||||
						},
 | 
			
		||||
						RedHat: {
 | 
			
		||||
						}},
 | 
			
		||||
						RedHat: []CveContent{{
 | 
			
		||||
							Type:       RedHat,
 | 
			
		||||
							Cvss3Score: 7.0,
 | 
			
		||||
						},
 | 
			
		||||
						}},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
@@ -456,19 +456,19 @@ func TestToSortedSlice(t *testing.T) {
 | 
			
		||||
				"CVE-2017-0002": {
 | 
			
		||||
					CveID: "CVE-2017-0002",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						Ubuntu: {
 | 
			
		||||
						Ubuntu: []CveContent{{
 | 
			
		||||
							Type:          Ubuntu,
 | 
			
		||||
							Cvss3Severity: "High",
 | 
			
		||||
						},
 | 
			
		||||
						}},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				"CVE-2017-0001": {
 | 
			
		||||
					CveID: "CVE-2017-0001",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						Ubuntu: {
 | 
			
		||||
						Ubuntu: []CveContent{{
 | 
			
		||||
							Type:          Ubuntu,
 | 
			
		||||
							Cvss3Severity: "Low",
 | 
			
		||||
						},
 | 
			
		||||
						}},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
@@ -476,19 +476,19 @@ func TestToSortedSlice(t *testing.T) {
 | 
			
		||||
				{
 | 
			
		||||
					CveID: "CVE-2017-0002",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						Ubuntu: {
 | 
			
		||||
						Ubuntu: []CveContent{{
 | 
			
		||||
							Type:          Ubuntu,
 | 
			
		||||
							Cvss3Severity: "High",
 | 
			
		||||
						},
 | 
			
		||||
						}},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					CveID: "CVE-2017-0001",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						Ubuntu: {
 | 
			
		||||
						Ubuntu: []CveContent{{
 | 
			
		||||
							Type:          Ubuntu,
 | 
			
		||||
							Cvss3Severity: "Low",
 | 
			
		||||
						},
 | 
			
		||||
						}},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
@@ -510,31 +510,31 @@ func TestCvss2Scores(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			in: VulnInfo{
 | 
			
		||||
				CveContents: CveContents{
 | 
			
		||||
					Jvn: {
 | 
			
		||||
					Jvn: []CveContent{{
 | 
			
		||||
						Type:          Jvn,
 | 
			
		||||
						Cvss2Severity: "HIGH",
 | 
			
		||||
						Cvss2Score:    8.2,
 | 
			
		||||
						Cvss2Vector:   "AV:N/AC:L/Au:N/C:N/I:N/A:P",
 | 
			
		||||
					},
 | 
			
		||||
					RedHat: {
 | 
			
		||||
					}},
 | 
			
		||||
					RedHat: []CveContent{{
 | 
			
		||||
						Type:          RedHat,
 | 
			
		||||
						Cvss2Severity: "HIGH",
 | 
			
		||||
						Cvss2Score:    8.0,
 | 
			
		||||
						Cvss2Vector:   "AV:N/AC:L/Au:N/C:N/I:N/A:P",
 | 
			
		||||
					},
 | 
			
		||||
					Nvd: {
 | 
			
		||||
					}},
 | 
			
		||||
					Nvd: []CveContent{{
 | 
			
		||||
						Type:          Nvd,
 | 
			
		||||
						Cvss2Score:    8.1,
 | 
			
		||||
						Cvss2Vector:   "AV:N/AC:L/Au:N/C:N/I:N/A:P",
 | 
			
		||||
						Cvss2Severity: "HIGH",
 | 
			
		||||
					},
 | 
			
		||||
					}},
 | 
			
		||||
					//v3
 | 
			
		||||
					RedHatAPI: {
 | 
			
		||||
					RedHatAPI: []CveContent{{
 | 
			
		||||
						Type:          RedHatAPI,
 | 
			
		||||
						Cvss3Score:    8.1,
 | 
			
		||||
						Cvss3Vector:   "AV:N/AC:L/Au:N/C:N/I:N/A:P",
 | 
			
		||||
						Cvss3Severity: "HIGH",
 | 
			
		||||
					},
 | 
			
		||||
					}},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: []CveContentCvss{
 | 
			
		||||
@@ -590,24 +590,24 @@ func TestMaxCvss2Scores(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			in: VulnInfo{
 | 
			
		||||
				CveContents: CveContents{
 | 
			
		||||
					Jvn: {
 | 
			
		||||
					Jvn: []CveContent{{
 | 
			
		||||
						Type:          Jvn,
 | 
			
		||||
						Cvss2Severity: "HIGH",
 | 
			
		||||
						Cvss2Score:    8.2,
 | 
			
		||||
						Cvss2Vector:   "AV:N/AC:L/Au:N/C:N/I:N/A:P",
 | 
			
		||||
					},
 | 
			
		||||
					RedHat: {
 | 
			
		||||
					}},
 | 
			
		||||
					RedHat: []CveContent{{
 | 
			
		||||
						Type:          RedHat,
 | 
			
		||||
						Cvss2Severity: "HIGH",
 | 
			
		||||
						Cvss2Score:    8.0,
 | 
			
		||||
						Cvss2Vector:   "AV:N/AC:L/Au:N/C:N/I:N/A:P",
 | 
			
		||||
					},
 | 
			
		||||
					Nvd: {
 | 
			
		||||
					}},
 | 
			
		||||
					Nvd: []CveContent{{
 | 
			
		||||
						Type:        Nvd,
 | 
			
		||||
						Cvss2Score:  8.1,
 | 
			
		||||
						Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
 | 
			
		||||
						// Severity is NOT included in NVD
 | 
			
		||||
					},
 | 
			
		||||
					}},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: CveContentCvss{
 | 
			
		||||
@@ -650,18 +650,18 @@ func TestCvss3Scores(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			in: VulnInfo{
 | 
			
		||||
				CveContents: CveContents{
 | 
			
		||||
					RedHat: {
 | 
			
		||||
					RedHat: []CveContent{{
 | 
			
		||||
						Type:          RedHat,
 | 
			
		||||
						Cvss3Severity: "HIGH",
 | 
			
		||||
						Cvss3Score:    8.0,
 | 
			
		||||
						Cvss3Vector:   "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
 | 
			
		||||
					},
 | 
			
		||||
					Nvd: {
 | 
			
		||||
					}},
 | 
			
		||||
					Nvd: []CveContent{{
 | 
			
		||||
						Type:          Nvd,
 | 
			
		||||
						Cvss2Score:    8.1,
 | 
			
		||||
						Cvss2Vector:   "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
 | 
			
		||||
						Cvss2Severity: "HIGH",
 | 
			
		||||
					},
 | 
			
		||||
					}},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: []CveContentCvss{
 | 
			
		||||
@@ -680,10 +680,10 @@ func TestCvss3Scores(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			in: VulnInfo{
 | 
			
		||||
				CveContents: CveContents{
 | 
			
		||||
					Ubuntu: {
 | 
			
		||||
					Ubuntu: []CveContent{{
 | 
			
		||||
						Type:          Ubuntu,
 | 
			
		||||
						Cvss3Severity: "HIGH",
 | 
			
		||||
					},
 | 
			
		||||
					}},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: []CveContentCvss{
 | 
			
		||||
@@ -720,12 +720,12 @@ func TestMaxCvss3Scores(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			in: VulnInfo{
 | 
			
		||||
				CveContents: CveContents{
 | 
			
		||||
					RedHat: {
 | 
			
		||||
					RedHat: []CveContent{{
 | 
			
		||||
						Type:          RedHat,
 | 
			
		||||
						Cvss3Severity: "HIGH",
 | 
			
		||||
						Cvss3Score:    8.0,
 | 
			
		||||
						Cvss3Vector:   "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
 | 
			
		||||
					},
 | 
			
		||||
					}},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: CveContentCvss{
 | 
			
		||||
@@ -768,14 +768,14 @@ func TestMaxCvssScores(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			in: VulnInfo{
 | 
			
		||||
				CveContents: CveContents{
 | 
			
		||||
					Nvd: {
 | 
			
		||||
					Nvd: []CveContent{{
 | 
			
		||||
						Type:       Nvd,
 | 
			
		||||
						Cvss3Score: 7.0,
 | 
			
		||||
					},
 | 
			
		||||
					RedHat: {
 | 
			
		||||
					}},
 | 
			
		||||
					RedHat: []CveContent{{
 | 
			
		||||
						Type:       RedHat,
 | 
			
		||||
						Cvss2Score: 8.0,
 | 
			
		||||
					},
 | 
			
		||||
					}},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: CveContentCvss{
 | 
			
		||||
@@ -789,10 +789,10 @@ func TestMaxCvssScores(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			in: VulnInfo{
 | 
			
		||||
				CveContents: CveContents{
 | 
			
		||||
					RedHat: {
 | 
			
		||||
					RedHat: []CveContent{{
 | 
			
		||||
						Type:       RedHat,
 | 
			
		||||
						Cvss3Score: 8.0,
 | 
			
		||||
					},
 | 
			
		||||
					}},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: CveContentCvss{
 | 
			
		||||
@@ -807,10 +807,10 @@ func TestMaxCvssScores(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			in: VulnInfo{
 | 
			
		||||
				CveContents: CveContents{
 | 
			
		||||
					Ubuntu: {
 | 
			
		||||
					Ubuntu: []CveContent{{
 | 
			
		||||
						Type:          Ubuntu,
 | 
			
		||||
						Cvss3Severity: "HIGH",
 | 
			
		||||
					},
 | 
			
		||||
					}},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: CveContentCvss{
 | 
			
		||||
@@ -827,15 +827,15 @@ func TestMaxCvssScores(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			in: VulnInfo{
 | 
			
		||||
				CveContents: CveContents{
 | 
			
		||||
					Ubuntu: {
 | 
			
		||||
					Ubuntu: []CveContent{{
 | 
			
		||||
						Type:          Ubuntu,
 | 
			
		||||
						Cvss3Severity: "MEDIUM",
 | 
			
		||||
					},
 | 
			
		||||
					Nvd: {
 | 
			
		||||
					}},
 | 
			
		||||
					Nvd: []CveContent{{
 | 
			
		||||
						Type:          Nvd,
 | 
			
		||||
						Cvss2Score:    7.0,
 | 
			
		||||
						Cvss2Severity: "HIGH",
 | 
			
		||||
					},
 | 
			
		||||
					}},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: CveContentCvss{
 | 
			
		||||
@@ -871,15 +871,15 @@ func TestMaxCvssScores(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			in: VulnInfo{
 | 
			
		||||
				CveContents: CveContents{
 | 
			
		||||
					Ubuntu: {
 | 
			
		||||
					Ubuntu: []CveContent{{
 | 
			
		||||
						Type:          Ubuntu,
 | 
			
		||||
						Cvss3Severity: "MEDIUM",
 | 
			
		||||
					},
 | 
			
		||||
					Nvd: {
 | 
			
		||||
					}},
 | 
			
		||||
					Nvd: []CveContent{{
 | 
			
		||||
						Type:          Nvd,
 | 
			
		||||
						Cvss2Score:    4.0,
 | 
			
		||||
						Cvss2Severity: "MEDIUM",
 | 
			
		||||
					},
 | 
			
		||||
					}},
 | 
			
		||||
				},
 | 
			
		||||
				DistroAdvisories: []DistroAdvisory{
 | 
			
		||||
					{
 | 
			
		||||
@@ -925,21 +925,21 @@ func TestFormatMaxCvssScore(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			in: VulnInfo{
 | 
			
		||||
				CveContents: CveContents{
 | 
			
		||||
					Jvn: {
 | 
			
		||||
					Jvn: []CveContent{{
 | 
			
		||||
						Type:          Jvn,
 | 
			
		||||
						Cvss2Severity: "HIGH",
 | 
			
		||||
						Cvss2Score:    8.3,
 | 
			
		||||
					},
 | 
			
		||||
					RedHat: {
 | 
			
		||||
					}},
 | 
			
		||||
					RedHat: []CveContent{{
 | 
			
		||||
						Type:          RedHat,
 | 
			
		||||
						Cvss3Severity: "HIGH",
 | 
			
		||||
						Cvss3Score:    8.0,
 | 
			
		||||
					},
 | 
			
		||||
					Nvd: {
 | 
			
		||||
					}},
 | 
			
		||||
					Nvd: []CveContent{{
 | 
			
		||||
						Type:       Nvd,
 | 
			
		||||
						Cvss2Score: 8.1,
 | 
			
		||||
						// Severity is NOT included in NVD
 | 
			
		||||
					},
 | 
			
		||||
					}},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: "8.0 HIGH (redhat)",
 | 
			
		||||
@@ -947,22 +947,22 @@ func TestFormatMaxCvssScore(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			in: VulnInfo{
 | 
			
		||||
				CveContents: CveContents{
 | 
			
		||||
					Jvn: {
 | 
			
		||||
					Jvn: []CveContent{{
 | 
			
		||||
						Type:          Jvn,
 | 
			
		||||
						Cvss2Severity: "HIGH",
 | 
			
		||||
						Cvss2Score:    8.3,
 | 
			
		||||
					},
 | 
			
		||||
					RedHat: {
 | 
			
		||||
					}},
 | 
			
		||||
					RedHat: []CveContent{{
 | 
			
		||||
						Type:          RedHat,
 | 
			
		||||
						Cvss2Severity: "HIGH",
 | 
			
		||||
						Cvss2Score:    8.0,
 | 
			
		||||
						Cvss3Severity: "HIGH",
 | 
			
		||||
						Cvss3Score:    9.9,
 | 
			
		||||
					},
 | 
			
		||||
					Nvd: {
 | 
			
		||||
					}},
 | 
			
		||||
					Nvd: []CveContent{{
 | 
			
		||||
						Type:       Nvd,
 | 
			
		||||
						Cvss2Score: 8.1,
 | 
			
		||||
					},
 | 
			
		||||
					}},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: "9.9 HIGH (redhat)",
 | 
			
		||||
@@ -1037,20 +1037,20 @@ func TestAppendIfMissing(t *testing.T) {
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			in: Confidences{
 | 
			
		||||
				CpeVersionMatch,
 | 
			
		||||
				NvdExactVersionMatch,
 | 
			
		||||
			},
 | 
			
		||||
			arg: CpeVersionMatch,
 | 
			
		||||
			arg: NvdExactVersionMatch,
 | 
			
		||||
			out: Confidences{
 | 
			
		||||
				CpeVersionMatch,
 | 
			
		||||
				NvdExactVersionMatch,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: Confidences{
 | 
			
		||||
				CpeVersionMatch,
 | 
			
		||||
				NvdExactVersionMatch,
 | 
			
		||||
			},
 | 
			
		||||
			arg: ChangelogExactMatch,
 | 
			
		||||
			out: Confidences{
 | 
			
		||||
				CpeVersionMatch,
 | 
			
		||||
				NvdExactVersionMatch,
 | 
			
		||||
				ChangelogExactMatch,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
@@ -1071,21 +1071,21 @@ func TestSortByConfident(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			in: Confidences{
 | 
			
		||||
				OvalMatch,
 | 
			
		||||
				CpeVersionMatch,
 | 
			
		||||
				NvdExactVersionMatch,
 | 
			
		||||
			},
 | 
			
		||||
			out: Confidences{
 | 
			
		||||
				OvalMatch,
 | 
			
		||||
				CpeVersionMatch,
 | 
			
		||||
				NvdExactVersionMatch,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: Confidences{
 | 
			
		||||
				CpeVersionMatch,
 | 
			
		||||
				NvdExactVersionMatch,
 | 
			
		||||
				OvalMatch,
 | 
			
		||||
			},
 | 
			
		||||
			out: Confidences{
 | 
			
		||||
				OvalMatch,
 | 
			
		||||
				CpeVersionMatch,
 | 
			
		||||
				NvdExactVersionMatch,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
@@ -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,100 @@ 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)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestVulnInfos_FilterByConfidenceOver(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		over int
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name  string
 | 
			
		||||
		v     VulnInfos
 | 
			
		||||
		args  args
 | 
			
		||||
		want  VulnInfos
 | 
			
		||||
		nwant int
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "over 0",
 | 
			
		||||
			v: map[string]VulnInfo{
 | 
			
		||||
				"CVE-2021-1111": {
 | 
			
		||||
					CveID:       "CVE-2021-1111",
 | 
			
		||||
					Confidences: Confidences{JvnVendorProductMatch},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			args: args{
 | 
			
		||||
				over: 0,
 | 
			
		||||
			},
 | 
			
		||||
			want: map[string]VulnInfo{
 | 
			
		||||
				"CVE-2021-1111": {
 | 
			
		||||
					CveID:       "CVE-2021-1111",
 | 
			
		||||
					Confidences: Confidences{JvnVendorProductMatch},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "over 20",
 | 
			
		||||
			v: map[string]VulnInfo{
 | 
			
		||||
				"CVE-2021-1111": {
 | 
			
		||||
					CveID:       "CVE-2021-1111",
 | 
			
		||||
					Confidences: Confidences{JvnVendorProductMatch},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			args: args{
 | 
			
		||||
				over: 20,
 | 
			
		||||
			},
 | 
			
		||||
			nwant: 1,
 | 
			
		||||
			want:  map[string]VulnInfo{},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "over 100",
 | 
			
		||||
			v: map[string]VulnInfo{
 | 
			
		||||
				"CVE-2021-1111": {
 | 
			
		||||
					CveID: "CVE-2021-1111",
 | 
			
		||||
					Confidences: Confidences{
 | 
			
		||||
						NvdExactVersionMatch,
 | 
			
		||||
						JvnVendorProductMatch,
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			args: args{
 | 
			
		||||
				over: 20,
 | 
			
		||||
			},
 | 
			
		||||
			want: map[string]VulnInfo{
 | 
			
		||||
				"CVE-2021-1111": {
 | 
			
		||||
					CveID: "CVE-2021-1111",
 | 
			
		||||
					Confidences: Confidences{
 | 
			
		||||
						NvdExactVersionMatch,
 | 
			
		||||
						JvnVendorProductMatch,
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			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)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package oval
 | 
			
		||||
@@ -32,7 +33,7 @@ func (o Alpine) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		driver, err := newOvalDB(o.Cnf, r.Family)
 | 
			
		||||
		driver, err := newOvalDB(o.Cnf)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
@@ -53,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)
 | 
			
		||||
@@ -64,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
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										156
									
								
								oval/debian.go
									
									
									
									
									
								
							
							
						
						
									
										156
									
								
								oval/debian.go
									
									
									
									
									
								
							@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package oval
 | 
			
		||||
@@ -11,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
 | 
			
		||||
@@ -19,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] = 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,
 | 
			
		||||
@@ -94,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
 | 
			
		||||
@@ -146,7 +158,7 @@ func (o Debian) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		driver, err := newOvalDB(o.Cnf, r.Family)
 | 
			
		||||
		driver, err := newOvalDB(o.Cnf)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
@@ -181,9 +193,11 @@ func (o Debian) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, vuln := range r.ScannedCves {
 | 
			
		||||
		if cont, ok := vuln.CveContents[models.Debian]; ok {
 | 
			
		||||
			cont.SourceLink = "https://security-tracker.debian.org/tracker/" + cont.CveID
 | 
			
		||||
			vuln.CveContents[models.Debian] = cont
 | 
			
		||||
		if conts, ok := vuln.CveContents[models.Debian]; ok {
 | 
			
		||||
			for i, cont := range conts {
 | 
			
		||||
				cont.SourceLink = "https://security-tracker.debian.org/tracker/" + cont.CveID
 | 
			
		||||
				vuln.CveContents[models.Debian][i] = cont
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return len(relatedDefs.entries), nil
 | 
			
		||||
@@ -458,7 +472,7 @@ func (o Ubuntu) fillWithOval(r *models.ScanResult, kernelNamesInOval []string) (
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		driver, err := newOvalDB(o.Cnf, r.Family)
 | 
			
		||||
		driver, err := newOvalDB(o.Cnf)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
@@ -498,9 +512,11 @@ func (o Ubuntu) fillWithOval(r *models.ScanResult, kernelNamesInOval []string) (
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, vuln := range r.ScannedCves {
 | 
			
		||||
		if cont, ok := vuln.CveContents[models.Ubuntu]; ok {
 | 
			
		||||
			cont.SourceLink = "http://people.ubuntu.com/~ubuntu-security/cve/" + cont.CveID
 | 
			
		||||
			vuln.CveContents[models.Ubuntu] = cont
 | 
			
		||||
		if conts, ok := vuln.CveContents[models.Ubuntu]; ok {
 | 
			
		||||
			for i, cont := range conts {
 | 
			
		||||
				cont.SourceLink = "http://people.ubuntu.com/~ubuntu-security/cve/" + cont.CveID
 | 
			
		||||
				vuln.CveContents[models.Ubuntu][i] = cont
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return len(relatedDefs.entries), nil
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package oval
 | 
			
		||||
@@ -7,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) {
 | 
			
		||||
@@ -29,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{
 | 
			
		||||
@@ -52,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)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								oval/oval.go
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								oval/oval.go
									
									
									
									
									
								
							@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package oval
 | 
			
		||||
@@ -10,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"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -35,7 +36,7 @@ func (b Base) CheckIfOvalFetched(osFamily, release string) (fetched bool, err er
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
	if !b.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		driver, err := newOvalDB(b.Cnf, ovalFamily)
 | 
			
		||||
		driver, err := newOvalDB(b.Cnf)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, err
 | 
			
		||||
		}
 | 
			
		||||
@@ -74,7 +75,7 @@ func (b Base) CheckIfOvalFresh(osFamily, release string) (ok bool, err error) {
 | 
			
		||||
	}
 | 
			
		||||
	var lastModified time.Time
 | 
			
		||||
	if !b.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		driver, err := newOvalDB(b.Cnf, ovalFamily)
 | 
			
		||||
		driver, err := newOvalDB(b.Cnf)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, err
 | 
			
		||||
		}
 | 
			
		||||
@@ -102,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
 | 
			
		||||
	}
 | 
			
		||||
@@ -111,7 +112,7 @@ func (b Base) CheckIfOvalFresh(osFamily, release string) (ok bool, err error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewOvalDB returns oval db client
 | 
			
		||||
func newOvalDB(cnf config.VulnDictInterface, familyInScanResult string) (driver db.DB, err error) {
 | 
			
		||||
func newOvalDB(cnf config.VulnDictInterface) (driver db.DB, err error) {
 | 
			
		||||
	if cnf.IsFetchViaHTTP() {
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -121,12 +122,7 @@ func newOvalDB(cnf config.VulnDictInterface, familyInScanResult string) (driver
 | 
			
		||||
		path = cnf.GetSQLite3Path()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ovalFamily, err := GetFamilyInOval(familyInScanResult)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	driver, locked, err := db.NewDB(ovalFamily, cnf.GetType(), path, cnf.GetDebugSQL())
 | 
			
		||||
	driver, locked, err := db.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), db.Option{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if locked {
 | 
			
		||||
			err = xerrors.Errorf("SQLite3: %s is locked. err: %w", cnf.GetSQLite3Path(), err)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										122
									
								
								oval/redhat.go
									
									
									
									
									
								
							
							
						
						
									
										122
									
								
								oval/redhat.go
									
									
									
									
									
								
							@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package oval
 | 
			
		||||
@@ -11,10 +12,10 @@ 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 and Rocky
 | 
			
		||||
// RedHatBase is the base struct for RedHat, CentOS, Alma and Rocky
 | 
			
		||||
type RedHatBase struct {
 | 
			
		||||
	Base
 | 
			
		||||
}
 | 
			
		||||
@@ -27,7 +28,7 @@ func (o RedHatBase) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		driver, err := newOvalDB(o.Cnf, r.Family)
 | 
			
		||||
		driver, err := newOvalDB(o.Cnf)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
@@ -50,14 +51,31 @@ func (o RedHatBase) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
	for _, vuln := range r.ScannedCves {
 | 
			
		||||
		switch models.NewCveContentType(o.family) {
 | 
			
		||||
		case models.RedHat:
 | 
			
		||||
			if cont, ok := vuln.CveContents[models.RedHat]; ok {
 | 
			
		||||
				cont.SourceLink = "https://access.redhat.com/security/cve/" + cont.CveID
 | 
			
		||||
				vuln.CveContents[models.RedHat] = cont
 | 
			
		||||
			if conts, ok := vuln.CveContents[models.RedHat]; ok {
 | 
			
		||||
				for i, cont := range conts {
 | 
			
		||||
					cont.SourceLink = "https://access.redhat.com/security/cve/" + cont.CveID
 | 
			
		||||
					vuln.CveContents[models.RedHat][i] = cont
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		case models.Oracle:
 | 
			
		||||
			if cont, ok := vuln.CveContents[models.Oracle]; ok {
 | 
			
		||||
				cont.SourceLink = fmt.Sprintf("https://linux.oracle.com/cve/%s.html", cont.CveID)
 | 
			
		||||
				vuln.CveContents[models.Oracle] = cont
 | 
			
		||||
			if conts, ok := vuln.CveContents[models.Oracle]; ok {
 | 
			
		||||
				for i, cont := range conts {
 | 
			
		||||
					cont.SourceLink = fmt.Sprintf("https://linux.oracle.com/cve/%s.html", cont.CveID)
 | 
			
		||||
					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
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -97,55 +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.LastModified.After(ovalContent.LastModified) {
 | 
			
		||||
					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)
 | 
			
		||||
			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)
 | 
			
		||||
					} else {
 | 
			
		||||
						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] = 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
 | 
			
		||||
	}
 | 
			
		||||
@@ -155,7 +184,7 @@ func (o RedHatBase) update(r *models.ScanResult, defPacks defPacks) (nCVEs int)
 | 
			
		||||
func (o RedHatBase) convertToDistroAdvisory(def *ovalmodels.Definition) *models.DistroAdvisory {
 | 
			
		||||
	advisoryID := def.Title
 | 
			
		||||
	switch o.family {
 | 
			
		||||
	case constant.RedHat, constant.CentOS, constant.Rocky, constant.Oracle:
 | 
			
		||||
	case constant.RedHat, constant.CentOS, constant.Alma, constant.Rocky, constant.Oracle:
 | 
			
		||||
		if def.Title != "" {
 | 
			
		||||
			ss := strings.Fields(def.Title)
 | 
			
		||||
			advisoryID = strings.TrimSuffix(ss[0], ":")
 | 
			
		||||
@@ -171,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)
 | 
			
		||||
@@ -323,6 +353,24 @@ func NewAmazon(cnf config.VulnDictInterface) Amazon {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Alma is the interface for RedhatBase OVAL
 | 
			
		||||
type Alma struct {
 | 
			
		||||
	// Base
 | 
			
		||||
	RedHatBase
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewAlma creates OVAL client for Alma Linux
 | 
			
		||||
func NewAlma(cnf config.VulnDictInterface) Alma {
 | 
			
		||||
	return Alma{
 | 
			
		||||
		RedHatBase{
 | 
			
		||||
			Base{
 | 
			
		||||
				family: constant.Alma,
 | 
			
		||||
				Cnf:    cnf,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Rocky is the interface for RedhatBase OVAL
 | 
			
		||||
type Rocky struct {
 | 
			
		||||
	// Base
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package oval
 | 
			
		||||
@@ -7,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) {
 | 
			
		||||
@@ -128,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)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										44
									
								
								oval/suse.go
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								oval/suse.go
									
									
									
									
									
								
							@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package oval
 | 
			
		||||
@@ -7,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
 | 
			
		||||
@@ -34,7 +35,7 @@ func (o SUSE) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		driver, err := newOvalDB(o.Cnf, r.Family)
 | 
			
		||||
		driver, err := newOvalDB(o.Cnf)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
@@ -53,22 +54,24 @@ func (o SUSE) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, vuln := range r.ScannedCves {
 | 
			
		||||
		if cont, ok := vuln.CveContents[models.SUSE]; ok {
 | 
			
		||||
			cont.SourceLink = "https://security-tracker.debian.org/tracker/" + cont.CveID
 | 
			
		||||
			vuln.CveContents[models.SUSE] = cont
 | 
			
		||||
		if conts, ok := vuln.CveContents[models.SUSE]; ok {
 | 
			
		||||
			for i, cont := range conts {
 | 
			
		||||
				cont.SourceLink = "https://security-tracker.debian.org/tracker/" + cont.CveID
 | 
			
		||||
				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),
 | 
			
		||||
		}
 | 
			
		||||
@@ -76,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] = 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 {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								oval/util.go
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								oval/util.go
									
									
									
									
									
								
							@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package oval
 | 
			
		||||
@@ -19,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"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -337,7 +338,7 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family string, ru
 | 
			
		||||
 | 
			
		||||
		if running.Release != "" {
 | 
			
		||||
			switch family {
 | 
			
		||||
			case constant.RedHat, constant.CentOS, constant.Rocky, constant.Oracle:
 | 
			
		||||
			case constant.RedHat, constant.CentOS, constant.Alma, constant.Rocky, constant.Oracle:
 | 
			
		||||
				// For kernel related packages, ignore OVAL information with different major versions
 | 
			
		||||
				if _, ok := kernelRelatedPackNames[ovalPack.Name]; ok {
 | 
			
		||||
					if util.Major(ovalPack.Version) != util.Major(running.Release) {
 | 
			
		||||
@@ -377,7 +378,7 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family string, ru
 | 
			
		||||
				return true, false, ovalPack.Version, nil
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// But CentOS/Rocky can't judge whether fixed or unfixed.
 | 
			
		||||
			// But CentOS/Alma/Rocky can't judge whether fixed or unfixed.
 | 
			
		||||
			// Because fixed state in RHEL OVAL is different.
 | 
			
		||||
			// So, it have to be judged version comparison.
 | 
			
		||||
 | 
			
		||||
@@ -436,6 +437,7 @@ func lessThan(family, newVer string, packInOVAL ovalmodels.Package) (bool, error
 | 
			
		||||
 | 
			
		||||
	case constant.RedHat,
 | 
			
		||||
		constant.CentOS,
 | 
			
		||||
		constant.Alma,
 | 
			
		||||
		constant.Rocky:
 | 
			
		||||
		vera := rpmver.NewVersion(rhelDownStreamOSVersionToRHEL(newVer))
 | 
			
		||||
		verb := rpmver.NewVersion(rhelDownStreamOSVersionToRHEL(packInOVAL.Version))
 | 
			
		||||
@@ -446,7 +448,7 @@ func lessThan(family, newVer string, packInOVAL ovalmodels.Package) (bool, error
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var rhelDownStreamOSVerPattern = regexp.MustCompile(`\.[es]l(\d+)(?:_\d+)?(?:\.(centos|rocky))?`)
 | 
			
		||||
var rhelDownStreamOSVerPattern = regexp.MustCompile(`\.[es]l(\d+)(?:_\d+)?(?:\.(centos|rocky|alma))?`)
 | 
			
		||||
 | 
			
		||||
func rhelDownStreamOSVersionToRHEL(ver string) string {
 | 
			
		||||
	return rhelDownStreamOSVerPattern.ReplaceAllString(ver, ".el$1")
 | 
			
		||||
@@ -463,6 +465,8 @@ func NewOVALClient(family string, cnf config.GovalDictConf) (Client, error) {
 | 
			
		||||
		return NewRedhat(&cnf), nil
 | 
			
		||||
	case constant.CentOS:
 | 
			
		||||
		return NewCentOS(&cnf), nil
 | 
			
		||||
	case constant.Alma:
 | 
			
		||||
		return NewAlma(&cnf), nil
 | 
			
		||||
	case constant.Rocky:
 | 
			
		||||
		return NewRocky(&cnf), nil
 | 
			
		||||
	case constant.Oracle:
 | 
			
		||||
@@ -487,14 +491,14 @@ func NewOVALClient(family string, cnf config.GovalDictConf) (Client, error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetFamilyInOval returns the OS family name in OVAL
 | 
			
		||||
// For example, CentOS/Rocky uses Red Hat's OVAL, so return 'redhat'
 | 
			
		||||
// For example, CentOS/Alma/Rocky uses Red Hat's OVAL, so return 'redhat'
 | 
			
		||||
func GetFamilyInOval(familyInScanResult string) (string, error) {
 | 
			
		||||
	switch familyInScanResult {
 | 
			
		||||
	case constant.Debian, constant.Raspbian:
 | 
			
		||||
		return constant.Debian, nil
 | 
			
		||||
	case constant.Ubuntu:
 | 
			
		||||
		return constant.Ubuntu, nil
 | 
			
		||||
	case constant.RedHat, constant.CentOS, constant.Rocky:
 | 
			
		||||
	case constant.RedHat, constant.CentOS, constant.Alma, constant.Rocky:
 | 
			
		||||
		return constant.RedHat, nil
 | 
			
		||||
	case constant.Oracle:
 | 
			
		||||
		return constant.Oracle, nil
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package oval
 | 
			
		||||
@@ -9,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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -269,19 +269,20 @@ func (w SlackWriter) attachmentText(vinfo models.VulnInfo, cweDict map[string]mo
 | 
			
		||||
				vinfo.CveID)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if cont, ok := vinfo.CveContents[cvss.Type]; ok {
 | 
			
		||||
			v := fmt.Sprintf("<%s|%s> %s (<%s|%s>)",
 | 
			
		||||
				calcURL,
 | 
			
		||||
				fmt.Sprintf("%3.1f/%s", cvss.Value.Score, cvss.Value.Vector),
 | 
			
		||||
				cvss.Value.Severity,
 | 
			
		||||
				cont.SourceLink,
 | 
			
		||||
				cvss.Type)
 | 
			
		||||
			vectors = append(vectors, v)
 | 
			
		||||
 | 
			
		||||
		if conts, ok := vinfo.CveContents[cvss.Type]; ok {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
				v := fmt.Sprintf("<%s|%s> %s (<%s|%s>)",
 | 
			
		||||
					calcURL,
 | 
			
		||||
					fmt.Sprintf("%3.1f/%s", cvss.Value.Score, cvss.Value.Vector),
 | 
			
		||||
					cvss.Value.Severity,
 | 
			
		||||
					cont.SourceLink,
 | 
			
		||||
					cvss.Type)
 | 
			
		||||
				vectors = append(vectors, v)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			if 0 < len(vinfo.DistroAdvisories) {
 | 
			
		||||
				links := []string{}
 | 
			
		||||
				for _, v := range vinfo.CveContents.PrimarySrcURLs(w.lang, w.osFamily, vinfo.CveID) {
 | 
			
		||||
				for _, v := range vinfo.CveContents.PrimarySrcURLs(w.lang, w.osFamily, vinfo.CveID, vinfo.Confidences) {
 | 
			
		||||
					links = append(links, fmt.Sprintf("<%s|%s>", v.Value, v.Type))
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -70,16 +70,20 @@ func (w SyslogWriter) encodeSyslog(result models.ScanResult) (messages []string)
 | 
			
		||||
			kvPairs = append(kvPairs, fmt.Sprintf(`cvss_vector_%s_v3="%s"`, cvss.Type, cvss.Value.Vector))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if content, ok := vinfo.CveContents[models.Nvd]; ok {
 | 
			
		||||
			cwes := strings.Join(content.CweIDs, ",")
 | 
			
		||||
			kvPairs = append(kvPairs, fmt.Sprintf(`cwe_ids="%s"`, cwes))
 | 
			
		||||
			if w.Cnf.Verbose {
 | 
			
		||||
				kvPairs = append(kvPairs, fmt.Sprintf(`source_link="%s"`, content.SourceLink))
 | 
			
		||||
				kvPairs = append(kvPairs, fmt.Sprintf(`summary="%s"`, content.Summary))
 | 
			
		||||
		if conts, ok := vinfo.CveContents[models.Nvd]; ok {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
				cwes := strings.Join(cont.CweIDs, ",")
 | 
			
		||||
				kvPairs = append(kvPairs, fmt.Sprintf(`cwe_ids="%s"`, cwes))
 | 
			
		||||
				if w.Cnf.Verbose {
 | 
			
		||||
					kvPairs = append(kvPairs, fmt.Sprintf(`source_link="%s"`, cont.SourceLink))
 | 
			
		||||
					kvPairs = append(kvPairs, fmt.Sprintf(`summary="%s"`, cont.Summary))
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if content, ok := vinfo.CveContents[models.RedHat]; ok {
 | 
			
		||||
			kvPairs = append(kvPairs, fmt.Sprintf(`title="%s"`, content.Title))
 | 
			
		||||
		if conts, ok := vinfo.CveContents[models.RedHat]; ok {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
				kvPairs = append(kvPairs, fmt.Sprintf(`title="%s"`, cont.Title))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// message: key1="value1" key2="value2"...
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,7 @@ func TestSyslogWriterEncodeSyslog(t *testing.T) {
 | 
			
		||||
							models.PackageFixStatus{Name: "pkg4"},
 | 
			
		||||
						},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							models.Nvd: models.CveContent{
 | 
			
		||||
							models.Nvd: []models.CveContent{{
 | 
			
		||||
								Cvss2Score:    5.0,
 | 
			
		||||
								Cvss2Vector:   "AV:L/AC:L/Au:N/C:N/I:N/A:C",
 | 
			
		||||
								Cvss2Severity: "MEDIUM",
 | 
			
		||||
@@ -41,7 +41,7 @@ func TestSyslogWriterEncodeSyslog(t *testing.T) {
 | 
			
		||||
								Cvss3Score:    9.8,
 | 
			
		||||
								Cvss3Vector:   "AV:L/AC:L/Au:N/C:N/I:N/A:C",
 | 
			
		||||
								Cvss3Severity: "HIGH",
 | 
			
		||||
							},
 | 
			
		||||
							}},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
@@ -65,13 +65,13 @@ func TestSyslogWriterEncodeSyslog(t *testing.T) {
 | 
			
		||||
							models.PackageFixStatus{Name: "pkg5"},
 | 
			
		||||
						},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							models.RedHat: models.CveContent{
 | 
			
		||||
							models.RedHat: []models.CveContent{{
 | 
			
		||||
								Cvss3Score:    5.0,
 | 
			
		||||
								Cvss3Severity: "Medium",
 | 
			
		||||
								Cvss3Vector:   "AV:L/AC:L/Au:N/C:N/I:N/A:C",
 | 
			
		||||
								CweIDs:        []string{"CWE-284"},
 | 
			
		||||
								Title:         "RHSA-2017:0001: pkg5 security update (Important)",
 | 
			
		||||
							},
 | 
			
		||||
							}},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ import (
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strings"
 | 
			
		||||
@@ -276,7 +277,7 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
			// fmt.Sprintf("%4.1f", v2max),
 | 
			
		||||
			// fmt.Sprintf("%4.1f", v3max),
 | 
			
		||||
			exploits,
 | 
			
		||||
			vinfo.AlertDict.FormatSource(),
 | 
			
		||||
			fmt.Sprintf("%9s", vinfo.AlertDict.FormatSource()),
 | 
			
		||||
			fmt.Sprintf("%7s", vinfo.PatchStatus(r.Packages)),
 | 
			
		||||
			link,
 | 
			
		||||
		})
 | 
			
		||||
@@ -291,7 +292,7 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
		// "v3",
 | 
			
		||||
		// "v2",
 | 
			
		||||
		"PoC",
 | 
			
		||||
		"CERT",
 | 
			
		||||
		"Alert",
 | 
			
		||||
		"Fixed",
 | 
			
		||||
		"NVD",
 | 
			
		||||
	})
 | 
			
		||||
@@ -347,7 +348,7 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
			data = append(data, []string{"Mitigation", m.URL})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		links := vuln.CveContents.PrimarySrcURLs(r.Lang, r.Family, vuln.CveID)
 | 
			
		||||
		links := vuln.CveContents.PrimarySrcURLs(r.Lang, r.Family, vuln.CveID, vuln.Confidences)
 | 
			
		||||
		for _, link := range links {
 | 
			
		||||
			data = append(data, []string{"Primary Src", link.Value})
 | 
			
		||||
		}
 | 
			
		||||
@@ -474,11 +475,15 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
			data = append(data, []string{"SANS/CWE Top25", sansTop25URLs[0]})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, alert := range vuln.AlertDict.Ja {
 | 
			
		||||
		for _, alert := range vuln.AlertDict.CISA {
 | 
			
		||||
			data = append(data, []string{"CISA Alert", alert.URL})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, alert := range vuln.AlertDict.JPCERT {
 | 
			
		||||
			data = append(data, []string{"JPCERT Alert", alert.URL})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, alert := range vuln.AlertDict.En {
 | 
			
		||||
		for _, alert := range vuln.AlertDict.USCERT {
 | 
			
		||||
			data = append(data, []string{"US-CERT Alert", alert.URL})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -620,7 +625,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)
 | 
			
		||||
@@ -673,32 +678,36 @@ func isCveInfoUpdated(cveID string, previous, current models.ScanResult) bool {
 | 
			
		||||
		models.NewCveContentType(current.Family),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	prevLastModified := map[models.CveContentType]time.Time{}
 | 
			
		||||
	prevLastModifieds := map[models.CveContentType][]time.Time{}
 | 
			
		||||
	preVinfo, ok := previous.ScannedCves[cveID]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	for _, cType := range cTypes {
 | 
			
		||||
		if content, ok := preVinfo.CveContents[cType]; ok {
 | 
			
		||||
			prevLastModified[cType] = content.LastModified
 | 
			
		||||
		if conts, ok := preVinfo.CveContents[cType]; ok {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
				prevLastModifieds[cType] = append(prevLastModifieds[cType], cont.LastModified)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	curLastModified := map[models.CveContentType]time.Time{}
 | 
			
		||||
	curLastModifieds := map[models.CveContentType][]time.Time{}
 | 
			
		||||
	curVinfo, ok := current.ScannedCves[cveID]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	for _, cType := range cTypes {
 | 
			
		||||
		if content, ok := curVinfo.CveContents[cType]; ok {
 | 
			
		||||
			curLastModified[cType] = content.LastModified
 | 
			
		||||
		if conts, ok := curVinfo.CveContents[cType]; ok {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
				curLastModifieds[cType] = append(curLastModifieds[cType], cont.LastModified)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, t := range cTypes {
 | 
			
		||||
		if !curLastModified[t].Equal(prevLastModified[t]) {
 | 
			
		||||
		if !reflect.DeepEqual(curLastModifieds[t], prevLastModifieds[t]) {
 | 
			
		||||
			logging.Log.Debugf("%s LastModified not equal: \n%s\n%s",
 | 
			
		||||
				cveID, curLastModified[t], prevLastModified[t])
 | 
			
		||||
				cveID, curLastModifieds[t], prevLastModifieds[t])
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										118
									
								
								scanner/alma.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								scanner/alma.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,118 @@
 | 
			
		||||
package scanner
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// inherit OsTypeInterface
 | 
			
		||||
type alma struct {
 | 
			
		||||
	redhatBase
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewAlma is constructor
 | 
			
		||||
func newAlma(c config.ServerInfo) *alma {
 | 
			
		||||
	r := &alma{
 | 
			
		||||
		redhatBase{
 | 
			
		||||
			base: base{
 | 
			
		||||
				osPackages: osPackages{
 | 
			
		||||
					Packages:  models.Packages{},
 | 
			
		||||
					VulnInfos: models.VulnInfos{},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			sudo: rootPrivAlma{},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	r.log = logging.NewNormalLogger()
 | 
			
		||||
	r.setServerInfo(c)
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *alma) checkScanMode() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *alma) checkDeps() error {
 | 
			
		||||
	if o.getServerInfo().Mode.IsFast() {
 | 
			
		||||
		return o.execCheckDeps(o.depsFast())
 | 
			
		||||
	} else if o.getServerInfo().Mode.IsFastRoot() {
 | 
			
		||||
		return o.execCheckDeps(o.depsFastRoot())
 | 
			
		||||
	} else {
 | 
			
		||||
		return o.execCheckDeps(o.depsDeep())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *alma) depsFast() []string {
 | 
			
		||||
	if o.getServerInfo().Mode.IsOffline() {
 | 
			
		||||
		return []string{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// repoquery
 | 
			
		||||
	// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8, Alma8, Rocky8
 | 
			
		||||
	return []string{"yum-utils"}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *alma) depsFastRoot() []string {
 | 
			
		||||
	if o.getServerInfo().Mode.IsOffline() {
 | 
			
		||||
		return []string{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// repoquery
 | 
			
		||||
	// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8, Alma8, Rocky8
 | 
			
		||||
	return []string{"yum-utils"}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *alma) depsDeep() []string {
 | 
			
		||||
	return o.depsFastRoot()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *alma) checkIfSudoNoPasswd() error {
 | 
			
		||||
	if o.getServerInfo().Mode.IsFast() {
 | 
			
		||||
		return o.execCheckIfSudoNoPasswd(o.sudoNoPasswdCmdsFast())
 | 
			
		||||
	} else if o.getServerInfo().Mode.IsFastRoot() {
 | 
			
		||||
		return o.execCheckIfSudoNoPasswd(o.sudoNoPasswdCmdsFastRoot())
 | 
			
		||||
	} else {
 | 
			
		||||
		return o.execCheckIfSudoNoPasswd(o.sudoNoPasswdCmdsDeep())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *alma) sudoNoPasswdCmdsFast() []cmd {
 | 
			
		||||
	return []cmd{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *alma) sudoNoPasswdCmdsFastRoot() []cmd {
 | 
			
		||||
	if !o.ServerInfo.IsContainer() {
 | 
			
		||||
		return []cmd{
 | 
			
		||||
			{"repoquery -h", exitStatusZero},
 | 
			
		||||
			{"needs-restarting", exitStatusZero},
 | 
			
		||||
			{"which which", exitStatusZero},
 | 
			
		||||
			{"stat /proc/1/exe", exitStatusZero},
 | 
			
		||||
			{"ls -l /proc/1/exe", exitStatusZero},
 | 
			
		||||
			{"cat /proc/1/maps", exitStatusZero},
 | 
			
		||||
			{"lsof -i -P", exitStatusZero},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return []cmd{
 | 
			
		||||
		{"repoquery -h", exitStatusZero},
 | 
			
		||||
		{"needs-restarting", exitStatusZero},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *alma) sudoNoPasswdCmdsDeep() []cmd {
 | 
			
		||||
	return o.sudoNoPasswdCmdsFastRoot()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type rootPrivAlma struct{}
 | 
			
		||||
 | 
			
		||||
func (o rootPrivAlma) repoquery() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o rootPrivAlma) yumMakeCache() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o rootPrivAlma) yumPS() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
@@ -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"
 | 
			
		||||
)
 | 
			
		||||
@@ -656,6 +664,7 @@ func AnalyzeLibraries(ctx context.Context, libFilemap map[string][]byte) (librar
 | 
			
		||||
			&wg,
 | 
			
		||||
			semaphore.NewWeighted(1),
 | 
			
		||||
			result,
 | 
			
		||||
			"",
 | 
			
		||||
			path,
 | 
			
		||||
			&DummyFileInfo{},
 | 
			
		||||
			func() ([]byte, error) { return b, nil }); err != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -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"
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ type centos struct {
 | 
			
		||||
	redhatBase
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewAmazon is constructor
 | 
			
		||||
// NewCentOS is constructor
 | 
			
		||||
func newCentOS(c config.ServerInfo) *centos {
 | 
			
		||||
	r := ¢os{
 | 
			
		||||
		redhatBase{
 | 
			
		||||
@@ -49,7 +49,7 @@ func (o *centos) depsFast() []string {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// repoquery
 | 
			
		||||
	// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8, Rocky8
 | 
			
		||||
	// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8, Alma8, Rocky8
 | 
			
		||||
	return []string{"yum-utils"}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -59,7 +59,7 @@ func (o *centos) depsFastRoot() []string {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// repoquery
 | 
			
		||||
	// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8, Rocky8
 | 
			
		||||
	// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8, Alma8, Rocky8
 | 
			
		||||
	return []string{"yum-utils"}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -931,7 +931,7 @@ func (o *debian) getCveIDsFromChangelog(
 | 
			
		||||
	if 1 < len(splittedByColon) {
 | 
			
		||||
		verAfterColon = splittedByColon[1]
 | 
			
		||||
		if cveIDs, pack, err := o.parseChangelog(
 | 
			
		||||
			changelog, name, verAfterColon, models.ChangelogLenientMatch); err == nil {
 | 
			
		||||
			changelog, name, verAfterColon, models.ChangelogRoughMatch); err == nil {
 | 
			
		||||
			return cveIDs, pack
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -948,7 +948,7 @@ func (o *debian) getCveIDsFromChangelog(
 | 
			
		||||
		ss := strings.Split(ver, d)
 | 
			
		||||
		if 1 < len(ss) {
 | 
			
		||||
			if cveIDs, pack, err := o.parseChangelog(
 | 
			
		||||
				changelog, name, ss[0], models.ChangelogLenientMatch); err == nil {
 | 
			
		||||
				changelog, name, ss[0], models.ChangelogRoughMatch); err == nil {
 | 
			
		||||
				return cveIDs, pack
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -956,7 +956,7 @@ func (o *debian) getCveIDsFromChangelog(
 | 
			
		||||
		ss = strings.Split(verAfterColon, d)
 | 
			
		||||
		if 1 < len(ss) {
 | 
			
		||||
			if cveIDs, pack, err := o.parseChangelog(
 | 
			
		||||
				changelog, name, ss[0], models.ChangelogLenientMatch); err == nil {
 | 
			
		||||
				changelog, name, ss[0], models.ChangelogRoughMatch); err == nil {
 | 
			
		||||
				return cveIDs, pack
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -1020,7 +1020,7 @@ func (o *debian) parseChangelog(changelog, name, ver string, confidence models.C
 | 
			
		||||
			pack := o.Packages[name]
 | 
			
		||||
			pack.Changelog = &models.Changelog{
 | 
			
		||||
				Contents: strings.Join(buf, "\n"),
 | 
			
		||||
				Method:   models.ChangelogLenientMatchStr,
 | 
			
		||||
				Method:   models.ChangelogRoughMatchStr,
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			cves := []DetectedCveID{}
 | 
			
		||||
 
 | 
			
		||||
@@ -144,12 +144,7 @@ systemd (228-5) unstable; urgency=medium`,
 | 
			
		||||
		 util-linux (2.27.1-1) unstable; urgency=medium
 | 
			
		||||
		 util-linux (2.27-3ubuntu1) xenial; urgency=medium`,
 | 
			
		||||
			},
 | 
			
		||||
			[]DetectedCveID{
 | 
			
		||||
				// {"CVE-2015-2325", models.ChangelogLenientMatch},
 | 
			
		||||
				// {"CVE-2015-2326", models.ChangelogLenientMatch},
 | 
			
		||||
				// {"CVE-2015-3210", models.ChangelogLenientMatch},
 | 
			
		||||
				// {"CVE-2016-1000000", models.ChangelogLenientMatch},
 | 
			
		||||
			},
 | 
			
		||||
			[]DetectedCveID{},
 | 
			
		||||
			models.Changelog{
 | 
			
		||||
				// Contents: `util-linux (2.27.1-3ubuntu1) xenial; urgency=medium
 | 
			
		||||
				// util-linux (2.27.1-3) unstable; urgency=medium
 | 
			
		||||
@@ -180,12 +175,7 @@ systemd (228-5) unstable; urgency=medium`,
 | 
			
		||||
		 util-linux (2.27.1-1) unstable; urgency=medium
 | 
			
		||||
		 util-linux (2.27-3) xenial; urgency=medium`,
 | 
			
		||||
			},
 | 
			
		||||
			[]DetectedCveID{
 | 
			
		||||
				// {"CVE-2015-2325", models.ChangelogLenientMatch},
 | 
			
		||||
				// {"CVE-2015-2326", models.ChangelogLenientMatch},
 | 
			
		||||
				// {"CVE-2015-3210", models.ChangelogLenientMatch},
 | 
			
		||||
				// {"CVE-2016-1000000", models.ChangelogLenientMatch},
 | 
			
		||||
			},
 | 
			
		||||
			[]DetectedCveID{},
 | 
			
		||||
			models.Changelog{
 | 
			
		||||
				// Contents: `util-linux (2.27.1-3ubuntu1) xenial; urgency=medium
 | 
			
		||||
				// util-linux (2.27.1-3) unstable; urgency=medium
 | 
			
		||||
@@ -845,7 +835,7 @@ vlc (3.0.11-0+deb10u1) buster-security; urgency=high
 | 
			
		||||
 | 
			
		||||
 -- RealVNC <noreply@realvnc.com>  Wed, 13 May 2020 19:51:40 +0100
 | 
			
		||||
`,
 | 
			
		||||
					Method: models.ChangelogLenientMatchStr,
 | 
			
		||||
					Method: models.ChangelogRoughMatchStr,
 | 
			
		||||
				}},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ type oracle struct {
 | 
			
		||||
	redhatBase
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewAmazon is constructor
 | 
			
		||||
// NewOracle is constructor
 | 
			
		||||
func newOracle(c config.ServerInfo) *oracle {
 | 
			
		||||
	r := &oracle{
 | 
			
		||||
		redhatBase{
 | 
			
		||||
 
 | 
			
		||||
@@ -58,12 +58,37 @@ func detectRedhat(c config.ServerInfo) (bool, osTypeInterface) {
 | 
			
		||||
				cent := newCentOS(c)
 | 
			
		||||
				cent.setDistro(constant.CentOS, release)
 | 
			
		||||
				return true, cent
 | 
			
		||||
			case "alma", "almalinux":
 | 
			
		||||
				alma := newAlma(c)
 | 
			
		||||
				alma.setDistro(constant.Alma, release)
 | 
			
		||||
				return true, alma
 | 
			
		||||
			default:
 | 
			
		||||
				logging.Log.Warnf("Failed to parse CentOS: %s", r)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if r := exec(c, "ls /etc/almalinux-release", noSudo); r.isSuccess() {
 | 
			
		||||
		if r := exec(c, "cat /etc/almalinux-release", noSudo); r.isSuccess() {
 | 
			
		||||
			re := regexp.MustCompile(`(.*) release (\d[\d\.]*)`)
 | 
			
		||||
			result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
 | 
			
		||||
			if len(result) != 3 {
 | 
			
		||||
				logging.Log.Warnf("Failed to parse Alma version: %s", r)
 | 
			
		||||
				return true, newAlma(c)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			release := result[2]
 | 
			
		||||
			switch strings.ToLower(result[1]) {
 | 
			
		||||
			case "alma", "almalinux":
 | 
			
		||||
				alma := newAlma(c)
 | 
			
		||||
				alma.setDistro(constant.Alma, release)
 | 
			
		||||
				return true, alma
 | 
			
		||||
			default:
 | 
			
		||||
				logging.Log.Warnf("Failed to parse Alma: %s", r)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if r := exec(c, "ls /etc/rocky-release", noSudo); r.isSuccess() {
 | 
			
		||||
		if r := exec(c, "cat /etc/rocky-release", noSudo); r.isSuccess() {
 | 
			
		||||
			re := regexp.MustCompile(`(.*) release (\d[\d\.]*)`)
 | 
			
		||||
@@ -104,6 +129,10 @@ func detectRedhat(c config.ServerInfo) (bool, osTypeInterface) {
 | 
			
		||||
				cent := newCentOS(c)
 | 
			
		||||
				cent.setDistro(constant.CentOS, release)
 | 
			
		||||
				return true, cent
 | 
			
		||||
			case "alma", "almalinux":
 | 
			
		||||
				alma := newAlma(c)
 | 
			
		||||
				alma.setDistro(constant.Alma, release)
 | 
			
		||||
				return true, alma
 | 
			
		||||
			case "rocky", "rocky linux":
 | 
			
		||||
				rocky := newRocky(c)
 | 
			
		||||
				rocky.setDistro(constant.Rocky, release)
 | 
			
		||||
@@ -659,7 +688,7 @@ func (o *redhatBase) rpmQf() string {
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) detectEnabledDnfModules() ([]string, error) {
 | 
			
		||||
	switch o.Distro.Family {
 | 
			
		||||
	case constant.RedHat, constant.CentOS, constant.Rocky:
 | 
			
		||||
	case constant.RedHat, constant.CentOS, constant.Alma, constant.Rocky:
 | 
			
		||||
		//TODO OracleLinux
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, nil
 | 
			
		||||
 
 | 
			
		||||
@@ -55,7 +55,7 @@ func (o *rhel) depsFastRoot() []string {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// repoquery
 | 
			
		||||
	// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8, Rocky8
 | 
			
		||||
	// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8, Alma8, Rocky8
 | 
			
		||||
	return []string{"yum-utils"}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -49,7 +49,7 @@ func (o *rocky) depsFast() []string {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// repoquery
 | 
			
		||||
	// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8, Rocky8
 | 
			
		||||
	// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8, Alma8, Rocky8
 | 
			
		||||
	return []string{"yum-utils"}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -59,7 +59,7 @@ func (o *rocky) depsFastRoot() []string {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// repoquery
 | 
			
		||||
	// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8, Rocky8
 | 
			
		||||
	// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8, Alma8, Rocky8
 | 
			
		||||
	return []string{"yum-utils"}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -217,6 +217,8 @@ func ParseInstalledPkgs(distro config.Distro, kernel models.Kernel, pkgList stri
 | 
			
		||||
		osType = &rhel{redhatBase: redhatBase{base: base}}
 | 
			
		||||
	case constant.CentOS:
 | 
			
		||||
		osType = ¢os{redhatBase: redhatBase{base: base}}
 | 
			
		||||
	case constant.Alma:
 | 
			
		||||
		osType = &alma{redhatBase: redhatBase{base: base}}
 | 
			
		||||
	case constant.Rocky:
 | 
			
		||||
		osType = &rocky{redhatBase: redhatBase{base: base}}
 | 
			
		||||
	case constant.Oracle:
 | 
			
		||||
@@ -653,7 +655,8 @@ func (s Scanner) getScanResults(scannedAt time.Time) (results models.ScanResults
 | 
			
		||||
		}
 | 
			
		||||
		if o.getServerInfo().Module.IsScanPort() {
 | 
			
		||||
			if err = o.scanPorts(); err != nil {
 | 
			
		||||
				return xerrors.Errorf("Failed to scan Ports: %w", err)
 | 
			
		||||
				// continue scanning
 | 
			
		||||
				logging.Log.Warnf("Failed to scan Ports: %+v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if o.getServerInfo().Module.IsScanWordPress() {
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@ func isRunningKernel(pack models.Package, family string, kernel models.Kernel) (
 | 
			
		||||
		}
 | 
			
		||||
		return false, false
 | 
			
		||||
 | 
			
		||||
	case constant.RedHat, constant.Oracle, constant.CentOS, constant.Rocky, constant.Amazon:
 | 
			
		||||
	case constant.RedHat, constant.Oracle, constant.CentOS, constant.Alma, constant.Rocky, constant.Amazon:
 | 
			
		||||
		switch pack.Name {
 | 
			
		||||
		case "kernel", "kernel-devel", "kernel-core", "kernel-modules", "kernel-uek":
 | 
			
		||||
			ver := fmt.Sprintf("%s-%s.%s", pack.Version, pack.Release, pack.Arch)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package server
 | 
			
		||||
@@ -93,6 +94,11 @@ func (h VulsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
 | 
			
		||||
	}
 | 
			
		||||
	logging.Log.Infof("%s: %d exploits are detected", r.FormatServerName(), nMetasploitCve)
 | 
			
		||||
 | 
			
		||||
	if err := detector.FillWithKEVuln(&r, config.Conf.KEVuln); err != nil {
 | 
			
		||||
		logging.Log.Errorf("Failed to fill with Known Exploited Vulnerabilities: %+v", err)
 | 
			
		||||
		http.Error(w, err.Error(), http.StatusServiceUnavailable)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	detector.FillCweDict(&r)
 | 
			
		||||
 | 
			
		||||
	// set ReportedAt to current time when it's set to the epoch, ensures that ReportedAt will be set
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -103,6 +103,11 @@ func printConfigToml(ips []string) (err error) {
 | 
			
		||||
#sqlite3Path = "/path/to/go-msfdb.sqlite3"
 | 
			
		||||
#url        = ""
 | 
			
		||||
 | 
			
		||||
[kevuln]
 | 
			
		||||
#type = ["sqlite3", "mysql", "postgres", "redis", "http" ]
 | 
			
		||||
#sqlite3Path = "/path/to/go-kev.sqlite3"
 | 
			
		||||
#url        = ""
 | 
			
		||||
 | 
			
		||||
# https://vuls.io/docs/en/config.toml.html#slack-section
 | 
			
		||||
#[slack]
 | 
			
		||||
#hookURL      = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
 | 
			
		||||
@@ -211,6 +216,7 @@ host                = "{{$ip}}"
 | 
			
		||||
#containerType      = "docker" #or "lxd" or "lxc" default: docker
 | 
			
		||||
#containersIncluded = ["${running}"]
 | 
			
		||||
#containersExcluded = ["container_name_a"]
 | 
			
		||||
#confidenceScoreOver = 80
 | 
			
		||||
 | 
			
		||||
#[servers.{{index $names $i}}.containers.container_name_a]
 | 
			
		||||
#cpeNames       = [ "cpe:/a:rubyonrails:ruby_on_rails:4.2.1" ]
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package subcmds
 | 
			
		||||
@@ -59,6 +60,7 @@ func (*ReportCmd) Usage() string {
 | 
			
		||||
		[-log-dir=/path/to/log]
 | 
			
		||||
		[-refresh-cve]
 | 
			
		||||
		[-cvss-over=7]
 | 
			
		||||
		[-confidence-over=80]
 | 
			
		||||
		[-diff]
 | 
			
		||||
		[-diff-minus]
 | 
			
		||||
		[-diff-plus]
 | 
			
		||||
@@ -117,6 +119,9 @@ func (p *ReportCmd) SetFlags(f *flag.FlagSet) {
 | 
			
		||||
	f.Float64Var(&config.Conf.CvssScoreOver, "cvss-over", 0,
 | 
			
		||||
		"-cvss-over=6.5 means reporting CVSS Score 6.5 and over (default: 0 (means report all))")
 | 
			
		||||
 | 
			
		||||
	f.IntVar(&config.Conf.ConfidenceScoreOver, "confidence-over", 80,
 | 
			
		||||
		"-confidence-over=40 means reporting Confidence Score 40 and over (default: 80)")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&config.Conf.DiffMinus, "diff-minus", false,
 | 
			
		||||
		"Minus Difference between previous result and current result")
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package subcmds
 | 
			
		||||
@@ -38,6 +39,7 @@ func (*ServerCmd) Usage() string {
 | 
			
		||||
		[-log-to-file]
 | 
			
		||||
		[-log-dir=/path/to/log]
 | 
			
		||||
		[-cvss-over=7]
 | 
			
		||||
		[-confidence-over=80]
 | 
			
		||||
		[-ignore-unscored-cves]
 | 
			
		||||
		[-ignore-unfixed]
 | 
			
		||||
		[-to-localfile]
 | 
			
		||||
@@ -70,6 +72,9 @@ func (p *ServerCmd) SetFlags(f *flag.FlagSet) {
 | 
			
		||||
	f.Float64Var(&config.Conf.CvssScoreOver, "cvss-over", 0,
 | 
			
		||||
		"-cvss-over=6.5 means Servering CVSS Score 6.5 and over (default: 0 (means Server all))")
 | 
			
		||||
 | 
			
		||||
	f.IntVar(&config.Conf.ConfidenceScoreOver, "confidence-over", 80,
 | 
			
		||||
		"-confidence-over=40 means reporting Confidence Score 40 and over (default: 80)")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&config.Conf.IgnoreUnscoredCves, "ignore-unscored-cves", false,
 | 
			
		||||
		"Don't Server the unscored CVEs")
 | 
			
		||||
 | 
			
		||||
@@ -85,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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package subcmds
 | 
			
		||||
@@ -36,6 +37,7 @@ func (*TuiCmd) Usage() string {
 | 
			
		||||
		[-refresh-cve]
 | 
			
		||||
		[-config=/path/to/config.toml]
 | 
			
		||||
		[-cvss-over=7]
 | 
			
		||||
		[-confidence-over=80]
 | 
			
		||||
		[-diff]
 | 
			
		||||
		[-diff-minus]
 | 
			
		||||
		[-diff-plus]
 | 
			
		||||
@@ -79,6 +81,9 @@ func (p *TuiCmd) SetFlags(f *flag.FlagSet) {
 | 
			
		||||
	f.Float64Var(&config.Conf.CvssScoreOver, "cvss-over", 0,
 | 
			
		||||
		"-cvss-over=6.5 means reporting CVSS Score 6.5 and over (default: 0 (means report all))")
 | 
			
		||||
 | 
			
		||||
	f.IntVar(&config.Conf.ConfidenceScoreOver, "confidence-over", 80,
 | 
			
		||||
		"-confidence-over=40 means reporting Confidence Score 40 and over (default: 80)")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&config.Conf.Diff, "diff", false,
 | 
			
		||||
		"Plus Difference between previous result and current result")
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user