Compare commits
	
		
			18 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					91ed318c5d | ||
| 
						 | 
					bfc3828ce1 | ||
| 
						 | 
					c7eac4e7fe | ||
| 
						 | 
					cc63a0eccf | ||
| 
						 | 
					fd18df1dd4 | ||
| 
						 | 
					8775b5efdf | ||
| 
						 | 
					a9f29a6c5d | ||
| 
						 | 
					05fdde48f9 | ||
| 
						 | 
					3dfbd6b616 | ||
| 
						 | 
					04f246cf8b | ||
| 
						 | 
					7500f41655 | ||
| 
						 | 
					a1cc152e81 | ||
| 
						 | 
					1c77bc1ba3 | ||
| 
						 | 
					ec31c54caf | ||
| 
						 | 
					2f05864813 | ||
| 
						 | 
					2fbc0a001e | ||
| 
						 | 
					7d8a24ee1a | ||
| 
						 | 
					7750347010 | 
@@ -1,7 +1,6 @@
 | 
			
		||||
.dockerignore
 | 
			
		||||
Dockerfile
 | 
			
		||||
vendor/
 | 
			
		||||
cve.sqlite3*
 | 
			
		||||
oval.sqlite3*
 | 
			
		||||
*.sqlite3*
 | 
			
		||||
setup/
 | 
			
		||||
img/
 | 
			
		||||
img/
 | 
			
		||||
							
								
								
									
										30
									
								
								.github/workflows/docker-publish.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										30
									
								
								.github/workflows/docker-publish.yml
									
									
									
									
										vendored
									
									
								
							@@ -20,26 +20,48 @@ jobs:
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v1
 | 
			
		||||
 | 
			
		||||
      - name: Docker meta
 | 
			
		||||
        id: meta
 | 
			
		||||
      - name: vuls/vuls image meta
 | 
			
		||||
        id: oss-meta
 | 
			
		||||
        uses: docker/metadata-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          images: vuls/vuls
 | 
			
		||||
          tags: |
 | 
			
		||||
            type=ref,event=tag
 | 
			
		||||
 | 
			
		||||
      - name: vuls/fvuls image meta
 | 
			
		||||
        id: fvuls-meta
 | 
			
		||||
        uses: docker/metadata-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          images: vuls/fvuls
 | 
			
		||||
          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
 | 
			
		||||
      - name: OSS image build and push
 | 
			
		||||
        uses: docker/build-push-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          context: .
 | 
			
		||||
          file: ./Dockerfile
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: |
 | 
			
		||||
            vuls/vuls:latest
 | 
			
		||||
            ${{ steps.meta.outputs.tags }}
 | 
			
		||||
            ${{ steps.oss-meta.outputs.tags }}
 | 
			
		||||
          secrets: |
 | 
			
		||||
            "github_token=${{ secrets.GITHUB_TOKEN }}"
 | 
			
		||||
 | 
			
		||||
      - name: FutureVuls image build and push
 | 
			
		||||
        uses: docker/build-push-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          context: .
 | 
			
		||||
          file: ./contrib/Dockerfile
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: |
 | 
			
		||||
            vuls/fvuls:latest
 | 
			
		||||
            ${{ steps.fvuls-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.42
 | 
			
		||||
          version: v1.45
 | 
			
		||||
          args: --timeout=10m
 | 
			
		||||
          
 | 
			
		||||
          # Optional: working directory, useful for monorepos
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/goreleaser.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/goreleaser.yml
									
									
									
									
										vendored
									
									
								
							@@ -19,7 +19,7 @@ jobs:
 | 
			
		||||
        name: Set up Go
 | 
			
		||||
        uses: actions/setup-go@v2
 | 
			
		||||
        with:
 | 
			
		||||
          go-version: 1.16
 | 
			
		||||
          go-version: 1.18
 | 
			
		||||
      -
 | 
			
		||||
        name: Run GoReleaser
 | 
			
		||||
        uses: goreleaser/goreleaser-action@v2
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							@@ -11,7 +11,7 @@ jobs:
 | 
			
		||||
    - name: Set up Go 1.x
 | 
			
		||||
      uses: actions/setup-go@v2
 | 
			
		||||
      with:
 | 
			
		||||
        go-version: 1.16.x
 | 
			
		||||
        go-version: 1.18.x
 | 
			
		||||
      id: go
 | 
			
		||||
 | 
			
		||||
    - name: Check out code into the Go module directory
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,9 @@
 | 
			
		||||
name: golang-ci
 | 
			
		||||
 | 
			
		||||
run:
 | 
			
		||||
  timeout: 10m
 | 
			
		||||
  go: '1.18'
 | 
			
		||||
  
 | 
			
		||||
linters-settings:
 | 
			
		||||
  revive:
 | 
			
		||||
    # see https://github.com/mgechev/revive#available-rules for details.
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ ENV REPOSITORY github.com/future-architect/vuls
 | 
			
		||||
COPY . $GOPATH/src/$REPOSITORY
 | 
			
		||||
RUN cd $GOPATH/src/$REPOSITORY && make install
 | 
			
		||||
 | 
			
		||||
FROM alpine:3.14
 | 
			
		||||
FROM alpine:3.15
 | 
			
		||||
 | 
			
		||||
ENV LOGDIR /var/log/vuls
 | 
			
		||||
ENV WORKDIR /vuls
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								GNUmakefile
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								GNUmakefile
									
									
									
									
									
								
							@@ -23,12 +23,9 @@ CGO_UNABLED := CGO_ENABLED=0 go
 | 
			
		||||
GO_OFF := GO111MODULE=off go
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
all: b
 | 
			
		||||
all: build test
 | 
			
		||||
 | 
			
		||||
build: ./cmd/vuls/main.go pretest fmt
 | 
			
		||||
	$(GO) build -a -ldflags "$(LDFLAGS)" -o vuls ./cmd/vuls
 | 
			
		||||
 | 
			
		||||
b: ./cmd/vuls/main.go 
 | 
			
		||||
build: ./cmd/vuls/main.go
 | 
			
		||||
	$(GO) build -a -ldflags "$(LDFLAGS)" -o vuls ./cmd/vuls
 | 
			
		||||
 | 
			
		||||
install: ./cmd/vuls/main.go
 | 
			
		||||
@@ -41,13 +38,14 @@ install-scanner: ./cmd/scanner/main.go
 | 
			
		||||
	$(CGO_UNABLED) install -tags=scanner -ldflags "$(LDFLAGS)" ./cmd/scanner
 | 
			
		||||
 | 
			
		||||
lint:
 | 
			
		||||
	$(GO_OFF) get -u github.com/mgechev/revive
 | 
			
		||||
	$(GO) install github.com/mgechev/revive@latest
 | 
			
		||||
	revive -config ./.revive.toml -formatter plain $(PKGS)
 | 
			
		||||
 | 
			
		||||
vet:
 | 
			
		||||
	echo $(PKGS) | xargs env $(GO) vet || exit;
 | 
			
		||||
 | 
			
		||||
golangci:
 | 
			
		||||
	$(GO) install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
 | 
			
		||||
	golangci-lint run
 | 
			
		||||
 | 
			
		||||
fmt:
 | 
			
		||||
@@ -59,9 +57,9 @@ mlint:
 | 
			
		||||
fmtcheck:
 | 
			
		||||
	$(foreach file,$(SRCS),gofmt -s -d $(file);)
 | 
			
		||||
 | 
			
		||||
pretest: lint vet fmtcheck golangci
 | 
			
		||||
pretest: lint vet fmtcheck
 | 
			
		||||
 | 
			
		||||
test: 
 | 
			
		||||
test: pretest
 | 
			
		||||
	$(GO) test -cover -v ./... || exit;
 | 
			
		||||
 | 
			
		||||
unused:
 | 
			
		||||
@@ -76,13 +74,12 @@ clean:
 | 
			
		||||
	echo $(PKGS) | xargs go clean || exit;
 | 
			
		||||
 | 
			
		||||
# trivy-to-vuls
 | 
			
		||||
build-trivy-to-vuls: pretest fmt
 | 
			
		||||
	$(GO) build -a -ldflags "$(LDFLAGS)" -o trivy-to-vuls contrib/trivy/cmd/*.go
 | 
			
		||||
build-trivy-to-vuls: ./contrib/trivy/cmd/main.go
 | 
			
		||||
	$(GO) build -a -ldflags "$(LDFLAGS)" -o trivy-to-vuls ./contrib/trivy/cmd
 | 
			
		||||
 | 
			
		||||
# future-vuls
 | 
			
		||||
build-future-vuls: pretest fmt
 | 
			
		||||
	$(GO) build -a -ldflags "$(LDFLAGS)" -o future-vuls contrib/future-vuls/cmd/*.go
 | 
			
		||||
 | 
			
		||||
build-future-vuls: ./contrib/future-vuls/cmd/main.go
 | 
			
		||||
	$(GO) build -a -ldflags "$(LDFLAGS)" -o future-vuls ./contrib/future-vuls/cmd
 | 
			
		||||
 | 
			
		||||
# integration-test
 | 
			
		||||
BASE_DIR := '${PWD}/integration/results'
 | 
			
		||||
@@ -91,7 +88,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 := 'bundler' 'pip' 'pipenv' 'poetry' 'composer' 'npm' 'yarn' 'cargo' 'gomod' 'gobinary' 'jar' 'pom' 'nuget-lock' 'nuget-config' 'nvd_exact' 'nvd_rough' 'nvd_vendor_product' 'nvd_match_no_jvn' 'jvn_vendor_product' 'jvn_vendor_product_nover'
 | 
			
		||||
LIBS := 'bundler' 'pip' 'pipenv' 'poetry' 'composer' 'npm' 'yarn' 'cargo' 'gomod' 'gosum' 'gobinary' 'jar' 'pom' 'nuget-lock' 'nuget-config' '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
 | 
			
		||||
 
 | 
			
		||||
@@ -82,6 +82,7 @@ Vuls is a tool created to solve the problems listed above. It has the following
 | 
			
		||||
  - [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)
 | 
			
		||||
  - [gmatuz/inthewilddb](https://github.com/gmatuz/inthewilddb)
 | 
			
		||||
 | 
			
		||||
- CERT
 | 
			
		||||
  - [US-CERT](https://www.us-cert.gov/ncas/alerts)
 | 
			
		||||
 
 | 
			
		||||
@@ -136,15 +136,20 @@ func GetEOL(family, release string) (eol EOL, found bool) {
 | 
			
		||||
			"19.10": {Ended: true},
 | 
			
		||||
			"20.04": {
 | 
			
		||||
				StandardSupportUntil: time.Date(2025, 4, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
				ExtendedSupportUntil: time.Date(2030, 4, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			},
 | 
			
		||||
			"20.10": {
 | 
			
		||||
				StandardSupportUntil: time.Date(2021, 7, 22, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			},
 | 
			
		||||
			"21.04": {
 | 
			
		||||
				StandardSupportUntil: time.Date(2022, 1, 22, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
				StandardSupportUntil: time.Date(2022, 1, 20, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			},
 | 
			
		||||
			"21.10": {
 | 
			
		||||
				StandardSupportUntil: time.Date(2022, 7, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
				StandardSupportUntil: time.Date(2022, 7, 14, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			},
 | 
			
		||||
			"22.04": {
 | 
			
		||||
				StandardSupportUntil: time.Date(2027, 4, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
				ExtendedSupportUntil: time.Date(2032, 4, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			},
 | 
			
		||||
		}[release]
 | 
			
		||||
	case constant.OpenSUSE:
 | 
			
		||||
 
 | 
			
		||||
@@ -204,28 +204,12 @@ func TestEOL_IsStandardSupportEnded(t *testing.T) {
 | 
			
		||||
		},
 | 
			
		||||
		//Ubuntu
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Ubuntu 18.04 supported",
 | 
			
		||||
			fields:   fields{family: Ubuntu, release: "18.04"},
 | 
			
		||||
			name:     "Ubuntu 12.10 not found",
 | 
			
		||||
			fields:   fields{family: Ubuntu, release: "12.10"},
 | 
			
		||||
			now:      time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			found:    false,
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Ubuntu 18.04 ext supported",
 | 
			
		||||
			fields:   fields{family: Ubuntu, release: "18.04"},
 | 
			
		||||
			now:      time.Date(2025, 1, 6, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: true,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Ubuntu 16.04 supported",
 | 
			
		||||
			fields:   fields{family: Ubuntu, release: "18.04"},
 | 
			
		||||
			now:      time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Ubuntu 14.04 eol",
 | 
			
		||||
@@ -244,12 +228,44 @@ func TestEOL_IsStandardSupportEnded(t *testing.T) {
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Ubuntu 12.10 not found",
 | 
			
		||||
			fields:   fields{family: Ubuntu, release: "12.10"},
 | 
			
		||||
			name:     "Ubuntu 16.04 supported",
 | 
			
		||||
			fields:   fields{family: Ubuntu, release: "18.04"},
 | 
			
		||||
			now:      time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			found:    false,
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Ubuntu 18.04 supported",
 | 
			
		||||
			fields:   fields{family: Ubuntu, release: "18.04"},
 | 
			
		||||
			now:      time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Ubuntu 18.04 ext supported",
 | 
			
		||||
			fields:   fields{family: Ubuntu, release: "18.04"},
 | 
			
		||||
			now:      time.Date(2025, 1, 6, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: true,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Ubuntu 20.04 supported",
 | 
			
		||||
			fields:   fields{family: Ubuntu, release: "20.04"},
 | 
			
		||||
			now:      time.Date(2021, 5, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			found:    true,
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Ubuntu 20.04 ext supported",
 | 
			
		||||
			fields:   fields{family: Ubuntu, release: "20.04"},
 | 
			
		||||
			now:      time.Date(2025, 5, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			found:    true,
 | 
			
		||||
			stdEnded: true,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Ubuntu 20.10 supported",
 | 
			
		||||
@@ -267,6 +283,22 @@ func TestEOL_IsStandardSupportEnded(t *testing.T) {
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Ubuntu 21.10 supported",
 | 
			
		||||
			fields:   fields{family: Ubuntu, release: "21.10"},
 | 
			
		||||
			now:      time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			found:    true,
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Ubuntu 22.04 supported",
 | 
			
		||||
			fields:   fields{family: Ubuntu, release: "22.04"},
 | 
			
		||||
			now:      time.Date(2022, 5, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			found:    true,
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
		},
 | 
			
		||||
		//Debian
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Debian 9 supported",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								contrib/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								contrib/Dockerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
FROM golang:alpine as builder
 | 
			
		||||
 | 
			
		||||
RUN apk add --no-cache \
 | 
			
		||||
        git \
 | 
			
		||||
        make \
 | 
			
		||||
        gcc \
 | 
			
		||||
        musl-dev
 | 
			
		||||
 | 
			
		||||
ENV REPOSITORY github.com/future-architect/vuls
 | 
			
		||||
COPY . $GOPATH/src/$REPOSITORY
 | 
			
		||||
RUN cd $GOPATH/src/$REPOSITORY && \
 | 
			
		||||
        make build-scanner && mv vuls $GOPATH/bin && \
 | 
			
		||||
        make build-trivy-to-vuls && mv trivy-to-vuls $GOPATH/bin && \
 | 
			
		||||
        make build-future-vuls && mv future-vuls $GOPATH/bin
 | 
			
		||||
 | 
			
		||||
FROM alpine:3.15
 | 
			
		||||
 | 
			
		||||
ENV LOGDIR /var/log/vuls
 | 
			
		||||
ENV WORKDIR /vuls
 | 
			
		||||
 | 
			
		||||
RUN apk add --no-cache \
 | 
			
		||||
        openssh-client \
 | 
			
		||||
        ca-certificates \
 | 
			
		||||
        git \
 | 
			
		||||
        nmap \
 | 
			
		||||
    && mkdir -p $WORKDIR $LOGDIR
 | 
			
		||||
 | 
			
		||||
COPY --from=builder /go/bin/vuls /go/bin/trivy-to-vuls /go/bin/future-vuls /usr/local/bin/
 | 
			
		||||
COPY --from=aquasec/trivy:latest /usr/local/bin/trivy /usr/local/bin/trivy
 | 
			
		||||
 | 
			
		||||
VOLUME ["$WORKDIR", "$LOGDIR"]
 | 
			
		||||
WORKDIR $WORKDIR
 | 
			
		||||
ENV PWD $WORKDIR
 | 
			
		||||
@@ -2,9 +2,11 @@ package v2
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/report"
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/types"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
	"github.com/future-architect/vuls/contrib/trivy/pkg"
 | 
			
		||||
@@ -17,7 +19,7 @@ type ParserV2 struct {
 | 
			
		||||
 | 
			
		||||
// Parse trivy's JSON and convert to the Vuls struct
 | 
			
		||||
func (p ParserV2) Parse(vulnJSON []byte) (result *models.ScanResult, err error) {
 | 
			
		||||
	var report report.Report
 | 
			
		||||
	var report types.Report
 | 
			
		||||
	if err = json.Unmarshal(vulnJSON, &report); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
@@ -27,34 +29,34 @@ func (p ParserV2) Parse(vulnJSON []byte) (result *models.ScanResult, err error)
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	setScanResultMeta(scanResult, &report)
 | 
			
		||||
	if err := setScanResultMeta(scanResult, &report); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return scanResult, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setScanResultMeta(scanResult *models.ScanResult, report *report.Report) {
 | 
			
		||||
	for _, r := range report.Results {
 | 
			
		||||
		const trivyTarget = "trivy-target"
 | 
			
		||||
		if pkg.IsTrivySupportedOS(r.Type) {
 | 
			
		||||
			scanResult.Family = r.Type
 | 
			
		||||
			scanResult.ServerName = r.Target
 | 
			
		||||
			scanResult.Optional = map[string]interface{}{
 | 
			
		||||
				trivyTarget: r.Target,
 | 
			
		||||
			}
 | 
			
		||||
		} else if pkg.IsTrivySupportedLib(r.Type) {
 | 
			
		||||
			if scanResult.Family == "" {
 | 
			
		||||
				scanResult.Family = constant.ServerTypePseudo
 | 
			
		||||
			}
 | 
			
		||||
			if scanResult.ServerName == "" {
 | 
			
		||||
				scanResult.ServerName = "library scan by trivy"
 | 
			
		||||
			}
 | 
			
		||||
			if _, ok := scanResult.Optional[trivyTarget]; !ok {
 | 
			
		||||
				scanResult.Optional = map[string]interface{}{
 | 
			
		||||
					trivyTarget: r.Target,
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		scanResult.ScannedAt = time.Now()
 | 
			
		||||
		scanResult.ScannedBy = "trivy"
 | 
			
		||||
		scanResult.ScannedVia = "trivy"
 | 
			
		||||
var dockerTagPattern = regexp.MustCompile(`:.+$`)
 | 
			
		||||
 | 
			
		||||
func setScanResultMeta(scanResult *models.ScanResult, report *types.Report) error {
 | 
			
		||||
	if len(report.Results) == 0 {
 | 
			
		||||
		return xerrors.Errorf("scanned images or libraries are not supported by Trivy. see https://aquasecurity.github.io/trivy/dev/vulnerability/detection/os/, https://aquasecurity.github.io/trivy/dev/vulnerability/detection/language/")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	scanResult.ServerName = report.ArtifactName
 | 
			
		||||
	if report.ArtifactType == "container_image" && !dockerTagPattern.MatchString(scanResult.ServerName) {
 | 
			
		||||
		scanResult.ServerName += ":latest" // Complement if the tag is omitted
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if report.Metadata.OS != nil {
 | 
			
		||||
		scanResult.Family = report.Metadata.OS.Family
 | 
			
		||||
		scanResult.Release = report.Metadata.OS.Name
 | 
			
		||||
	} else {
 | 
			
		||||
		scanResult.Family = constant.ServerTypePseudo
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	scanResult.ScannedAt = time.Now()
 | 
			
		||||
	scanResult.ScannedBy = "trivy"
 | 
			
		||||
	scanResult.ScannedVia = "trivy"
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/d4l3k/messagediff"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
)
 | 
			
		||||
@@ -202,8 +203,9 @@ var redisTrivy = []byte(`
 | 
			
		||||
`)
 | 
			
		||||
var redisSR = &models.ScanResult{
 | 
			
		||||
	JSONVersion: 4,
 | 
			
		||||
	ServerName:  "redis (debian 10.10)",
 | 
			
		||||
	ServerName:  "redis:latest",
 | 
			
		||||
	Family:      "debian",
 | 
			
		||||
	Release:     "10.10",
 | 
			
		||||
	ScannedBy:   "trivy",
 | 
			
		||||
	ScannedVia:  "trivy",
 | 
			
		||||
	ScannedCves: models.VulnInfos{
 | 
			
		||||
@@ -261,9 +263,7 @@ var redisSR = &models.ScanResult{
 | 
			
		||||
			BinaryNames: []string{"bsdutils", "pkgA"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	Optional: map[string]interface{}{
 | 
			
		||||
		"trivy-target": "redis (debian 10.10)",
 | 
			
		||||
	},
 | 
			
		||||
	Optional: nil,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var strutsTrivy = []byte(`
 | 
			
		||||
@@ -372,7 +372,7 @@ var strutsTrivy = []byte(`
 | 
			
		||||
 | 
			
		||||
var strutsSR = &models.ScanResult{
 | 
			
		||||
	JSONVersion: 4,
 | 
			
		||||
	ServerName:  "library scan by trivy",
 | 
			
		||||
	ServerName:  "/data/struts-1.2.7/lib",
 | 
			
		||||
	Family:      "pseudo",
 | 
			
		||||
	ScannedBy:   "trivy",
 | 
			
		||||
	ScannedVia:  "trivy",
 | 
			
		||||
@@ -458,9 +458,7 @@ var strutsSR = &models.ScanResult{
 | 
			
		||||
	},
 | 
			
		||||
	Packages:    models.Packages{},
 | 
			
		||||
	SrcPackages: models.SrcPackages{},
 | 
			
		||||
	Optional: map[string]interface{}{
 | 
			
		||||
		"trivy-target": "Java",
 | 
			
		||||
	},
 | 
			
		||||
	Optional:    nil,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var osAndLibTrivy = []byte(`
 | 
			
		||||
@@ -632,8 +630,9 @@ var osAndLibTrivy = []byte(`
 | 
			
		||||
 | 
			
		||||
var osAndLibSR = &models.ScanResult{
 | 
			
		||||
	JSONVersion: 4,
 | 
			
		||||
	ServerName:  "quay.io/fluentd_elasticsearch/fluentd:v2.9.0 (debian 10.2)",
 | 
			
		||||
	ServerName:  "quay.io/fluentd_elasticsearch/fluentd:v2.9.0",
 | 
			
		||||
	Family:      "debian",
 | 
			
		||||
	Release:     "10.2",
 | 
			
		||||
	ScannedBy:   "trivy",
 | 
			
		||||
	ScannedVia:  "trivy",
 | 
			
		||||
	ScannedCves: models.VulnInfos{
 | 
			
		||||
@@ -719,7 +718,82 @@ var osAndLibSR = &models.ScanResult{
 | 
			
		||||
			BinaryNames: []string{"libgnutls30"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	Optional: map[string]interface{}{
 | 
			
		||||
		"trivy-target": "quay.io/fluentd_elasticsearch/fluentd:v2.9.0 (debian 10.2)",
 | 
			
		||||
	},
 | 
			
		||||
	Optional: nil,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParseError(t *testing.T) {
 | 
			
		||||
	cases := map[string]struct {
 | 
			
		||||
		vulnJSON []byte
 | 
			
		||||
		expected error
 | 
			
		||||
	}{
 | 
			
		||||
		"image hello-world": {
 | 
			
		||||
			vulnJSON: helloWorldTrivy,
 | 
			
		||||
			expected: xerrors.Errorf("scanned images or libraries are not supported by Trivy. see https://aquasecurity.github.io/trivy/dev/vulnerability/detection/os/, https://aquasecurity.github.io/trivy/dev/vulnerability/detection/language/"),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for testcase, v := range cases {
 | 
			
		||||
		_, err := ParserV2{}.Parse(v.vulnJSON)
 | 
			
		||||
 | 
			
		||||
		diff, equal := messagediff.PrettyDiff(
 | 
			
		||||
			v.expected,
 | 
			
		||||
			err,
 | 
			
		||||
			messagediff.IgnoreStructField("frame"),
 | 
			
		||||
		)
 | 
			
		||||
		if !equal {
 | 
			
		||||
			t.Errorf("test: %s, diff %s", testcase, diff)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var helloWorldTrivy = []byte(`
 | 
			
		||||
{
 | 
			
		||||
  "SchemaVersion": 2,
 | 
			
		||||
  "ArtifactName": "hello-world:latest",
 | 
			
		||||
  "ArtifactType": "container_image",
 | 
			
		||||
  "Metadata": {
 | 
			
		||||
    "ImageID": "sha256:feb5d9fea6a5e9606aa995e879d862b825965ba48de054caab5ef356dc6b3412",
 | 
			
		||||
    "DiffIDs": [
 | 
			
		||||
      "sha256:e07ee1baac5fae6a26f30cabfe54a36d3402f96afda318fe0a96cec4ca393359"
 | 
			
		||||
    ],
 | 
			
		||||
    "RepoTags": [
 | 
			
		||||
      "hello-world:latest"
 | 
			
		||||
    ],
 | 
			
		||||
    "RepoDigests": [
 | 
			
		||||
      "hello-world@sha256:97a379f4f88575512824f3b352bc03cd75e239179eea0fecc38e597b2209f49a"
 | 
			
		||||
    ],
 | 
			
		||||
    "ImageConfig": {
 | 
			
		||||
      "architecture": "amd64",
 | 
			
		||||
      "container": "8746661ca3c2f215da94e6d3f7dfdcafaff5ec0b21c9aff6af3dc379a82fbc72",
 | 
			
		||||
      "created": "2021-09-23T23:47:57.442225064Z",
 | 
			
		||||
      "docker_version": "20.10.7",
 | 
			
		||||
      "history": [
 | 
			
		||||
        {
 | 
			
		||||
          "created": "2021-09-23T23:47:57Z",
 | 
			
		||||
          "created_by": "/bin/sh -c #(nop) COPY file:50563a97010fd7ce1ceebd1fa4f4891ac3decdf428333fb2683696f4358af6c2 in / "
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "created": "2021-09-23T23:47:57Z",
 | 
			
		||||
          "created_by": "/bin/sh -c #(nop)  CMD [\"/hello\"]",
 | 
			
		||||
          "empty_layer": true
 | 
			
		||||
        }
 | 
			
		||||
      ],
 | 
			
		||||
      "os": "linux",
 | 
			
		||||
      "rootfs": {
 | 
			
		||||
        "type": "layers",
 | 
			
		||||
        "diff_ids": [
 | 
			
		||||
          "sha256:e07ee1baac5fae6a26f30cabfe54a36d3402f96afda318fe0a96cec4ca393359"
 | 
			
		||||
        ]
 | 
			
		||||
      },
 | 
			
		||||
      "config": {
 | 
			
		||||
        "Cmd": [
 | 
			
		||||
          "/hello"
 | 
			
		||||
        ],
 | 
			
		||||
        "Env": [
 | 
			
		||||
          "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
 | 
			
		||||
        ],
 | 
			
		||||
        "Image": "sha256:b9935d4e8431fb1a7f0989304ec86b3329a99a25f5efdc7f09f3f8c41434ca6d"
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}`)
 | 
			
		||||
 
 | 
			
		||||
@@ -4,16 +4,14 @@ import (
 | 
			
		||||
	"sort"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	ftypes "github.com/aquasecurity/fanal/types"
 | 
			
		||||
 | 
			
		||||
	"github.com/aquasecurity/fanal/analyzer/os"
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/report"
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/types"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Convert :
 | 
			
		||||
func Convert(results report.Results) (result *models.ScanResult, err error) {
 | 
			
		||||
func Convert(results types.Results) (result *models.ScanResult, err error) {
 | 
			
		||||
	scanResult := &models.ScanResult{
 | 
			
		||||
		JSONVersion: models.JSONVersion,
 | 
			
		||||
		ScannedCves: models.VulnInfos{},
 | 
			
		||||
@@ -79,8 +77,8 @@ func Convert(results report.Results) (result *models.ScanResult, err error) {
 | 
			
		||||
					LastModified:  lastModified,
 | 
			
		||||
				}},
 | 
			
		||||
			}
 | 
			
		||||
			// do onlyIif image type is Vuln
 | 
			
		||||
			if IsTrivySupportedOS(trivyResult.Type) {
 | 
			
		||||
			// do only if image type is Vuln
 | 
			
		||||
			if isTrivySupportedOS(trivyResult.Type) {
 | 
			
		||||
				pkgs[vuln.PkgName] = models.Package{
 | 
			
		||||
					Name:    vuln.PkgName,
 | 
			
		||||
					Version: vuln.InstalledVersion,
 | 
			
		||||
@@ -111,7 +109,7 @@ func Convert(results report.Results) (result *models.ScanResult, err error) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// --list-all-pkgs flg of trivy will output all installed packages, so collect them.
 | 
			
		||||
		if trivyResult.Class == report.ClassOSPkg {
 | 
			
		||||
		if trivyResult.Class == types.ClassOSPkg {
 | 
			
		||||
			for _, p := range trivyResult.Packages {
 | 
			
		||||
				pkgs[p.Name] = models.Package{
 | 
			
		||||
					Name:    p.Name,
 | 
			
		||||
@@ -130,7 +128,7 @@ func Convert(results report.Results) (result *models.ScanResult, err error) {
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		} else if trivyResult.Class == report.ClassLangPkg {
 | 
			
		||||
		} else if trivyResult.Class == types.ClassLangPkg {
 | 
			
		||||
			libScanner := uniqueLibraryScannerPaths[trivyResult.Target]
 | 
			
		||||
			libScanner.Type = trivyResult.Type
 | 
			
		||||
			for _, p := range trivyResult.Packages {
 | 
			
		||||
@@ -178,51 +176,25 @@ func Convert(results report.Results) (result *models.ScanResult, err error) {
 | 
			
		||||
	return scanResult, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsTrivySupportedOS :
 | 
			
		||||
func IsTrivySupportedOS(family string) bool {
 | 
			
		||||
	supportedFamilies := map[string]interface{}{
 | 
			
		||||
		os.RedHat:             struct{}{},
 | 
			
		||||
		os.Debian:             struct{}{},
 | 
			
		||||
		os.Ubuntu:             struct{}{},
 | 
			
		||||
		os.CentOS:             struct{}{},
 | 
			
		||||
		os.Rocky:              struct{}{},
 | 
			
		||||
		os.Alma:               struct{}{},
 | 
			
		||||
		os.Fedora:             struct{}{},
 | 
			
		||||
		os.Amazon:             struct{}{},
 | 
			
		||||
		os.Oracle:             struct{}{},
 | 
			
		||||
		os.Windows:            struct{}{},
 | 
			
		||||
		os.OpenSUSE:           struct{}{},
 | 
			
		||||
		os.OpenSUSELeap:       struct{}{},
 | 
			
		||||
		os.OpenSUSETumbleweed: struct{}{},
 | 
			
		||||
		os.SLES:               struct{}{},
 | 
			
		||||
		os.Photon:             struct{}{},
 | 
			
		||||
		os.Alpine:             struct{}{},
 | 
			
		||||
		// os.Fedora:             struct{}{}, not supported yet
 | 
			
		||||
func isTrivySupportedOS(family string) bool {
 | 
			
		||||
	supportedFamilies := map[string]struct{}{
 | 
			
		||||
		os.RedHat:             {},
 | 
			
		||||
		os.Debian:             {},
 | 
			
		||||
		os.Ubuntu:             {},
 | 
			
		||||
		os.CentOS:             {},
 | 
			
		||||
		os.Rocky:              {},
 | 
			
		||||
		os.Alma:               {},
 | 
			
		||||
		os.Fedora:             {},
 | 
			
		||||
		os.Amazon:             {},
 | 
			
		||||
		os.Oracle:             {},
 | 
			
		||||
		os.Windows:            {},
 | 
			
		||||
		os.OpenSUSE:           {},
 | 
			
		||||
		os.OpenSUSELeap:       {},
 | 
			
		||||
		os.OpenSUSETumbleweed: {},
 | 
			
		||||
		os.SLES:               {},
 | 
			
		||||
		os.Photon:             {},
 | 
			
		||||
		os.Alpine:             {},
 | 
			
		||||
	}
 | 
			
		||||
	_, ok := supportedFamilies[family]
 | 
			
		||||
	return ok
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsTrivySupportedLib :
 | 
			
		||||
func IsTrivySupportedLib(typestr string) bool {
 | 
			
		||||
	supportedLibs := map[string]interface{}{
 | 
			
		||||
		ftypes.Bundler:   struct{}{},
 | 
			
		||||
		ftypes.GemSpec:   struct{}{},
 | 
			
		||||
		ftypes.Cargo:     struct{}{},
 | 
			
		||||
		ftypes.Composer:  struct{}{},
 | 
			
		||||
		ftypes.Npm:       struct{}{},
 | 
			
		||||
		ftypes.NuGet:     struct{}{},
 | 
			
		||||
		ftypes.Pip:       struct{}{},
 | 
			
		||||
		ftypes.Pipenv:    struct{}{},
 | 
			
		||||
		ftypes.Poetry:    struct{}{},
 | 
			
		||||
		ftypes.PythonPkg: struct{}{},
 | 
			
		||||
		ftypes.NodePkg:   struct{}{},
 | 
			
		||||
		ftypes.Yarn:      struct{}{},
 | 
			
		||||
		ftypes.Jar:       struct{}{},
 | 
			
		||||
		ftypes.Pom:       struct{}{},
 | 
			
		||||
		ftypes.GoBinary:  struct{}{},
 | 
			
		||||
		ftypes.GoMod:     struct{}{},
 | 
			
		||||
	}
 | 
			
		||||
	_, ok := supportedLibs[typestr]
 | 
			
		||||
	return ok
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -22,40 +22,27 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type goCveDictClient struct {
 | 
			
		||||
	cnf    config.VulnDictInterface
 | 
			
		||||
	driver cvedb.DB
 | 
			
		||||
	driver  cvedb.DB
 | 
			
		||||
	baseURL string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newGoCveDictClient(cnf config.VulnDictInterface, o logging.LogOpts) (*goCveDictClient, error) {
 | 
			
		||||
	if err := cvelog.SetLogger(o.LogToFile, o.LogDir, o.Debug, o.LogJSON); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to set go-cve-dictionary logger. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	driver, locked, err := newCveDB(cnf)
 | 
			
		||||
	if locked {
 | 
			
		||||
		return nil, xerrors.Errorf("SQLite3 is locked: %s", cnf.GetSQLite3Path())
 | 
			
		||||
	} else if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	driver, err := newCveDB(cnf)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to newCveDB. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	return &goCveDictClient{cnf: cnf, driver: driver}, nil
 | 
			
		||||
	return &goCveDictClient{driver: driver, baseURL: cnf.GetURL()}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api goCveDictClient) closeDB() error {
 | 
			
		||||
	if api.driver == nil {
 | 
			
		||||
func (client goCveDictClient) closeDB() error {
 | 
			
		||||
	if client.driver == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return api.driver.CloseDB()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api goCveDictClient) fetchCveDetails(cveIDs []string) (cveDetails []cvemodels.CveDetail, err error) {
 | 
			
		||||
	m, err := api.driver.GetMulti(cveIDs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to GetMulti. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	for _, v := range m {
 | 
			
		||||
		cveDetails = append(cveDetails, v)
 | 
			
		||||
	}
 | 
			
		||||
	return cveDetails, nil
 | 
			
		||||
	return client.driver.CloseDB()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type response struct {
 | 
			
		||||
@@ -63,57 +50,67 @@ type response struct {
 | 
			
		||||
	CveDetail cvemodels.CveDetail
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api goCveDictClient) fetchCveDetailsViaHTTP(cveIDs []string) (cveDetails []cvemodels.CveDetail, err error) {
 | 
			
		||||
	reqChan := make(chan string, len(cveIDs))
 | 
			
		||||
	resChan := make(chan response, len(cveIDs))
 | 
			
		||||
	errChan := make(chan error, len(cveIDs))
 | 
			
		||||
	defer close(reqChan)
 | 
			
		||||
	defer close(resChan)
 | 
			
		||||
	defer close(errChan)
 | 
			
		||||
func (client goCveDictClient) fetchCveDetails(cveIDs []string) (cveDetails []cvemodels.CveDetail, err error) {
 | 
			
		||||
	if client.driver == nil {
 | 
			
		||||
		reqChan := make(chan string, len(cveIDs))
 | 
			
		||||
		resChan := make(chan response, len(cveIDs))
 | 
			
		||||
		errChan := make(chan error, len(cveIDs))
 | 
			
		||||
		defer close(reqChan)
 | 
			
		||||
		defer close(resChan)
 | 
			
		||||
		defer close(errChan)
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		for _, cveID := range cveIDs {
 | 
			
		||||
			reqChan <- cveID
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
		go func() {
 | 
			
		||||
			for _, cveID := range cveIDs {
 | 
			
		||||
				reqChan <- cveID
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
	concurrency := 10
 | 
			
		||||
	tasks := util.GenWorkers(concurrency)
 | 
			
		||||
	for range cveIDs {
 | 
			
		||||
		tasks <- func() {
 | 
			
		||||
			select {
 | 
			
		||||
			case cveID := <-reqChan:
 | 
			
		||||
				url, err := util.URLPathJoin(api.cnf.GetURL(), "cves", cveID)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					errChan <- err
 | 
			
		||||
				} else {
 | 
			
		||||
					logging.Log.Debugf("HTTP Request to %s", url)
 | 
			
		||||
					api.httpGet(cveID, url, resChan, errChan)
 | 
			
		||||
		concurrency := 10
 | 
			
		||||
		tasks := util.GenWorkers(concurrency)
 | 
			
		||||
		for range cveIDs {
 | 
			
		||||
			tasks <- func() {
 | 
			
		||||
				select {
 | 
			
		||||
				case cveID := <-reqChan:
 | 
			
		||||
					url, err := util.URLPathJoin(client.baseURL, "cves", cveID)
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						errChan <- err
 | 
			
		||||
					} else {
 | 
			
		||||
						logging.Log.Debugf("HTTP Request to %s", url)
 | 
			
		||||
						httpGet(cveID, url, resChan, errChan)
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	timeout := time.After(2 * 60 * time.Second)
 | 
			
		||||
	var errs []error
 | 
			
		||||
	for range cveIDs {
 | 
			
		||||
		select {
 | 
			
		||||
		case res := <-resChan:
 | 
			
		||||
			cveDetails = append(cveDetails, res.CveDetail)
 | 
			
		||||
		case err := <-errChan:
 | 
			
		||||
			errs = append(errs, err)
 | 
			
		||||
		case <-timeout:
 | 
			
		||||
			return nil, xerrors.New("Timeout Fetching CVE")
 | 
			
		||||
		timeout := time.After(2 * 60 * time.Second)
 | 
			
		||||
		var errs []error
 | 
			
		||||
		for range cveIDs {
 | 
			
		||||
			select {
 | 
			
		||||
			case res := <-resChan:
 | 
			
		||||
				cveDetails = append(cveDetails, res.CveDetail)
 | 
			
		||||
			case err := <-errChan:
 | 
			
		||||
				errs = append(errs, err)
 | 
			
		||||
			case <-timeout:
 | 
			
		||||
				return nil, xerrors.New("Timeout Fetching CVE")
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if len(errs) != 0 {
 | 
			
		||||
			return nil,
 | 
			
		||||
				xerrors.Errorf("Failed to fetch CVE. err: %w", errs)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		m, err := client.driver.GetMulti(cveIDs)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to GetMulti. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		for _, v := range m {
 | 
			
		||||
			cveDetails = append(cveDetails, v)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(errs) != 0 {
 | 
			
		||||
		return nil,
 | 
			
		||||
			xerrors.Errorf("Failed to fetch CVE. err: %w", errs)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
	return cveDetails, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api goCveDictClient) httpGet(key, url string, resChan chan<- response, errChan chan<- error) {
 | 
			
		||||
func httpGet(key, url string, resChan chan<- response, errChan chan<- error) {
 | 
			
		||||
	var body string
 | 
			
		||||
	var errs []error
 | 
			
		||||
	var resp *http.Response
 | 
			
		||||
@@ -144,21 +141,21 @@ func (api goCveDictClient) httpGet(key, url string, resChan chan<- response, err
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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")
 | 
			
		||||
func (client goCveDictClient) detectCveByCpeURI(cpeURI string, useJVN bool) (cves []cvemodels.CveDetail, err error) {
 | 
			
		||||
	if client.driver == nil {
 | 
			
		||||
		url, err := util.URLPathJoin(client.baseURL, "cpes")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to join URLPath. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		query := map[string]string{"name": cpeURI}
 | 
			
		||||
		logging.Log.Debugf("HTTP Request to %s, query: %#v", url, query)
 | 
			
		||||
		if cves, err = api.httpPost(url, query); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		if cves, err = httpPost(url, query); err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to post HTTP Request. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if cves, err = api.driver.GetByCpeURI(cpeURI); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		if cves, err = client.driver.GetByCpeURI(cpeURI); err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to get CVEs by CPEURI. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -177,7 +174,7 @@ func (api goCveDictClient) detectCveByCpeURI(cpeURI string, useJVN bool) (cves [
 | 
			
		||||
	return nvdCves, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api goCveDictClient) httpPost(url string, query map[string]string) ([]cvemodels.CveDetail, error) {
 | 
			
		||||
func httpPost(url string, query map[string]string) ([]cvemodels.CveDetail, error) {
 | 
			
		||||
	var body string
 | 
			
		||||
	var errs []error
 | 
			
		||||
	var resp *http.Response
 | 
			
		||||
@@ -208,18 +205,20 @@ func (api goCveDictClient) httpPost(url string, query map[string]string) ([]cvem
 | 
			
		||||
	return cveDetails, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newCveDB(cnf config.VulnDictInterface) (driver cvedb.DB, locked bool, err error) {
 | 
			
		||||
func newCveDB(cnf config.VulnDictInterface) (cvedb.DB, error) {
 | 
			
		||||
	if cnf.IsFetchViaHTTP() {
 | 
			
		||||
		return nil, false, nil
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
	path := cnf.GetURL()
 | 
			
		||||
	if cnf.GetType() == "sqlite3" {
 | 
			
		||||
		path = cnf.GetSQLite3Path()
 | 
			
		||||
	}
 | 
			
		||||
	driver, locked, err = cvedb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), cvedb.Option{})
 | 
			
		||||
	driver, locked, err := cvedb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), cvedb.Option{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		err = xerrors.Errorf("Failed to init CVE DB. err: %w, path: %s", err, path)
 | 
			
		||||
		return nil, locked, err
 | 
			
		||||
		if locked {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to init CVE DB. SQLite3: %s is locked. err: %w", cnf.GetSQLite3Path(), err)
 | 
			
		||||
		}
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to init CVE DB. DB Path: %s, err: %w", path, err)
 | 
			
		||||
	}
 | 
			
		||||
	return driver, false, nil
 | 
			
		||||
	return driver, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,8 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
	"github.com/future-architect/vuls/contrib/owasp-dependency-check/parser"
 | 
			
		||||
@@ -19,7 +21,6 @@ import (
 | 
			
		||||
	"github.com/future-architect/vuls/reporter"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	cvemodels "github.com/vulsio/go-cve-dictionary/models"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Cpe :
 | 
			
		||||
@@ -47,7 +48,7 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to fill with Library dependency: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := DetectPkgCves(&r, config.Conf.OvalDict, config.Conf.Gost); err != nil {
 | 
			
		||||
		if err := DetectPkgCves(&r, config.Conf.OvalDict, config.Conf.Gost, config.Conf.LogOpts); err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to detect Pkg CVE: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -91,7 +92,7 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to detect WordPress Cves: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := gost.FillCVEsWithRedHat(&r, config.Conf.Gost); err != nil {
 | 
			
		||||
		if err := gost.FillCVEsWithRedHat(&r, config.Conf.Gost, config.Conf.LogOpts); err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to fill with gost: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -99,19 +100,19 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to fill with CVE: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		nExploitCve, err := FillWithExploit(&r, config.Conf.Exploit)
 | 
			
		||||
		nExploitCve, err := FillWithExploit(&r, config.Conf.Exploit, config.Conf.LogOpts)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to fill with exploit: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		logging.Log.Infof("%s: %d PoC are detected", r.FormatServerName(), nExploitCve)
 | 
			
		||||
 | 
			
		||||
		nMetasploitCve, err := FillWithMetasploit(&r, config.Conf.Metasploit)
 | 
			
		||||
		nMetasploitCve, err := FillWithMetasploit(&r, config.Conf.Metasploit, config.Conf.LogOpts)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to fill with metasploit: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		logging.Log.Infof("%s: %d exploits are detected", r.FormatServerName(), nMetasploitCve)
 | 
			
		||||
 | 
			
		||||
		if err := FillWithKEVuln(&r, config.Conf.KEVuln); err != nil {
 | 
			
		||||
		if err := FillWithKEVuln(&r, config.Conf.KEVuln, config.Conf.LogOpts); err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to fill with Known Exploited Vulnerabilities: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -143,7 +144,7 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
 | 
			
		||||
	if config.Conf.DiffPlus || config.Conf.DiffMinus {
 | 
			
		||||
		prevs, err := loadPrevious(rs, config.Conf.ResultsDir)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to load previous results. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		rs = diff(rs, prevs, config.Conf.DiffPlus, config.Conf.DiffMinus)
 | 
			
		||||
	}
 | 
			
		||||
@@ -205,33 +206,23 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
 | 
			
		||||
 | 
			
		||||
// DetectPkgCves detects OS pkg cves
 | 
			
		||||
// pass 2 configs
 | 
			
		||||
func DetectPkgCves(r *models.ScanResult, ovalCnf config.GovalDictConf, gostCnf config.GostConf) error {
 | 
			
		||||
func DetectPkgCves(r *models.ScanResult, ovalCnf config.GovalDictConf, gostCnf config.GostConf, logOpts logging.LogOpts) error {
 | 
			
		||||
	// Pkg Scan
 | 
			
		||||
	if r.Release != "" {
 | 
			
		||||
		if len(r.Packages)+len(r.SrcPackages) > 0 {
 | 
			
		||||
			// 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)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// gost
 | 
			
		||||
			if err := detectPkgsCvesWithGost(gostCnf, r); err != nil {
 | 
			
		||||
				return xerrors.Errorf("Failed to detect CVE with gost: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			logging.Log.Infof("Number of packages is 0. Skip OVAL and gost detection")
 | 
			
		||||
	if isPkgCvesDetactable(r) {
 | 
			
		||||
		// 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, logOpts); err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to detect CVE with OVAL: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// gost
 | 
			
		||||
		if err := detectPkgsCvesWithGost(gostCnf, r, logOpts); err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to detect CVE with gost: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	} else if reuseScannedCves(r) {
 | 
			
		||||
		logging.Log.Infof("r.Release is empty. Use CVEs as it as.")
 | 
			
		||||
	} else if r.Family == constant.ServerTypePseudo {
 | 
			
		||||
		logging.Log.Infof("pseudo type. Skip OVAL and gost detection")
 | 
			
		||||
	} else {
 | 
			
		||||
		logging.Log.Infof("r.Release is empty. detect as pseudo type. Skip OVAL and gost detection")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, v := range r.ScannedCves {
 | 
			
		||||
@@ -264,6 +255,31 @@ func DetectPkgCves(r *models.ScanResult, ovalCnf config.GovalDictConf, gostCnf c
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// isPkgCvesDetactable checks whether CVEs is detactable with gost and oval from the result
 | 
			
		||||
func isPkgCvesDetactable(r *models.ScanResult) bool {
 | 
			
		||||
	if r.Release == "" {
 | 
			
		||||
		logging.Log.Infof("r.Release is empty. Skip OVAL and gost detection")
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if r.ScannedBy == "trivy" {
 | 
			
		||||
		logging.Log.Infof("r.ScannedBy is trivy. Skip OVAL and gost detection")
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch r.Family {
 | 
			
		||||
	case constant.FreeBSD, constant.ServerTypePseudo:
 | 
			
		||||
		logging.Log.Infof("%s type. Skip OVAL and gost detection", r.Family)
 | 
			
		||||
		return false
 | 
			
		||||
	default:
 | 
			
		||||
		if len(r.Packages)+len(r.SrcPackages) == 0 {
 | 
			
		||||
			logging.Log.Infof("Number of packages is 0. Skip OVAL and gost detection")
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DetectGitHubCves fetches CVEs from GitHub Security Alerts
 | 
			
		||||
func DetectGitHubCves(r *models.ScanResult, githubConfs map[string]config.GitHubConf) error {
 | 
			
		||||
	if len(githubConfs) == 0 {
 | 
			
		||||
@@ -308,7 +324,7 @@ func FillCvesWithNvdJvn(r *models.ScanResult, cnf config.GoCveDictConf, logOpts
 | 
			
		||||
 | 
			
		||||
	client, err := newGoCveDictClient(&cnf, logOpts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
		return xerrors.Errorf("Failed to newGoCveDictClient. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if err := client.closeDB(); err != nil {
 | 
			
		||||
@@ -316,14 +332,9 @@ func FillCvesWithNvdJvn(r *models.ScanResult, cnf config.GoCveDictConf, logOpts
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	var ds []cvemodels.CveDetail
 | 
			
		||||
	if cnf.IsFetchViaHTTP() {
 | 
			
		||||
		ds, err = client.fetchCveDetailsViaHTTP(cveIDs)
 | 
			
		||||
	} else {
 | 
			
		||||
		ds, err = client.fetchCveDetails(cveIDs)
 | 
			
		||||
	}
 | 
			
		||||
	ds, err := client.fetchCveDetails(cveIDs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
		return xerrors.Errorf("Failed to fetchCveDetails. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, d := range ds {
 | 
			
		||||
@@ -391,37 +402,43 @@ func fillCertAlerts(cvedetail *cvemodels.CveDetail) (dict models.AlertDict) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// detectPkgsCvesWithOval fetches OVAL database
 | 
			
		||||
func detectPkgsCvesWithOval(cnf config.GovalDictConf, r *models.ScanResult) error {
 | 
			
		||||
	ovalClient, err := oval.NewOVALClient(r.Family, cnf)
 | 
			
		||||
func detectPkgsCvesWithOval(cnf config.GovalDictConf, r *models.ScanResult, logOpts logging.LogOpts) error {
 | 
			
		||||
	client, err := oval.NewOVALClient(r.Family, cnf, logOpts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if ovalClient == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if err := client.CloseDB(); err != nil {
 | 
			
		||||
			logging.Log.Errorf("Failed to close the OVAL DB. err: %+v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	logging.Log.Debugf("Check if oval fetched: %s %s", r.Family, r.Release)
 | 
			
		||||
	ok, err := ovalClient.CheckIfOvalFetched(r.Family, r.Release)
 | 
			
		||||
	ok, err := client.CheckIfOvalFetched(r.Family, r.Release)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if !ok {
 | 
			
		||||
		if r.Family == constant.Debian {
 | 
			
		||||
		switch r.Family {
 | 
			
		||||
		case 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
 | 
			
		||||
		case constant.Windows, constant.FreeBSD, constant.ServerTypePseudo:
 | 
			
		||||
			return nil
 | 
			
		||||
		default:
 | 
			
		||||
			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)
 | 
			
		||||
		}
 | 
			
		||||
		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)
 | 
			
		||||
	_, err = ovalClient.CheckIfOvalFresh(r.Family, r.Release)
 | 
			
		||||
	_, err = client.CheckIfOvalFresh(r.Family, r.Release)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logging.Log.Debugf("Fill with oval: %s %s", r.Family, r.Release)
 | 
			
		||||
	nCVEs, err := ovalClient.FillWithOval(r)
 | 
			
		||||
	nCVEs, err := client.FillWithOval(r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
@@ -430,12 +447,11 @@ func detectPkgsCvesWithOval(cnf config.GovalDictConf, r *models.ScanResult) erro
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func detectPkgsCvesWithGost(cnf config.GostConf, r *models.ScanResult) error {
 | 
			
		||||
	client, err := gost.NewClient(cnf, r.Family)
 | 
			
		||||
func detectPkgsCvesWithGost(cnf config.GostConf, r *models.ScanResult, logOpts logging.LogOpts) error {
 | 
			
		||||
	client, err := gost.NewGostClient(cnf, r.Family, logOpts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to new a gost client: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if err := client.CloseDB(); err != nil {
 | 
			
		||||
			logging.Log.Errorf("Failed to close the gost DB. err: %+v", err)
 | 
			
		||||
@@ -464,7 +480,7 @@ func detectPkgsCvesWithGost(cnf config.GostConf, r *models.ScanResult) 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
 | 
			
		||||
		return xerrors.Errorf("Failed to newGoCveDictClient. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if err := client.closeDB(); err != nil {
 | 
			
		||||
@@ -476,7 +492,7 @@ func DetectCpeURIsCves(r *models.ScanResult, cpes []Cpe, cnf config.GoCveDictCon
 | 
			
		||||
	for _, cpe := range cpes {
 | 
			
		||||
		details, err := client.detectCveByCpeURI(cpe.CpeURI, cpe.UseJVN)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
			return xerrors.Errorf("Failed to detectCveByCpeURI. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, detail := range details {
 | 
			
		||||
 
 | 
			
		||||
@@ -9,33 +9,73 @@ import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cenkalti/backoff"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	exploitdb "github.com/vulsio/go-exploitdb/db"
 | 
			
		||||
	exploitmodels "github.com/vulsio/go-exploitdb/models"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
	exploitlog "github.com/vulsio/go-exploitdb/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// goExploitDBClient is a DB Driver
 | 
			
		||||
type goExploitDBClient struct {
 | 
			
		||||
	driver  exploitdb.DB
 | 
			
		||||
	baseURL string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// closeDB close a DB connection
 | 
			
		||||
func (client goExploitDBClient) closeDB() error {
 | 
			
		||||
	if client.driver == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return client.driver.CloseDB()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newGoExploitDBClient(cnf config.VulnDictInterface, o logging.LogOpts) (*goExploitDBClient, error) {
 | 
			
		||||
	if err := exploitlog.SetLogger(o.LogToFile, o.LogDir, o.Debug, o.LogJSON); err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to set go-exploitdb logger. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	db, err := newExploitDB(cnf)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to newExploitDB. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	return &goExploitDBClient{driver: db, baseURL: cnf.GetURL()}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillWithExploit fills exploit information that has in Exploit
 | 
			
		||||
func FillWithExploit(r *models.ScanResult, cnf config.ExploitConf) (nExploitCve int, err error) {
 | 
			
		||||
	if cnf.IsFetchViaHTTP() {
 | 
			
		||||
func FillWithExploit(r *models.ScanResult, cnf config.ExploitConf, logOpts logging.LogOpts) (nExploitCve int, err error) {
 | 
			
		||||
	client, err := newGoExploitDBClient(&cnf, logOpts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, xerrors.Errorf("Failed to newGoExploitDBClient. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if err := client.closeDB(); err != nil {
 | 
			
		||||
			logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	if client.driver == nil {
 | 
			
		||||
		var cveIDs []string
 | 
			
		||||
		for cveID := range r.ScannedCves {
 | 
			
		||||
			cveIDs = append(cveIDs, cveID)
 | 
			
		||||
		}
 | 
			
		||||
		prefix, _ := util.URLPathJoin(cnf.GetURL(), "cves")
 | 
			
		||||
		prefix, err := util.URLPathJoin(client.baseURL, "cves")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to join URLPath. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		responses, err := getExploitsViaHTTP(cveIDs, prefix)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get Exploits via HTTP. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		for _, res := range responses {
 | 
			
		||||
			exps := []exploitmodels.Exploit{}
 | 
			
		||||
			if err := json.Unmarshal([]byte(res.json), &exps); err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
				return 0, xerrors.Errorf("Failed to unmarshal json. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			exploits := ConvertToModelsExploit(exps)
 | 
			
		||||
			v, ok := r.ScannedCves[res.request.cveID]
 | 
			
		||||
@@ -46,25 +86,13 @@ func FillWithExploit(r *models.ScanResult, cnf config.ExploitConf) (nExploitCve
 | 
			
		||||
			nExploitCve++
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		driver, locked, err := newExploitDB(&cnf)
 | 
			
		||||
		if locked {
 | 
			
		||||
			return 0, xerrors.Errorf("SQLite3 is locked: %s", cnf.GetSQLite3Path())
 | 
			
		||||
		} else if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		defer func() {
 | 
			
		||||
			if err := driver.CloseDB(); err != nil {
 | 
			
		||||
				logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
		for cveID, vuln := range r.ScannedCves {
 | 
			
		||||
			if cveID == "" {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			es, err := driver.GetExploitByCveID(cveID)
 | 
			
		||||
			es, err := client.driver.GetExploitByCveID(cveID)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
				return 0, xerrors.Errorf("Failed to get Exploits by CVE-ID. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			if len(es) == 0 {
 | 
			
		||||
				continue
 | 
			
		||||
@@ -203,19 +231,20 @@ func httpGetExploit(url string, req exploitRequest, resChan chan<- exploitRespon
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newExploitDB(cnf config.VulnDictInterface) (driver exploitdb.DB, locked bool, err error) {
 | 
			
		||||
func newExploitDB(cnf config.VulnDictInterface) (driver exploitdb.DB, err error) {
 | 
			
		||||
	if cnf.IsFetchViaHTTP() {
 | 
			
		||||
		return nil, false, nil
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
	path := cnf.GetURL()
 | 
			
		||||
	if cnf.GetType() == "sqlite3" {
 | 
			
		||||
		path = cnf.GetSQLite3Path()
 | 
			
		||||
	}
 | 
			
		||||
	if driver, locked, err = exploitdb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), exploitdb.Option{}); err != nil {
 | 
			
		||||
	driver, locked, err := exploitdb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), exploitdb.Option{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if locked {
 | 
			
		||||
			return nil, true, xerrors.Errorf("exploitDB is locked. err: %w", err)
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to init exploit DB. SQLite3: %s is locked. err: %w", cnf.GetSQLite3Path(), err)
 | 
			
		||||
		}
 | 
			
		||||
		return nil, false, err
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to init exploit DB. DB Path: %s, err: %w", path, err)
 | 
			
		||||
	}
 | 
			
		||||
	return driver, false, nil
 | 
			
		||||
	return driver, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -9,25 +9,62 @@ import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cenkalti/backoff"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	kevulndb "github.com/vulsio/go-kev/db"
 | 
			
		||||
	kevulnmodels "github.com/vulsio/go-kev/models"
 | 
			
		||||
	kevulnlog "github.com/vulsio/go-kev/utils"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// goKEVulnDBClient is a DB Driver
 | 
			
		||||
type goKEVulnDBClient struct {
 | 
			
		||||
	driver  kevulndb.DB
 | 
			
		||||
	baseURL string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// closeDB close a DB connection
 | 
			
		||||
func (client goKEVulnDBClient) closeDB() error {
 | 
			
		||||
	if client.driver == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return client.driver.CloseDB()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newGoKEVulnDBClient(cnf config.VulnDictInterface, o logging.LogOpts) (*goKEVulnDBClient, error) {
 | 
			
		||||
	if err := kevulnlog.SetLogger(o.LogToFile, o.LogDir, o.Debug, o.LogJSON); err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to set go-kev logger. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	db, err := newKEVulnDB(cnf)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to newKEVulnDB. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	return &goKEVulnDBClient{driver: db, baseURL: cnf.GetURL()}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillWithKEVuln :
 | 
			
		||||
func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf) error {
 | 
			
		||||
	if cnf.IsFetchViaHTTP() {
 | 
			
		||||
func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf, logOpts logging.LogOpts) error {
 | 
			
		||||
	client, err := newGoKEVulnDBClient(&cnf, logOpts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if err := client.closeDB(); err != nil {
 | 
			
		||||
			logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	if client.driver == nil {
 | 
			
		||||
		var cveIDs []string
 | 
			
		||||
		for cveID := range r.ScannedCves {
 | 
			
		||||
			cveIDs = append(cveIDs, cveID)
 | 
			
		||||
		}
 | 
			
		||||
		prefix, err := util.URLPathJoin(cnf.GetURL(), "cves")
 | 
			
		||||
		prefix, err := util.URLPathJoin(client.baseURL, "cves")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
@@ -57,23 +94,11 @@ func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf) error {
 | 
			
		||||
			r.ScannedCves[res.request.cveID] = v
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		driver, locked, err := newKEVulnDB(&cnf)
 | 
			
		||||
		if locked {
 | 
			
		||||
			return xerrors.Errorf("SQLite3 is locked: %s", cnf.GetSQLite3Path())
 | 
			
		||||
		} else if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		defer func() {
 | 
			
		||||
			if err := driver.CloseDB(); err != nil {
 | 
			
		||||
				logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
		for cveID, vuln := range r.ScannedCves {
 | 
			
		||||
			if cveID == "" {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			kevulns, err := driver.GetKEVulnByCveID(cveID)
 | 
			
		||||
			kevulns, err := client.driver.GetKEVulnByCveID(cveID)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
@@ -196,19 +221,20 @@ func httpGetKEVuln(url string, req kevulnRequest, resChan chan<- kevulnResponse,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newKEVulnDB(cnf config.VulnDictInterface) (driver kevulndb.DB, locked bool, err error) {
 | 
			
		||||
func newKEVulnDB(cnf config.VulnDictInterface) (kevulndb.DB, error) {
 | 
			
		||||
	if cnf.IsFetchViaHTTP() {
 | 
			
		||||
		return nil, false, nil
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
	path := cnf.GetURL()
 | 
			
		||||
	if cnf.GetType() == "sqlite3" {
 | 
			
		||||
		path = cnf.GetSQLite3Path()
 | 
			
		||||
	}
 | 
			
		||||
	if driver, locked, err = kevulndb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), kevulndb.Option{}); err != nil {
 | 
			
		||||
	driver, locked, err := kevulndb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), kevulndb.Option{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if locked {
 | 
			
		||||
			return nil, true, xerrors.Errorf("kevulnDB is locked. err: %w", err)
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to init kevuln DB. SQLite3: %s is locked. err: %w", cnf.GetSQLite3Path(), err)
 | 
			
		||||
		}
 | 
			
		||||
		return nil, false, err
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to init kevuln DB. DB Path: %s, err: %w", path, err)
 | 
			
		||||
	}
 | 
			
		||||
	return driver, false, nil
 | 
			
		||||
	return driver, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -9,35 +9,73 @@ import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cenkalti/backoff"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	metasploitdb "github.com/vulsio/go-msfdb/db"
 | 
			
		||||
	metasploitmodels "github.com/vulsio/go-msfdb/models"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
	metasploitlog "github.com/vulsio/go-msfdb/utils"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// goMetasploitDBClient is a DB Driver
 | 
			
		||||
type goMetasploitDBClient struct {
 | 
			
		||||
	driver  metasploitdb.DB
 | 
			
		||||
	baseURL string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// closeDB close a DB connection
 | 
			
		||||
func (client goMetasploitDBClient) closeDB() error {
 | 
			
		||||
	if client.driver == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return client.driver.CloseDB()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newGoMetasploitDBClient(cnf config.VulnDictInterface, o logging.LogOpts) (*goMetasploitDBClient, error) {
 | 
			
		||||
	if err := metasploitlog.SetLogger(o.LogToFile, o.LogDir, o.Debug, o.LogJSON); err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to set go-msfdb logger. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	db, err := newMetasploitDB(cnf)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to newMetasploitDB. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	return &goMetasploitDBClient{driver: db, baseURL: cnf.GetURL()}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillWithMetasploit fills metasploit module information that has in module
 | 
			
		||||
func FillWithMetasploit(r *models.ScanResult, cnf config.MetasploitConf) (nMetasploitCve int, err error) {
 | 
			
		||||
	if cnf.IsFetchViaHTTP() {
 | 
			
		||||
func FillWithMetasploit(r *models.ScanResult, cnf config.MetasploitConf, logOpts logging.LogOpts) (nMetasploitCve int, err error) {
 | 
			
		||||
	client, err := newGoMetasploitDBClient(&cnf, logOpts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, xerrors.Errorf("Failed to newGoMetasploitDBClient. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if err := client.closeDB(); err != nil {
 | 
			
		||||
			logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	if client.driver == nil {
 | 
			
		||||
		var cveIDs []string
 | 
			
		||||
		for cveID := range r.ScannedCves {
 | 
			
		||||
			cveIDs = append(cveIDs, cveID)
 | 
			
		||||
		}
 | 
			
		||||
		prefix, err := util.URLPathJoin(cnf.GetURL(), "cves")
 | 
			
		||||
		prefix, err := util.URLPathJoin(client.baseURL, "cves")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to join URLPath. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		responses, err := getMetasploitsViaHTTP(cveIDs, prefix)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get Metasploits via HTTP. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		for _, res := range responses {
 | 
			
		||||
			msfs := []metasploitmodels.Metasploit{}
 | 
			
		||||
			if err := json.Unmarshal([]byte(res.json), &msfs); err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
				return 0, xerrors.Errorf("Failed to unmarshal json. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			metasploits := ConvertToModelsMsf(msfs)
 | 
			
		||||
			v, ok := r.ScannedCves[res.request.cveID]
 | 
			
		||||
@@ -48,25 +86,13 @@ func FillWithMetasploit(r *models.ScanResult, cnf config.MetasploitConf) (nMetas
 | 
			
		||||
			nMetasploitCve++
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		driver, locked, err := newMetasploitDB(&cnf)
 | 
			
		||||
		if locked {
 | 
			
		||||
			return 0, xerrors.Errorf("SQLite3 is locked: %s", cnf.GetSQLite3Path())
 | 
			
		||||
		} else if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		defer func() {
 | 
			
		||||
			if err := driver.CloseDB(); err != nil {
 | 
			
		||||
				logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
		for cveID, vuln := range r.ScannedCves {
 | 
			
		||||
			if cveID == "" {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			ms, err := driver.GetModuleByCveID(cveID)
 | 
			
		||||
			ms, err := client.driver.GetModuleByCveID(cveID)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
				return 0, xerrors.Errorf("Failed to get Metasploits by CVE-ID. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			if len(ms) == 0 {
 | 
			
		||||
				continue
 | 
			
		||||
@@ -199,19 +225,20 @@ func ConvertToModelsMsf(ms []metasploitmodels.Metasploit) (modules []models.Meta
 | 
			
		||||
	return modules
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newMetasploitDB(cnf config.VulnDictInterface) (driver metasploitdb.DB, locked bool, err error) {
 | 
			
		||||
func newMetasploitDB(cnf config.VulnDictInterface) (metasploitdb.DB, error) {
 | 
			
		||||
	if cnf.IsFetchViaHTTP() {
 | 
			
		||||
		return nil, false, nil
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
	path := cnf.GetURL()
 | 
			
		||||
	if cnf.GetType() == "sqlite3" {
 | 
			
		||||
		path = cnf.GetSQLite3Path()
 | 
			
		||||
	}
 | 
			
		||||
	if driver, locked, err = metasploitdb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), metasploitdb.Option{}); err != nil {
 | 
			
		||||
	driver, locked, err := metasploitdb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), metasploitdb.Option{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if locked {
 | 
			
		||||
			return nil, true, xerrors.Errorf("metasploitDB is locked. err: %w", err)
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to init metasploit DB. SQLite3: %s is locked. err: %w", cnf.GetSQLite3Path(), err)
 | 
			
		||||
		}
 | 
			
		||||
		return nil, false, err
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to init metasploit DB. DB Path: %s, err: %w", path, err)
 | 
			
		||||
	}
 | 
			
		||||
	return driver, false, nil
 | 
			
		||||
	return driver, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -26,12 +26,7 @@ func reuseScannedCves(r *models.ScanResult) bool {
 | 
			
		||||
	case constant.FreeBSD, constant.Raspbian:
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return isTrivyResult(r)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isTrivyResult(r *models.ScanResult) bool {
 | 
			
		||||
	_, ok := r.Optional["trivy-target"]
 | 
			
		||||
	return ok
 | 
			
		||||
	return r.ScannedBy == "trivy"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func needToRefreshCve(r models.ScanResult) bool {
 | 
			
		||||
@@ -130,7 +125,7 @@ func getPlusDiffCves(previous, current models.ScanResult) models.VulnInfos {
 | 
			
		||||
		previousCveIDsSet[previousVulnInfo.CveID] = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	new := models.VulnInfos{}
 | 
			
		||||
	newer := models.VulnInfos{}
 | 
			
		||||
	updated := models.VulnInfos{}
 | 
			
		||||
	for _, v := range current.ScannedCves {
 | 
			
		||||
		if previousCveIDsSet[v.CveID] {
 | 
			
		||||
@@ -150,17 +145,17 @@ func getPlusDiffCves(previous, current models.ScanResult) models.VulnInfos {
 | 
			
		||||
				logging.Log.Debugf("same: %s", v.CveID)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			logging.Log.Debugf("new: %s", v.CveID)
 | 
			
		||||
			logging.Log.Debugf("newer: %s", v.CveID)
 | 
			
		||||
			v.DiffStatus = models.DiffPlus
 | 
			
		||||
			new[v.CveID] = v
 | 
			
		||||
			newer[v.CveID] = v
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(updated) == 0 && len(new) == 0 {
 | 
			
		||||
	if len(updated) == 0 && len(newer) == 0 {
 | 
			
		||||
		logging.Log.Infof("%s: There are %d vulnerabilities, but no difference between current result and previous one.", current.FormatServerName(), len(current.ScannedCves))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for cveID, vuln := range new {
 | 
			
		||||
	for cveID, vuln := range newer {
 | 
			
		||||
		updated[cveID] = vuln
 | 
			
		||||
	}
 | 
			
		||||
	return updated
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										139
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										139
									
								
								go.mod
									
									
									
									
									
								
							@@ -1,35 +1,28 @@
 | 
			
		||||
module github.com/future-architect/vuls
 | 
			
		||||
 | 
			
		||||
go 1.17
 | 
			
		||||
go 1.18
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/Azure/azure-sdk-for-go v61.2.0+incompatible
 | 
			
		||||
	github.com/BurntSushi/toml v1.0.0
 | 
			
		||||
	github.com/Azure/azure-sdk-for-go v63.0.0+incompatible
 | 
			
		||||
	github.com/BurntSushi/toml v1.1.0
 | 
			
		||||
	github.com/Ullaakut/nmap/v2 v2.1.2-0.20210406060955-59a52fe80a4f
 | 
			
		||||
	github.com/VividCortex/ewma v1.2.0 // indirect
 | 
			
		||||
	github.com/aquasecurity/fanal v0.0.0-20220129174924-b9e05fcccc57
 | 
			
		||||
	github.com/aquasecurity/go-dep-parser v0.0.0-20220110153540-4a30ebc4b509
 | 
			
		||||
	github.com/aquasecurity/trivy v0.23.0
 | 
			
		||||
	github.com/aquasecurity/trivy-db v0.0.0-20220130223604-df65ebde46f4
 | 
			
		||||
	github.com/aquasecurity/fanal v0.0.0-20220426115253-1d75fc0c7219
 | 
			
		||||
	github.com/aquasecurity/go-dep-parser v0.0.0-20220422134844-880747206031
 | 
			
		||||
	github.com/aquasecurity/trivy v0.27.1
 | 
			
		||||
	github.com/aquasecurity/trivy-db v0.0.0-20220327074450-74195d9604b2
 | 
			
		||||
	github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
 | 
			
		||||
	github.com/aws/aws-sdk-go v1.42.30
 | 
			
		||||
	github.com/aws/aws-sdk-go v1.43.31
 | 
			
		||||
	github.com/boltdb/bolt v1.3.1
 | 
			
		||||
	github.com/briandowns/spinner v1.16.0 // indirect
 | 
			
		||||
	github.com/briandowns/spinner v1.18.1 // indirect
 | 
			
		||||
	github.com/cenkalti/backoff v2.2.1+incompatible
 | 
			
		||||
	github.com/cespare/xxhash/v2 v2.1.2 // indirect
 | 
			
		||||
	github.com/cheggaaa/pb/v3 v3.0.8 // indirect
 | 
			
		||||
	github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b
 | 
			
		||||
	github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21
 | 
			
		||||
	github.com/emersion/go-smtp v0.14.0
 | 
			
		||||
	github.com/fatih/color v1.13.0 // indirect
 | 
			
		||||
	github.com/fsnotify/fsnotify v1.5.1 // indirect
 | 
			
		||||
	github.com/go-redis/redis/v8 v8.11.4 // indirect
 | 
			
		||||
	github.com/go-stack/stack v1.8.1 // indirect
 | 
			
		||||
	github.com/google/subcommands v1.2.0
 | 
			
		||||
	github.com/gosuri/uitable v0.0.4
 | 
			
		||||
	github.com/hashicorp/go-uuid v1.0.2
 | 
			
		||||
	github.com/hashicorp/go-version v1.4.0
 | 
			
		||||
	github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c
 | 
			
		||||
	github.com/jesseduffield/gocui v0.3.0
 | 
			
		||||
	github.com/k0kubun/pp v3.0.1+incompatible
 | 
			
		||||
	github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f
 | 
			
		||||
@@ -38,7 +31,6 @@ require (
 | 
			
		||||
	github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936
 | 
			
		||||
	github.com/kotakanbe/go-pingscanner v0.1.0
 | 
			
		||||
	github.com/kotakanbe/logrus-prefixed-formatter v0.0.0-20180123152602-928f7356cb96
 | 
			
		||||
	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
 | 
			
		||||
@@ -46,41 +38,36 @@ require (
 | 
			
		||||
	github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1 // indirect
 | 
			
		||||
	github.com/olekukonko/tablewriter v0.0.5
 | 
			
		||||
	github.com/parnurzeal/gorequest v0.2.16
 | 
			
		||||
	github.com/pelletier/go-toml v1.9.4 // indirect
 | 
			
		||||
	github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
 | 
			
		||||
	github.com/sirupsen/logrus v1.8.1
 | 
			
		||||
	github.com/spf13/cast v1.4.1 // indirect
 | 
			
		||||
	github.com/spf13/cobra v1.3.0
 | 
			
		||||
	github.com/spf13/cobra v1.4.0
 | 
			
		||||
	github.com/vulsio/go-cve-dictionary v0.8.2-0.20211028094424-0a854f8e8f85
 | 
			
		||||
	github.com/vulsio/go-exploitdb v0.4.2-0.20211028071949-1ebf9c4f6c4d
 | 
			
		||||
	github.com/vulsio/go-kev v0.1.0
 | 
			
		||||
	github.com/vulsio/go-exploitdb v0.4.2
 | 
			
		||||
	github.com/vulsio/go-kev v0.1.1-0.20220118062020-5f69b364106f
 | 
			
		||||
	github.com/vulsio/go-msfdb v0.2.1-0.20211028071756-4a9759bd9f14
 | 
			
		||||
	github.com/vulsio/gost v0.4.1-0.20211028071837-7ad032a6ffa8
 | 
			
		||||
	github.com/vulsio/goval-dictionary v0.7.1-0.20220212015000-031fc960b77c
 | 
			
		||||
	golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce // indirect
 | 
			
		||||
	golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d // indirect
 | 
			
		||||
	golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
 | 
			
		||||
	github.com/vulsio/gost v0.4.1
 | 
			
		||||
	github.com/vulsio/goval-dictionary v0.7.3
 | 
			
		||||
	golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5
 | 
			
		||||
	golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
 | 
			
		||||
	golang.org/x/text v0.3.7 // indirect
 | 
			
		||||
	golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
 | 
			
		||||
	golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f
 | 
			
		||||
	gopkg.in/ini.v1 v1.66.4 // indirect
 | 
			
		||||
	gorm.io/driver/mysql v1.2.3 // indirect
 | 
			
		||||
	gorm.io/driver/postgres v1.2.3 // indirect
 | 
			
		||||
	gorm.io/driver/sqlite v1.2.6 // indirect
 | 
			
		||||
	gorm.io/driver/mysql v1.3.3 // indirect
 | 
			
		||||
	gorm.io/driver/postgres v1.3.5 // indirect
 | 
			
		||||
	gorm.io/driver/sqlite v1.3.2 // indirect
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	cloud.google.com/go v0.99.0 // indirect
 | 
			
		||||
	cloud.google.com/go v0.100.2 // indirect
 | 
			
		||||
	cloud.google.com/go/compute v1.5.0 // indirect
 | 
			
		||||
	cloud.google.com/go/iam v0.3.0 // indirect
 | 
			
		||||
	cloud.google.com/go/storage v1.14.0 // indirect
 | 
			
		||||
	github.com/Azure/go-autorest v14.2.0+incompatible // indirect
 | 
			
		||||
	github.com/Azure/go-autorest/autorest v0.11.24 // indirect
 | 
			
		||||
	github.com/Azure/go-autorest/autorest v0.11.25 // indirect
 | 
			
		||||
	github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect
 | 
			
		||||
	github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
 | 
			
		||||
	github.com/Azure/go-autorest/autorest/to v0.3.0 // indirect
 | 
			
		||||
	github.com/Azure/go-autorest/logger v0.2.1 // 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.6.1 // indirect
 | 
			
		||||
	github.com/andybalholm/cascadia v1.2.0 // indirect
 | 
			
		||||
	github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce // indirect
 | 
			
		||||
@@ -88,83 +75,93 @@ require (
 | 
			
		||||
	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/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
 | 
			
		||||
	github.com/caarlos0/env/v6 v6.0.0 // indirect
 | 
			
		||||
	github.com/caarlos0/env/v6 v6.9.1 // indirect
 | 
			
		||||
	github.com/cespare/xxhash/v2 v2.1.2 // indirect
 | 
			
		||||
	github.com/cheggaaa/pb/v3 v3.0.8 // indirect
 | 
			
		||||
	github.com/davecgh/go-spew v1.1.1 // indirect
 | 
			
		||||
	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
 | 
			
		||||
	github.com/docker/cli v20.10.11+incompatible // indirect
 | 
			
		||||
	github.com/docker/cli v20.10.12+incompatible // indirect
 | 
			
		||||
	github.com/docker/distribution v2.7.1+incompatible // indirect
 | 
			
		||||
	github.com/docker/docker v20.10.12+incompatible // indirect
 | 
			
		||||
	github.com/docker/docker v20.10.14+incompatible // indirect
 | 
			
		||||
	github.com/docker/docker-credential-helpers v0.6.4 // indirect
 | 
			
		||||
	github.com/fatih/color v1.13.0 // indirect
 | 
			
		||||
	github.com/fsnotify/fsnotify v1.5.4 // indirect
 | 
			
		||||
	github.com/go-redis/redis/v8 v8.11.5 // indirect
 | 
			
		||||
	github.com/go-sql-driver/mysql v1.6.0 // indirect
 | 
			
		||||
	github.com/go-stack/stack v1.8.1 // indirect
 | 
			
		||||
	github.com/gofrs/uuid v4.0.0+incompatible // indirect
 | 
			
		||||
	github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
 | 
			
		||||
	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 | 
			
		||||
	github.com/golang/protobuf v1.5.2 // indirect
 | 
			
		||||
	github.com/google/go-containerregistry v0.7.1-0.20211214010025-a65b7844a475 // indirect
 | 
			
		||||
	github.com/google/uuid v1.3.0 // indirect
 | 
			
		||||
	github.com/googleapis/gax-go/v2 v2.1.1 // indirect
 | 
			
		||||
	github.com/google/go-containerregistry v0.8.0 // indirect
 | 
			
		||||
	github.com/googleapis/gax-go/v2 v2.3.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.2 // indirect
 | 
			
		||||
	github.com/hashicorp/go-getter v1.5.2 // indirect
 | 
			
		||||
	github.com/hashicorp/go-getter v1.5.11 // indirect
 | 
			
		||||
	github.com/hashicorp/go-multierror v1.1.1 // indirect
 | 
			
		||||
	github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
 | 
			
		||||
	github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
 | 
			
		||||
	github.com/hashicorp/go-safetemp v1.0.0 // indirect
 | 
			
		||||
	github.com/hashicorp/hcl v1.0.0 // 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.11.0 // indirect
 | 
			
		||||
	github.com/jackc/pgconn v1.12.0 // indirect
 | 
			
		||||
	github.com/jackc/pgio v1.0.0 // indirect
 | 
			
		||||
	github.com/jackc/pgpassfile v1.0.0 // indirect
 | 
			
		||||
	github.com/jackc/pgproto3/v2 v2.2.0 // indirect
 | 
			
		||||
	github.com/jackc/pgproto3/v2 v2.3.0 // indirect
 | 
			
		||||
	github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
 | 
			
		||||
	github.com/jackc/pgtype v1.10.0 // indirect
 | 
			
		||||
	github.com/jackc/pgx/v4 v4.15.0 // indirect
 | 
			
		||||
	github.com/jackc/pgtype v1.11.0 // indirect
 | 
			
		||||
	github.com/jackc/pgx/v4 v4.16.0 // indirect
 | 
			
		||||
	github.com/jinzhu/inflection v1.0.0 // indirect
 | 
			
		||||
	github.com/jinzhu/now v1.1.4 // indirect
 | 
			
		||||
	github.com/jinzhu/now v1.1.5 // indirect
 | 
			
		||||
	github.com/jmespath/go-jmespath v0.4.0 // indirect
 | 
			
		||||
	github.com/klauspost/compress v1.13.6 // indirect
 | 
			
		||||
	github.com/magiconair/properties v1.8.5 // indirect
 | 
			
		||||
	github.com/klauspost/compress v1.14.2 // indirect
 | 
			
		||||
	github.com/magiconair/properties v1.8.6 // indirect
 | 
			
		||||
	github.com/masahiro331/go-mvn-version v0.0.0-20210429150710-d3157d602a08 // indirect
 | 
			
		||||
	github.com/mattn/go-colorable v0.1.12 // indirect
 | 
			
		||||
	github.com/mattn/go-sqlite3 v1.14.11 // indirect
 | 
			
		||||
	github.com/mitchellh/copystructure v1.1.1 // indirect
 | 
			
		||||
	github.com/mattn/go-isatty v0.0.14 // indirect
 | 
			
		||||
	github.com/mattn/go-sqlite3 v1.14.12 // indirect
 | 
			
		||||
	github.com/mitchellh/go-testing-interface v1.0.0 // indirect
 | 
			
		||||
	github.com/mitchellh/mapstructure v1.4.3 // indirect
 | 
			
		||||
	github.com/mitchellh/reflectwalk v1.0.1 // indirect
 | 
			
		||||
	github.com/mitchellh/mapstructure v1.5.0 // indirect
 | 
			
		||||
	github.com/opencontainers/go-digest v1.0.0 // indirect
 | 
			
		||||
	github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5 // indirect
 | 
			
		||||
	github.com/owenrumney/go-sarif/v2 v2.0.17 // indirect
 | 
			
		||||
	github.com/opencontainers/image-spec v1.0.2 // indirect
 | 
			
		||||
	github.com/pelletier/go-toml v1.9.5 // indirect
 | 
			
		||||
	github.com/pelletier/go-toml/v2 v2.0.0-beta.8 // 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/spf13/afero v1.8.1 // indirect
 | 
			
		||||
	github.com/spf13/afero v1.8.2 // indirect
 | 
			
		||||
	github.com/spf13/cast v1.4.1 // indirect
 | 
			
		||||
	github.com/spf13/jwalterweatherman v1.1.0 // indirect
 | 
			
		||||
	github.com/spf13/pflag v1.0.5 // indirect
 | 
			
		||||
	github.com/spf13/viper v1.10.0 // indirect
 | 
			
		||||
	github.com/spf13/viper v1.11.0 // indirect
 | 
			
		||||
	github.com/stretchr/objx v0.3.0 // indirect
 | 
			
		||||
	github.com/stretchr/testify v1.7.0 // indirect
 | 
			
		||||
	github.com/stretchr/testify v1.7.1 // indirect
 | 
			
		||||
	github.com/subosito/gotenv v1.2.0 // indirect
 | 
			
		||||
	github.com/ulikunitz/xz v0.5.10 // indirect
 | 
			
		||||
	go.etcd.io/bbolt v1.3.6 // indirect
 | 
			
		||||
	go.opencensus.io v0.23.0 // indirect
 | 
			
		||||
	go.uber.org/atomic v1.7.0 // indirect
 | 
			
		||||
	go.uber.org/goleak v1.1.12 // indirect
 | 
			
		||||
	go.uber.org/multierr v1.6.0 // indirect
 | 
			
		||||
	go.uber.org/zap v1.20.0 // indirect
 | 
			
		||||
	golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect
 | 
			
		||||
	golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
 | 
			
		||||
	google.golang.org/api v0.62.0 // indirect
 | 
			
		||||
	go.uber.org/zap v1.21.0 // indirect
 | 
			
		||||
	golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
 | 
			
		||||
	golang.org/x/exp v0.0.0-20220407100705-7b9b53b0aca4 // indirect
 | 
			
		||||
	golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 // indirect
 | 
			
		||||
	golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect
 | 
			
		||||
	golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 // indirect
 | 
			
		||||
	golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
 | 
			
		||||
	golang.org/x/text v0.3.7 // indirect
 | 
			
		||||
	google.golang.org/api v0.74.0 // indirect
 | 
			
		||||
	google.golang.org/appengine v1.6.7 // indirect
 | 
			
		||||
	google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect
 | 
			
		||||
	google.golang.org/grpc v1.43.0 // indirect
 | 
			
		||||
	google.golang.org/protobuf v1.27.1 // indirect
 | 
			
		||||
	google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac // indirect
 | 
			
		||||
	google.golang.org/grpc v1.45.0 // indirect
 | 
			
		||||
	google.golang.org/protobuf v1.28.0 // indirect
 | 
			
		||||
	gopkg.in/yaml.v2 v2.4.0 // indirect
 | 
			
		||||
	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
 | 
			
		||||
	gorm.io/gorm v1.22.5 // indirect
 | 
			
		||||
	gorm.io/gorm v1.23.5 // indirect
 | 
			
		||||
	k8s.io/utils v0.0.0-20201110183641-67b214c5f920 // indirect
 | 
			
		||||
	moul.io/http2curl v1.0.0 // indirect
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -6,12 +6,13 @@ package gost
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
 | 
			
		||||
	debver "github.com/knqyf263/go-deb-version"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	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
 | 
			
		||||
@@ -67,7 +68,7 @@ func (deb Debian) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error
 | 
			
		||||
	}
 | 
			
		||||
	nFixedCVEs, err := deb.detectCVEsWithFixState(r, "resolved")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
		return 0, xerrors.Errorf("Failed to detect fixed CVEs. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if stashLinuxPackage.Name != "" {
 | 
			
		||||
@@ -75,7 +76,7 @@ func (deb Debian) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error
 | 
			
		||||
	}
 | 
			
		||||
	nUnfixedCVEs, err := deb.detectCVEsWithFixState(r, "open")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
		return 0, xerrors.Errorf("Failed to detect unfixed CVEs. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return (nFixedCVEs + nUnfixedCVEs), nil
 | 
			
		||||
@@ -87,22 +88,25 @@ func (deb Debian) detectCVEsWithFixState(r *models.ScanResult, fixStatus string)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	packCvesList := []packCves{}
 | 
			
		||||
	if deb.DBDriver.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		url, _ := util.URLPathJoin(deb.DBDriver.Cnf.GetURL(), "debian", major(r.Release), "pkgs")
 | 
			
		||||
	if deb.driver == nil {
 | 
			
		||||
		url, err := util.URLPathJoin(deb.baseURL, "debian", major(r.Release), "pkgs")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to join URLPath. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s := "unfixed-cves"
 | 
			
		||||
		if s == "resolved" {
 | 
			
		||||
			s = "fixed-cves"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		responses, err := getCvesWithFixStateViaHTTP(r, url, s)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get CVEs via HTTP. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, res := range responses {
 | 
			
		||||
			debCves := map[string]gostmodels.DebianCVE{}
 | 
			
		||||
			if err := json.Unmarshal([]byte(res.json), &debCves); err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
				return 0, xerrors.Errorf("Failed to unmarshal json. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			cves := []models.CveContent{}
 | 
			
		||||
			fixes := []models.PackageFixStatus{}
 | 
			
		||||
@@ -118,13 +122,10 @@ func (deb Debian) detectCVEsWithFixState(r *models.ScanResult, fixStatus string)
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if deb.DBDriver.DB == nil {
 | 
			
		||||
			return 0, nil
 | 
			
		||||
		}
 | 
			
		||||
		for _, pack := range r.Packages {
 | 
			
		||||
			cves, fixes, err := deb.getCvesDebianWithfixStatus(fixStatus, major(r.Release), pack.Name)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
				return 0, xerrors.Errorf("Failed to get CVEs for Package. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			packCvesList = append(packCvesList, packCves{
 | 
			
		||||
				packName:  pack.Name,
 | 
			
		||||
@@ -138,7 +139,7 @@ func (deb Debian) detectCVEsWithFixState(r *models.ScanResult, fixStatus string)
 | 
			
		||||
		for _, pack := range r.SrcPackages {
 | 
			
		||||
			cves, fixes, err := deb.getCvesDebianWithfixStatus(fixStatus, major(r.Release), pack.Name)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
				return 0, xerrors.Errorf("Failed to get CVEs for SrcPackage. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			packCvesList = append(packCvesList, packCves{
 | 
			
		||||
				packName:  pack.Name,
 | 
			
		||||
@@ -239,11 +240,11 @@ func (deb Debian) detectCVEsWithFixState(r *models.ScanResult, fixStatus string)
 | 
			
		||||
func isGostDefAffected(versionRelease, gostVersion string) (affected bool, err error) {
 | 
			
		||||
	vera, err := debver.NewVersion(versionRelease)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
		return false, xerrors.Errorf("Failed to parse version. version: %s, err: %w", versionRelease, err)
 | 
			
		||||
	}
 | 
			
		||||
	verb, err := debver.NewVersion(gostVersion)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
		return false, xerrors.Errorf("Failed to parse version. version: %s, err: %w", gostVersion, err)
 | 
			
		||||
	}
 | 
			
		||||
	return vera.LessThan(verb), nil
 | 
			
		||||
}
 | 
			
		||||
@@ -251,13 +252,13 @@ func isGostDefAffected(versionRelease, gostVersion string) (affected bool, err e
 | 
			
		||||
func (deb Debian) getCvesDebianWithfixStatus(fixStatus, release, pkgName string) ([]models.CveContent, []models.PackageFixStatus, error) {
 | 
			
		||||
	var f func(string, string) (map[string]gostmodels.DebianCVE, error)
 | 
			
		||||
	if fixStatus == "resolved" {
 | 
			
		||||
		f = deb.DBDriver.DB.GetFixedCvesDebian
 | 
			
		||||
		f = deb.driver.GetFixedCvesDebian
 | 
			
		||||
	} else {
 | 
			
		||||
		f = deb.DBDriver.DB.GetUnfixedCvesDebian
 | 
			
		||||
		f = deb.driver.GetUnfixedCvesDebian
 | 
			
		||||
	}
 | 
			
		||||
	debCves, err := f(release, pkgName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
		return nil, nil, xerrors.Errorf("Failed to get CVEs. fixStatus: %s, release: %s, src package: %s, err: %w", fixStatus, release, pkgName, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cves := []models.CveContent{}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										94
									
								
								gost/gost.go
									
									
									
									
									
								
							
							
						
						
									
										94
									
								
								gost/gost.go
									
									
									
									
									
								
							@@ -4,22 +4,17 @@
 | 
			
		||||
package gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/vulsio/gost/db"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	gostdb "github.com/vulsio/gost/db"
 | 
			
		||||
	gostlog "github.com/vulsio/gost/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DBDriver is a DB Driver
 | 
			
		||||
type DBDriver struct {
 | 
			
		||||
	DB  db.DB
 | 
			
		||||
	Cnf config.VulnDictInterface
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Client is the interface of OVAL client.
 | 
			
		||||
// Client is the interface of Gost client.
 | 
			
		||||
type Client interface {
 | 
			
		||||
	DetectCVEs(*models.ScanResult, bool) (int, error)
 | 
			
		||||
	CloseDB() error
 | 
			
		||||
@@ -27,74 +22,79 @@ type Client interface {
 | 
			
		||||
 | 
			
		||||
// Base is a base struct
 | 
			
		||||
type Base struct {
 | 
			
		||||
	DBDriver DBDriver
 | 
			
		||||
	driver  gostdb.DB
 | 
			
		||||
	baseURL string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CloseDB close a DB connection
 | 
			
		||||
func (b Base) CloseDB() error {
 | 
			
		||||
	if b.DBDriver.DB == nil {
 | 
			
		||||
	if b.driver == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return b.DBDriver.DB.CloseDB()
 | 
			
		||||
	return b.driver.CloseDB()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillCVEsWithRedHat fills CVE detailed with Red Hat Security
 | 
			
		||||
func FillCVEsWithRedHat(r *models.ScanResult, cnf config.GostConf) error {
 | 
			
		||||
	db, locked, err := newGostDB(cnf)
 | 
			
		||||
	if locked {
 | 
			
		||||
		return xerrors.Errorf("SQLite3 is locked: %s", cnf.GetSQLite3Path())
 | 
			
		||||
	} else if err != nil {
 | 
			
		||||
func FillCVEsWithRedHat(r *models.ScanResult, cnf config.GostConf, o logging.LogOpts) error {
 | 
			
		||||
	if err := gostlog.SetLogger(o.LogToFile, o.LogDir, o.Debug, o.LogJSON); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if db != nil {
 | 
			
		||||
			if err := db.CloseDB(); err != nil {
 | 
			
		||||
				logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	return RedHat{Base{DBDriver{DB: db, Cnf: &cnf}}}.fillCvesWithRedHatAPI(r)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewClient make Client by family
 | 
			
		||||
func NewClient(cnf config.GostConf, family string) (Client, error) {
 | 
			
		||||
	db, locked, err := newGostDB(cnf)
 | 
			
		||||
	if locked {
 | 
			
		||||
		return nil, xerrors.Errorf("SQLite3 is locked: %s", cnf.GetSQLite3Path())
 | 
			
		||||
	} else if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	db, err := newGostDB(&cnf)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to newGostDB. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	driver := DBDriver{DB: db, Cnf: &cnf}
 | 
			
		||||
	client := RedHat{Base{driver: db, baseURL: cnf.GetURL()}}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if err := client.CloseDB(); err != nil {
 | 
			
		||||
			logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	return client.fillCvesWithRedHatAPI(r)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewGostClient make Client by family
 | 
			
		||||
func NewGostClient(cnf config.GostConf, family string, o logging.LogOpts) (Client, error) {
 | 
			
		||||
	if err := gostlog.SetLogger(o.LogToFile, o.LogDir, o.Debug, o.LogJSON); err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to set gost logger. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	db, err := newGostDB(&cnf)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to newGostDB. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	base := Base{driver: db, baseURL: cnf.GetURL()}
 | 
			
		||||
	switch family {
 | 
			
		||||
	case constant.RedHat, constant.CentOS, constant.Rocky, constant.Alma:
 | 
			
		||||
		return RedHat{Base{DBDriver: driver}}, nil
 | 
			
		||||
		return RedHat{base}, nil
 | 
			
		||||
	case constant.Debian, constant.Raspbian:
 | 
			
		||||
		return Debian{Base{DBDriver: driver}}, nil
 | 
			
		||||
		return Debian{base}, nil
 | 
			
		||||
	case constant.Ubuntu:
 | 
			
		||||
		return Ubuntu{Base{DBDriver: driver}}, nil
 | 
			
		||||
		return Ubuntu{base}, nil
 | 
			
		||||
	case constant.Windows:
 | 
			
		||||
		return Microsoft{Base{DBDriver: driver}}, nil
 | 
			
		||||
		return Microsoft{base}, nil
 | 
			
		||||
	default:
 | 
			
		||||
		return Pseudo{Base{DBDriver: driver}}, nil
 | 
			
		||||
		return Pseudo{base}, nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewGostDB returns db client for Gost
 | 
			
		||||
func newGostDB(cnf config.GostConf) (driver db.DB, locked bool, err error) {
 | 
			
		||||
func newGostDB(cnf config.VulnDictInterface) (gostdb.DB, error) {
 | 
			
		||||
	if cnf.IsFetchViaHTTP() {
 | 
			
		||||
		return nil, false, nil
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
	path := cnf.GetURL()
 | 
			
		||||
	if cnf.GetType() == "sqlite3" {
 | 
			
		||||
		path = cnf.GetSQLite3Path()
 | 
			
		||||
	}
 | 
			
		||||
	if driver, locked, err = db.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), db.Option{}); err != nil {
 | 
			
		||||
	driver, locked, err := gostdb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), gostdb.Option{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if locked {
 | 
			
		||||
			return nil, true, xerrors.Errorf("gostDB is locked. err: %w", err)
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to init gost DB. SQLite3: %s is locked. err: %w", cnf.GetSQLite3Path(), err)
 | 
			
		||||
		}
 | 
			
		||||
		return nil, false, err
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to init gost DB. DB Path: %s, err: %w", path, err)
 | 
			
		||||
	}
 | 
			
		||||
	return driver, false, nil
 | 
			
		||||
	return driver, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -18,14 +18,14 @@ type Microsoft struct {
 | 
			
		||||
 | 
			
		||||
// 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 {
 | 
			
		||||
	if ms.driver == nil {
 | 
			
		||||
		return 0, nil
 | 
			
		||||
	}
 | 
			
		||||
	cveIDs := []string{}
 | 
			
		||||
	for cveID := range r.ScannedCves {
 | 
			
		||||
		cveIDs = append(cveIDs, cveID)
 | 
			
		||||
	}
 | 
			
		||||
	msCves, err := ms.DBDriver.DB.GetMicrosoftMulti(cveIDs)
 | 
			
		||||
	msCves, err := ms.driver.GetMicrosoftMulti(cveIDs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -34,7 +34,7 @@ func (ms Microsoft) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err err
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		cveCont, mitigations := ms.ConvertToModel(&msCve)
 | 
			
		||||
		v, _ := r.ScannedCves[cveID]
 | 
			
		||||
		v := r.ScannedCves[cveID]
 | 
			
		||||
		if v.CveContents == nil {
 | 
			
		||||
			v.CveContents = models.CveContents{}
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ import (
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Pseudo is Gost client except for RedHat family and Debian
 | 
			
		||||
// Pseudo is Gost client except for RedHat family, Debian, Ubuntu and Windows
 | 
			
		||||
type Pseudo struct {
 | 
			
		||||
	Base
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,8 @@ import (
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
@@ -26,17 +27,20 @@ func (red RedHat) DetectCVEs(r *models.ScanResult, ignoreWillNotFix bool) (nCVEs
 | 
			
		||||
	if r.Family == constant.CentOS {
 | 
			
		||||
		gostRelease = strings.TrimPrefix(r.Release, "stream")
 | 
			
		||||
	}
 | 
			
		||||
	if red.DBDriver.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		prefix, _ := util.URLPathJoin(red.DBDriver.Cnf.GetURL(), "redhat", major(gostRelease), "pkgs")
 | 
			
		||||
	if red.driver == nil {
 | 
			
		||||
		prefix, err := util.URLPathJoin(red.baseURL, "redhat", major(gostRelease), "pkgs")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to join URLPath. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		responses, err := getAllUnfixedCvesViaHTTP(r, prefix)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get Unfixed CVEs via HTTP. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		for _, res := range responses {
 | 
			
		||||
			// CVE-ID: RedhatCVE
 | 
			
		||||
			cves := map[string]gostmodels.RedhatCVE{}
 | 
			
		||||
			if err := json.Unmarshal([]byte(res.json), &cves); err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
				return 0, xerrors.Errorf("Failed to unmarshal json. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			for _, cve := range cves {
 | 
			
		||||
				if newly := red.setUnfixedCveToScanResult(&cve, r); newly {
 | 
			
		||||
@@ -45,14 +49,11 @@ func (red RedHat) DetectCVEs(r *models.ScanResult, ignoreWillNotFix bool) (nCVEs
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if red.DBDriver.DB == nil {
 | 
			
		||||
			return 0, nil
 | 
			
		||||
		}
 | 
			
		||||
		for _, pack := range r.Packages {
 | 
			
		||||
			// CVE-ID: RedhatCVE
 | 
			
		||||
			cves, err := red.DBDriver.DB.GetUnfixedCvesRedhat(major(gostRelease), pack.Name, ignoreWillNotFix)
 | 
			
		||||
			cves, err := red.driver.GetUnfixedCvesRedhat(major(gostRelease), pack.Name, ignoreWillNotFix)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
				return 0, xerrors.Errorf("Failed to get Unfixed CVEs. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			for _, cve := range cves {
 | 
			
		||||
				if newly := red.setUnfixedCveToScanResult(&cve, r); newly {
 | 
			
		||||
@@ -73,8 +74,11 @@ func (red RedHat) fillCvesWithRedHatAPI(r *models.ScanResult) error {
 | 
			
		||||
		cveIDs = append(cveIDs, cveID)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if red.DBDriver.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		prefix, _ := util.URLPathJoin(config.Conf.Gost.URL, "redhat", "cves")
 | 
			
		||||
	if red.driver == nil {
 | 
			
		||||
		prefix, err := util.URLPathJoin(red.baseURL, "redhat", "cves")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		responses, err := getCvesViaHTTP(cveIDs, prefix)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
@@ -90,10 +94,7 @@ func (red RedHat) fillCvesWithRedHatAPI(r *models.ScanResult) error {
 | 
			
		||||
			red.setFixedCveToScanResult(&redCve, r)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if red.DBDriver.DB == nil {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		redCves, err := red.DBDriver.DB.GetRedhatMulti(cveIDs)
 | 
			
		||||
		redCves, err := red.driver.GetRedhatMulti(cveIDs)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,8 @@ import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
@@ -23,9 +25,12 @@ func (ubu Ubuntu) supported(version string) bool {
 | 
			
		||||
		"1404": "trusty",
 | 
			
		||||
		"1604": "xenial",
 | 
			
		||||
		"1804": "bionic",
 | 
			
		||||
		"1910": "eoan",
 | 
			
		||||
		"2004": "focal",
 | 
			
		||||
		"2010": "groovy",
 | 
			
		||||
		"2104": "hirsute",
 | 
			
		||||
		"2110": "impish",
 | 
			
		||||
		"2204": "jammy",
 | 
			
		||||
	}[version]
 | 
			
		||||
	return ok
 | 
			
		||||
}
 | 
			
		||||
@@ -53,17 +58,20 @@ func (ubu Ubuntu) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	packCvesList := []packCves{}
 | 
			
		||||
	if ubu.DBDriver.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		url, _ := util.URLPathJoin(ubu.DBDriver.Cnf.GetURL(), "ubuntu", ubuReleaseVer, "pkgs")
 | 
			
		||||
	if ubu.driver == nil {
 | 
			
		||||
		url, err := util.URLPathJoin(ubu.baseURL, "ubuntu", ubuReleaseVer, "pkgs")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to join URLPath. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		responses, err := getAllUnfixedCvesViaHTTP(r, url)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get Unfixed CVEs via HTTP. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, res := range responses {
 | 
			
		||||
			ubuCves := map[string]gostmodels.UbuntuCVE{}
 | 
			
		||||
			if err := json.Unmarshal([]byte(res.json), &ubuCves); err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
				return 0, xerrors.Errorf("Failed to unmarshal json. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			cves := []models.CveContent{}
 | 
			
		||||
			for _, ubucve := range ubuCves {
 | 
			
		||||
@@ -76,13 +84,10 @@ func (ubu Ubuntu) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if ubu.DBDriver.DB == nil {
 | 
			
		||||
			return 0, nil
 | 
			
		||||
		}
 | 
			
		||||
		for _, pack := range r.Packages {
 | 
			
		||||
			ubuCves, err := ubu.DBDriver.DB.GetUnfixedCvesUbuntu(ubuReleaseVer, pack.Name)
 | 
			
		||||
			ubuCves, err := ubu.driver.GetUnfixedCvesUbuntu(ubuReleaseVer, pack.Name)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return 0, nil
 | 
			
		||||
				return 0, xerrors.Errorf("Failed to get Unfixed CVEs For Package. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			cves := []models.CveContent{}
 | 
			
		||||
			for _, ubucve := range ubuCves {
 | 
			
		||||
@@ -97,9 +102,9 @@ func (ubu Ubuntu) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error
 | 
			
		||||
 | 
			
		||||
		// SrcPack
 | 
			
		||||
		for _, pack := range r.SrcPackages {
 | 
			
		||||
			ubuCves, err := ubu.DBDriver.DB.GetUnfixedCvesUbuntu(ubuReleaseVer, pack.Name)
 | 
			
		||||
			ubuCves, err := ubu.driver.GetUnfixedCvesUbuntu(ubuReleaseVer, pack.Name)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return 0, nil
 | 
			
		||||
				return 0, xerrors.Errorf("Failed to get Unfixed CVEs For SrcPackage. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			cves := []models.CveContent{}
 | 
			
		||||
			for _, ubucve := range ubuCves {
 | 
			
		||||
 
 | 
			
		||||
 Submodule integration updated: 75327e7431...d97bf53114
									
								
							@@ -3,13 +3,14 @@ package models
 | 
			
		||||
import (
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	ftypes "github.com/aquasecurity/fanal/types"
 | 
			
		||||
	"github.com/aquasecurity/trivy-db/pkg/db"
 | 
			
		||||
	trivyDBTypes "github.com/aquasecurity/trivy-db/pkg/types"
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/detector/library"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/types"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// LibraryScanners is an array of LibraryScanner
 | 
			
		||||
@@ -132,25 +133,53 @@ func getCveContents(cveID string, vul trivyDBTypes.Vulnerability) (contents map[
 | 
			
		||||
 | 
			
		||||
// LibraryMap is filename and library type
 | 
			
		||||
var LibraryMap = map[string]string{
 | 
			
		||||
	"package-lock.json":  "node",
 | 
			
		||||
	"yarn.lock":          "node",
 | 
			
		||||
	"Gemfile.lock":       "ruby",
 | 
			
		||||
	"Cargo.lock":         "rust",
 | 
			
		||||
	"composer.lock":      "php",
 | 
			
		||||
	"Pipfile.lock":       "python",
 | 
			
		||||
	"poetry.lock":        "python",
 | 
			
		||||
	"packages.lock.json": ".net",
 | 
			
		||||
	"go.sum":             "gomod",
 | 
			
		||||
	ftypes.NpmPkgLock:      "node",
 | 
			
		||||
	ftypes.YarnLock:        "node",
 | 
			
		||||
	ftypes.GemfileLock:     "ruby",
 | 
			
		||||
	ftypes.CargoLock:       "rust",
 | 
			
		||||
	ftypes.ComposerLock:    "php",
 | 
			
		||||
	ftypes.PipRequirements: "python",
 | 
			
		||||
	ftypes.PipfileLock:     "python",
 | 
			
		||||
	ftypes.PoetryLock:      "python",
 | 
			
		||||
	ftypes.NuGetPkgsLock:   ".net",
 | 
			
		||||
	ftypes.NuGetPkgsConfig: ".net",
 | 
			
		||||
	ftypes.GoMod:           "gomod",
 | 
			
		||||
	ftypes.GoSum:           "gomod",
 | 
			
		||||
	ftypes.MavenPom:        "java",
 | 
			
		||||
	"*.jar":                "java",
 | 
			
		||||
	"*.war":                "java",
 | 
			
		||||
	"*.ear":                "java",
 | 
			
		||||
	"*.par":                "java",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetLibraryKey returns target library key
 | 
			
		||||
func (s LibraryScanner) GetLibraryKey() string {
 | 
			
		||||
	fileName := filepath.Base(s.LockfilePath)
 | 
			
		||||
	switch s.Type {
 | 
			
		||||
	case "jar", "war", "ear":
 | 
			
		||||
	case ftypes.Bundler, ftypes.GemSpec:
 | 
			
		||||
		return "ruby"
 | 
			
		||||
	case ftypes.Cargo:
 | 
			
		||||
		return "rust"
 | 
			
		||||
	case ftypes.Composer:
 | 
			
		||||
		return "php"
 | 
			
		||||
	case ftypes.GoBinary, ftypes.GoModule:
 | 
			
		||||
		return "gomod"
 | 
			
		||||
	case ftypes.Jar, ftypes.Pom:
 | 
			
		||||
		return "java"
 | 
			
		||||
	case ftypes.Npm, ftypes.Yarn, ftypes.NodePkg, ftypes.JavaScript:
 | 
			
		||||
		return "node"
 | 
			
		||||
	case ftypes.NuGet:
 | 
			
		||||
		return ".net"
 | 
			
		||||
	case ftypes.Pipenv, ftypes.Poetry, ftypes.Pip, ftypes.PythonPkg:
 | 
			
		||||
		return "python"
 | 
			
		||||
	default:
 | 
			
		||||
		filename := filepath.Base(s.LockfilePath)
 | 
			
		||||
		switch filepath.Ext(filename) {
 | 
			
		||||
		case ".jar", ".war", ".ear", ".par":
 | 
			
		||||
			return "java"
 | 
			
		||||
		default:
 | 
			
		||||
			return LibraryMap[filename]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return LibraryMap[fileName]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LibraryFixedIn has library fixed information
 | 
			
		||||
 
 | 
			
		||||
@@ -4,10 +4,12 @@
 | 
			
		||||
package oval
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	ovaldb "github.com/vulsio/goval-dictionary/db"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Alpine is the struct of Alpine Linux
 | 
			
		||||
@@ -16,11 +18,12 @@ type Alpine struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewAlpine creates OVAL client for SUSE
 | 
			
		||||
func NewAlpine(cnf config.VulnDictInterface) Alpine {
 | 
			
		||||
func NewAlpine(driver ovaldb.DB, baseURL string) Alpine {
 | 
			
		||||
	return Alpine{
 | 
			
		||||
		Base{
 | 
			
		||||
			family: constant.Alpine,
 | 
			
		||||
			Cnf:    cnf,
 | 
			
		||||
			driver:  driver,
 | 
			
		||||
			baseURL: baseURL,
 | 
			
		||||
			family:  constant.Alpine,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -28,23 +31,13 @@ func NewAlpine(cnf config.VulnDictInterface) Alpine {
 | 
			
		||||
// FillWithOval returns scan result after updating CVE info by OVAL
 | 
			
		||||
func (o Alpine) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
	var relatedDefs ovalResult
 | 
			
		||||
	if o.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.Cnf.GetURL()); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
	if o.driver == nil {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.baseURL); err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get Definitions via HTTP. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		driver, err := newOvalDB(o.Cnf)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		defer func() {
 | 
			
		||||
			if err := driver.CloseDB(); err != nil {
 | 
			
		||||
				logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameFromOvalDB(r, o.driver); err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get Definitions from DB. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for _, defPacks := range relatedDefs.entries {
 | 
			
		||||
 
 | 
			
		||||
@@ -7,11 +7,13 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	ovaldb "github.com/vulsio/goval-dictionary/db"
 | 
			
		||||
	ovalmodels "github.com/vulsio/goval-dictionary/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -122,12 +124,13 @@ type Debian struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewDebian creates OVAL client for Debian
 | 
			
		||||
func NewDebian(cnf config.VulnDictInterface) Debian {
 | 
			
		||||
func NewDebian(driver ovaldb.DB, baseURL string) Debian {
 | 
			
		||||
	return Debian{
 | 
			
		||||
		DebianBase{
 | 
			
		||||
			Base{
 | 
			
		||||
				family: constant.Debian,
 | 
			
		||||
				Cnf:    cnf,
 | 
			
		||||
				driver:  driver,
 | 
			
		||||
				baseURL: baseURL,
 | 
			
		||||
				family:  constant.Debian,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
@@ -157,23 +160,13 @@ func (o Debian) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var relatedDefs ovalResult
 | 
			
		||||
	if o.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.Cnf.GetURL()); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
	if o.driver == nil {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.baseURL); err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get Definitions via HTTP. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		driver, err := newOvalDB(o.Cnf)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		defer func() {
 | 
			
		||||
			if err := driver.CloseDB(); err != nil {
 | 
			
		||||
				logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameFromOvalDB(r, o.driver); err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get Definitions from DB. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -213,12 +206,13 @@ type Ubuntu struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewUbuntu creates OVAL client for Debian
 | 
			
		||||
func NewUbuntu(cnf config.VulnDictInterface) Ubuntu {
 | 
			
		||||
func NewUbuntu(driver ovaldb.DB, baseURL string) Ubuntu {
 | 
			
		||||
	return Ubuntu{
 | 
			
		||||
		DebianBase{
 | 
			
		||||
			Base{
 | 
			
		||||
				family: constant.Ubuntu,
 | 
			
		||||
				Cnf:    cnf,
 | 
			
		||||
				driver:  driver,
 | 
			
		||||
				baseURL: baseURL,
 | 
			
		||||
				family:  constant.Ubuntu,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
@@ -401,6 +395,35 @@ func (o Ubuntu) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
			"linux-virtual",
 | 
			
		||||
		}
 | 
			
		||||
		return o.fillWithOval(r, kernelNamesInOval)
 | 
			
		||||
	case "22":
 | 
			
		||||
		kernelNamesInOval := []string{
 | 
			
		||||
			"linux-aws",
 | 
			
		||||
			"linux-azure",
 | 
			
		||||
			"linux-gcp",
 | 
			
		||||
			"linux-generic",
 | 
			
		||||
			"linux-gke",
 | 
			
		||||
			"linux-header-aws",
 | 
			
		||||
			"linux-header-azure",
 | 
			
		||||
			"linux-header-gcp",
 | 
			
		||||
			"linux-header-generic",
 | 
			
		||||
			"linux-header-gke",
 | 
			
		||||
			"linux-header-oracle",
 | 
			
		||||
			"linux-image-aws",
 | 
			
		||||
			"linux-image-azure",
 | 
			
		||||
			"linux-image-gcp",
 | 
			
		||||
			"linux-image-generic",
 | 
			
		||||
			"linux-image-gke",
 | 
			
		||||
			"linux-image-oracle",
 | 
			
		||||
			"linux-oracle",
 | 
			
		||||
			"linux-tools-aws",
 | 
			
		||||
			"linux-tools-azure",
 | 
			
		||||
			"linux-tools-common",
 | 
			
		||||
			"linux-tools-gcp",
 | 
			
		||||
			"linux-tools-generic",
 | 
			
		||||
			"linux-tools-gke",
 | 
			
		||||
			"linux-tools-oracle",
 | 
			
		||||
		}
 | 
			
		||||
		return o.fillWithOval(r, kernelNamesInOval)
 | 
			
		||||
	}
 | 
			
		||||
	return 0, fmt.Errorf("Ubuntu %s is not support for now", r.Release)
 | 
			
		||||
}
 | 
			
		||||
@@ -471,23 +494,13 @@ func (o Ubuntu) fillWithOval(r *models.ScanResult, kernelNamesInOval []string) (
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var relatedDefs ovalResult
 | 
			
		||||
	if o.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.Cnf.GetURL()); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
	if o.driver == nil {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.baseURL); err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get Definitions via HTTP. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		driver, err := newOvalDB(o.Cnf)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		defer func() {
 | 
			
		||||
			if err := driver.CloseDB(); err != nil {
 | 
			
		||||
				logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameFromOvalDB(r, o.driver); err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get Definitions from DB. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										102
									
								
								oval/oval.go
									
									
									
									
									
								
							
							
						
						
									
										102
									
								
								oval/oval.go
									
									
									
									
									
								
							@@ -8,14 +8,15 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"github.com/vulsio/goval-dictionary/db"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
	ovaldb "github.com/vulsio/goval-dictionary/db"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Client is the interface of OVAL client.
 | 
			
		||||
@@ -23,51 +24,56 @@ type Client interface {
 | 
			
		||||
	FillWithOval(*models.ScanResult) (int, error)
 | 
			
		||||
	CheckIfOvalFetched(string, string) (bool, error)
 | 
			
		||||
	CheckIfOvalFresh(string, string) (bool, error)
 | 
			
		||||
	CloseDB() error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Base is a base struct
 | 
			
		||||
type Base struct {
 | 
			
		||||
	family string
 | 
			
		||||
	Cnf    config.VulnDictInterface
 | 
			
		||||
	driver  ovaldb.DB
 | 
			
		||||
	baseURL string
 | 
			
		||||
	family  string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CloseDB close a DB connection
 | 
			
		||||
func (b Base) CloseDB() error {
 | 
			
		||||
	if b.driver == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return b.driver.CloseDB()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckIfOvalFetched checks if oval entries are in DB by family, release.
 | 
			
		||||
func (b Base) CheckIfOvalFetched(osFamily, release string) (fetched bool, err error) {
 | 
			
		||||
func (b Base) CheckIfOvalFetched(osFamily, release string) (bool, error) {
 | 
			
		||||
	ovalFamily, err := GetFamilyInOval(osFamily)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, xerrors.Errorf("Failed to GetFamilyInOval. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	if ovalFamily == "" {
 | 
			
		||||
		return false, nil
 | 
			
		||||
	}
 | 
			
		||||
	ovalRelease := release
 | 
			
		||||
	if osFamily == constant.CentOS {
 | 
			
		||||
		ovalRelease = strings.TrimPrefix(release, "stream")
 | 
			
		||||
	}
 | 
			
		||||
	if !b.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		driver, err := newOvalDB(b.Cnf)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, err
 | 
			
		||||
		}
 | 
			
		||||
		defer func() {
 | 
			
		||||
			if err := driver.CloseDB(); err != nil {
 | 
			
		||||
				logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
		count, err := driver.CountDefs(ovalFamily, ovalRelease)
 | 
			
		||||
	var count int
 | 
			
		||||
	if b.driver == nil {
 | 
			
		||||
		url, err := util.URLPathJoin(b.baseURL, "count", ovalFamily, ovalRelease)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, xerrors.Errorf("Failed to join URLPath. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		resp, body, errs := gorequest.New().Timeout(10 * time.Second).Get(url).End()
 | 
			
		||||
		if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
			return false, xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %+v", url, resp, errs)
 | 
			
		||||
		}
 | 
			
		||||
		if err := json.Unmarshal([]byte(body), &count); err != nil {
 | 
			
		||||
			return false, xerrors.Errorf("Failed to Unmarshal. body: %s, err: %w", body, err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		count, err = b.driver.CountDefs(ovalFamily, ovalRelease)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, xerrors.Errorf("Failed to count OVAL defs: %s, %s, %w", ovalFamily, ovalRelease, err)
 | 
			
		||||
		}
 | 
			
		||||
		logging.Log.Infof("OVAL %s %s found. defs: %d", ovalFamily, ovalRelease, count)
 | 
			
		||||
		return 0 < count, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	url, _ := util.URLPathJoin(config.Conf.OvalDict.URL, "count", ovalFamily, ovalRelease)
 | 
			
		||||
	resp, body, errs := gorequest.New().Timeout(10 * time.Second).Get(url).End()
 | 
			
		||||
	if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
		return false, xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %+v", url, resp, errs)
 | 
			
		||||
	}
 | 
			
		||||
	count := 0
 | 
			
		||||
	if err := json.Unmarshal([]byte(body), &count); err != nil {
 | 
			
		||||
		return false, xerrors.Errorf("Failed to Unmarshal. body: %s, err: %w", body, err)
 | 
			
		||||
	}
 | 
			
		||||
	logging.Log.Infof("OVAL %s %s found. defs: %d", ovalFamily, ovalRelease, count)
 | 
			
		||||
	return 0 < count, nil
 | 
			
		||||
@@ -79,35 +85,32 @@ func (b Base) CheckIfOvalFresh(osFamily, release string) (ok bool, err error) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, xerrors.Errorf("Failed to GetFamilyInOval. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	if ovalFamily == "" {
 | 
			
		||||
		return false, nil
 | 
			
		||||
	}
 | 
			
		||||
	ovalRelease := release
 | 
			
		||||
	if osFamily == constant.CentOS {
 | 
			
		||||
		ovalRelease = strings.TrimPrefix(release, "stream")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var lastModified time.Time
 | 
			
		||||
	if !b.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		driver, err := newOvalDB(b.Cnf)
 | 
			
		||||
	if b.driver == nil {
 | 
			
		||||
		url, err := util.URLPathJoin(b.baseURL, "lastmodified", ovalFamily, ovalRelease)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, err
 | 
			
		||||
			return false, xerrors.Errorf("Failed to join URLPath. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		defer func() {
 | 
			
		||||
			if err := driver.CloseDB(); err != nil {
 | 
			
		||||
				logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
		lastModified, err = driver.GetLastModified(ovalFamily, ovalRelease)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, xerrors.Errorf("Failed to GetLastModified: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		url, _ := util.URLPathJoin(config.Conf.OvalDict.URL, "lastmodified", ovalFamily, ovalRelease)
 | 
			
		||||
		resp, body, errs := gorequest.New().Timeout(10 * time.Second).Get(url).End()
 | 
			
		||||
		if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
			return false, xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %+v", url, resp, errs)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := json.Unmarshal([]byte(body), &lastModified); err != nil {
 | 
			
		||||
			return false, xerrors.Errorf("Failed to Unmarshal. body: %s, err: %w", body, err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		lastModified, err = b.driver.GetLastModified(ovalFamily, ovalRelease)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, xerrors.Errorf("Failed to GetLastModified: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	since := time.Now()
 | 
			
		||||
@@ -122,23 +125,20 @@ func (b Base) CheckIfOvalFresh(osFamily, release string) (ok bool, err error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewOvalDB returns oval db client
 | 
			
		||||
func newOvalDB(cnf config.VulnDictInterface) (driver db.DB, err error) {
 | 
			
		||||
func newOvalDB(cnf config.VulnDictInterface) (ovaldb.DB, error) {
 | 
			
		||||
	if cnf.IsFetchViaHTTP() {
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	path := cnf.GetURL()
 | 
			
		||||
	if cnf.GetType() == "sqlite3" {
 | 
			
		||||
		path = cnf.GetSQLite3Path()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	driver, locked, err := db.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), db.Option{})
 | 
			
		||||
	driver, locked, err := ovaldb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), ovaldb.Option{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if locked {
 | 
			
		||||
			err = xerrors.Errorf("SQLite3: %s is locked. err: %w", cnf.GetSQLite3Path(), err)
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to init OVAL DB. SQLite3: %s is locked. err: %w, ", cnf.GetSQLite3Path(), err)
 | 
			
		||||
		}
 | 
			
		||||
		err = xerrors.Errorf("Failed to new OVAL DB. err: %w", err)
 | 
			
		||||
		return nil, err
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to init OVAL DB. DB Path: %s, err: %w", path, err)
 | 
			
		||||
	}
 | 
			
		||||
	return driver, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								oval/pseudo.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								oval/pseudo.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
package oval
 | 
			
		||||
 | 
			
		||||
import "github.com/future-architect/vuls/models"
 | 
			
		||||
 | 
			
		||||
// Pseudo is OVAL client for Windows, FreeBSD and Pseudo
 | 
			
		||||
type Pseudo struct {
 | 
			
		||||
	Base
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewPseudo creates OVAL client for Windows, FreeBSD and Pseudo
 | 
			
		||||
func NewPseudo(family string) Pseudo {
 | 
			
		||||
	return Pseudo{
 | 
			
		||||
		Base{
 | 
			
		||||
			driver:  nil,
 | 
			
		||||
			baseURL: "",
 | 
			
		||||
			family:  family,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillWithOval is a mock function for operating systems that do not use OVAL
 | 
			
		||||
func (pse Pseudo) FillWithOval(_ *models.ScanResult) (int, error) {
 | 
			
		||||
	return 0, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -7,10 +7,12 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	ovaldb "github.com/vulsio/goval-dictionary/db"
 | 
			
		||||
	ovalmodels "github.com/vulsio/goval-dictionary/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -22,23 +24,13 @@ type RedHatBase struct {
 | 
			
		||||
// FillWithOval returns scan result after updating CVE info by OVAL
 | 
			
		||||
func (o RedHatBase) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
	var relatedDefs ovalResult
 | 
			
		||||
	if o.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.Cnf.GetURL()); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
	if o.driver == nil {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.baseURL); err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get Definitions via HTTP. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		driver, err := newOvalDB(o.Cnf)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		defer func() {
 | 
			
		||||
			if err := driver.CloseDB(); err != nil {
 | 
			
		||||
				logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameFromOvalDB(r, o.driver); err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get Definitions from DB. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -267,12 +259,13 @@ type RedHat struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewRedhat creates OVAL client for Redhat
 | 
			
		||||
func NewRedhat(cnf config.VulnDictInterface) RedHat {
 | 
			
		||||
func NewRedhat(driver ovaldb.DB, baseURL string) RedHat {
 | 
			
		||||
	return RedHat{
 | 
			
		||||
		RedHatBase{
 | 
			
		||||
			Base{
 | 
			
		||||
				family: constant.RedHat,
 | 
			
		||||
				Cnf:    cnf,
 | 
			
		||||
				driver:  driver,
 | 
			
		||||
				baseURL: baseURL,
 | 
			
		||||
				family:  constant.RedHat,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
@@ -284,12 +277,13 @@ type CentOS struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewCentOS creates OVAL client for CentOS
 | 
			
		||||
func NewCentOS(cnf config.VulnDictInterface) CentOS {
 | 
			
		||||
func NewCentOS(driver ovaldb.DB, baseURL string) CentOS {
 | 
			
		||||
	return CentOS{
 | 
			
		||||
		RedHatBase{
 | 
			
		||||
			Base{
 | 
			
		||||
				family: constant.CentOS,
 | 
			
		||||
				Cnf:    cnf,
 | 
			
		||||
				driver:  driver,
 | 
			
		||||
				baseURL: baseURL,
 | 
			
		||||
				family:  constant.CentOS,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
@@ -301,12 +295,13 @@ type Oracle struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewOracle creates OVAL client for Oracle
 | 
			
		||||
func NewOracle(cnf config.VulnDictInterface) Oracle {
 | 
			
		||||
func NewOracle(driver ovaldb.DB, baseURL string) Oracle {
 | 
			
		||||
	return Oracle{
 | 
			
		||||
		RedHatBase{
 | 
			
		||||
			Base{
 | 
			
		||||
				family: constant.Oracle,
 | 
			
		||||
				Cnf:    cnf,
 | 
			
		||||
				driver:  driver,
 | 
			
		||||
				baseURL: baseURL,
 | 
			
		||||
				family:  constant.Oracle,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
@@ -319,12 +314,13 @@ type Amazon struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewAmazon creates OVAL client for Amazon Linux
 | 
			
		||||
func NewAmazon(cnf config.VulnDictInterface) Amazon {
 | 
			
		||||
func NewAmazon(driver ovaldb.DB, baseURL string) Amazon {
 | 
			
		||||
	return Amazon{
 | 
			
		||||
		RedHatBase{
 | 
			
		||||
			Base{
 | 
			
		||||
				family: constant.Amazon,
 | 
			
		||||
				Cnf:    cnf,
 | 
			
		||||
				driver:  driver,
 | 
			
		||||
				baseURL: baseURL,
 | 
			
		||||
				family:  constant.Amazon,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
@@ -337,12 +333,13 @@ type Alma struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewAlma creates OVAL client for Alma Linux
 | 
			
		||||
func NewAlma(cnf config.VulnDictInterface) Alma {
 | 
			
		||||
func NewAlma(driver ovaldb.DB, baseURL string) Alma {
 | 
			
		||||
	return Alma{
 | 
			
		||||
		RedHatBase{
 | 
			
		||||
			Base{
 | 
			
		||||
				family: constant.Alma,
 | 
			
		||||
				Cnf:    cnf,
 | 
			
		||||
				driver:  driver,
 | 
			
		||||
				baseURL: baseURL,
 | 
			
		||||
				family:  constant.Alma,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
@@ -355,12 +352,13 @@ type Rocky struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewRocky creates OVAL client for Rocky Linux
 | 
			
		||||
func NewRocky(cnf config.VulnDictInterface) Rocky {
 | 
			
		||||
func NewRocky(driver ovaldb.DB, baseURL string) Rocky {
 | 
			
		||||
	return Rocky{
 | 
			
		||||
		RedHatBase{
 | 
			
		||||
			Base{
 | 
			
		||||
				family: constant.Rocky,
 | 
			
		||||
				Cnf:    cnf,
 | 
			
		||||
				driver:  driver,
 | 
			
		||||
				baseURL: baseURL,
 | 
			
		||||
				family:  constant.Rocky,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
@@ -373,12 +371,13 @@ type Fedora struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewFedora creates OVAL client for Fedora Linux
 | 
			
		||||
func NewFedora(cnf config.VulnDictInterface) Fedora {
 | 
			
		||||
func NewFedora(driver ovaldb.DB, baseURL string) Fedora {
 | 
			
		||||
	return Fedora{
 | 
			
		||||
		RedHatBase{
 | 
			
		||||
			Base{
 | 
			
		||||
				family: constant.Fedora,
 | 
			
		||||
				Cnf:    cnf,
 | 
			
		||||
				driver:  driver,
 | 
			
		||||
				baseURL: baseURL,
 | 
			
		||||
				family:  constant.Fedora,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										86
									
								
								oval/suse.go
									
									
									
									
									
								
							
							
						
						
									
										86
									
								
								oval/suse.go
									
									
									
									
									
								
							@@ -6,9 +6,11 @@ package oval
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	ovaldb "github.com/vulsio/goval-dictionary/db"
 | 
			
		||||
	ovalmodels "github.com/vulsio/goval-dictionary/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -18,11 +20,13 @@ type SUSE struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewSUSE creates OVAL client for SUSE
 | 
			
		||||
func NewSUSE(cnf config.VulnDictInterface, family string) SUSE {
 | 
			
		||||
func NewSUSE(driver ovaldb.DB, baseURL, family string) SUSE {
 | 
			
		||||
	// TODO implement other family
 | 
			
		||||
	return SUSE{
 | 
			
		||||
		Base{
 | 
			
		||||
			family: family,
 | 
			
		||||
			Cnf:    cnf,
 | 
			
		||||
			driver:  driver,
 | 
			
		||||
			baseURL: baseURL,
 | 
			
		||||
			family:  family,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -30,23 +34,13 @@ func NewSUSE(cnf config.VulnDictInterface, family string) SUSE {
 | 
			
		||||
// FillWithOval returns scan result after updating CVE info by OVAL
 | 
			
		||||
func (o SUSE) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
	var relatedDefs ovalResult
 | 
			
		||||
	if o.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.Cnf.GetURL()); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
	if o.driver == nil {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.baseURL); err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get Definitions via HTTP. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		driver, err := newOvalDB(o.Cnf)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		defer func() {
 | 
			
		||||
			if err := driver.CloseDB(); err != nil {
 | 
			
		||||
				logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameFromOvalDB(r, o.driver); err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get Definitions from DB. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for _, defPacks := range relatedDefs.entries {
 | 
			
		||||
@@ -65,27 +59,30 @@ func (o SUSE) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o SUSE) update(r *models.ScanResult, defpacks defPacks) {
 | 
			
		||||
	ovalContent := *o.convertToModel(&defpacks.def)
 | 
			
		||||
	ovalContent := o.convertToModel(&defpacks.def)
 | 
			
		||||
	if ovalContent == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	ovalContent.Type = models.NewCveContentType(o.family)
 | 
			
		||||
	vinfo, ok := r.ScannedCves[defpacks.def.Title]
 | 
			
		||||
	vinfo, ok := r.ScannedCves[ovalContent.CveID]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		logging.Log.Debugf("%s is newly detected by OVAL", defpacks.def.Title)
 | 
			
		||||
		logging.Log.Debugf("%s is newly detected by OVAL", ovalContent.CveID)
 | 
			
		||||
		vinfo = models.VulnInfo{
 | 
			
		||||
			CveID:       defpacks.def.Title,
 | 
			
		||||
			CveID:       ovalContent.CveID,
 | 
			
		||||
			Confidences: models.Confidences{models.OvalMatch},
 | 
			
		||||
			CveContents: models.NewCveContents(ovalContent),
 | 
			
		||||
			CveContents: models.NewCveContents(*ovalContent),
 | 
			
		||||
		}
 | 
			
		||||
	} 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.Title)
 | 
			
		||||
			logging.Log.Debugf("%s OVAL will be overwritten", ovalContent.CveID)
 | 
			
		||||
		} else {
 | 
			
		||||
			logging.Log.Debugf("%s is also detected by OVAL", defpacks.def.Title)
 | 
			
		||||
			logging.Log.Debugf("%s is also detected by OVAL", ovalContent.CveID)
 | 
			
		||||
			cveContents = models.CveContents{}
 | 
			
		||||
		}
 | 
			
		||||
		vinfo.Confidences.AppendIfMissing(models.OvalMatch)
 | 
			
		||||
		cveContents[ctype] = []models.CveContent{ovalContent}
 | 
			
		||||
		cveContents[ctype] = []models.CveContent{*ovalContent}
 | 
			
		||||
		vinfo.CveContents = cveContents
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -105,10 +102,15 @@ func (o SUSE) update(r *models.ScanResult, defpacks defPacks) {
 | 
			
		||||
	}
 | 
			
		||||
	vinfo.AffectedPackages = collectBinpkgFixstat.toPackStatuses()
 | 
			
		||||
	vinfo.AffectedPackages.Sort()
 | 
			
		||||
	r.ScannedCves[defpacks.def.Title] = vinfo
 | 
			
		||||
	r.ScannedCves[ovalContent.CveID] = vinfo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o SUSE) convertToModel(def *ovalmodels.Definition) *models.CveContent {
 | 
			
		||||
	if len(def.Advisory.Cves) != 1 {
 | 
			
		||||
		logging.Log.Warnf("Unknown Oval format. Please register the issue as it needs to be investigated. https://github.com/vulsio/goval-dictionary/issues family: %s, defID: %s", o.family, def.DefinitionID)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	refs := []models.Reference{}
 | 
			
		||||
	for _, r := range def.References {
 | 
			
		||||
		refs = append(refs, models.Reference{
 | 
			
		||||
@@ -117,23 +119,15 @@ func (o SUSE) convertToModel(def *ovalmodels.Definition) *models.CveContent {
 | 
			
		||||
			RefID:  r.RefID,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	cveCont := models.CveContent{
 | 
			
		||||
		CveID:      def.Title,
 | 
			
		||||
		Title:      def.Title,
 | 
			
		||||
		Summary:    def.Description,
 | 
			
		||||
		References: refs,
 | 
			
		||||
	cve := def.Advisory.Cves[0]
 | 
			
		||||
	score3, vec3 := parseCvss3(cve.Cvss3)
 | 
			
		||||
	return &models.CveContent{
 | 
			
		||||
		Title:         def.Title,
 | 
			
		||||
		Summary:       def.Description,
 | 
			
		||||
		CveID:         cve.CveID,
 | 
			
		||||
		Cvss3Score:    score3,
 | 
			
		||||
		Cvss3Vector:   vec3,
 | 
			
		||||
		Cvss3Severity: cve.Impact,
 | 
			
		||||
		References:    refs,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if 0 < len(def.Advisory.Cves) {
 | 
			
		||||
		if len(def.Advisory.Cves) == 1 {
 | 
			
		||||
			cve := def.Advisory.Cves[0]
 | 
			
		||||
			score3, vec3 := parseCvss3(cve.Cvss3)
 | 
			
		||||
			cveCont.Cvss3Score = score3
 | 
			
		||||
			cveCont.Cvss3Vector = vec3
 | 
			
		||||
			cveCont.Cvss3Severity = cve.Impact
 | 
			
		||||
		} else {
 | 
			
		||||
			logging.Log.Warnf("Unknown Oval format. Please register the issue as it needs to be investigated. https://github.com/future-architect/vuls/issues family: %s, defID: %s", o.family, def.DefinitionID)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return &cveCont
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										67
									
								
								oval/util.go
									
									
									
									
									
								
							
							
						
						
									
										67
									
								
								oval/util.go
									
									
									
									
									
								
							@@ -14,18 +14,20 @@ import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cenkalti/backoff"
 | 
			
		||||
	apkver "github.com/knqyf263/go-apk-version"
 | 
			
		||||
	debver "github.com/knqyf263/go-deb-version"
 | 
			
		||||
	rpmver "github.com/knqyf263/go-rpm-version"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	apkver "github.com/knqyf263/go-apk-version"
 | 
			
		||||
	debver "github.com/knqyf263/go-deb-version"
 | 
			
		||||
	rpmver "github.com/knqyf263/go-rpm-version"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"github.com/vulsio/goval-dictionary/db"
 | 
			
		||||
	ovaldb "github.com/vulsio/goval-dictionary/db"
 | 
			
		||||
	ovallog "github.com/vulsio/goval-dictionary/log"
 | 
			
		||||
	ovalmodels "github.com/vulsio/goval-dictionary/models"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ovalResult struct {
 | 
			
		||||
@@ -245,7 +247,7 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getDefsByPackNameFromOvalDB(driver db.DB, r *models.ScanResult) (relatedDefs ovalResult, err error) {
 | 
			
		||||
func getDefsByPackNameFromOvalDB(r *models.ScanResult, driver ovaldb.DB) (relatedDefs ovalResult, err error) {
 | 
			
		||||
	requests := []request{}
 | 
			
		||||
	for _, pack := range r.Packages {
 | 
			
		||||
		requests = append(requests, request{
 | 
			
		||||
@@ -441,22 +443,22 @@ func lessThan(family, newVer string, packInOVAL ovalmodels.Package) (bool, error
 | 
			
		||||
		constant.Raspbian:
 | 
			
		||||
		vera, err := debver.NewVersion(newVer)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, err
 | 
			
		||||
			return false, xerrors.Errorf("Failed to parse version. version: %s, err: %w", newVer, err)
 | 
			
		||||
		}
 | 
			
		||||
		verb, err := debver.NewVersion(packInOVAL.Version)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, err
 | 
			
		||||
			return false, xerrors.Errorf("Failed to parse version. version: %s, err: %w", packInOVAL.Version, err)
 | 
			
		||||
		}
 | 
			
		||||
		return vera.LessThan(verb), nil
 | 
			
		||||
 | 
			
		||||
	case constant.Alpine:
 | 
			
		||||
		vera, err := apkver.NewVersion(newVer)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, err
 | 
			
		||||
			return false, xerrors.Errorf("Failed to parse version. version: %s, err: %w", newVer, err)
 | 
			
		||||
		}
 | 
			
		||||
		verb, err := apkver.NewVersion(packInOVAL.Version)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, err
 | 
			
		||||
			return false, xerrors.Errorf("Failed to parse version. version: %s, err: %w", packInOVAL.Version, err)
 | 
			
		||||
		}
 | 
			
		||||
		return vera.LessThan(verb), nil
 | 
			
		||||
 | 
			
		||||
@@ -491,40 +493,49 @@ func rhelRebuildOSVersionToRHEL(ver string) string {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewOVALClient returns a client for OVAL database
 | 
			
		||||
func NewOVALClient(family string, cnf config.GovalDictConf) (Client, error) {
 | 
			
		||||
func NewOVALClient(family string, cnf config.GovalDictConf, o logging.LogOpts) (Client, error) {
 | 
			
		||||
	if err := ovallog.SetLogger(o.LogToFile, o.LogDir, o.Debug, o.LogJSON); err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to set goval-dictionary logger. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	driver, err := newOvalDB(&cnf)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to newOvalDB. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch family {
 | 
			
		||||
	case constant.Debian, constant.Raspbian:
 | 
			
		||||
		return NewDebian(&cnf), nil
 | 
			
		||||
		return NewDebian(driver, cnf.GetURL()), nil
 | 
			
		||||
	case constant.Ubuntu:
 | 
			
		||||
		return NewUbuntu(&cnf), nil
 | 
			
		||||
		return NewUbuntu(driver, cnf.GetURL()), nil
 | 
			
		||||
	case constant.RedHat:
 | 
			
		||||
		return NewRedhat(&cnf), nil
 | 
			
		||||
		return NewRedhat(driver, cnf.GetURL()), nil
 | 
			
		||||
	case constant.CentOS:
 | 
			
		||||
		return NewCentOS(&cnf), nil
 | 
			
		||||
		return NewCentOS(driver, cnf.GetURL()), nil
 | 
			
		||||
	case constant.Alma:
 | 
			
		||||
		return NewAlma(&cnf), nil
 | 
			
		||||
		return NewAlma(driver, cnf.GetURL()), nil
 | 
			
		||||
	case constant.Rocky:
 | 
			
		||||
		return NewRocky(&cnf), nil
 | 
			
		||||
		return NewRocky(driver, cnf.GetURL()), nil
 | 
			
		||||
	case constant.Oracle:
 | 
			
		||||
		return NewOracle(&cnf), nil
 | 
			
		||||
		return NewOracle(driver, cnf.GetURL()), nil
 | 
			
		||||
	case constant.OpenSUSE:
 | 
			
		||||
		return NewSUSE(&cnf, constant.OpenSUSE), nil
 | 
			
		||||
		return NewSUSE(driver, cnf.GetURL(), constant.OpenSUSE), nil
 | 
			
		||||
	case constant.OpenSUSELeap:
 | 
			
		||||
		return NewSUSE(&cnf, constant.OpenSUSELeap), nil
 | 
			
		||||
		return NewSUSE(driver, cnf.GetURL(), constant.OpenSUSELeap), nil
 | 
			
		||||
	case constant.SUSEEnterpriseServer:
 | 
			
		||||
		return NewSUSE(&cnf, constant.SUSEEnterpriseServer), nil
 | 
			
		||||
		return NewSUSE(driver, cnf.GetURL(), constant.SUSEEnterpriseServer), nil
 | 
			
		||||
	case constant.SUSEEnterpriseDesktop:
 | 
			
		||||
		return NewSUSE(&cnf, constant.SUSEEnterpriseDesktop), nil
 | 
			
		||||
		return NewSUSE(driver, cnf.GetURL(), constant.SUSEEnterpriseDesktop), nil
 | 
			
		||||
	case constant.Alpine:
 | 
			
		||||
		return NewAlpine(&cnf), nil
 | 
			
		||||
		return NewAlpine(driver, cnf.GetURL()), nil
 | 
			
		||||
	case constant.Amazon:
 | 
			
		||||
		return NewAmazon(&cnf), nil
 | 
			
		||||
		return NewAmazon(driver, cnf.GetURL()), nil
 | 
			
		||||
	case constant.Fedora:
 | 
			
		||||
		return NewFedora(&cnf), nil
 | 
			
		||||
		return NewFedora(driver, cnf.GetURL()), nil
 | 
			
		||||
	case constant.FreeBSD, constant.Windows:
 | 
			
		||||
		return nil, nil
 | 
			
		||||
		return NewPseudo(family), nil
 | 
			
		||||
	case constant.ServerTypePseudo:
 | 
			
		||||
		return nil, nil
 | 
			
		||||
		return NewPseudo(family), nil
 | 
			
		||||
	default:
 | 
			
		||||
		if family == "" {
 | 
			
		||||
			return nil, xerrors.New("Probably an error occurred during scanning. Check the error message")
 | 
			
		||||
 
 | 
			
		||||
@@ -255,6 +255,7 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
		// v2max := vinfo.MaxCvss2Score().Value.Score
 | 
			
		||||
		// v3max := vinfo.MaxCvss3Score().Value.Score
 | 
			
		||||
 | 
			
		||||
		packnames := strings.Join(vinfo.AffectedPackages.Names(), ", ")
 | 
			
		||||
		// packname := vinfo.AffectedPackages.FormatTuiSummary()
 | 
			
		||||
		// packname += strings.Join(vinfo.CpeURIs, ", ")
 | 
			
		||||
 | 
			
		||||
@@ -263,12 +264,12 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
			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-"))
 | 
			
		||||
		}
 | 
			
		||||
		// 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-"))
 | 
			
		||||
		// }
 | 
			
		||||
 | 
			
		||||
		data = append(data, []string{
 | 
			
		||||
			vinfo.CveIDDiffFormat(),
 | 
			
		||||
@@ -279,7 +280,7 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
			exploits,
 | 
			
		||||
			fmt.Sprintf("%9s", vinfo.AlertDict.FormatSource()),
 | 
			
		||||
			fmt.Sprintf("%7s", vinfo.PatchStatus(r.Packages)),
 | 
			
		||||
			link,
 | 
			
		||||
			packnames,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -294,9 +295,11 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
		"PoC",
 | 
			
		||||
		"Alert",
 | 
			
		||||
		"Fixed",
 | 
			
		||||
		"NVD",
 | 
			
		||||
		// "NVD",
 | 
			
		||||
		"Packages",
 | 
			
		||||
	})
 | 
			
		||||
	table.SetBorder(true)
 | 
			
		||||
	table.SetRowLine(true)
 | 
			
		||||
	table.AppendBulk(data)
 | 
			
		||||
	table.Render()
 | 
			
		||||
	return fmt.Sprintf("%s\n%s", header, b.String())
 | 
			
		||||
@@ -461,9 +464,16 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
		for _, url := range cweURLs {
 | 
			
		||||
			data = append(data, []string{"CWE", url})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		m := map[string]struct{}{}
 | 
			
		||||
		for _, exploit := range vuln.Exploits {
 | 
			
		||||
			if _, ok := m[exploit.URL]; ok {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			data = append(data, []string{string(exploit.ExploitType), exploit.URL})
 | 
			
		||||
			m[exploit.URL] = struct{}{}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, url := range top10URLs {
 | 
			
		||||
			data = append(data, []string{"OWASP Top10", url})
 | 
			
		||||
		}
 | 
			
		||||
@@ -613,7 +623,7 @@ func getPlusDiffCves(previous, current models.ScanResult) models.VulnInfos {
 | 
			
		||||
		previousCveIDsSet[previousVulnInfo.CveID] = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	new := models.VulnInfos{}
 | 
			
		||||
	newer := models.VulnInfos{}
 | 
			
		||||
	updated := models.VulnInfos{}
 | 
			
		||||
	for _, v := range current.ScannedCves {
 | 
			
		||||
		if previousCveIDsSet[v.CveID] {
 | 
			
		||||
@@ -633,17 +643,17 @@ func getPlusDiffCves(previous, current models.ScanResult) models.VulnInfos {
 | 
			
		||||
				logging.Log.Debugf("same: %s", v.CveID)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			logging.Log.Debugf("new: %s", v.CveID)
 | 
			
		||||
			logging.Log.Debugf("newer: %s", v.CveID)
 | 
			
		||||
			v.DiffStatus = models.DiffPlus
 | 
			
		||||
			new[v.CveID] = v
 | 
			
		||||
			newer[v.CveID] = v
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(updated) == 0 && len(new) == 0 {
 | 
			
		||||
	if len(updated) == 0 && len(newer) == 0 {
 | 
			
		||||
		logging.Log.Infof("%s: There are %d vulnerabilities, but no difference between current result and previous one.", current.FormatServerName(), len(current.ScannedCves))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for cveID, vuln := range new {
 | 
			
		||||
	for cveID, vuln := range newer {
 | 
			
		||||
		updated[cveID] = vuln
 | 
			
		||||
	}
 | 
			
		||||
	return updated
 | 
			
		||||
 
 | 
			
		||||
@@ -19,8 +19,8 @@ func TestMain(m *testing.M) {
 | 
			
		||||
 | 
			
		||||
func TestIsCveInfoUpdated(t *testing.T) {
 | 
			
		||||
	f := "2006-01-02"
 | 
			
		||||
	old, _ := time.Parse(f, "2015-12-15")
 | 
			
		||||
	new, _ := time.Parse(f, "2015-12-16")
 | 
			
		||||
	base, _ := time.Parse(f, "2015-12-15")
 | 
			
		||||
	newer, _ := time.Parse(f, "2015-12-16")
 | 
			
		||||
 | 
			
		||||
	type In struct {
 | 
			
		||||
		cveID string
 | 
			
		||||
@@ -78,7 +78,7 @@ func TestIsCveInfoUpdated(t *testing.T) {
 | 
			
		||||
								models.CveContent{
 | 
			
		||||
									Type:         models.Jvn,
 | 
			
		||||
									CveID:        "CVE-2017-0002",
 | 
			
		||||
									LastModified: old,
 | 
			
		||||
									LastModified: base,
 | 
			
		||||
								},
 | 
			
		||||
							),
 | 
			
		||||
						},
 | 
			
		||||
@@ -92,7 +92,7 @@ func TestIsCveInfoUpdated(t *testing.T) {
 | 
			
		||||
								models.CveContent{
 | 
			
		||||
									Type:         models.Jvn,
 | 
			
		||||
									CveID:        "CVE-2017-0002",
 | 
			
		||||
									LastModified: old,
 | 
			
		||||
									LastModified: base,
 | 
			
		||||
								},
 | 
			
		||||
							),
 | 
			
		||||
						},
 | 
			
		||||
@@ -114,7 +114,7 @@ func TestIsCveInfoUpdated(t *testing.T) {
 | 
			
		||||
								models.CveContent{
 | 
			
		||||
									Type:         models.Nvd,
 | 
			
		||||
									CveID:        "CVE-2017-0002",
 | 
			
		||||
									LastModified: new,
 | 
			
		||||
									LastModified: newer,
 | 
			
		||||
								},
 | 
			
		||||
							),
 | 
			
		||||
						},
 | 
			
		||||
@@ -129,7 +129,7 @@ func TestIsCveInfoUpdated(t *testing.T) {
 | 
			
		||||
								models.CveContent{
 | 
			
		||||
									Type:         models.Nvd,
 | 
			
		||||
									CveID:        "CVE-2017-0002",
 | 
			
		||||
									LastModified: old,
 | 
			
		||||
									LastModified: base,
 | 
			
		||||
								},
 | 
			
		||||
							),
 | 
			
		||||
						},
 | 
			
		||||
@@ -151,7 +151,7 @@ func TestIsCveInfoUpdated(t *testing.T) {
 | 
			
		||||
								models.CveContent{
 | 
			
		||||
									Type:         models.Nvd,
 | 
			
		||||
									CveID:        "CVE-2017-0002",
 | 
			
		||||
									LastModified: old,
 | 
			
		||||
									LastModified: base,
 | 
			
		||||
								},
 | 
			
		||||
							),
 | 
			
		||||
						},
 | 
			
		||||
 
 | 
			
		||||
@@ -592,12 +592,12 @@ func (l *base) scanLibraries() (err error) {
 | 
			
		||||
	if l.ServerInfo.FindLock {
 | 
			
		||||
		findopt := ""
 | 
			
		||||
		for filename := range models.LibraryMap {
 | 
			
		||||
			findopt += fmt.Sprintf("-name %q -o ", "*"+filename)
 | 
			
		||||
			findopt += fmt.Sprintf("-name %q -o ", filename)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// delete last "-o "
 | 
			
		||||
		// find / -name "*package-lock.json" -o -name "*yarn.lock" ... 2>&1 | grep -v "find: "
 | 
			
		||||
		cmd := fmt.Sprintf(`find / ` + findopt[:len(findopt)-3] + ` 2>&1 | grep -v "find: "`)
 | 
			
		||||
		// find / -type f -and \( -name "package-lock.json" -o -name "yarn.lock" ... \) 2>&1 | grep -v "find: "
 | 
			
		||||
		cmd := fmt.Sprintf(`find / -type f -and \( ` + findopt[:len(findopt)-3] + ` \) 2>&1 | grep -v "find: "`)
 | 
			
		||||
		r := exec(l.ServerInfo, cmd, noSudo)
 | 
			
		||||
		if r.ExitStatus != 0 && r.ExitStatus != 1 {
 | 
			
		||||
			return xerrors.Errorf("Failed to find lock files")
 | 
			
		||||
@@ -686,6 +686,7 @@ func AnalyzeLibraries(ctx context.Context, libFilemap map[string]LibFile, isOffl
 | 
			
		||||
		analyzer.TypeJSON,
 | 
			
		||||
		analyzer.TypeDockerfile,
 | 
			
		||||
		analyzer.TypeHCL,
 | 
			
		||||
		analyzer.TypeSecret,
 | 
			
		||||
	}
 | 
			
		||||
	anal := analyzer.NewAnalyzerGroup(analyzer.GroupBuiltin, disabledAnalyzers)
 | 
			
		||||
 | 
			
		||||
@@ -701,6 +702,7 @@ func AnalyzeLibraries(ctx context.Context, libFilemap map[string]LibFile, isOffl
 | 
			
		||||
			path,
 | 
			
		||||
			&DummyFileInfo{size: int64(len(f.Contents)), filemode: f.Filemode},
 | 
			
		||||
			func() (dio.ReadSeekCloserAt, error) { return dio.NopCloser(bytes.NewReader(f.Contents)), nil },
 | 
			
		||||
			nil,
 | 
			
		||||
			analyzer.AnalysisOptions{Offline: isOffline},
 | 
			
		||||
		); err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to get libs. err: %w", err)
 | 
			
		||||
 
 | 
			
		||||
@@ -784,49 +784,49 @@ func (o *redhatBase) getOwnerPkgs(paths []string) (names []string, _ error) {
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) rpmQa() string {
 | 
			
		||||
	const old = `rpm -qa --queryformat "%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH}\n"`
 | 
			
		||||
	const new = `rpm -qa --queryformat "%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH}\n"`
 | 
			
		||||
	const newer = `rpm -qa --queryformat "%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH}\n"`
 | 
			
		||||
	switch o.Distro.Family {
 | 
			
		||||
	case constant.OpenSUSE:
 | 
			
		||||
		if o.Distro.Release == "tumbleweed" {
 | 
			
		||||
			return new
 | 
			
		||||
			return newer
 | 
			
		||||
		}
 | 
			
		||||
		return old
 | 
			
		||||
	case constant.OpenSUSELeap:
 | 
			
		||||
		return new
 | 
			
		||||
		return newer
 | 
			
		||||
	case constant.SUSEEnterpriseServer, constant.SUSEEnterpriseDesktop:
 | 
			
		||||
		if v, _ := o.Distro.MajorVersion(); v < 12 {
 | 
			
		||||
			return old
 | 
			
		||||
		}
 | 
			
		||||
		return new
 | 
			
		||||
		return newer
 | 
			
		||||
	default:
 | 
			
		||||
		if v, _ := o.Distro.MajorVersion(); v < 6 {
 | 
			
		||||
			return old
 | 
			
		||||
		}
 | 
			
		||||
		return new
 | 
			
		||||
		return newer
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) rpmQf() string {
 | 
			
		||||
	const old = `rpm -qf --queryformat "%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH}\n" `
 | 
			
		||||
	const new = `rpm -qf --queryformat "%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH}\n" `
 | 
			
		||||
	const newer = `rpm -qf --queryformat "%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH}\n" `
 | 
			
		||||
	switch o.Distro.Family {
 | 
			
		||||
	case constant.OpenSUSE:
 | 
			
		||||
		if o.Distro.Release == "tumbleweed" {
 | 
			
		||||
			return new
 | 
			
		||||
			return newer
 | 
			
		||||
		}
 | 
			
		||||
		return old
 | 
			
		||||
	case constant.OpenSUSELeap:
 | 
			
		||||
		return new
 | 
			
		||||
		return newer
 | 
			
		||||
	case constant.SUSEEnterpriseServer, constant.SUSEEnterpriseDesktop:
 | 
			
		||||
		if v, _ := o.Distro.MajorVersion(); v < 12 {
 | 
			
		||||
			return old
 | 
			
		||||
		}
 | 
			
		||||
		return new
 | 
			
		||||
		return newer
 | 
			
		||||
	default:
 | 
			
		||||
		if v, _ := o.Distro.MajorVersion(); v < 6 {
 | 
			
		||||
			return old
 | 
			
		||||
		}
 | 
			
		||||
		return new
 | 
			
		||||
		return newer
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -237,6 +237,8 @@ func ParseInstalledPkgs(distro config.Distro, kernel models.Kernel, pkgList stri
 | 
			
		||||
		osType = &amazon{redhatBase: redhatBase{base: base}}
 | 
			
		||||
	case constant.Fedora:
 | 
			
		||||
		osType = &fedora{redhatBase: redhatBase{base: base}}
 | 
			
		||||
	case constant.OpenSUSE, constant.OpenSUSELeap, constant.SUSEEnterpriseServer, constant.SUSEEnterpriseDesktop:
 | 
			
		||||
		osType = &suse{redhatBase: redhatBase{base: base}}
 | 
			
		||||
	default:
 | 
			
		||||
		return models.Packages{}, models.SrcPackages{}, xerrors.Errorf("Server mode for %s is not implemented yet", base.Distro.Family)
 | 
			
		||||
	}
 | 
			
		||||
@@ -456,7 +458,7 @@ func validateSSHConfig(c *config.ServerInfo) error {
 | 
			
		||||
	sshKeyScanArgs = append(sshKeyScanArgs, fmt.Sprintf("%s >> %s", hostname, knownHostsPaths[0]))
 | 
			
		||||
	sshConnCmd := fmt.Sprintf("ssh %s", strings.Join(sshConnArgs, " "))
 | 
			
		||||
	sshKeyScancmd := fmt.Sprintf("ssh-keyscan %s", strings.Join(sshKeyScanArgs, " "))
 | 
			
		||||
	return xerrors.Errorf("Failed to find the host in known_hosts. Plaese exec `$ %s` or `$ %s`", sshConnCmd, sshKeyScancmd)
 | 
			
		||||
	return xerrors.Errorf("Failed to find the host in known_hosts. Please exec `$ %s` or `$ %s`", sshConnCmd, sshKeyScancmd)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s Scanner) detectContainerOSes(hosts []osTypeInterface) (actives, inactives []osTypeInterface) {
 | 
			
		||||
@@ -580,49 +582,42 @@ func (s Scanner) detectContainerOSesOnServer(containerHost osTypeInterface) (ose
 | 
			
		||||
	return oses
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s Scanner) detectOS(c config.ServerInfo) (osType osTypeInterface) {
 | 
			
		||||
	var itsMe bool
 | 
			
		||||
	var fatalErr error
 | 
			
		||||
 | 
			
		||||
	if itsMe, osType, _ = detectPseudo(c); itsMe {
 | 
			
		||||
		return
 | 
			
		||||
func (s Scanner) detectOS(c config.ServerInfo) osTypeInterface {
 | 
			
		||||
	if itsMe, osType, _ := detectPseudo(c); itsMe {
 | 
			
		||||
		return osType
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	itsMe, osType, fatalErr = s.detectDebianWithRetry(c)
 | 
			
		||||
	if fatalErr != nil {
 | 
			
		||||
		osType.setErrs([]error{
 | 
			
		||||
			xerrors.Errorf("Failed to detect OS: %w", fatalErr)})
 | 
			
		||||
		return
 | 
			
		||||
	if itsMe, osType, fatalErr := s.detectDebianWithRetry(c); fatalErr != nil {
 | 
			
		||||
		osType.setErrs([]error{xerrors.Errorf("Failed to detect OS: %w", fatalErr)})
 | 
			
		||||
		return osType
 | 
			
		||||
	} else if itsMe {
 | 
			
		||||
		logging.Log.Debugf("Debian based Linux. Host: %s:%s", c.Host, c.Port)
 | 
			
		||||
		return osType
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if itsMe {
 | 
			
		||||
		logging.Log.Debugf("Debian like Linux. Host: %s:%s", c.Host, c.Port)
 | 
			
		||||
		return
 | 
			
		||||
	if itsMe, osType := detectRedhat(c); itsMe {
 | 
			
		||||
		logging.Log.Debugf("Redhat based Linux. Host: %s:%s", c.Host, c.Port)
 | 
			
		||||
		return osType
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if itsMe, osType = detectRedhat(c); itsMe {
 | 
			
		||||
		logging.Log.Debugf("Redhat like Linux. Host: %s:%s", c.Host, c.Port)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if itsMe, osType = detectSUSE(c); itsMe {
 | 
			
		||||
	if itsMe, osType := detectSUSE(c); itsMe {
 | 
			
		||||
		logging.Log.Debugf("SUSE Linux. Host: %s:%s", c.Host, c.Port)
 | 
			
		||||
		return
 | 
			
		||||
		return osType
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if itsMe, osType = detectFreebsd(c); itsMe {
 | 
			
		||||
	if itsMe, osType := detectFreebsd(c); itsMe {
 | 
			
		||||
		logging.Log.Debugf("FreeBSD. Host: %s:%s", c.Host, c.Port)
 | 
			
		||||
		return
 | 
			
		||||
		return osType
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if itsMe, osType = detectAlpine(c); itsMe {
 | 
			
		||||
	if itsMe, osType := detectAlpine(c); itsMe {
 | 
			
		||||
		logging.Log.Debugf("Alpine. Host: %s:%s", c.Host, c.Port)
 | 
			
		||||
		return
 | 
			
		||||
		return osType
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//TODO darwin https://github.com/mizzy/specinfra/blob/master/lib/specinfra/helper/detect_os/darwin.rb
 | 
			
		||||
	osType := &unknown{base{ServerInfo: c}}
 | 
			
		||||
	osType.setErrs([]error{xerrors.New("Unknown OS Type")})
 | 
			
		||||
	return
 | 
			
		||||
	return osType
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Retry as it may stall on the first SSH connection
 | 
			
		||||
@@ -62,14 +62,14 @@ func (h VulsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := detector.DetectPkgCves(&r, config.Conf.OvalDict, config.Conf.Gost); err != nil {
 | 
			
		||||
	if err := detector.DetectPkgCves(&r, config.Conf.OvalDict, config.Conf.Gost, config.Conf.LogOpts); err != nil {
 | 
			
		||||
		logging.Log.Errorf("Failed to detect Pkg CVE: %+v", err)
 | 
			
		||||
		http.Error(w, err.Error(), http.StatusServiceUnavailable)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logging.Log.Infof("Fill CVE detailed with gost")
 | 
			
		||||
	if err := gost.FillCVEsWithRedHat(&r, config.Conf.Gost); err != nil {
 | 
			
		||||
	if err := gost.FillCVEsWithRedHat(&r, config.Conf.Gost, config.Conf.LogOpts); err != nil {
 | 
			
		||||
		logging.Log.Errorf("Failed to fill with gost: %+v", err)
 | 
			
		||||
		http.Error(w, err.Error(), http.StatusServiceUnavailable)
 | 
			
		||||
	}
 | 
			
		||||
@@ -80,21 +80,21 @@ func (h VulsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
 | 
			
		||||
		http.Error(w, err.Error(), http.StatusServiceUnavailable)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	nExploitCve, err := detector.FillWithExploit(&r, config.Conf.Exploit)
 | 
			
		||||
	nExploitCve, err := detector.FillWithExploit(&r, config.Conf.Exploit, config.Conf.LogOpts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logging.Log.Errorf("Failed to fill with exploit: %+v", err)
 | 
			
		||||
		http.Error(w, err.Error(), http.StatusServiceUnavailable)
 | 
			
		||||
	}
 | 
			
		||||
	logging.Log.Infof("%s: %d PoC detected", r.FormatServerName(), nExploitCve)
 | 
			
		||||
 | 
			
		||||
	nMetasploitCve, err := detector.FillWithMetasploit(&r, config.Conf.Metasploit)
 | 
			
		||||
	nMetasploitCve, err := detector.FillWithMetasploit(&r, config.Conf.Metasploit, config.Conf.LogOpts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logging.Log.Errorf("Failed to fill with metasploit: %+v", err)
 | 
			
		||||
		http.Error(w, err.Error(), http.StatusServiceUnavailable)
 | 
			
		||||
	}
 | 
			
		||||
	logging.Log.Infof("%s: %d exploits are detected", r.FormatServerName(), nMetasploitCve)
 | 
			
		||||
 | 
			
		||||
	if err := detector.FillWithKEVuln(&r, config.Conf.KEVuln); err != nil {
 | 
			
		||||
	if err := detector.FillWithKEVuln(&r, config.Conf.KEVuln, config.Conf.LogOpts); err != nil {
 | 
			
		||||
		logging.Log.Errorf("Failed to fill with Known Exploited Vulnerabilities: %+v", err)
 | 
			
		||||
		http.Error(w, err.Error(), http.StatusServiceUnavailable)
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -185,12 +185,12 @@ func printConfigToml(ips []string) (err error) {
 | 
			
		||||
#keyPath            = "/home/username/.ssh/id_rsa"
 | 
			
		||||
#scanMode           = ["fast", "fast-root", "deep", "offline"]
 | 
			
		||||
#scanModules        = ["ospkg", "wordpress", "lockfile", "port"]
 | 
			
		||||
#lockfiles = ["/path/to/package-lock.json"]
 | 
			
		||||
#cpeNames = [
 | 
			
		||||
#  "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
 | 
			
		||||
#]
 | 
			
		||||
#owaspDCXMLPath     = "/tmp/dependency-check-report.xml"
 | 
			
		||||
#ignoreCves         = ["CVE-2014-6271"]
 | 
			
		||||
#containersOnly     = false
 | 
			
		||||
#containerType      = "docker" #or "lxd" or "lxc" default: docker
 | 
			
		||||
#containersIncluded = ["${running}"]
 | 
			
		||||
#containersExcluded = ["container_name_a"]
 | 
			
		||||
@@ -209,6 +209,8 @@ host                = "{{$ip}}"
 | 
			
		||||
#scanModules        = ["ospkg", "wordpress", "lockfile", "port"]
 | 
			
		||||
#type               = "pseudo"
 | 
			
		||||
#memo               = "DB Server"
 | 
			
		||||
#findLock = true
 | 
			
		||||
#lockfiles = ["/path/to/package-lock.json"]
 | 
			
		||||
#cpeNames           = [ "cpe:/a:rubyonrails:ruby_on_rails:4.2.1" ]
 | 
			
		||||
#owaspDCXMLPath     = "/path/to/dependency-check-report.xml"
 | 
			
		||||
#ignoreCves         = ["CVE-2014-0160"]
 | 
			
		||||
 
 | 
			
		||||
@@ -175,10 +175,24 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
 | 
			
		||||
	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 {
 | 
			
		||||
		logging.Log.Errorf("Error loading %s, %+v", p.configPath, err)
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	if p.configPath == "" {
 | 
			
		||||
		for _, cnf := range []config.VulnDictInterface{
 | 
			
		||||
			&config.Conf.CveDict,
 | 
			
		||||
			&config.Conf.OvalDict,
 | 
			
		||||
			&config.Conf.Gost,
 | 
			
		||||
			&config.Conf.Exploit,
 | 
			
		||||
			&config.Conf.Metasploit,
 | 
			
		||||
			&config.Conf.KEVuln,
 | 
			
		||||
		} {
 | 
			
		||||
			cnf.Init()
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if err := config.Load(p.configPath); err != nil {
 | 
			
		||||
			logging.Log.Errorf("Error loading %s. err: %+v", p.configPath, err)
 | 
			
		||||
			return subcommands.ExitUsageError
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	config.Conf.Slack.Enabled = p.toSlack
 | 
			
		||||
	config.Conf.ChatWork.Enabled = p.toChatWork
 | 
			
		||||
	config.Conf.GoogleChat.Enabled = p.toGoogleChat
 | 
			
		||||
 
 | 
			
		||||
@@ -11,10 +11,11 @@ import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/subcommands"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/server"
 | 
			
		||||
	"github.com/google/subcommands"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ServerCmd is subcommand for server
 | 
			
		||||
@@ -93,9 +94,23 @@ func (p *ServerCmd) SetFlags(f *flag.FlagSet) {
 | 
			
		||||
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 {
 | 
			
		||||
		logging.Log.Errorf("Error loading %s. err: %+v", p.configPath, err)
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
 | 
			
		||||
	if p.configPath == "" {
 | 
			
		||||
		for _, cnf := range []config.VulnDictInterface{
 | 
			
		||||
			&config.Conf.CveDict,
 | 
			
		||||
			&config.Conf.OvalDict,
 | 
			
		||||
			&config.Conf.Gost,
 | 
			
		||||
			&config.Conf.Exploit,
 | 
			
		||||
			&config.Conf.Metasploit,
 | 
			
		||||
			&config.Conf.KEVuln,
 | 
			
		||||
		} {
 | 
			
		||||
			cnf.Init()
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if err := config.Load(p.configPath); err != nil {
 | 
			
		||||
			logging.Log.Errorf("Error loading %s. err: %+v", p.configPath, err)
 | 
			
		||||
			return subcommands.ExitUsageError
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logging.Log.Info("Validating config...")
 | 
			
		||||
 
 | 
			
		||||
@@ -781,13 +781,18 @@ func setChangelogLayout(g *gocui.Gui) error {
 | 
			
		||||
			lines = append(lines, adv.Format())
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		m := map[string]struct{}{}
 | 
			
		||||
		if len(vinfo.Exploits) != 0 {
 | 
			
		||||
			lines = append(lines, "\n",
 | 
			
		||||
				"Exploit Codes",
 | 
			
		||||
				"PoC",
 | 
			
		||||
				"=============",
 | 
			
		||||
			)
 | 
			
		||||
			for _, exploit := range vinfo.Exploits {
 | 
			
		||||
				if _, ok := m[exploit.URL]; ok {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				lines = append(lines, fmt.Sprintf("* [%s](%s)", exploit.Description, exploit.URL))
 | 
			
		||||
				m[exploit.URL] = struct{}{}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user