Compare commits
	
		
			42 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					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 | ||
| 
						 | 
					543dc99ecd | ||
| 
						 | 
					f0b3a8b1db | ||
| 
						 | 
					0b9ec05181 | ||
| 
						 | 
					0bf12412d6 | ||
| 
						 | 
					0ea4d58c63 | ||
| 
						 | 
					5755b00576 | ||
| 
						 | 
					1c8e074c9d | ||
| 
						 | 
					0e0e5ce4be | ||
| 
						 | 
					23dfe53885 | ||
| 
						 | 
					8e6351a9e4 | ||
| 
						 | 
					3086e2760f | ||
| 
						 | 
					b8db2e0b74 | ||
| 
						 | 
					43b46cb324 | ||
| 
						 | 
					d0559c7719 | ||
| 
						 | 
					231c63cf62 | ||
| 
						 | 
					2a9aebe059 | ||
| 
						 | 
					4e535d792f | 
							
								
								
									
										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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,5 +1,6 @@
 | 
			
		||||
.vscode
 | 
			
		||||
*.txt
 | 
			
		||||
*.swp
 | 
			
		||||
*.sqlite3*
 | 
			
		||||
*.db
 | 
			
		||||
tags
 | 
			
		||||
@@ -8,10 +9,13 @@ coverage.out
 | 
			
		||||
issues/
 | 
			
		||||
vendor/
 | 
			
		||||
log/
 | 
			
		||||
results/
 | 
			
		||||
results
 | 
			
		||||
!integration/data/results
 | 
			
		||||
config.toml
 | 
			
		||||
!setup/docker/*
 | 
			
		||||
.DS_Store
 | 
			
		||||
dist/
 | 
			
		||||
.idea
 | 
			
		||||
vuls.*
 | 
			
		||||
vuls
 | 
			
		||||
!cmd/vuls
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								GNUmakefile
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								GNUmakefile
									
									
									
									
									
								
							@@ -24,7 +24,7 @@ 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 +42,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 +60,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;
 | 
			
		||||
@@ -89,7 +92,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'
 | 
			
		||||
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
 | 
			
		||||
@@ -108,14 +111,14 @@ endif
 | 
			
		||||
	sleep 1
 | 
			
		||||
	./vuls.old scan -config=./integration/int-config.toml --results-dir=${BASE_DIR} ${LIBS}
 | 
			
		||||
	cp ${BASE_DIR}/current/*.json ${NOW_JSON_DIR}
 | 
			
		||||
	cp integration/data/results/*.json ${NOW_JSON_DIR}
 | 
			
		||||
	- cp integration/data/results/*.json ${NOW_JSON_DIR}
 | 
			
		||||
	./vuls.old report --format-json --refresh-cve --results-dir=${BASE_DIR} -config=./integration/int-config.toml ${NOW}
 | 
			
		||||
 | 
			
		||||
	mkdir -p ${ONE_SEC_AFTER_JSON_DIR}
 | 
			
		||||
	sleep 1
 | 
			
		||||
	./vuls.new scan -config=./integration/int-config.toml --results-dir=${BASE_DIR} ${LIBS}
 | 
			
		||||
	cp ${BASE_DIR}/current/*.json ${ONE_SEC_AFTER_JSON_DIR}
 | 
			
		||||
	cp integration/data/results/*.json ${ONE_SEC_AFTER_JSON_DIR}
 | 
			
		||||
	- cp integration/data/results/*.json ${ONE_SEC_AFTER_JSON_DIR}
 | 
			
		||||
	./vuls.new report --format-json --refresh-cve --results-dir=${BASE_DIR} -config=./integration/int-config.toml ${ONE_SEC_AFTER}
 | 
			
		||||
 | 
			
		||||
	$(call sed-d)
 | 
			
		||||
@@ -141,14 +144,14 @@ endif
 | 
			
		||||
	sleep 1
 | 
			
		||||
	./vuls.old scan -config=./integration/int-config.toml --results-dir=${BASE_DIR} ${LIBS}
 | 
			
		||||
	cp -f ${BASE_DIR}/current/*.json ${NOW_JSON_DIR}
 | 
			
		||||
	cp integration/data/results/*.json ${NOW_JSON_DIR}
 | 
			
		||||
	- cp integration/data/results/*.json ${NOW_JSON_DIR}
 | 
			
		||||
	./vuls.old report --format-json --refresh-cve --results-dir=${BASE_DIR} -config=./integration/int-redis-config.toml ${NOW}
 | 
			
		||||
 | 
			
		||||
	mkdir -p ${ONE_SEC_AFTER_JSON_DIR}
 | 
			
		||||
	sleep 1
 | 
			
		||||
	./vuls.new scan -config=./integration/int-config.toml --results-dir=${BASE_DIR} ${LIBS}
 | 
			
		||||
	cp -f ${BASE_DIR}/current/*.json ${ONE_SEC_AFTER_JSON_DIR}
 | 
			
		||||
	cp integration/data/results/*.json ${ONE_SEC_AFTER_JSON_DIR}
 | 
			
		||||
	- cp integration/data/results/*.json ${ONE_SEC_AFTER_JSON_DIR}
 | 
			
		||||
	./vuls.new report --format-json --refresh-cve --results-dir=${BASE_DIR} -config=./integration/int-redis-config.toml ${ONE_SEC_AFTER}
 | 
			
		||||
 | 
			
		||||
	$(call sed-d)
 | 
			
		||||
@@ -240,4 +243,4 @@ define count-cve
 | 
			
		||||
	for jsonfile in ${ONE_SEC_AFTER_JSON_DIR}/*.json ;  do \
 | 
			
		||||
		echo $$jsonfile; cat $$jsonfile | jq ".scannedCves | length" ; \
 | 
			
		||||
	done
 | 
			
		||||
endef
 | 
			
		||||
endef
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								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, 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
 | 
			
		||||
 | 
			
		||||
@@ -71,6 +71,7 @@ Vuls is a tool created to solve the problems listed above. It has the following
 | 
			
		||||
  - [Alpine-secdb](https://git.alpinelinux.org/cgit/alpine-secdb/)
 | 
			
		||||
  - [Red Hat Security Advisories](https://access.redhat.com/security/security-updates/)
 | 
			
		||||
  - [Debian Security Bug Tracker](https://security-tracker.debian.org/tracker/)
 | 
			
		||||
  - [Ubuntu CVE Tracker](https://people.canonical.com/~ubuntu-security/cve/)
 | 
			
		||||
 | 
			
		||||
- Commands(yum, zypper, pkg-audit)
 | 
			
		||||
  - RHSA / ALAS / ELSA / FreeBSD-SA
 | 
			
		||||
@@ -79,6 +80,8 @@ 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)
 | 
			
		||||
@@ -100,15 +103,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, 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, 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, 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)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -42,16 +42,17 @@ type Config struct {
 | 
			
		||||
	Exploit    ExploitConf    `json:"exploit,omitempty"`
 | 
			
		||||
	Metasploit MetasploitConf `json:"metasploit,omitempty"`
 | 
			
		||||
 | 
			
		||||
	Slack    SlackConf    `json:"-"`
 | 
			
		||||
	EMail    SMTPConf     `json:"-"`
 | 
			
		||||
	HTTP     HTTPConf     `json:"-"`
 | 
			
		||||
	Syslog   SyslogConf   `json:"-"`
 | 
			
		||||
	AWS      AWSConf      `json:"-"`
 | 
			
		||||
	Azure    AzureConf    `json:"-"`
 | 
			
		||||
	ChatWork ChatWorkConf `json:"-"`
 | 
			
		||||
	Telegram TelegramConf `json:"-"`
 | 
			
		||||
	WpScan   WpScanConf   `json:"-"`
 | 
			
		||||
	Saas     SaasConf     `json:"-"`
 | 
			
		||||
	Slack      SlackConf      `json:"-"`
 | 
			
		||||
	EMail      SMTPConf       `json:"-"`
 | 
			
		||||
	HTTP       HTTPConf       `json:"-"`
 | 
			
		||||
	Syslog     SyslogConf     `json:"-"`
 | 
			
		||||
	AWS        AWSConf        `json:"-"`
 | 
			
		||||
	Azure      AzureConf      `json:"-"`
 | 
			
		||||
	ChatWork   ChatWorkConf   `json:"-"`
 | 
			
		||||
	GoogleChat GoogleChatConf `json:"-"`
 | 
			
		||||
	Telegram   TelegramConf   `json:"-"`
 | 
			
		||||
	WpScan     WpScanConf     `json:"-"`
 | 
			
		||||
	Saas       SaasConf       `json:"-"`
 | 
			
		||||
 | 
			
		||||
	ReportOpts
 | 
			
		||||
}
 | 
			
		||||
@@ -68,17 +69,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
 | 
			
		||||
@@ -157,6 +158,7 @@ func (c *Config) ValidateOnReport() bool {
 | 
			
		||||
		&c.EMail,
 | 
			
		||||
		&c.Slack,
 | 
			
		||||
		&c.ChatWork,
 | 
			
		||||
		&c.GoogleChat,
 | 
			
		||||
		&c.Telegram,
 | 
			
		||||
		&c.Syslog,
 | 
			
		||||
		&c.HTTP,
 | 
			
		||||
@@ -228,18 +230,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, 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:"-"`
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										32
									
								
								config/googlechatconf.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								config/googlechatconf.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/asaskevich/govalidator"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GoogleChatConf is GoogleChat config
 | 
			
		||||
type GoogleChatConf struct {
 | 
			
		||||
	WebHookURL       string `valid:"url" json:"-" toml:"webHookURL,omitempty"`
 | 
			
		||||
	SkipIfNoCve      bool   `valid:"type(bool)" json:"-" toml:"skipIfNoCve"`
 | 
			
		||||
	ServerNameRegexp string `valid:"type(string)" json:"-" toml:"serverNameRegexp,omitempty"`
 | 
			
		||||
	Enabled          bool   `valid:"type(bool)" json:"-" toml:"-"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Validate validates configuration
 | 
			
		||||
func (c *GoogleChatConf) Validate() (errs []error) {
 | 
			
		||||
	if !c.Enabled {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if len(c.WebHookURL) == 0 {
 | 
			
		||||
		errs = append(errs, xerrors.New("googleChatConf.webHookURL must not be empty"))
 | 
			
		||||
	}
 | 
			
		||||
	if !govalidator.IsRegex(c.ServerNameRegexp) {
 | 
			
		||||
		errs = append(errs, xerrors.New("googleChatConf.serverNameRegexp must be regex"))
 | 
			
		||||
	}
 | 
			
		||||
	_, err := govalidator.ValidateStruct(c)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		errs = append(errs, err)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
@@ -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")
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										14
									
								
								config/os.go
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								config/os.go
									
									
									
									
									
								
							@@ -75,6 +75,14 @@ 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)},
 | 
			
		||||
		}[major(release)]
 | 
			
		||||
	case constant.Oracle:
 | 
			
		||||
		eol, found = map[string]EOL{
 | 
			
		||||
			// Source:
 | 
			
		||||
@@ -102,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
 | 
			
		||||
@@ -132,10 +141,10 @@ func GetEOL(family, release string) (eol EOL, found bool) {
 | 
			
		||||
				StandardSupportUntil: time.Date(2025, 4, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			},
 | 
			
		||||
			"20.10": {
 | 
			
		||||
				StandardSupportUntil: time.Date(2021, 7, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
				StandardSupportUntil: time.Date(2021, 7, 22, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			},
 | 
			
		||||
			"21.04": {
 | 
			
		||||
				StandardSupportUntil: time.Date(2022, 1, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
				StandardSupportUntil: time.Date(2022, 1, 22, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			},
 | 
			
		||||
			"21.10": {
 | 
			
		||||
				StandardSupportUntil: time.Date(2022, 7, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
@@ -179,6 +188,7 @@ func GetEOL(family, release string) (eol EOL, found bool) {
 | 
			
		||||
			"10": {Ended: true},
 | 
			
		||||
			"11": {StandardSupportUntil: time.Date(2021, 9, 30, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"12": {StandardSupportUntil: time.Date(2024, 6, 30, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"13": {StandardSupportUntil: time.Date(2026, 1, 31, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
		}[major(release)]
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
 
 | 
			
		||||
@@ -111,6 +111,56 @@ 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",
 | 
			
		||||
			fields:   fields{family: Rocky, release: "8"},
 | 
			
		||||
			now:      time.Date(2021, 7, 2, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Rocky Linux 8 EOL",
 | 
			
		||||
			fields:   fields{family: Rocky, release: "8"},
 | 
			
		||||
			now:      time.Date(2026, 2, 1, 0, 0, 0, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Rocky Linux 9 Not Found",
 | 
			
		||||
			fields:   fields{family: Rocky, release: "9"},
 | 
			
		||||
			now:      time.Date(2021, 7, 2, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    false,
 | 
			
		||||
		},
 | 
			
		||||
		//Oracle
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Oracle Linux 7 supported",
 | 
			
		||||
@@ -240,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
 | 
			
		||||
@@ -308,6 +366,14 @@ func TestEOL_IsStandardSupportEnded(t *testing.T) {
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "freebsd 13 supported",
 | 
			
		||||
			fields:   fields{family: FreeBSD, release: "13"},
 | 
			
		||||
			now:      time.Date(2021, 7, 2, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "freebsd 10 eol",
 | 
			
		||||
			fields:   fields{family: FreeBSD, release: "10"},
 | 
			
		||||
 
 | 
			
		||||
@@ -84,7 +84,7 @@ func (s ScanMode) String() string {
 | 
			
		||||
	return ss + " mode"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setScanMode(server *ServerInfo, d ServerInfo) error {
 | 
			
		||||
func setScanMode(server *ServerInfo) error {
 | 
			
		||||
	if len(server.ScanMode) == 0 {
 | 
			
		||||
		server.ScanMode = Conf.Default.ScanMode
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ type TOMLLoader struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Load load the configuration TOML file specified by path arg.
 | 
			
		||||
func (c TOMLLoader) Load(pathToToml, keyPass string) error {
 | 
			
		||||
func (c TOMLLoader) Load(pathToToml, _ string) error {
 | 
			
		||||
	// util.Log.Infof("Loading config: %s", pathToToml)
 | 
			
		||||
	if _, err := toml.DecodeFile(pathToToml, &Conf); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
@@ -34,11 +34,11 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
 | 
			
		||||
	index := 0
 | 
			
		||||
	for name, server := range Conf.Servers {
 | 
			
		||||
		server.ServerName = name
 | 
			
		||||
		if err := setDefaultIfEmpty(&server, Conf.Default); err != nil {
 | 
			
		||||
		if err := setDefaultIfEmpty(&server); err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to set default value to config. server: %s, err: %w", name, err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := setScanMode(&server, Conf.Default); err != nil {
 | 
			
		||||
		if err := setScanMode(&server); err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to set ScanMode: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -137,7 +137,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setDefaultIfEmpty(server *ServerInfo, d ServerInfo) error {
 | 
			
		||||
func setDefaultIfEmpty(server *ServerInfo) error {
 | 
			
		||||
	if server.Type != constant.ServerTypePseudo {
 | 
			
		||||
		if len(server.Host) == 0 {
 | 
			
		||||
			return xerrors.Errorf("server.host is empty")
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,12 @@ const (
 | 
			
		||||
	// CentOS is
 | 
			
		||||
	CentOS = "centos"
 | 
			
		||||
 | 
			
		||||
	// Alma is
 | 
			
		||||
	Alma = "alma"
 | 
			
		||||
 | 
			
		||||
	// Rocky is
 | 
			
		||||
	Rocky = "rocky"
 | 
			
		||||
 | 
			
		||||
	// Fedora is
 | 
			
		||||
	// Fedora = "fedora"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,8 +6,11 @@ import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/aquasecurity/fanal/analyzer/os"
 | 
			
		||||
	ftypes "github.com/aquasecurity/fanal/types"
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/report"
 | 
			
		||||
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/types"
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -22,9 +25,7 @@ func Parse(vulnJSON []byte, scanResult *models.ScanResult) (result *models.ScanR
 | 
			
		||||
	vulnInfos := models.VulnInfos{}
 | 
			
		||||
	uniqueLibraryScannerPaths := map[string]models.LibraryScanner{}
 | 
			
		||||
	for _, trivyResult := range trivyResults {
 | 
			
		||||
		if IsTrivySupportedOS(trivyResult.Type) {
 | 
			
		||||
			overrideServerData(scanResult, &trivyResult)
 | 
			
		||||
		}
 | 
			
		||||
		setScanResultMeta(scanResult, &trivyResult)
 | 
			
		||||
		for _, vuln := range trivyResult.Vulnerabilities {
 | 
			
		||||
			if _, ok := vulnInfos[vuln.VulnerabilityID]; !ok {
 | 
			
		||||
				vulnInfos[vuln.VulnerabilityID] = models.VulnInfo{
 | 
			
		||||
@@ -71,17 +72,17 @@ func Parse(vulnJSON []byte, scanResult *models.ScanResult) (result *models.ScanR
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			vulnInfo.CveContents = models.CveContents{
 | 
			
		||||
				models.Trivy: models.CveContent{
 | 
			
		||||
				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) {
 | 
			
		||||
			if isTrivySupportedOS(trivyResult.Type) {
 | 
			
		||||
				pkgs[vuln.PkgName] = models.Package{
 | 
			
		||||
					Name:    vuln.PkgName,
 | 
			
		||||
					Version: vuln.InstalledVersion,
 | 
			
		||||
@@ -93,7 +94,6 @@ func Parse(vulnJSON []byte, scanResult *models.ScanResult) (result *models.ScanR
 | 
			
		||||
					FixedIn:     vuln.FixedVersion,
 | 
			
		||||
				})
 | 
			
		||||
			} else {
 | 
			
		||||
				// LibraryScanの結果
 | 
			
		||||
				vulnInfo.LibraryFixedIns = append(vulnInfo.LibraryFixedIns, models.LibraryFixedIn{
 | 
			
		||||
					Key:     trivyResult.Type,
 | 
			
		||||
					Name:    vuln.PkgName,
 | 
			
		||||
@@ -101,6 +101,7 @@ func Parse(vulnJSON []byte, scanResult *models.ScanResult) (result *models.ScanR
 | 
			
		||||
					FixedIn: vuln.FixedVersion,
 | 
			
		||||
				})
 | 
			
		||||
				libScanner := uniqueLibraryScannerPaths[trivyResult.Target]
 | 
			
		||||
				libScanner.Type = trivyResult.Type
 | 
			
		||||
				libScanner.Libs = append(libScanner.Libs, types.Library{
 | 
			
		||||
					Name:    vuln.PkgName,
 | 
			
		||||
					Version: vuln.InstalledVersion,
 | 
			
		||||
@@ -128,6 +129,7 @@ func Parse(vulnJSON []byte, scanResult *models.ScanResult) (result *models.ScanR
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		libscanner := models.LibraryScanner{
 | 
			
		||||
			Type: v.Type,
 | 
			
		||||
			Path: path,
 | 
			
		||||
			Libs: libraries,
 | 
			
		||||
		}
 | 
			
		||||
@@ -142,39 +144,70 @@ func Parse(vulnJSON []byte, scanResult *models.ScanResult) (result *models.ScanR
 | 
			
		||||
	return scanResult, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
}
 | 
			
		||||
const trivyTarget = "trivy-target"
 | 
			
		||||
 | 
			
		||||
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,
 | 
			
		||||
func setScanResultMeta(scanResult *models.ScanResult, trivyResult *report.Result) {
 | 
			
		||||
	if isTrivySupportedOS(trivyResult.Type) {
 | 
			
		||||
		scanResult.Family = trivyResult.Type
 | 
			
		||||
		scanResult.ServerName = trivyResult.Target
 | 
			
		||||
		scanResult.Optional = map[string]interface{}{
 | 
			
		||||
			trivyTarget: trivyResult.Target,
 | 
			
		||||
		}
 | 
			
		||||
	} else if isTrivySupportedLib(trivyResult.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: trivyResult.Target,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	scanResult.ScannedAt = time.Now()
 | 
			
		||||
	scanResult.ScannedBy = "trivy"
 | 
			
		||||
	scanResult.ScannedVia = "trivy"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// isTrivySupportedOS :
 | 
			
		||||
func isTrivySupportedOS(family string) bool {
 | 
			
		||||
	supportedFamilies := map[string]interface{}{
 | 
			
		||||
		os.RedHat:             struct{}{},
 | 
			
		||||
		os.Debian:             struct{}{},
 | 
			
		||||
		os.Ubuntu:             struct{}{},
 | 
			
		||||
		os.CentOS:             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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isTrivySupportedLib(typestr string) bool {
 | 
			
		||||
	supportedLibs := map[string]interface{}{
 | 
			
		||||
		ftypes.Bundler:  struct{}{},
 | 
			
		||||
		ftypes.Cargo:    struct{}{},
 | 
			
		||||
		ftypes.Composer: struct{}{},
 | 
			
		||||
		ftypes.Npm:      struct{}{},
 | 
			
		||||
		ftypes.NuGet:    struct{}{},
 | 
			
		||||
		ftypes.Pip:      struct{}{},
 | 
			
		||||
		ftypes.Pipenv:   struct{}{},
 | 
			
		||||
		ftypes.Poetry:   struct{}{},
 | 
			
		||||
		ftypes.Yarn:     struct{}{},
 | 
			
		||||
		ftypes.Jar:      struct{}{},
 | 
			
		||||
		ftypes.GoBinary: struct{}{},
 | 
			
		||||
		ftypes.GoMod:    struct{}{},
 | 
			
		||||
	}
 | 
			
		||||
	_, ok := supportedLibs[typestr]
 | 
			
		||||
	return ok
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -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, false); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	driver, locked, err := newCveDB(cnf)
 | 
			
		||||
	if locked {
 | 
			
		||||
@@ -53,11 +56,7 @@ func (api goCveDictClient) fetchCveDetails(cveIDs []string) (cveDetails []cvemod
 | 
			
		||||
		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)
 | 
			
		||||
		}
 | 
			
		||||
		cveDetails = append(cveDetails, *cveDetail)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
@@ -103,13 +102,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 +147,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
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -134,6 +147,7 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
 | 
			
		||||
	for i, r := range rs {
 | 
			
		||||
		r.ScannedCves = r.ScannedCves.FilterByCvssOver(config.Conf.CvssScoreOver)
 | 
			
		||||
		r.ScannedCves = r.ScannedCves.FilterUnfixed(config.Conf.IgnoreUnfixed)
 | 
			
		||||
		r.ScannedCves = r.ScannedCves.FilterByConfidenceOver(config.Conf.ConfidenceScoreOver)
 | 
			
		||||
 | 
			
		||||
		// IgnoreCves
 | 
			
		||||
		ignoreCves := []string{}
 | 
			
		||||
@@ -169,6 +183,11 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
 | 
			
		||||
func DetectPkgCves(r *models.ScanResult, ovalCnf config.GovalDictConf, gostCnf config.GostConf) error {
 | 
			
		||||
	// Pkg Scan
 | 
			
		||||
	if r.Release != "" {
 | 
			
		||||
		// OVAL, gost(Debian Security Tracker) does not support Package for Raspbian, so skip it.
 | 
			
		||||
		if r.Family == constant.Raspbian {
 | 
			
		||||
			r = r.RemoveRaspbianPackFromResult()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// OVAL
 | 
			
		||||
		if err := detectPkgsCvesWithOval(ovalCnf, r); err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to detect CVE with OVAL: %w", err)
 | 
			
		||||
@@ -183,7 +202,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 {
 | 
			
		||||
@@ -279,8 +298,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 {
 | 
			
		||||
@@ -288,9 +307,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
 | 
			
		||||
@@ -305,8 +338,8 @@ 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 {
 | 
			
		||||
	for _, nvd := range cvedetail.Nvds {
 | 
			
		||||
		for _, cert := range nvd.Certs {
 | 
			
		||||
			dict.En = append(dict.En, models.Alert{
 | 
			
		||||
				URL:   cert.Link,
 | 
			
		||||
				Title: cert.Title,
 | 
			
		||||
@@ -314,8 +347,9 @@ func fillCertAlerts(cvedetail *cvemodels.CveDetail) (dict models.AlertDict) {
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if cvedetail.Jvn != nil {
 | 
			
		||||
		for _, cert := range cvedetail.Jvn.Certs {
 | 
			
		||||
 | 
			
		||||
	for _, jvn := range cvedetail.Jvns {
 | 
			
		||||
		for _, cert := range jvn.Certs {
 | 
			
		||||
			dict.Ja = append(dict.Ja, models.Alert{
 | 
			
		||||
				URL:   cert.Link,
 | 
			
		||||
				Title: cert.Title,
 | 
			
		||||
@@ -323,6 +357,7 @@ func fillCertAlerts(cvedetail *cvemodels.CveDetail) (dict models.AlertDict) {
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return dict
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -342,7 +377,12 @@ func detectPkgsCvesWithOval(cnf config.GovalDictConf, r *models.ScanResult) erro
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if !ok {
 | 
			
		||||
		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)
 | 
			
		||||
		if r.Family == constant.Debian {
 | 
			
		||||
			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/vulsio/goval-dictionary#usage`", r.Family, r.Release)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logging.Log.Debugf("Check if oval fresh: %s %s", r.Family, r.Release)
 | 
			
		||||
@@ -373,17 +413,26 @@ func detectPkgsCvesWithGost(cnf config.GostConf, r *models.ScanResult) error {
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	nCVEs, err := client.DetectUnfixed(r, true)
 | 
			
		||||
	nCVEs, err := client.DetectCVEs(r, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if r.Family == constant.Debian {
 | 
			
		||||
			return xerrors.Errorf("Failed to detect CVEs with gost: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		return xerrors.Errorf("Failed to detect unfixed CVEs with gost: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logging.Log.Infof("%s: %d unfixed CVEs are detected with gost", r.FormatServerName(), nCVEs)
 | 
			
		||||
	if r.Family == constant.Debian {
 | 
			
		||||
		logging.Log.Infof("%s: %d CVEs are detected with gost",
 | 
			
		||||
			r.FormatServerName(), nCVEs)
 | 
			
		||||
	} else {
 | 
			
		||||
		logging.Log.Infof("%s: %d unfixed CVEs are detected with gost",
 | 
			
		||||
			r.FormatServerName(), nCVEs)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
@@ -395,23 +444,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 {
 | 
			
		||||
			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 {
 | 
			
		||||
				names := val.CpeURIs
 | 
			
		||||
				names = util.AppendIfMissing(names, name)
 | 
			
		||||
				val.CpeURIs = names
 | 
			
		||||
				val.Confidences.AppendIfMissing(models.CpeNameMatch)
 | 
			
		||||
				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{models.CpeNameMatch},
 | 
			
		||||
					CveID:            detail.CveID,
 | 
			
		||||
					CpeURIs:          []string{cpe.CpeURI},
 | 
			
		||||
					Confidences:      models.Confidences{maxConfidence},
 | 
			
		||||
					DistroAdvisories: advisories,
 | 
			
		||||
				}
 | 
			
		||||
				r.ScannedCves[detail.CveID] = v
 | 
			
		||||
				nCVEs++
 | 
			
		||||
@@ -422,15 +482,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
 | 
			
		||||
@@ -32,7 +33,7 @@ func FillWithExploit(r *models.ScanResult, cnf config.ExploitConf) (nExploitCve
 | 
			
		||||
			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
 | 
			
		||||
			}
 | 
			
		||||
@@ -76,7 +77,7 @@ func FillWithExploit(r *models.ScanResult, cnf config.ExploitConf) (nExploitCve
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConvertToModels converts gost model to vuls model
 | 
			
		||||
func ConvertToModels(es []*exploitmodels.Exploit) (exploits []models.Exploit) {
 | 
			
		||||
func ConvertToModels(es []exploitmodels.Exploit) (exploits []models.Exploit) {
 | 
			
		||||
	for _, e := range es {
 | 
			
		||||
		var documentURL, shellURL *string
 | 
			
		||||
		if e.OffensiveSecurity != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -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{
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package detector
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package detector
 | 
			
		||||
@@ -6,8 +7,8 @@ import (
 | 
			
		||||
	"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"
 | 
			
		||||
	metasploitdb "github.com/vulsio/go-msfdb/db"
 | 
			
		||||
	metasploitmodels "github.com/vulsio/go-msfdb/models"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -44,7 +45,7 @@ func FillWithMetasploit(r *models.ScanResult, cnf config.MetasploitConf) (nMetas
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConvertToModelsMsf converts gost model to vuls model
 | 
			
		||||
func ConvertToModelsMsf(ms []*metasploitmodels.Metasploit) (modules []models.Metasploit) {
 | 
			
		||||
func ConvertToModelsMsf(ms []metasploitmodels.Metasploit) (modules []models.Metasploit) {
 | 
			
		||||
	for _, m := range ms {
 | 
			
		||||
		var links []string
 | 
			
		||||
		if 0 < len(m.References) {
 | 
			
		||||
@@ -71,7 +72,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()); 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										141
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										141
									
								
								go.mod
									
									
									
									
									
								
							@@ -1,22 +1,29 @@
 | 
			
		||||
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/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/VividCortex/ewma v1.2.0 // indirect
 | 
			
		||||
	github.com/aquasecurity/fanal v0.0.0-20210815095355-42429a80d0e3
 | 
			
		||||
	github.com/aquasecurity/trivy v0.19.3-0.20210909113250-19c0b70d2613
 | 
			
		||||
	github.com/aquasecurity/trivy-db v0.0.0-20210809142931-da8e09204404
 | 
			
		||||
	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.22
 | 
			
		||||
	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/go-sql-driver/mysql v1.6.0 // indirect
 | 
			
		||||
	github.com/fatih/color v1.12.0 // indirect
 | 
			
		||||
	github.com/fsnotify/fsnotify v1.5.1 // indirect
 | 
			
		||||
	github.com/go-redis/redis/v8 v8.11.3 // 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
 | 
			
		||||
@@ -28,35 +35,119 @@ require (
 | 
			
		||||
	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.1.10
 | 
			
		||||
	github.com/kotakanbe/go-cve-dictionary v0.5.12
 | 
			
		||||
	github.com/kotakanbe/go-pingscanner v0.1.0
 | 
			
		||||
	github.com/kotakanbe/goval-dictionary v0.3.6-0.20210429000733-6db1754b1d87
 | 
			
		||||
	github.com/kotakanbe/logrus-prefixed-formatter v0.0.0-20180123152602-928f7356cb96
 | 
			
		||||
	github.com/lib/pq v1.10.1 // indirect
 | 
			
		||||
	github.com/magiconair/properties v1.8.4 // indirect
 | 
			
		||||
	github.com/mattn/go-runewidth v0.0.12 // 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
 | 
			
		||||
	github.com/nlopes/slack v0.6.0
 | 
			
		||||
	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.8.1 // indirect
 | 
			
		||||
	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/takuzoo3868/go-msfdb v0.1.5
 | 
			
		||||
	github.com/vulsio/go-exploitdb v0.1.7
 | 
			
		||||
	golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect
 | 
			
		||||
	golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect
 | 
			
		||||
	golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 // indirect
 | 
			
		||||
	golang.org/x/oauth2 v0.0.0-20210125201302-af13f521f196
 | 
			
		||||
	github.com/spf13/cast v1.4.1 // indirect
 | 
			
		||||
	github.com/spf13/cobra v1.2.1
 | 
			
		||||
	github.com/vulsio/go-cve-dictionary v0.8.1
 | 
			
		||||
	github.com/vulsio/go-exploitdb v0.4.0
 | 
			
		||||
	github.com/vulsio/go-msfdb v0.2.0
 | 
			
		||||
	github.com/vulsio/gost v0.4.1-0.20210919121048-2a7957bf1885
 | 
			
		||||
	github.com/vulsio/goval-dictionary v0.6.1
 | 
			
		||||
	golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
 | 
			
		||||
	golang.org/x/net v0.0.0-20210902165921-8d991716f632 // indirect
 | 
			
		||||
	golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c
 | 
			
		||||
	golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
 | 
			
		||||
	golang.org/x/sys v0.0.0-20210426230700-d19ff857e887 // indirect
 | 
			
		||||
	golang.org/x/tools v0.1.0 // indirect
 | 
			
		||||
	golang.org/x/text v0.3.7 // indirect
 | 
			
		||||
	golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
 | 
			
		||||
	gopkg.in/ini.v1 v1.62.0 // indirect
 | 
			
		||||
	gopkg.in/ini.v1 v1.63.2 // indirect
 | 
			
		||||
	gorm.io/driver/mysql v1.1.2 // indirect
 | 
			
		||||
	gorm.io/driver/postgres v1.1.1 // indirect
 | 
			
		||||
	gorm.io/driver/sqlite v1.1.5 // 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.2.0 // indirect
 | 
			
		||||
	github.com/aquasecurity/go-dep-parser v0.0.0-20210815080135-5be65146849a // 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-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.6.8 // 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.2 // 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.8 // indirect
 | 
			
		||||
	github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
 | 
			
		||||
	github.com/mitchellh/copystructure v1.1.1 // indirect
 | 
			
		||||
	github.com/mitchellh/mapstructure v1.4.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.8.1 // 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.17.0 // indirect
 | 
			
		||||
	golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect
 | 
			
		||||
	golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
 | 
			
		||||
	google.golang.org/appengine v1.6.7 // indirect
 | 
			
		||||
	google.golang.org/protobuf v1.26.0 // indirect
 | 
			
		||||
	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.21.15 // indirect
 | 
			
		||||
	moul.io/http2curl v1.0.0 // indirect
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										174
									
								
								gost/debian.go
									
									
									
									
									
								
							
							
						
						
									
										174
									
								
								gost/debian.go
									
									
									
									
									
								
							@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package gost
 | 
			
		||||
@@ -5,11 +6,12 @@ package gost
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	gostmodels "github.com/knqyf263/gost/models"
 | 
			
		||||
	debver "github.com/knqyf263/go-deb-version"
 | 
			
		||||
	gostmodels "github.com/vulsio/gost/models"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Debian is Gost client for Debian GNU/Linux
 | 
			
		||||
@@ -21,6 +23,7 @@ type packCves struct {
 | 
			
		||||
	packName  string
 | 
			
		||||
	isSrcPack bool
 | 
			
		||||
	cves      []models.CveContent
 | 
			
		||||
	fixes     models.PackageFixStatuses
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (deb Debian) supported(major string) bool {
 | 
			
		||||
@@ -28,23 +31,23 @@ func (deb Debian) supported(major string) bool {
 | 
			
		||||
		"8":  "jessie",
 | 
			
		||||
		"9":  "stretch",
 | 
			
		||||
		"10": "buster",
 | 
			
		||||
		"11": "bullseye",
 | 
			
		||||
	}[major]
 | 
			
		||||
	return ok
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DetectUnfixed fills cve information that has in Gost
 | 
			
		||||
func (deb Debian) DetectUnfixed(r *models.ScanResult, _ bool) (nCVEs int, err error) {
 | 
			
		||||
// DetectCVEs fills cve information that has in Gost
 | 
			
		||||
func (deb Debian) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error) {
 | 
			
		||||
	if !deb.supported(major(r.Release)) {
 | 
			
		||||
		// only logging
 | 
			
		||||
		logging.Log.Warnf("Debian %s is not supported yet", r.Release)
 | 
			
		||||
		return 0, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	linuxImage := "linux-image-" + r.RunningKernel.Release
 | 
			
		||||
	// Add linux and set the version of running kernel to search OVAL.
 | 
			
		||||
	// Add linux and set the version of running kernel to search Gost.
 | 
			
		||||
	if r.Container.ContainerID == "" {
 | 
			
		||||
		newVer := ""
 | 
			
		||||
		if p, ok := r.Packages[linuxImage]; ok {
 | 
			
		||||
		if p, ok := r.Packages["linux-image-"+r.RunningKernel.Release]; ok {
 | 
			
		||||
			newVer = p.NewVersion
 | 
			
		||||
		}
 | 
			
		||||
		r.Packages["linux"] = models.Package{
 | 
			
		||||
@@ -54,18 +57,35 @@ func (deb Debian) DetectUnfixed(r *models.ScanResult, _ bool) (nCVEs int, err er
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Debian Security Tracker does not support Package for Raspbian, so skip it.
 | 
			
		||||
	var scanResult models.ScanResult
 | 
			
		||||
	if r.Family != constant.Raspbian {
 | 
			
		||||
		scanResult = *r
 | 
			
		||||
	} else {
 | 
			
		||||
		scanResult = r.RemoveRaspbianPackFromResult()
 | 
			
		||||
	stashLinuxPackage := r.Packages["linux"]
 | 
			
		||||
	nFixedCVEs, err := deb.detectCVEsWithFixState(r, "resolved")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r.Packages["linux"] = stashLinuxPackage
 | 
			
		||||
	nUnfixedCVEs, err := deb.detectCVEsWithFixState(r, "open")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return (nFixedCVEs + nUnfixedCVEs), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (deb Debian) detectCVEsWithFixState(r *models.ScanResult, fixStatus string) (nCVEs int, err error) {
 | 
			
		||||
	if fixStatus != "resolved" && fixStatus != "open" {
 | 
			
		||||
		return 0, xerrors.Errorf(`Failed to detectCVEsWithFixState. fixStatus is not allowed except "open" and "resolved"(actual: fixStatus -> %s).`, fixStatus)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	packCvesList := []packCves{}
 | 
			
		||||
	if deb.DBDriver.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		url, _ := util.URLPathJoin(deb.DBDriver.Cnf.GetURL(), "debian", major(scanResult.Release), "pkgs")
 | 
			
		||||
		responses, err := getAllUnfixedCvesViaHTTP(r, url)
 | 
			
		||||
		url, _ := util.URLPathJoin(deb.DBDriver.Cnf.GetURL(), "debian", major(r.Release), "pkgs")
 | 
			
		||||
		s := "unfixed-cves"
 | 
			
		||||
		if s == "resolved" {
 | 
			
		||||
			s = "fixed-cves"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		responses, err := getCvesWithFixStateViaHTTP(r, url, s)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
@@ -76,43 +96,40 @@ func (deb Debian) DetectUnfixed(r *models.ScanResult, _ bool) (nCVEs int, err er
 | 
			
		||||
				return 0, err
 | 
			
		||||
			}
 | 
			
		||||
			cves := []models.CveContent{}
 | 
			
		||||
			fixes := []models.PackageFixStatus{}
 | 
			
		||||
			for _, debcve := range debCves {
 | 
			
		||||
				cves = append(cves, *deb.ConvertToModel(&debcve))
 | 
			
		||||
				fixes = append(fixes, checkPackageFixStatus(&debcve)...)
 | 
			
		||||
			}
 | 
			
		||||
			packCvesList = append(packCvesList, packCves{
 | 
			
		||||
				packName:  res.request.packName,
 | 
			
		||||
				isSrcPack: res.request.isSrcPack,
 | 
			
		||||
				cves:      cves,
 | 
			
		||||
				fixes:     fixes,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if deb.DBDriver.DB == nil {
 | 
			
		||||
			return 0, nil
 | 
			
		||||
		}
 | 
			
		||||
		for _, pack := range scanResult.Packages {
 | 
			
		||||
			cveDebs := deb.DBDriver.DB.GetUnfixedCvesDebian(major(scanResult.Release), pack.Name)
 | 
			
		||||
			cves := []models.CveContent{}
 | 
			
		||||
			for _, cveDeb := range cveDebs {
 | 
			
		||||
				cves = append(cves, *deb.ConvertToModel(&cveDeb))
 | 
			
		||||
			}
 | 
			
		||||
		for _, pack := range r.Packages {
 | 
			
		||||
			cves, fixes := deb.getCvesDebianWithfixStatus(fixStatus, major(r.Release), pack.Name)
 | 
			
		||||
			packCvesList = append(packCvesList, packCves{
 | 
			
		||||
				packName:  pack.Name,
 | 
			
		||||
				isSrcPack: false,
 | 
			
		||||
				cves:      cves,
 | 
			
		||||
				fixes:     fixes,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// SrcPack
 | 
			
		||||
		for _, pack := range scanResult.SrcPackages {
 | 
			
		||||
			cveDebs := deb.DBDriver.DB.GetUnfixedCvesDebian(major(scanResult.Release), pack.Name)
 | 
			
		||||
			cves := []models.CveContent{}
 | 
			
		||||
			for _, cveDeb := range cveDebs {
 | 
			
		||||
				cves = append(cves, *deb.ConvertToModel(&cveDeb))
 | 
			
		||||
			}
 | 
			
		||||
		for _, pack := range r.SrcPackages {
 | 
			
		||||
			cves, fixes := deb.getCvesDebianWithfixStatus(fixStatus, major(r.Release), pack.Name)
 | 
			
		||||
			packCvesList = append(packCvesList, packCves{
 | 
			
		||||
				packName:  pack.Name,
 | 
			
		||||
				isSrcPack: true,
 | 
			
		||||
				cves:      cves,
 | 
			
		||||
				fixes:     fixes,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -120,13 +137,14 @@ func (deb Debian) DetectUnfixed(r *models.ScanResult, _ bool) (nCVEs int, err er
 | 
			
		||||
	delete(r.Packages, "linux")
 | 
			
		||||
 | 
			
		||||
	for _, p := range packCvesList {
 | 
			
		||||
		for _, cve := range p.cves {
 | 
			
		||||
		for i, cve := range p.cves {
 | 
			
		||||
			v, ok := r.ScannedCves[cve.CveID]
 | 
			
		||||
			if ok {
 | 
			
		||||
				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 {
 | 
			
		||||
				v = models.VulnInfo{
 | 
			
		||||
@@ -134,6 +152,31 @@ func (deb Debian) DetectUnfixed(r *models.ScanResult, _ bool) (nCVEs int, err er
 | 
			
		||||
					CveContents: models.NewCveContents(cve),
 | 
			
		||||
					Confidences: models.Confidences{models.DebianSecurityTrackerMatch},
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if fixStatus == "resolved" {
 | 
			
		||||
					versionRelease := ""
 | 
			
		||||
					if p.isSrcPack {
 | 
			
		||||
						versionRelease = r.SrcPackages[p.packName].Version
 | 
			
		||||
					} else {
 | 
			
		||||
						versionRelease = r.Packages[p.packName].FormatVer()
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					if versionRelease == "" {
 | 
			
		||||
						break
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					affected, err := isGostDefAffected(versionRelease, p.fixes[i].FixedIn)
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						logging.Log.Debugf("Failed to parse versions: %s, Ver: %s, Gost: %s",
 | 
			
		||||
							err, versionRelease, p.fixes[i].FixedIn)
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					if !affected {
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				nCVEs++
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
@@ -148,25 +191,65 @@ func (deb Debian) DetectUnfixed(r *models.ScanResult, _ bool) (nCVEs int, err er
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				if p.packName == "linux" {
 | 
			
		||||
					names = append(names, linuxImage)
 | 
			
		||||
					names = append(names, "linux-image-"+r.RunningKernel.Release)
 | 
			
		||||
				} else {
 | 
			
		||||
					names = append(names, p.packName)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, name := range names {
 | 
			
		||||
				v.AffectedPackages = v.AffectedPackages.Store(models.PackageFixStatus{
 | 
			
		||||
					Name:        name,
 | 
			
		||||
					FixState:    "open",
 | 
			
		||||
					NotFixedYet: true,
 | 
			
		||||
				})
 | 
			
		||||
			if fixStatus == "resolved" {
 | 
			
		||||
				for _, name := range names {
 | 
			
		||||
					v.AffectedPackages = v.AffectedPackages.Store(models.PackageFixStatus{
 | 
			
		||||
						Name:    name,
 | 
			
		||||
						FixedIn: p.fixes[i].FixedIn,
 | 
			
		||||
					})
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				for _, name := range names {
 | 
			
		||||
					v.AffectedPackages = v.AffectedPackages.Store(models.PackageFixStatus{
 | 
			
		||||
						Name:        name,
 | 
			
		||||
						FixState:    "open",
 | 
			
		||||
						NotFixedYet: true,
 | 
			
		||||
					})
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			r.ScannedCves[cve.CveID] = v
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nCVEs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isGostDefAffected(versionRelease, gostVersion string) (affected bool, err error) {
 | 
			
		||||
	vera, err := debver.NewVersion(versionRelease)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
	verb, err := debver.NewVersion(gostVersion)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
	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
 | 
			
		||||
 | 
			
		||||
	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)...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConvertToModel converts gost model to vuls model
 | 
			
		||||
func (deb Debian) ConvertToModel(cve *gostmodels.DebianCVE) *models.CveContent {
 | 
			
		||||
	severity := ""
 | 
			
		||||
@@ -188,3 +271,22 @@ func (deb Debian) ConvertToModel(cve *gostmodels.DebianCVE) *models.CveContent {
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func checkPackageFixStatus(cve *gostmodels.DebianCVE) []models.PackageFixStatus {
 | 
			
		||||
	fixes := []models.PackageFixStatus{}
 | 
			
		||||
	for _, p := range cve.Package {
 | 
			
		||||
		for _, r := range p.Release {
 | 
			
		||||
			f := models.PackageFixStatus{Name: p.PackageName}
 | 
			
		||||
 | 
			
		||||
			if r.Status == "open" {
 | 
			
		||||
				f.NotFixedYet = true
 | 
			
		||||
			} else {
 | 
			
		||||
				f.FixedIn = r.FixedVersion
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			fixes = append(fixes, f)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return fixes
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								gost/gost.go
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								gost/gost.go
									
									
									
									
									
								
							@@ -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"
 | 
			
		||||
@@ -20,7 +21,7 @@ type DBDriver struct {
 | 
			
		||||
 | 
			
		||||
// Client is the interface of OVAL client.
 | 
			
		||||
type Client interface {
 | 
			
		||||
	DetectUnfixed(*models.ScanResult, bool) (int, error)
 | 
			
		||||
	DetectCVEs(*models.ScanResult, bool) (int, error)
 | 
			
		||||
	CloseDB() error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -65,14 +66,16 @@ func NewClient(cnf config.GostConf, family string) (Client, error) {
 | 
			
		||||
	driver := DBDriver{DB: db, Cnf: &cnf}
 | 
			
		||||
 | 
			
		||||
	switch family {
 | 
			
		||||
	case constant.RedHat, constant.CentOS:
 | 
			
		||||
	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
 | 
			
		||||
	case constant.Ubuntu:
 | 
			
		||||
		return Ubuntu{Base{DBDriver: driver}}, nil
 | 
			
		||||
	case constant.Windows:
 | 
			
		||||
		return Microsoft{Base{DBDriver: driver}}, nil
 | 
			
		||||
	default:
 | 
			
		||||
		return Pseudo{}, nil
 | 
			
		||||
		return Pseudo{Base{DBDriver: driver}}, nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -14,8 +16,8 @@ type Microsoft struct {
 | 
			
		||||
	Base
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DetectUnfixed fills cve information that has in Gost
 | 
			
		||||
func (ms Microsoft) DetectUnfixed(r *models.ScanResult, _ bool) (nCVEs int, err error) {
 | 
			
		||||
// DetectCVEs fills cve information that has in Gost
 | 
			
		||||
func (ms Microsoft) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error) {
 | 
			
		||||
	if ms.DBDriver.DB == nil {
 | 
			
		||||
		return 0, nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -32,7 +34,7 @@ func (ms Microsoft) DetectUnfixed(r *models.ScanResult, _ bool) (nCVEs int, 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 +43,9 @@ func (ms Microsoft) DetectUnfixed(r *models.ScanResult, _ bool) (nCVEs int, 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
 | 
			
		||||
@@ -11,7 +12,7 @@ type Pseudo struct {
 | 
			
		||||
	Base
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DetectUnfixed fills cve information that has in Gost
 | 
			
		||||
func (pse Pseudo) DetectUnfixed(r *models.ScanResult, _ bool) (int, error) {
 | 
			
		||||
// DetectCVEs fills cve information that has in Gost
 | 
			
		||||
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
 | 
			
		||||
@@ -18,8 +19,8 @@ type RedHat struct {
 | 
			
		||||
	Base
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DetectUnfixed fills cve information that has in Gost
 | 
			
		||||
func (red RedHat) DetectUnfixed(r *models.ScanResult, ignoreWillNotFix bool) (nCVEs int, err error) {
 | 
			
		||||
// DetectCVEs fills cve information that has in Gost
 | 
			
		||||
func (red RedHat) DetectCVEs(r *models.ScanResult, ignoreWillNotFix bool) (nCVEs int, err error) {
 | 
			
		||||
	if red.DBDriver.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		prefix, _ := util.URLPathJoin(red.DBDriver.Cnf.GetURL(), "redhat", major(r.Release), "pkgs")
 | 
			
		||||
		responses, err := getAllUnfixedCvesViaHTTP(r, prefix)
 | 
			
		||||
@@ -102,7 +103,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 +123,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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										191
									
								
								gost/ubuntu.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								gost/ubuntu.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,191 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	gostmodels "github.com/vulsio/gost/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Ubuntu is Gost client for Ubuntu
 | 
			
		||||
type Ubuntu struct {
 | 
			
		||||
	Base
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ubu Ubuntu) supported(version string) bool {
 | 
			
		||||
	_, ok := map[string]string{
 | 
			
		||||
		"1404": "trusty",
 | 
			
		||||
		"1604": "xenial",
 | 
			
		||||
		"1804": "bionic",
 | 
			
		||||
		"2004": "focal",
 | 
			
		||||
		"2010": "groovy",
 | 
			
		||||
		"2104": "hirsute",
 | 
			
		||||
	}[version]
 | 
			
		||||
	return ok
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DetectCVEs fills cve information that has in Gost
 | 
			
		||||
func (ubu Ubuntu) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error) {
 | 
			
		||||
	ubuReleaseVer := strings.Replace(r.Release, ".", "", 1)
 | 
			
		||||
	if !ubu.supported(ubuReleaseVer) {
 | 
			
		||||
		logging.Log.Warnf("Ubuntu %s is not supported yet", r.Release)
 | 
			
		||||
		return 0, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	linuxImage := "linux-image-" + r.RunningKernel.Release
 | 
			
		||||
	// Add linux and set the version of running kernel to search Gost.
 | 
			
		||||
	if r.Container.ContainerID == "" {
 | 
			
		||||
		newVer := ""
 | 
			
		||||
		if p, ok := r.Packages[linuxImage]; ok {
 | 
			
		||||
			newVer = p.NewVersion
 | 
			
		||||
		}
 | 
			
		||||
		r.Packages["linux"] = models.Package{
 | 
			
		||||
			Name:       "linux",
 | 
			
		||||
			Version:    r.RunningKernel.Version,
 | 
			
		||||
			NewVersion: newVer,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	packCvesList := []packCves{}
 | 
			
		||||
	if ubu.DBDriver.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		url, _ := util.URLPathJoin(ubu.DBDriver.Cnf.GetURL(), "ubuntu", ubuReleaseVer, "pkgs")
 | 
			
		||||
		responses, err := getAllUnfixedCvesViaHTTP(r, url)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, res := range responses {
 | 
			
		||||
			ubuCves := map[string]gostmodels.UbuntuCVE{}
 | 
			
		||||
			if err := json.Unmarshal([]byte(res.json), &ubuCves); err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
			}
 | 
			
		||||
			cves := []models.CveContent{}
 | 
			
		||||
			for _, ubucve := range ubuCves {
 | 
			
		||||
				cves = append(cves, *ubu.ConvertToModel(&ubucve))
 | 
			
		||||
			}
 | 
			
		||||
			packCvesList = append(packCvesList, packCves{
 | 
			
		||||
				packName:  res.request.packName,
 | 
			
		||||
				isSrcPack: res.request.isSrcPack,
 | 
			
		||||
				cves:      cves,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if ubu.DBDriver.DB == nil {
 | 
			
		||||
			return 0, nil
 | 
			
		||||
		}
 | 
			
		||||
		for _, pack := range r.Packages {
 | 
			
		||||
			ubuCves := ubu.DBDriver.DB.GetUnfixedCvesUbuntu(ubuReleaseVer, pack.Name)
 | 
			
		||||
			cves := []models.CveContent{}
 | 
			
		||||
			for _, ubucve := range ubuCves {
 | 
			
		||||
				cves = append(cves, *ubu.ConvertToModel(&ubucve))
 | 
			
		||||
			}
 | 
			
		||||
			packCvesList = append(packCvesList, packCves{
 | 
			
		||||
				packName:  pack.Name,
 | 
			
		||||
				isSrcPack: false,
 | 
			
		||||
				cves:      cves,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// SrcPack
 | 
			
		||||
		for _, pack := range r.SrcPackages {
 | 
			
		||||
			ubuCves := ubu.DBDriver.DB.GetUnfixedCvesUbuntu(ubuReleaseVer, pack.Name)
 | 
			
		||||
			cves := []models.CveContent{}
 | 
			
		||||
			for _, ubucve := range ubuCves {
 | 
			
		||||
				cves = append(cves, *ubu.ConvertToModel(&ubucve))
 | 
			
		||||
			}
 | 
			
		||||
			packCvesList = append(packCvesList, packCves{
 | 
			
		||||
				packName:  pack.Name,
 | 
			
		||||
				isSrcPack: true,
 | 
			
		||||
				cves:      cves,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	delete(r.Packages, "linux")
 | 
			
		||||
 | 
			
		||||
	for _, p := range packCvesList {
 | 
			
		||||
		for _, cve := range p.cves {
 | 
			
		||||
			v, ok := r.ScannedCves[cve.CveID]
 | 
			
		||||
			if ok {
 | 
			
		||||
				if v.CveContents == nil {
 | 
			
		||||
					v.CveContents = models.NewCveContents(cve)
 | 
			
		||||
				} else {
 | 
			
		||||
					v.CveContents[models.UbuntuAPI] = []models.CveContent{cve}
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				v = models.VulnInfo{
 | 
			
		||||
					CveID:       cve.CveID,
 | 
			
		||||
					CveContents: models.NewCveContents(cve),
 | 
			
		||||
					Confidences: models.Confidences{models.UbuntuAPIMatch},
 | 
			
		||||
				}
 | 
			
		||||
				nCVEs++
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			names := []string{}
 | 
			
		||||
			if p.isSrcPack {
 | 
			
		||||
				if srcPack, ok := r.SrcPackages[p.packName]; ok {
 | 
			
		||||
					for _, binName := range srcPack.BinaryNames {
 | 
			
		||||
						if _, ok := r.Packages[binName]; ok {
 | 
			
		||||
							names = append(names, binName)
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				if p.packName == "linux" {
 | 
			
		||||
					names = append(names, linuxImage)
 | 
			
		||||
				} else {
 | 
			
		||||
					names = append(names, p.packName)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, name := range names {
 | 
			
		||||
				v.AffectedPackages = v.AffectedPackages.Store(models.PackageFixStatus{
 | 
			
		||||
					Name:        name,
 | 
			
		||||
					FixState:    "open",
 | 
			
		||||
					NotFixedYet: true,
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
			r.ScannedCves[cve.CveID] = v
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nCVEs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConvertToModel converts gost model to vuls model
 | 
			
		||||
func (ubu Ubuntu) ConvertToModel(cve *gostmodels.UbuntuCVE) *models.CveContent {
 | 
			
		||||
	references := []models.Reference{}
 | 
			
		||||
	for _, r := range cve.References {
 | 
			
		||||
		if strings.Contains(r.Reference, "https://cve.mitre.org/cgi-bin/cvename.cgi?name=") {
 | 
			
		||||
			references = append(references, models.Reference{Source: "CVE", Link: r.Reference})
 | 
			
		||||
		} else {
 | 
			
		||||
			references = append(references, models.Reference{Link: r.Reference})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, b := range cve.Bugs {
 | 
			
		||||
		references = append(references, models.Reference{Source: "Bug", Link: b.Bug})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, u := range cve.Upstreams {
 | 
			
		||||
		for _, upstreamLink := range u.UpstreamLinks {
 | 
			
		||||
			references = append(references, models.Reference{Source: "UPSTREAM", Link: upstreamLink.Link})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &models.CveContent{
 | 
			
		||||
		Type:          models.UbuntuAPI,
 | 
			
		||||
		CveID:         cve.Candidate,
 | 
			
		||||
		Summary:       cve.Description,
 | 
			
		||||
		Cvss2Severity: cve.Priority,
 | 
			
		||||
		Cvss3Severity: cve.Priority,
 | 
			
		||||
		SourceLink:    "https://ubuntu.com/security/" + cve.Candidate,
 | 
			
		||||
		References:    references,
 | 
			
		||||
		Published:     cve.PublicDate,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										137
									
								
								gost/ubuntu_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								gost/ubuntu_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,137 @@
 | 
			
		||||
package gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	gostmodels "github.com/vulsio/gost/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestUbuntu_Supported(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		ubuReleaseVer string
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name string
 | 
			
		||||
		args args
 | 
			
		||||
		want bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "14.04 is supported",
 | 
			
		||||
			args: args{
 | 
			
		||||
				ubuReleaseVer: "1404",
 | 
			
		||||
			},
 | 
			
		||||
			want: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "16.04 is supported",
 | 
			
		||||
			args: args{
 | 
			
		||||
				ubuReleaseVer: "1604",
 | 
			
		||||
			},
 | 
			
		||||
			want: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "18.04 is supported",
 | 
			
		||||
			args: args{
 | 
			
		||||
				ubuReleaseVer: "1804",
 | 
			
		||||
			},
 | 
			
		||||
			want: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "20.04 is supported",
 | 
			
		||||
			args: args{
 | 
			
		||||
				ubuReleaseVer: "2004",
 | 
			
		||||
			},
 | 
			
		||||
			want: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "20.10 is supported",
 | 
			
		||||
			args: args{
 | 
			
		||||
				ubuReleaseVer: "2010",
 | 
			
		||||
			},
 | 
			
		||||
			want: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "21.04 is supported",
 | 
			
		||||
			args: args{
 | 
			
		||||
				ubuReleaseVer: "2104",
 | 
			
		||||
			},
 | 
			
		||||
			want: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "empty string is not supported yet",
 | 
			
		||||
			args: args{
 | 
			
		||||
				ubuReleaseVer: "",
 | 
			
		||||
			},
 | 
			
		||||
			want: false,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			ubu := Ubuntu{}
 | 
			
		||||
			if got := ubu.supported(tt.args.ubuReleaseVer); got != tt.want {
 | 
			
		||||
				t.Errorf("Ubuntu.Supported() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestUbuntuConvertToModel(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name     string
 | 
			
		||||
		input    gostmodels.UbuntuCVE
 | 
			
		||||
		expected models.CveContent
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "gost Ubuntu.ConvertToModel",
 | 
			
		||||
			input: gostmodels.UbuntuCVE{
 | 
			
		||||
				Candidate:  "CVE-2021-3517",
 | 
			
		||||
				PublicDate: time.Date(2021, 5, 19, 14, 15, 0, 0, time.UTC),
 | 
			
		||||
				References: []gostmodels.UbuntuReference{
 | 
			
		||||
					{Reference: "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-3517"},
 | 
			
		||||
					{Reference: "https://gitlab.gnome.org/GNOME/libxml2/-/issues/235"},
 | 
			
		||||
					{Reference: "https://gitlab.gnome.org/GNOME/libxml2/-/commit/bf22713507fe1fc3a2c4b525cf0a88c2dc87a3a2"}},
 | 
			
		||||
				Description: "description.",
 | 
			
		||||
				Notes:       []gostmodels.UbuntuNote{},
 | 
			
		||||
				Bugs:        []gostmodels.UbuntuBug{{Bug: "http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=987738"}},
 | 
			
		||||
				Priority:    "medium",
 | 
			
		||||
				Patches: []gostmodels.UbuntuPatch{
 | 
			
		||||
					{PackageName: "libxml2", ReleasePatches: []gostmodels.UbuntuReleasePatch{
 | 
			
		||||
						{ReleaseName: "focal", Status: "needed", Note: ""},
 | 
			
		||||
					}},
 | 
			
		||||
				},
 | 
			
		||||
				Upstreams: []gostmodels.UbuntuUpstream{{
 | 
			
		||||
					PackageName: "libxml2", UpstreamLinks: []gostmodels.UbuntuUpstreamLink{
 | 
			
		||||
						{Link: "https://gitlab.gnome.org/GNOME/libxml2/-/commit/50f06b3efb638efb0abd95dc62dca05ae67882c2"},
 | 
			
		||||
					},
 | 
			
		||||
				}},
 | 
			
		||||
			},
 | 
			
		||||
			expected: models.CveContent{
 | 
			
		||||
				Type:          models.UbuntuAPI,
 | 
			
		||||
				CveID:         "CVE-2021-3517",
 | 
			
		||||
				Summary:       "description.",
 | 
			
		||||
				Cvss2Severity: "medium",
 | 
			
		||||
				Cvss3Severity: "medium",
 | 
			
		||||
				SourceLink:    "https://ubuntu.com/security/CVE-2021-3517",
 | 
			
		||||
				References: []models.Reference{
 | 
			
		||||
					{Source: "CVE", Link: "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-3517"},
 | 
			
		||||
					{Link: "https://gitlab.gnome.org/GNOME/libxml2/-/issues/235"},
 | 
			
		||||
					{Link: "https://gitlab.gnome.org/GNOME/libxml2/-/commit/bf22713507fe1fc3a2c4b525cf0a88c2dc87a3a2"},
 | 
			
		||||
					{Source: "Bug", Link: "http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=987738"},
 | 
			
		||||
					{Source: "UPSTREAM", Link: "https://gitlab.gnome.org/GNOME/libxml2/-/commit/50f06b3efb638efb0abd95dc62dca05ae67882c2"}},
 | 
			
		||||
				Published: time.Date(2021, 5, 19, 14, 15, 0, 0, time.UTC),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			ubu := Ubuntu{}
 | 
			
		||||
			got := ubu.ConvertToModel(&tt.input)
 | 
			
		||||
			if !reflect.DeepEqual(got, &tt.expected) {
 | 
			
		||||
				t.Errorf("Ubuntu.ConvertToModel() = %#v, want %#v", got, &tt.expected)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package gost
 | 
			
		||||
@@ -85,7 +86,10 @@ type request struct {
 | 
			
		||||
 | 
			
		||||
func getAllUnfixedCvesViaHTTP(r *models.ScanResult, urlPrefix string) (
 | 
			
		||||
	responses []response, err error) {
 | 
			
		||||
	return getCvesWithFixStateViaHTTP(r, urlPrefix, "unfixed-cves")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getCvesWithFixStateViaHTTP(r *models.ScanResult, urlPrefix, fixState string) (responses []response, err error) {
 | 
			
		||||
	nReq := len(r.Packages) + len(r.SrcPackages)
 | 
			
		||||
	reqChan := make(chan request, nReq)
 | 
			
		||||
	resChan := make(chan response, nReq)
 | 
			
		||||
@@ -120,7 +124,7 @@ func getAllUnfixedCvesViaHTTP(r *models.ScanResult, urlPrefix string) (
 | 
			
		||||
				url, err := util.URLPathJoin(
 | 
			
		||||
					urlPrefix,
 | 
			
		||||
					req.packName,
 | 
			
		||||
					"unfixed-cves",
 | 
			
		||||
					fixState,
 | 
			
		||||
				)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					errChan <- err
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6534
									
								
								integration/data/results/amazon_2.json
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										6534
									
								
								integration/data/results/amazon_2.json
									
									
									
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										5634
									
								
								integration/data/results/centos_7.json
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										5634
									
								
								integration/data/results/centos_7.json
									
									
									
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										5631
									
								
								integration/data/results/debian_10.json
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										5631
									
								
								integration/data/results/debian_10.json
									
									
									
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										3031
									
								
								integration/data/results/oracle.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3031
									
								
								integration/data/results/oracle.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										5158
									
								
								integration/data/results/rhel_71.json
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										5158
									
								
								integration/data/results/rhel_71.json
									
									
									
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										6936
									
								
								integration/data/results/rhel_8.json
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										6936
									
								
								integration/data/results/rhel_8.json
									
									
									
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										8609
									
								
								integration/data/results/ubuntu_1804.json
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										8609
									
								
								integration/data/results/ubuntu_1804.json
									
									
									
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										8559
									
								
								integration/data/results/ubuntu_2004.json
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										8559
									
								
								integration/data/results/ubuntu_2004.json
									
									
									
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,30 +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:ruby_on_rails:3.0.1" ]
 | 
			
		||||
cpeNames = [ "cpe:/a:rubyonrails:rails:3.0.1" ]
 | 
			
		||||
 | 
			
		||||
[servers.nvd_rough]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
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,9 +22,30 @@ Url = "redis://127.0.0.1/3"
 | 
			
		||||
 | 
			
		||||
[servers]
 | 
			
		||||
 | 
			
		||||
[servers.rails]
 | 
			
		||||
[servers.nvd_exact]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
cpeNames       = [ "cpe:/a:rubyonrails:ruby_on_rails:3.0.1" ]
 | 
			
		||||
cpeNames = [ "cpe:/a:rubyonrails:rails:3.0.1" ]
 | 
			
		||||
#cpeNames = [ "cpe:/a:rubyonrails:rails:4.0.0" ]
 | 
			
		||||
 | 
			
		||||
[servers.nvd_rough]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
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"
 | 
			
		||||
 
 | 
			
		||||
@@ -100,7 +100,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":
 | 
			
		||||
	case "redhat", "centos", "alma", "rocky":
 | 
			
		||||
		return RedHat
 | 
			
		||||
	case "oracle":
 | 
			
		||||
		return Oracle
 | 
			
		||||
@@ -245,6 +331,8 @@ func NewCveContentType(name string) CveContentType {
 | 
			
		||||
		return RedHatAPI
 | 
			
		||||
	case "debian_security_tracker":
 | 
			
		||||
		return DebianSecurityTracker
 | 
			
		||||
	case "ubuntu_api":
 | 
			
		||||
		return UbuntuAPI
 | 
			
		||||
	case "microsoft":
 | 
			
		||||
		return Microsoft
 | 
			
		||||
	case "wordpress":
 | 
			
		||||
@@ -282,6 +370,9 @@ const (
 | 
			
		||||
	// Ubuntu is Ubuntu
 | 
			
		||||
	Ubuntu CveContentType = "ubuntu"
 | 
			
		||||
 | 
			
		||||
	// UbuntuAPI is Ubuntu
 | 
			
		||||
	UbuntuAPI CveContentType = "ubuntu_api"
 | 
			
		||||
 | 
			
		||||
	// Oracle is Oracle Linux
 | 
			
		||||
	Oracle CveContentType = "oracle"
 | 
			
		||||
 | 
			
		||||
@@ -317,10 +408,11 @@ var AllCveContetTypes = CveContentTypes{
 | 
			
		||||
	RedHat,
 | 
			
		||||
	RedHatAPI,
 | 
			
		||||
	Debian,
 | 
			
		||||
	DebianSecurityTracker,
 | 
			
		||||
	Ubuntu,
 | 
			
		||||
	UbuntuAPI,
 | 
			
		||||
	Amazon,
 | 
			
		||||
	SUSE,
 | 
			
		||||
	DebianSecurityTracker,
 | 
			
		||||
	WpScan,
 | 
			
		||||
	Trivy,
 | 
			
		||||
	GitHub,
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -88,35 +88,34 @@ func (s LibraryScanner) getVulnDetail(tvuln types.DetectedVulnerability) (vinfo
 | 
			
		||||
 | 
			
		||||
	vinfo.CveID = tvuln.VulnerabilityID
 | 
			
		||||
	vinfo.CveContents = getCveContents(tvuln.VulnerabilityID, vul)
 | 
			
		||||
	if tvuln.FixedVersion != "" {
 | 
			
		||||
		vinfo.LibraryFixedIns = []LibraryFixedIn{
 | 
			
		||||
			{
 | 
			
		||||
				Key:     s.GetLibraryKey(),
 | 
			
		||||
				Name:    tvuln.PkgName,
 | 
			
		||||
				FixedIn: tvuln.FixedVersion,
 | 
			
		||||
				Path:    s.Path,
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
	vinfo.LibraryFixedIns = []LibraryFixedIn{
 | 
			
		||||
		{
 | 
			
		||||
			Key:     s.GetLibraryKey(),
 | 
			
		||||
			Name:    tvuln.PkgName,
 | 
			
		||||
			FixedIn: tvuln.FixedVersion,
 | 
			
		||||
			Path:    s.Path,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -291,12 +291,11 @@ func (r ScanResult) IsContainer() bool {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RemoveRaspbianPackFromResult is for Raspberry Pi and removes the Raspberry Pi dedicated package from ScanResult.
 | 
			
		||||
func (r ScanResult) RemoveRaspbianPackFromResult() ScanResult {
 | 
			
		||||
func (r ScanResult) RemoveRaspbianPackFromResult() *ScanResult {
 | 
			
		||||
	if r.Family != constant.Raspbian {
 | 
			
		||||
		return r
 | 
			
		||||
		return &r
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result := r
 | 
			
		||||
	packs := make(Packages)
 | 
			
		||||
	for _, pack := range r.Packages {
 | 
			
		||||
		if !IsRaspbianPackage(pack.Name, pack.Version) {
 | 
			
		||||
@@ -311,10 +310,10 @@ func (r ScanResult) RemoveRaspbianPackFromResult() ScanResult {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result.Packages = packs
 | 
			
		||||
	result.SrcPackages = srcPacks
 | 
			
		||||
	r.Packages = packs
 | 
			
		||||
	r.SrcPackages = srcPacks
 | 
			
		||||
 | 
			
		||||
	return result
 | 
			
		||||
	return &r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ClearFields clears a given fields of ScanResult
 | 
			
		||||
@@ -416,22 +415,9 @@ 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
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		v.CveContents.Sort()
 | 
			
		||||
 | 
			
		||||
		sort.Slice(v.AlertDict.En, func(i, j int) bool {
 | 
			
		||||
			return v.AlertDict.En[i].Title < v.AlertDict.En[j].Title
 | 
			
		||||
		})
 | 
			
		||||
 
 | 
			
		||||
@@ -56,6 +56,16 @@ 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,
 | 
			
		||||
			expected: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			mode:     []byte{config.Fast},
 | 
			
		||||
			family:   constant.Amazon,
 | 
			
		||||
@@ -94,6 +104,55 @@ func TestIsDisplayUpdatableNum(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestRemoveRaspbianPackFromResult(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in       ScanResult
 | 
			
		||||
		expected ScanResult
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			in: ScanResult{
 | 
			
		||||
				Family: constant.Raspbian,
 | 
			
		||||
				Packages: Packages{
 | 
			
		||||
					"apt":                Package{Name: "apt", Version: "1.8.2.1"},
 | 
			
		||||
					"libraspberrypi-dev": Package{Name: "libraspberrypi-dev", Version: "1.20200811-1"},
 | 
			
		||||
				},
 | 
			
		||||
				SrcPackages: SrcPackages{},
 | 
			
		||||
			},
 | 
			
		||||
			expected: ScanResult{
 | 
			
		||||
				Family: constant.Raspbian,
 | 
			
		||||
				Packages: Packages{
 | 
			
		||||
					"apt": Package{Name: "apt", Version: "1.8.2.1"},
 | 
			
		||||
				},
 | 
			
		||||
				SrcPackages: SrcPackages{},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: ScanResult{
 | 
			
		||||
				Family: constant.Debian,
 | 
			
		||||
				Packages: Packages{
 | 
			
		||||
					"apt": Package{Name: "apt", Version: "1.8.2.1"},
 | 
			
		||||
				},
 | 
			
		||||
				SrcPackages: SrcPackages{},
 | 
			
		||||
			},
 | 
			
		||||
			expected: ScanResult{
 | 
			
		||||
				Family: constant.Debian,
 | 
			
		||||
				Packages: Packages{
 | 
			
		||||
					"apt": Package{Name: "apt", Version: "1.8.2.1"},
 | 
			
		||||
				},
 | 
			
		||||
				SrcPackages: SrcPackages{},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, tt := range tests {
 | 
			
		||||
		r := tt.in
 | 
			
		||||
		r = *r.RemoveRaspbianPackFromResult()
 | 
			
		||||
		if !reflect.DeepEqual(r, tt.expected) {
 | 
			
		||||
			t.Errorf("[%d] expected %+v, actual %+v", i, tt.expected, r)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestScanResult_Sort(t *testing.T) {
 | 
			
		||||
	type fields struct {
 | 
			
		||||
		Packages    Packages
 | 
			
		||||
@@ -139,17 +198,17 @@ 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{
 | 
			
		||||
@@ -198,17 +257,17 @@ 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{
 | 
			
		||||
@@ -260,17 +319,17 @@ 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{
 | 
			
		||||
@@ -319,17 +378,17 @@ 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{
 | 
			
		||||
@@ -346,6 +405,115 @@ func TestScanResult_Sort(t *testing.T) {
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			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,
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -37,6 +37,18 @@ func (v VulnInfos) FilterByCvssOver(over float64) VulnInfos {
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FilterByConfidenceOver scored vulnerabilities
 | 
			
		||||
func (v VulnInfos) FilterByConfidenceOver(over int) VulnInfos {
 | 
			
		||||
	return v.Find(func(v VulnInfo) bool {
 | 
			
		||||
		for _, c := range v.Confidences {
 | 
			
		||||
			if over <= c.Score {
 | 
			
		||||
				return true
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return false
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FilterIgnoreCves filter function.
 | 
			
		||||
func (v VulnInfos) FilterIgnoreCves(ignoreCveIDs []string) VulnInfos {
 | 
			
		||||
	return v.Find(func(v VulnInfo) bool {
 | 
			
		||||
@@ -75,9 +87,8 @@ func (v VulnInfos) FilterIgnorePkgs(ignorePkgsRegexps []string) VulnInfos {
 | 
			
		||||
		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
 | 
			
		||||
@@ -347,30 +358,46 @@ func (v VulnInfo) CveIDDiffFormat() string {
 | 
			
		||||
// 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 +420,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 +455,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 +480,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 +505,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 +600,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 ""
 | 
			
		||||
@@ -808,53 +859,56 @@ func (c Confidence) String() string {
 | 
			
		||||
type DetectionMethod string
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// CpeNameMatchStr is a String representation of CpeNameMatch
 | 
			
		||||
	CpeNameMatchStr = "CpeNameMatch"
 | 
			
		||||
	// NvdExactVersionMatchStr :
 | 
			
		||||
	NvdExactVersionMatchStr = "NvdExactVersionMatch"
 | 
			
		||||
 | 
			
		||||
	// YumUpdateSecurityMatchStr is a String representation of YumUpdateSecurityMatch
 | 
			
		||||
	YumUpdateSecurityMatchStr = "YumUpdateSecurityMatch"
 | 
			
		||||
	// NvdRoughVersionMatchStr :
 | 
			
		||||
	NvdRoughVersionMatchStr = "NvdRoughVersionMatch"
 | 
			
		||||
 | 
			
		||||
	// PkgAuditMatchStr is a String representation of PkgAuditMatch
 | 
			
		||||
	// NvdVendorProductMatchStr :
 | 
			
		||||
	NvdVendorProductMatchStr = "NvdVendorProductMatch"
 | 
			
		||||
 | 
			
		||||
	// 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"
 | 
			
		||||
 | 
			
		||||
	// TrivyMatchStr is a String representation of Trivy
 | 
			
		||||
	// UbuntuAPIMatchStr :
 | 
			
		||||
	UbuntuAPIMatchStr = "UbuntuAPIMatch"
 | 
			
		||||
 | 
			
		||||
	// 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 (
 | 
			
		||||
	// CpeNameMatch is a ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	CpeNameMatch = Confidence{100, CpeNameMatchStr, 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}
 | 
			
		||||
 | 
			
		||||
@@ -867,18 +921,33 @@ var (
 | 
			
		||||
	// DebianSecurityTrackerMatch ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	DebianSecurityTrackerMatch = Confidence{100, DebianSecurityTrackerMatchStr, 0}
 | 
			
		||||
 | 
			
		||||
	// UbuntuAPIMatch ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	UbuntuAPIMatch = Confidence{100, UbuntuAPIMatchStr, 0}
 | 
			
		||||
 | 
			
		||||
	// TrivyMatch ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	TrivyMatch = Confidence{100, TrivyMatchStr, 0}
 | 
			
		||||
 | 
			
		||||
	// 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}
 | 
			
		||||
 | 
			
		||||
	// 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{
 | 
			
		||||
				CpeNameMatch,
 | 
			
		||||
				NvdExactVersionMatch,
 | 
			
		||||
			},
 | 
			
		||||
			arg: CpeNameMatch,
 | 
			
		||||
			arg: NvdExactVersionMatch,
 | 
			
		||||
			out: Confidences{
 | 
			
		||||
				CpeNameMatch,
 | 
			
		||||
				NvdExactVersionMatch,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: Confidences{
 | 
			
		||||
				CpeNameMatch,
 | 
			
		||||
				NvdExactVersionMatch,
 | 
			
		||||
			},
 | 
			
		||||
			arg: ChangelogExactMatch,
 | 
			
		||||
			out: Confidences{
 | 
			
		||||
				CpeNameMatch,
 | 
			
		||||
				NvdExactVersionMatch,
 | 
			
		||||
				ChangelogExactMatch,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
@@ -1071,21 +1071,21 @@ func TestSortByConfident(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			in: Confidences{
 | 
			
		||||
				OvalMatch,
 | 
			
		||||
				CpeNameMatch,
 | 
			
		||||
				NvdExactVersionMatch,
 | 
			
		||||
			},
 | 
			
		||||
			out: Confidences{
 | 
			
		||||
				OvalMatch,
 | 
			
		||||
				CpeNameMatch,
 | 
			
		||||
				NvdExactVersionMatch,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: Confidences{
 | 
			
		||||
				CpeNameMatch,
 | 
			
		||||
				NvdExactVersionMatch,
 | 
			
		||||
				OvalMatch,
 | 
			
		||||
			},
 | 
			
		||||
			out: Confidences{
 | 
			
		||||
				OvalMatch,
 | 
			
		||||
				CpeNameMatch,
 | 
			
		||||
				NvdExactVersionMatch,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
@@ -1610,3 +1610,78 @@ func TestVulnInfos_FilterIgnorePkgs(t *testing.T) {
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestVulnInfos_FilterByConfidenceOver(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		over int
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name string
 | 
			
		||||
		v    VulnInfos
 | 
			
		||||
		args args
 | 
			
		||||
		want VulnInfos
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			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,
 | 
			
		||||
			},
 | 
			
		||||
			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) {
 | 
			
		||||
			if got := tt.v.FilterByConfidenceOver(tt.args.over); !reflect.DeepEqual(got, tt.want) {
 | 
			
		||||
				t.Errorf("VulnInfos.FilterByConfidenceOver() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package oval
 | 
			
		||||
@@ -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
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										217
									
								
								oval/debian.go
									
									
									
									
									
								
							
							
						
						
									
										217
									
								
								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
 | 
			
		||||
@@ -142,16 +154,8 @@ func (o Debian) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
 | 
			
		||||
	var relatedDefs ovalResult
 | 
			
		||||
	if o.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		if r.Family != constant.Raspbian {
 | 
			
		||||
			if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.Cnf.GetURL()); err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			// OVAL does not support Package for Raspbian, so skip it.
 | 
			
		||||
			result := r.RemoveRaspbianPackFromResult()
 | 
			
		||||
			if relatedDefs, err = getDefsByPackNameViaHTTP(&result, o.Cnf.GetURL()); err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
			}
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.Cnf.GetURL()); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		driver, err := newOvalDB(o.Cnf, r.Family)
 | 
			
		||||
@@ -164,16 +168,8 @@ func (o Debian) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
		if r.Family != constant.Raspbian {
 | 
			
		||||
			if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			// OVAL does not support Package for Raspbian, so skip it.
 | 
			
		||||
			result := r.RemoveRaspbianPackFromResult()
 | 
			
		||||
			if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, &result); err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
			}
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -197,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
 | 
			
		||||
@@ -358,6 +356,47 @@ func (o Ubuntu) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
			"linux",
 | 
			
		||||
		}
 | 
			
		||||
		return o.fillWithOval(r, kernelNamesInOval)
 | 
			
		||||
	case "21":
 | 
			
		||||
		kernelNamesInOval := []string{
 | 
			
		||||
			"linux-aws",
 | 
			
		||||
			"linux-base-sgx",
 | 
			
		||||
			"linux-base",
 | 
			
		||||
			"linux-cloud-tools-common",
 | 
			
		||||
			"linux-cloud-tools-generic",
 | 
			
		||||
			"linux-cloud-tools-lowlatency",
 | 
			
		||||
			"linux-cloud-tools-virtual",
 | 
			
		||||
			"linux-gcp",
 | 
			
		||||
			"linux-generic",
 | 
			
		||||
			"linux-gke",
 | 
			
		||||
			"linux-headers-aws",
 | 
			
		||||
			"linux-headers-gcp",
 | 
			
		||||
			"linux-headers-gke",
 | 
			
		||||
			"linux-headers-oracle",
 | 
			
		||||
			"linux-image-aws",
 | 
			
		||||
			"linux-image-extra-virtual",
 | 
			
		||||
			"linux-image-gcp",
 | 
			
		||||
			"linux-image-generic",
 | 
			
		||||
			"linux-image-gke",
 | 
			
		||||
			"linux-image-lowlatency",
 | 
			
		||||
			"linux-image-oracle",
 | 
			
		||||
			"linux-image-virtual",
 | 
			
		||||
			"linux-lowlatency",
 | 
			
		||||
			"linux-modules-extra-aws",
 | 
			
		||||
			"linux-modules-extra-gcp",
 | 
			
		||||
			"linux-modules-extra-gke",
 | 
			
		||||
			"linux-oracle",
 | 
			
		||||
			"linux-tools-aws",
 | 
			
		||||
			"linux-tools-common",
 | 
			
		||||
			"linux-tools-gcp",
 | 
			
		||||
			"linux-tools-generic",
 | 
			
		||||
			"linux-tools-gke",
 | 
			
		||||
			"linux-tools-host",
 | 
			
		||||
			"linux-tools-lowlatency",
 | 
			
		||||
			"linux-tools-oracle",
 | 
			
		||||
			"linux-tools-virtual",
 | 
			
		||||
			"linux-virtual",
 | 
			
		||||
		}
 | 
			
		||||
		return o.fillWithOval(r, kernelNamesInOval)
 | 
			
		||||
	}
 | 
			
		||||
	return 0, fmt.Errorf("Ubuntu %s is not support for now", r.Release)
 | 
			
		||||
}
 | 
			
		||||
@@ -473,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)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								oval/oval.go
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								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"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -83,7 +84,10 @@ func (b Base) CheckIfOvalFresh(osFamily, release string) (ok bool, err error) {
 | 
			
		||||
				logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
		lastModified = driver.GetLastModified(ovalFamily, release)
 | 
			
		||||
		lastModified, err = driver.GetLastModified(ovalFamily, release)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, xerrors.Errorf("Failed to GetLastModified: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		url, _ := util.URLPathJoin(config.Conf.OvalDict.URL, "lastmodified", ovalFamily, release)
 | 
			
		||||
		resp, body, errs := gorequest.New().Timeout(10 * time.Second).Get(url).End()
 | 
			
		||||
@@ -99,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
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										125
									
								
								oval/redhat.go
									
									
									
									
									
								
							
							
						
						
									
										125
									
								
								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 and CentOS
 | 
			
		||||
// RedHatBase is the base struct for RedHat, CentOS, Alma and Rocky
 | 
			
		||||
type RedHatBase struct {
 | 
			
		||||
	Base
 | 
			
		||||
}
 | 
			
		||||
@@ -50,14 +51,18 @@ 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
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -97,55 +102,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 +171,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.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 +187,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)
 | 
			
		||||
@@ -322,3 +339,39 @@ 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
 | 
			
		||||
	RedHatBase
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewRocky creates OVAL client for Rocky Linux
 | 
			
		||||
func NewRocky(cnf config.VulnDictInterface) Rocky {
 | 
			
		||||
	return Rocky{
 | 
			
		||||
		RedHatBase{
 | 
			
		||||
			Base{
 | 
			
		||||
				family: constant.Rocky,
 | 
			
		||||
				Cnf:    cnf,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										42
									
								
								oval/suse.go
									
									
									
									
									
								
							
							
						
						
									
										42
									
								
								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
 | 
			
		||||
@@ -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 {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										37
									
								
								oval/util.go
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								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"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -306,7 +307,7 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family string, ru
 | 
			
		||||
		switch family {
 | 
			
		||||
		case constant.Oracle, constant.Amazon:
 | 
			
		||||
			if ovalPack.Arch == "" {
 | 
			
		||||
				logging.Log.Infof("Arch is needed to detect Vulns for Amazon and Oracle Linux, but empty. You need refresh OVAL maybe. oval: %s, defID: %s", ovalPack, def.DefinitionID)
 | 
			
		||||
				logging.Log.Infof("Arch is needed to detect Vulns for Amazon and Oracle Linux, but empty. You need refresh OVAL maybe. oval: %#v, defID: %s", ovalPack, def.DefinitionID)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -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.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 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.
 | 
			
		||||
 | 
			
		||||
@@ -435,9 +436,11 @@ func lessThan(family, newVer string, packInOVAL ovalmodels.Package) (bool, error
 | 
			
		||||
		return vera.LessThan(verb), nil
 | 
			
		||||
 | 
			
		||||
	case constant.RedHat,
 | 
			
		||||
		constant.CentOS:
 | 
			
		||||
		vera := rpmver.NewVersion(centOSVersionToRHEL(newVer))
 | 
			
		||||
		verb := rpmver.NewVersion(centOSVersionToRHEL(packInOVAL.Version))
 | 
			
		||||
		constant.CentOS,
 | 
			
		||||
		constant.Alma,
 | 
			
		||||
		constant.Rocky:
 | 
			
		||||
		vera := rpmver.NewVersion(rhelDownStreamOSVersionToRHEL(newVer))
 | 
			
		||||
		verb := rpmver.NewVersion(rhelDownStreamOSVersionToRHEL(packInOVAL.Version))
 | 
			
		||||
		return vera.LessThan(verb), nil
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
@@ -445,10 +448,10 @@ func lessThan(family, newVer string, packInOVAL ovalmodels.Package) (bool, error
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var centosVerPattern = regexp.MustCompile(`\.[es]l(\d+)(?:_\d+)?(?:\.centos)?`)
 | 
			
		||||
var rhelDownStreamOSVerPattern = regexp.MustCompile(`\.[es]l(\d+)(?:_\d+)?(?:\.(centos|rocky|alma))?`)
 | 
			
		||||
 | 
			
		||||
func centOSVersionToRHEL(ver string) string {
 | 
			
		||||
	return centosVerPattern.ReplaceAllString(ver, ".el$1")
 | 
			
		||||
func rhelDownStreamOSVersionToRHEL(ver string) string {
 | 
			
		||||
	return rhelDownStreamOSVerPattern.ReplaceAllString(ver, ".el$1")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewOVALClient returns a client for OVAL database
 | 
			
		||||
@@ -461,8 +464,11 @@ func NewOVALClient(family string, cnf config.GovalDictConf) (Client, error) {
 | 
			
		||||
	case constant.RedHat:
 | 
			
		||||
		return NewRedhat(&cnf), nil
 | 
			
		||||
	case constant.CentOS:
 | 
			
		||||
		//use RedHat's OVAL
 | 
			
		||||
		return NewCentOS(&cnf), nil
 | 
			
		||||
	case constant.Alma:
 | 
			
		||||
		return NewAlma(&cnf), nil
 | 
			
		||||
	case constant.Rocky:
 | 
			
		||||
		return NewRocky(&cnf), nil
 | 
			
		||||
	case constant.Oracle:
 | 
			
		||||
		return NewOracle(&cnf), nil
 | 
			
		||||
	case constant.SUSEEnterpriseServer:
 | 
			
		||||
@@ -485,17 +491,14 @@ func NewOVALClient(family string, cnf config.GovalDictConf) (Client, error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetFamilyInOval returns the OS family name in OVAL
 | 
			
		||||
// For example, CentOS 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:
 | 
			
		||||
		return constant.RedHat, nil
 | 
			
		||||
	case constant.CentOS:
 | 
			
		||||
		//use RedHat's OVAL
 | 
			
		||||
	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) {
 | 
			
		||||
@@ -1078,6 +1079,472 @@ func TestIsOvalDefAffected(t *testing.T) {
 | 
			
		||||
			notFixedYet: false,
 | 
			
		||||
			fixedIn:     "3.1.0",
 | 
			
		||||
		},
 | 
			
		||||
		// Rocky Linux
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				family: "rocky",
 | 
			
		||||
				def: ovalmodels.Definition{
 | 
			
		||||
					AffectedPacks: []ovalmodels.Package{
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "a",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "b",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
							Version:     "0:1.2.3-45.el6_7.8",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				req: request{
 | 
			
		||||
					packName:          "b",
 | 
			
		||||
					isSrcPack:         false,
 | 
			
		||||
					versionRelease:    "0:1.2.3-45.el6.rocky.7",
 | 
			
		||||
					newVersionRelease: "",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    true,
 | 
			
		||||
			notFixedYet: false,
 | 
			
		||||
			fixedIn:     "0:1.2.3-45.el6_7.8",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				family: "rocky",
 | 
			
		||||
				def: ovalmodels.Definition{
 | 
			
		||||
					AffectedPacks: []ovalmodels.Package{
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "a",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "b",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
							Version:     "0:1.2.3-45.el6_7.8",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				req: request{
 | 
			
		||||
					packName:       "b",
 | 
			
		||||
					isSrcPack:      false,
 | 
			
		||||
					versionRelease: "0:1.2.3-45.el6.rocky.8",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    false,
 | 
			
		||||
			notFixedYet: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				family: "rocky",
 | 
			
		||||
				def: ovalmodels.Definition{
 | 
			
		||||
					AffectedPacks: []ovalmodels.Package{
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "a",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "b",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
							Version:     "0:1.2.3-45.el6_7.8",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				req: request{
 | 
			
		||||
					packName:       "b",
 | 
			
		||||
					isSrcPack:      false,
 | 
			
		||||
					versionRelease: "0:1.2.3-45.el6.rocky.9",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    false,
 | 
			
		||||
			notFixedYet: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				family: "rocky",
 | 
			
		||||
				def: ovalmodels.Definition{
 | 
			
		||||
					AffectedPacks: []ovalmodels.Package{
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "a",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "b",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
							Version:     "0:1.2.3-45.el6_7.8",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				req: request{
 | 
			
		||||
					packName:          "b",
 | 
			
		||||
					isSrcPack:         false,
 | 
			
		||||
					versionRelease:    "0:1.2.3-45.el6.rocky.6",
 | 
			
		||||
					newVersionRelease: "0:1.2.3-45.el6.rocky.7",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    true,
 | 
			
		||||
			notFixedYet: true,
 | 
			
		||||
			fixedIn:     "0:1.2.3-45.el6_7.8",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				family: "rocky",
 | 
			
		||||
				def: ovalmodels.Definition{
 | 
			
		||||
					AffectedPacks: []ovalmodels.Package{
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "a",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "b",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
							Version:     "0:1.2.3-45.el6_7.8",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				req: request{
 | 
			
		||||
					packName:          "b",
 | 
			
		||||
					isSrcPack:         false,
 | 
			
		||||
					versionRelease:    "0:1.2.3-45.el6.rocky.6",
 | 
			
		||||
					newVersionRelease: "0:1.2.3-45.el6.rocky.8",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    true,
 | 
			
		||||
			notFixedYet: false,
 | 
			
		||||
			fixedIn:     "0:1.2.3-45.el6_7.8",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				family: "rocky",
 | 
			
		||||
				def: ovalmodels.Definition{
 | 
			
		||||
					AffectedPacks: []ovalmodels.Package{
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "a",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "b",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
							Version:     "0:1.2.3-45.el6_7.8",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				req: request{
 | 
			
		||||
					packName:          "b",
 | 
			
		||||
					isSrcPack:         false,
 | 
			
		||||
					versionRelease:    "0:1.2.3-45.el6.rocky.6",
 | 
			
		||||
					newVersionRelease: "0:1.2.3-45.el6.rocky.9",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    true,
 | 
			
		||||
			notFixedYet: false,
 | 
			
		||||
			fixedIn:     "0:1.2.3-45.el6_7.8",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				family: "rocky",
 | 
			
		||||
				def: ovalmodels.Definition{
 | 
			
		||||
					AffectedPacks: []ovalmodels.Package{
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "a",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "b",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
							Version:     "0:1.2.3-45.el6_7.8",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				req: request{
 | 
			
		||||
					packName:       "b",
 | 
			
		||||
					isSrcPack:      false,
 | 
			
		||||
					versionRelease: "0:1.2.3-45.el6.8",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    false,
 | 
			
		||||
			notFixedYet: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				family: "rocky",
 | 
			
		||||
				def: ovalmodels.Definition{
 | 
			
		||||
					AffectedPacks: []ovalmodels.Package{
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "a",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "b",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
							Version:     "0:1.2.3-45.el6.8",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				req: request{
 | 
			
		||||
					packName:       "b",
 | 
			
		||||
					isSrcPack:      false,
 | 
			
		||||
					versionRelease: "0:1.2.3-45.el6_7.8",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    false,
 | 
			
		||||
			notFixedYet: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				family: "rocky",
 | 
			
		||||
				def: ovalmodels.Definition{
 | 
			
		||||
					AffectedPacks: []ovalmodels.Package{
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "a",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "b",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
							Version:     "0:1.2.3-45.el6_7.8",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				req: request{
 | 
			
		||||
					packName:       "b",
 | 
			
		||||
					isSrcPack:      false,
 | 
			
		||||
					versionRelease: "0:1.2.3-45.sl6.7",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    true,
 | 
			
		||||
			notFixedYet: false,
 | 
			
		||||
			fixedIn:     "0:1.2.3-45.el6_7.8",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				family: "rocky",
 | 
			
		||||
				def: ovalmodels.Definition{
 | 
			
		||||
					AffectedPacks: []ovalmodels.Package{
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "a",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "b",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
							Version:     "0:1.2.3-45.el6_7.8",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				req: request{
 | 
			
		||||
					packName:       "b",
 | 
			
		||||
					isSrcPack:      false,
 | 
			
		||||
					versionRelease: "0:1.2.3-45.sl6.8",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    false,
 | 
			
		||||
			notFixedYet: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				family: "rocky",
 | 
			
		||||
				def: ovalmodels.Definition{
 | 
			
		||||
					AffectedPacks: []ovalmodels.Package{
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "a",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "b",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
							Version:     "0:1.2.3-45.el6_7.8",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				req: request{
 | 
			
		||||
					packName:       "b",
 | 
			
		||||
					isSrcPack:      false,
 | 
			
		||||
					versionRelease: "0:1.2.3-45.sl6.9",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    false,
 | 
			
		||||
			notFixedYet: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				family: "rocky",
 | 
			
		||||
				def: ovalmodels.Definition{
 | 
			
		||||
					AffectedPacks: []ovalmodels.Package{
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "a",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "b",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
							Version:     "0:1.2.3-45.el6_7.8",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				req: request{
 | 
			
		||||
					packName:          "b",
 | 
			
		||||
					isSrcPack:         false,
 | 
			
		||||
					versionRelease:    "0:1.2.3-45.sl6.6",
 | 
			
		||||
					newVersionRelease: "0:1.2.3-45.sl6.7",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    true,
 | 
			
		||||
			notFixedYet: true,
 | 
			
		||||
			fixedIn:     "0:1.2.3-45.el6_7.8",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				family: "rocky",
 | 
			
		||||
				def: ovalmodels.Definition{
 | 
			
		||||
					AffectedPacks: []ovalmodels.Package{
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "a",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "b",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
							Version:     "0:1.2.3-45.el6_7.8",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				req: request{
 | 
			
		||||
					packName:          "b",
 | 
			
		||||
					isSrcPack:         false,
 | 
			
		||||
					versionRelease:    "0:1.2.3-45.sl6.6",
 | 
			
		||||
					newVersionRelease: "0:1.2.3-45.sl6.8",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    true,
 | 
			
		||||
			notFixedYet: false,
 | 
			
		||||
			fixedIn:     "0:1.2.3-45.el6_7.8",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				family: "rocky",
 | 
			
		||||
				def: ovalmodels.Definition{
 | 
			
		||||
					AffectedPacks: []ovalmodels.Package{
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "a",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "b",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
							Version:     "0:1.2.3-45.el6_7.8",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				req: request{
 | 
			
		||||
					packName:          "b",
 | 
			
		||||
					isSrcPack:         false,
 | 
			
		||||
					versionRelease:    "0:1.2.3-45.sl6.6",
 | 
			
		||||
					newVersionRelease: "0:1.2.3-45.sl6.9",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    true,
 | 
			
		||||
			notFixedYet: false,
 | 
			
		||||
			fixedIn:     "0:1.2.3-45.el6_7.8",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				family: "rocky",
 | 
			
		||||
				def: ovalmodels.Definition{
 | 
			
		||||
					AffectedPacks: []ovalmodels.Package{
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "a",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "b",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
							Version:     "0:1.2.3-45.el6_7.8",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				req: request{
 | 
			
		||||
					packName:       "b",
 | 
			
		||||
					isSrcPack:      false,
 | 
			
		||||
					versionRelease: "0:1.2.3-45.el6.8",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    false,
 | 
			
		||||
			notFixedYet: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				family: "rocky",
 | 
			
		||||
				def: ovalmodels.Definition{
 | 
			
		||||
					AffectedPacks: []ovalmodels.Package{
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "a",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "b",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
							Version:     "0:1.2.3-45.el6.8",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				req: request{
 | 
			
		||||
					packName:       "b",
 | 
			
		||||
					isSrcPack:      false,
 | 
			
		||||
					versionRelease: "0:1.2.3-45.el6_7.8",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    false,
 | 
			
		||||
			notFixedYet: false,
 | 
			
		||||
		},
 | 
			
		||||
		// For kernel related packages, ignore OVAL with different major versions
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				family: constant.Rocky,
 | 
			
		||||
				def: ovalmodels.Definition{
 | 
			
		||||
					AffectedPacks: []ovalmodels.Package{
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "kernel",
 | 
			
		||||
							Version:     "4.1.0",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				req: request{
 | 
			
		||||
					packName:          "kernel",
 | 
			
		||||
					versionRelease:    "3.0.0",
 | 
			
		||||
					newVersionRelease: "3.2.0",
 | 
			
		||||
				},
 | 
			
		||||
				kernel: models.Kernel{
 | 
			
		||||
					Release: "3.0.0",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    false,
 | 
			
		||||
			notFixedYet: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				family: constant.Rocky,
 | 
			
		||||
				def: ovalmodels.Definition{
 | 
			
		||||
					AffectedPacks: []ovalmodels.Package{
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "kernel",
 | 
			
		||||
							Version:     "3.1.0",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				req: request{
 | 
			
		||||
					packName:          "kernel",
 | 
			
		||||
					versionRelease:    "3.0.0",
 | 
			
		||||
					newVersionRelease: "3.2.0",
 | 
			
		||||
				},
 | 
			
		||||
				kernel: models.Kernel{
 | 
			
		||||
					Release: "3.0.0",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    true,
 | 
			
		||||
			notFixedYet: false,
 | 
			
		||||
			fixedIn:     "3.1.0",
 | 
			
		||||
		},
 | 
			
		||||
		// dnf module
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
@@ -1326,7 +1793,7 @@ func TestIsOvalDefAffected(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_centOSVersionToRHEL(t *testing.T) {
 | 
			
		||||
func Test_rhelDownStreamOSVersionToRHEL(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		ver string
 | 
			
		||||
	}
 | 
			
		||||
@@ -1342,6 +1809,13 @@ func Test_centOSVersionToRHEL(t *testing.T) {
 | 
			
		||||
			},
 | 
			
		||||
			want: "grub2-tools-2.02-0.80.el7.x86_64",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "remove rocky.",
 | 
			
		||||
			args: args{
 | 
			
		||||
				ver: "platform-python-3.6.8-37.el8.rocky.x86_64",
 | 
			
		||||
			},
 | 
			
		||||
			want: "platform-python-3.6.8-37.el8.x86_64",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "noop",
 | 
			
		||||
			args: args{
 | 
			
		||||
@@ -1359,8 +1833,8 @@ func Test_centOSVersionToRHEL(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			if got := centOSVersionToRHEL(tt.args.ver); got != tt.want {
 | 
			
		||||
				t.Errorf("centOSVersionToRHEL() = %v, want %v", got, tt.want)
 | 
			
		||||
			if got := rhelDownStreamOSVersionToRHEL(tt.args.ver); got != tt.want {
 | 
			
		||||
				t.Errorf("rhelDownStreamOSVersionToRHEL() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										102
									
								
								reporter/googlechat.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								reporter/googlechat.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,102 @@
 | 
			
		||||
package reporter
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GoogleChatWriter send report to GoogleChat
 | 
			
		||||
type GoogleChatWriter struct {
 | 
			
		||||
	Cnf   config.GoogleChatConf
 | 
			
		||||
	Proxy string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w GoogleChatWriter) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
	re := regexp.MustCompile(w.Cnf.ServerNameRegexp)
 | 
			
		||||
 | 
			
		||||
	for _, r := range rs {
 | 
			
		||||
		if re.Match([]byte(r.FormatServerName())) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		msgs := []string{fmt.Sprintf("*%s*\n%s\t%s\t%s",
 | 
			
		||||
			r.ServerInfo(),
 | 
			
		||||
			r.ScannedCves.FormatCveSummary(),
 | 
			
		||||
			r.ScannedCves.FormatFixedStatus(r.Packages),
 | 
			
		||||
			r.FormatUpdatablePkgsSummary())}
 | 
			
		||||
		for _, vinfo := range r.ScannedCves.ToSortedSlice() {
 | 
			
		||||
			max := vinfo.MaxCvssScore().Value.Score
 | 
			
		||||
 | 
			
		||||
			exploits := ""
 | 
			
		||||
			if 0 < len(vinfo.Exploits) || 0 < len(vinfo.Metasploits) {
 | 
			
		||||
				exploits = "*PoC*"
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			link := ""
 | 
			
		||||
			if strings.HasPrefix(vinfo.CveID, "CVE-") {
 | 
			
		||||
				link = fmt.Sprintf("https://nvd.nist.gov/vuln/detail/%s", vinfo.CveID)
 | 
			
		||||
			} else if strings.HasPrefix(vinfo.CveID, "WPVDBID-") {
 | 
			
		||||
				link = fmt.Sprintf("https://wpscan.com/vulnerabilities/%s", strings.TrimPrefix(vinfo.CveID, "WPVDBID-"))
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			msgs = append(msgs, fmt.Sprintf(`%s %s %4.1f %5s %s`,
 | 
			
		||||
				vinfo.CveIDDiffFormat(),
 | 
			
		||||
				link,
 | 
			
		||||
				max,
 | 
			
		||||
				vinfo.AttackVector(),
 | 
			
		||||
				exploits))
 | 
			
		||||
			if len(msgs) == 50 {
 | 
			
		||||
				msgs = append(msgs, "(The rest is omitted.)")
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if len(msgs) == 1 && w.Cnf.SkipIfNoCve {
 | 
			
		||||
			msgs = []string{}
 | 
			
		||||
		}
 | 
			
		||||
		if len(msgs) != 0 {
 | 
			
		||||
			if err = w.postMessage(strings.Join(msgs, "\n")); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w GoogleChatWriter) postMessage(message string) error {
 | 
			
		||||
	uri := fmt.Sprintf("%s", w.Cnf.WebHookURL)
 | 
			
		||||
	payload := `{"text": "` + message + `" }`
 | 
			
		||||
 | 
			
		||||
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 | 
			
		||||
	req, err := http.NewRequestWithContext(ctx, http.MethodPost, uri, bytes.NewBuffer([]byte(payload)))
 | 
			
		||||
	defer cancel()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	req.Header.Add("Content-Type", "application/json; charset=utf-8")
 | 
			
		||||
	client, err := util.GetHTTPClient(w.Proxy)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	resp, err := client.Do(req)
 | 
			
		||||
	if checkResponse(resp) != nil && err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w GoogleChatWriter) checkResponse(r *http.Response) error {
 | 
			
		||||
	if c := r.StatusCode; 200 <= c && c <= 299 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return xerrors.Errorf("API call to %s failed: %s", r.Request.URL.String(), r.Status)
 | 
			
		||||
}
 | 
			
		||||
@@ -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"
 | 
			
		||||
@@ -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})
 | 
			
		||||
		}
 | 
			
		||||
@@ -620,7 +621,7 @@ func getPlusDiffCves(previous, current models.ScanResult) models.VulnInfos {
 | 
			
		||||
 | 
			
		||||
				// TODO commented out because  a bug of diff logic when multiple oval defs found for a certain CVE-ID and same updated_at
 | 
			
		||||
				// if these OVAL defs have different affected packages, this logic detects as updated.
 | 
			
		||||
				// This logic will be uncomented after integration with gost https://github.com/knqyf263/gost
 | 
			
		||||
				// This logic will be uncomented after integration with gost https://github.com/vulsio/gost
 | 
			
		||||
				// } else if isCveFixed(v, previous) {
 | 
			
		||||
				// updated[v.CveID] = v
 | 
			
		||||
				// logging.Log.Debugf("fixed: %s", v.CveID)
 | 
			
		||||
@@ -673,32 +674,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"
 | 
			
		||||
)
 | 
			
		||||
@@ -62,7 +70,7 @@ type osPackages struct {
 | 
			
		||||
	// enabled dnf modules or packages
 | 
			
		||||
	EnabledDnfModules []string
 | 
			
		||||
 | 
			
		||||
	// unsecure packages
 | 
			
		||||
	// Detected Vulnerabilities Key: CVE-ID
 | 
			
		||||
	VulnInfos models.VulnInfos
 | 
			
		||||
 | 
			
		||||
	// kernel information
 | 
			
		||||
@@ -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
 | 
			
		||||
	// `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
 | 
			
		||||
	// `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,
 | 
			
		||||
				}},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
 
 | 
			
		||||
@@ -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,58 @@ 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\.]*)`)
 | 
			
		||||
			result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
 | 
			
		||||
			if len(result) != 3 {
 | 
			
		||||
				logging.Log.Warnf("Failed to parse Rocky version: %s", r)
 | 
			
		||||
				return true, newRocky(c)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			release := result[2]
 | 
			
		||||
			switch strings.ToLower(result[1]) {
 | 
			
		||||
			case "rocky", "rocky linux":
 | 
			
		||||
				rocky := newRocky(c)
 | 
			
		||||
				rocky.setDistro(constant.Rocky, release)
 | 
			
		||||
				return true, rocky
 | 
			
		||||
			default:
 | 
			
		||||
				logging.Log.Warnf("Failed to parse Rocky: %s", r)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if r := exec(c, "ls /etc/redhat-release", noSudo); r.isSuccess() {
 | 
			
		||||
		// https://www.rackaid.com/blog/how-to-determine-centos-or-red-hat-version/
 | 
			
		||||
		// e.g.
 | 
			
		||||
@@ -79,10 +125,18 @@ func detectRedhat(c config.ServerInfo) (bool, osTypeInterface) {
 | 
			
		||||
 | 
			
		||||
			release := result[2]
 | 
			
		||||
			switch strings.ToLower(result[1]) {
 | 
			
		||||
			case "centos", "centos linux":
 | 
			
		||||
			case "centos", "centos linux", "centos stream":
 | 
			
		||||
				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)
 | 
			
		||||
				return true, rocky
 | 
			
		||||
			default:
 | 
			
		||||
				// RHEL
 | 
			
		||||
				rhel := newRHEL(c)
 | 
			
		||||
@@ -455,7 +509,7 @@ func (o *redhatBase) isExecNeedsRestarting() bool {
 | 
			
		||||
		// TODO zypper ps
 | 
			
		||||
		// https://github.com/future-architect/vuls/issues/696
 | 
			
		||||
		return false
 | 
			
		||||
	case constant.RedHat, constant.CentOS, constant.Oracle:
 | 
			
		||||
	case constant.RedHat, constant.CentOS, constant.Rocky, constant.Oracle:
 | 
			
		||||
		majorVersion, err := o.Distro.MajorVersion()
 | 
			
		||||
		if err != nil || majorVersion < 6 {
 | 
			
		||||
			o.log.Errorf("Not implemented yet: %s, err: %+v", o.Distro, err)
 | 
			
		||||
@@ -634,7 +688,7 @@ func (o *redhatBase) rpmQf() string {
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) detectEnabledDnfModules() ([]string, error) {
 | 
			
		||||
	switch o.Distro.Family {
 | 
			
		||||
	case constant.RedHat, constant.CentOS:
 | 
			
		||||
	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
 | 
			
		||||
	// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8, Alma8, Rocky8
 | 
			
		||||
	return []string{"yum-utils"}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										118
									
								
								scanner/rocky.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								scanner/rocky.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 rocky struct {
 | 
			
		||||
	redhatBase
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewRocky is constructor
 | 
			
		||||
func newRocky(c config.ServerInfo) *rocky {
 | 
			
		||||
	r := &rocky{
 | 
			
		||||
		redhatBase{
 | 
			
		||||
			base: base{
 | 
			
		||||
				osPackages: osPackages{
 | 
			
		||||
					Packages:  models.Packages{},
 | 
			
		||||
					VulnInfos: models.VulnInfos{},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			sudo: rootPrivRocky{},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	r.log = logging.NewNormalLogger()
 | 
			
		||||
	r.setServerInfo(c)
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *rocky) checkScanMode() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *rocky) 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 *rocky) 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 *rocky) 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 *rocky) depsDeep() []string {
 | 
			
		||||
	return o.depsFastRoot()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *rocky) 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 *rocky) sudoNoPasswdCmdsFast() []cmd {
 | 
			
		||||
	return []cmd{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *rocky) 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 *rocky) sudoNoPasswdCmdsDeep() []cmd {
 | 
			
		||||
	return o.sudoNoPasswdCmdsFastRoot()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type rootPrivRocky struct{}
 | 
			
		||||
 | 
			
		||||
func (o rootPrivRocky) repoquery() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o rootPrivRocky) yumMakeCache() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o rootPrivRocky) yumPS() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
@@ -211,12 +211,16 @@ func ParseInstalledPkgs(distro config.Distro, kernel models.Kernel, pkgList stri
 | 
			
		||||
 | 
			
		||||
	var osType osTypeInterface
 | 
			
		||||
	switch distro.Family {
 | 
			
		||||
	case constant.Debian, constant.Ubuntu:
 | 
			
		||||
	case constant.Debian, constant.Ubuntu, constant.Raspbian:
 | 
			
		||||
		osType = &debian{base: base}
 | 
			
		||||
	case constant.RedHat:
 | 
			
		||||
		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:
 | 
			
		||||
		osType = &oracle{redhatBase: redhatBase{base: base}}
 | 
			
		||||
	case constant.Amazon:
 | 
			
		||||
@@ -651,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.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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -157,6 +157,13 @@ func printConfigToml(ips []string) (err error) {
 | 
			
		||||
#room     = "xxxxxxxxxxx"
 | 
			
		||||
#apiToken = "xxxxxxxxxxxxxxxxxx"
 | 
			
		||||
 | 
			
		||||
# https://vuls.io/docs/en/config.toml.html#googlechat-section
 | 
			
		||||
#[googlechat]
 | 
			
		||||
#webHookURL = "https://chat.googleapis.com/v1/spaces/xxxxxxxxxx/messages?key=yyyyyyyyyy&token=zzzzzzzzzz%3D"
 | 
			
		||||
#skipIfNoCve = false
 | 
			
		||||
#serverNameRegexp = "^(\\[Reboot Required\\] )?((spam|ham).*|.*(egg)$)" # include spamonigiri, hamburger, boiledegg
 | 
			
		||||
#serverNameRegexp = "^(\\[Reboot Required\\] )?(?:(spam|ham).*|.*(?:egg)$)" # exclude spamonigiri, hamburger, boiledegg
 | 
			
		||||
 | 
			
		||||
# https://vuls.io/docs/en/config.toml.html#telegram-section
 | 
			
		||||
#[telegram]
 | 
			
		||||
#chatID     = "xxxxxxxxxxx"
 | 
			
		||||
@@ -204,6 +211,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
 | 
			
		||||
@@ -30,15 +31,16 @@ type ReportCmd struct {
 | 
			
		||||
	formatList        bool
 | 
			
		||||
	gzip              bool
 | 
			
		||||
 | 
			
		||||
	toSlack     bool
 | 
			
		||||
	toChatWork  bool
 | 
			
		||||
	toTelegram  bool
 | 
			
		||||
	toEmail     bool
 | 
			
		||||
	toSyslog    bool
 | 
			
		||||
	toLocalFile bool
 | 
			
		||||
	toS3        bool
 | 
			
		||||
	toAzureBlob bool
 | 
			
		||||
	toHTTP      bool
 | 
			
		||||
	toSlack      bool
 | 
			
		||||
	toChatWork   bool
 | 
			
		||||
	toGoogleChat bool
 | 
			
		||||
	toTelegram   bool
 | 
			
		||||
	toEmail      bool
 | 
			
		||||
	toSyslog     bool
 | 
			
		||||
	toLocalFile  bool
 | 
			
		||||
	toS3         bool
 | 
			
		||||
	toAzureBlob  bool
 | 
			
		||||
	toHTTP       bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Name return subcommand name
 | 
			
		||||
@@ -58,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]
 | 
			
		||||
@@ -67,6 +70,7 @@ func (*ReportCmd) Usage() string {
 | 
			
		||||
		[-to-http]
 | 
			
		||||
		[-to-slack]
 | 
			
		||||
		[-to-chatwork]
 | 
			
		||||
		[-to-googlechat]
 | 
			
		||||
		[-to-telegram]
 | 
			
		||||
		[-to-localfile]
 | 
			
		||||
		[-to-s3]
 | 
			
		||||
@@ -115,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")
 | 
			
		||||
 | 
			
		||||
@@ -146,6 +153,7 @@ func (p *ReportCmd) SetFlags(f *flag.FlagSet) {
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&p.toSlack, "to-slack", false, "Send report via Slack")
 | 
			
		||||
	f.BoolVar(&p.toChatWork, "to-chatwork", false, "Send report via chatwork")
 | 
			
		||||
	f.BoolVar(&p.toGoogleChat, "to-googlechat", false, "Send report via Google Chat")
 | 
			
		||||
	f.BoolVar(&p.toTelegram, "to-telegram", false, "Send report via Telegram")
 | 
			
		||||
	f.BoolVar(&p.toEmail, "to-email", false, "Send report via Email")
 | 
			
		||||
	f.BoolVar(&p.toSyslog, "to-syslog", false, "Send report via Syslog")
 | 
			
		||||
@@ -173,6 +181,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
 | 
			
		||||
	}
 | 
			
		||||
	config.Conf.Slack.Enabled = p.toSlack
 | 
			
		||||
	config.Conf.ChatWork.Enabled = p.toChatWork
 | 
			
		||||
	config.Conf.GoogleChat.Enabled = p.toGoogleChat
 | 
			
		||||
	config.Conf.Telegram.Enabled = p.toTelegram
 | 
			
		||||
	config.Conf.EMail.Enabled = p.toEmail
 | 
			
		||||
	config.Conf.Syslog.Enabled = p.toSyslog
 | 
			
		||||
@@ -261,6 +270,10 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
 | 
			
		||||
		reports = append(reports, reporter.ChatWorkWriter{Cnf: config.Conf.ChatWork, Proxy: config.Conf.HTTPProxy})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if p.toGoogleChat {
 | 
			
		||||
		reports = append(reports, reporter.GoogleChatWriter{Cnf: config.Conf.GoogleChat, Proxy: config.Conf.HTTPProxy})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if p.toTelegram {
 | 
			
		||||
		reports = append(reports, reporter.TelegramWriter{Cnf: config.Conf.Telegram})
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -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")
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										21
									
								
								tui/tui.go
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								tui/tui.go
									
									
									
									
									
								
							@@ -301,10 +301,7 @@ func cursorMoveMiddle(g *gocui.Gui, v *gocui.View) error {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if err := onMovingCursorRedrawView(g, v); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
	return onMovingCursorRedrawView(g, v)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func cursorPageDown(g *gocui.Gui, v *gocui.View) error {
 | 
			
		||||
@@ -505,7 +502,7 @@ func showMsg(g *gocui.Gui, v *gocui.View) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func delMsg(g *gocui.Gui, v *gocui.View) error {
 | 
			
		||||
func delMsg(g *gocui.Gui, _ *gocui.View) error {
 | 
			
		||||
	if err := g.DeleteView("msg"); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
@@ -513,7 +510,7 @@ func delMsg(g *gocui.Gui, v *gocui.View) error {
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func quit(g *gocui.Gui, v *gocui.View) error {
 | 
			
		||||
func quit(_ *gocui.Gui, _ *gocui.View) error {
 | 
			
		||||
	return gocui.ErrQuit
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -895,7 +892,7 @@ func detailLines() (string, error) {
 | 
			
		||||
 | 
			
		||||
	vinfo := vinfos[currentVinfo]
 | 
			
		||||
	links := []string{}
 | 
			
		||||
	for _, r := range vinfo.CveContents.PrimarySrcURLs(r.Lang, r.Family, vinfo.CveID) {
 | 
			
		||||
	for _, r := range vinfo.CveContents.PrimarySrcURLs(r.Lang, r.Family, vinfo.CveID, vinfo.Confidences) {
 | 
			
		||||
		links = append(links, r.Value)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -908,9 +905,11 @@ func detailLines() (string, error) {
 | 
			
		||||
			refsMap[ref.Link] = ref
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if cont, found := vinfo.CveContents[models.Trivy]; found {
 | 
			
		||||
		for _, ref := range cont.References {
 | 
			
		||||
			refsMap[ref.Link] = ref
 | 
			
		||||
	if conts, found := vinfo.CveContents[models.Trivy]; found {
 | 
			
		||||
		for _, cont := range conts {
 | 
			
		||||
			for _, ref := range cont.References {
 | 
			
		||||
				refsMap[ref.Link] = ref
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	refs := []models.Reference{}
 | 
			
		||||
@@ -1014,7 +1013,7 @@ CWE
 | 
			
		||||
Confidence
 | 
			
		||||
-----------
 | 
			
		||||
{{range $confidence := .Confidences -}}
 | 
			
		||||
* {{$confidence.DetectionMethod}}
 | 
			
		||||
* {{$confidence.Score}} / {{$confidence.DetectionMethod}}
 | 
			
		||||
{{end}}
 | 
			
		||||
References
 | 
			
		||||
-----------
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user