Compare commits
	
		
			56 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					4cf9a723fe | ||
| 
						 | 
					bd1b135db3 | ||
| 
						 | 
					8c3b305149 | ||
| 
						 | 
					a3719038b8 | ||
| 
						 | 
					c68a261c0b | ||
| 
						 | 
					75fea79ac1 | ||
| 
						 | 
					eb9f9680ec | ||
| 
						 | 
					3634afdb81 | ||
| 
						 | 
					77b5df896a | ||
| 
						 | 
					b81f64058c | ||
| 
						 | 
					a8a90d7c63 | ||
| 
						 | 
					17bb575002 | ||
| 
						 | 
					abcea1a14d | ||
| 
						 | 
					10942f7c08 | ||
| 
						 | 
					87ee829e80 | ||
| 
						 | 
					fcc2c1e4c7 | ||
| 
						 | 
					269095d034 | ||
| 
						 | 
					40492ee00a | ||
| 
						 | 
					64cdd5aedc | ||
| 
						 | 
					3bb650cb77 | ||
| 
						 | 
					774544c975 | ||
| 
						 | 
					299805a726 | ||
| 
						 | 
					276363e793 | ||
| 
						 | 
					e750bd53fc | ||
| 
						 | 
					98fee7b5d2 | ||
| 
						 | 
					53aaea9fe2 | ||
| 
						 | 
					824fbb6368 | ||
| 
						 | 
					80566b91ab | ||
| 
						 | 
					533d05a1b5 | ||
| 
						 | 
					6a1fc4fade | ||
| 
						 | 
					9008d0ddf0 | ||
| 
						 | 
					583f4577bc | ||
| 
						 | 
					e5716d5092 | ||
| 
						 | 
					7192ae1287 | ||
| 
						 | 
					99c65eff48 | ||
| 
						 | 
					91df593566 | ||
| 
						 | 
					07aeaeb989 | ||
| 
						 | 
					cfeecdacd0 | ||
| 
						 | 
					564dfa8b62 | ||
| 
						 | 
					75dd6f2010 | ||
| 
						 | 
					e26fd0b759 | ||
| 
						 | 
					d630680a51 | ||
| 
						 | 
					1723c3f6a0 | ||
| 
						 | 
					53dd90302e | ||
| 
						 | 
					5c6e06b05e | ||
| 
						 | 
					cf6fb0c8a5 | ||
| 
						 | 
					e0e71b2eae | ||
| 
						 | 
					53f4a29fb1 | ||
| 
						 | 
					89d58d1abc | ||
| 
						 | 
					d6b6969cb3 | ||
| 
						 | 
					e7bf6fa69d | ||
| 
						 | 
					6e51970b91 | ||
| 
						 | 
					56d7d43768 | ||
| 
						 | 
					256c99ffa2 | ||
| 
						 | 
					9c0bc3b13b | ||
| 
						 | 
					9b8a323d85 | 
@@ -1,3 +1,8 @@
 | 
			
		||||
---
 | 
			
		||||
name: Bug Report
 | 
			
		||||
labels: bug
 | 
			
		||||
about: If something isn't working as expected.
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
# What did you do? (required. The issue will be **closed** when not provided.)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
---
 | 
			
		||||
name: Feature Request
 | 
			
		||||
labels: enhancement
 | 
			
		||||
about: I have a suggestion (and might want to implement myself)!
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
<!--
 | 
			
		||||
If this is a FEATURE REQUEST, request format does not matter!
 | 
			
		||||
-->
 | 
			
		||||
							
								
								
									
										10
									
								
								.github/ISSUE_TEMPLATE/SUPPORT_QUESTION.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								.github/ISSUE_TEMPLATE/SUPPORT_QUESTION.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
---
 | 
			
		||||
name: Support Question
 | 
			
		||||
labels: question
 | 
			
		||||
about: If you have a question about Vuls.
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
<!--
 | 
			
		||||
If you have a trouble, feel free to ask.
 | 
			
		||||
Make sure you're not asking duplicate question by searching on the issues lists.
 | 
			
		||||
-->
 | 
			
		||||
							
								
								
									
										7
									
								
								.github/ISSUE_TEMPLATE/VULSREPO.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.github/ISSUE_TEMPLATE/VULSREPO.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
---
 | 
			
		||||
name: Vuls Repo
 | 
			
		||||
labels: vulsrepo
 | 
			
		||||
about: If something isn't working as expected.
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -13,3 +13,6 @@ log/
 | 
			
		||||
results/
 | 
			
		||||
*config.toml
 | 
			
		||||
!setup/docker/*
 | 
			
		||||
.DS_Store
 | 
			
		||||
dist/
 | 
			
		||||
.idea
 | 
			
		||||
@@ -1,4 +1,6 @@
 | 
			
		||||
project_name: vuls
 | 
			
		||||
env:
 | 
			
		||||
  - GO111MODULE=on
 | 
			
		||||
release:
 | 
			
		||||
  github:
 | 
			
		||||
    owner: future-architect
 | 
			
		||||
@@ -9,7 +11,9 @@ builds:
 | 
			
		||||
  goarch:
 | 
			
		||||
  - amd64
 | 
			
		||||
  main: .
 | 
			
		||||
  ldflags: -s -w -X main.version={{.Version}} -X main.revision={{.Commit}}
 | 
			
		||||
  flags:
 | 
			
		||||
      - -a
 | 
			
		||||
  ldflags: -s -w -X main.version={{.Version}} -X main.revision={{.Commit}} 
 | 
			
		||||
  binary: vuls
 | 
			
		||||
archive:
 | 
			
		||||
  format: tar.gz
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
language: go
 | 
			
		||||
 | 
			
		||||
go:
 | 
			
		||||
  - "1.11.x"
 | 
			
		||||
  - "1.12.x"
 | 
			
		||||
 | 
			
		||||
after_success:
 | 
			
		||||
  - test -n "$TRAVIS_TAG" && curl -sL https://git.io/goreleaser | bash
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@ RUN apk add --no-cache \
 | 
			
		||||
 | 
			
		||||
COPY --from=builder /go/bin/vuls /usr/local/bin/
 | 
			
		||||
 | 
			
		||||
VOLUME [$WORKDIR, $LOGDIR]
 | 
			
		||||
VOLUME ["$WORKDIR", "$LOGDIR"]
 | 
			
		||||
WORKDIR $WORKDIR
 | 
			
		||||
ENV PWD $WORKDIR
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										35
									
								
								GNUmakefile
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								GNUmakefile
									
									
									
									
									
								
							@@ -1,11 +1,9 @@
 | 
			
		||||
.PHONY: \
 | 
			
		||||
	dep \
 | 
			
		||||
	depup \
 | 
			
		||||
	build \
 | 
			
		||||
	install \
 | 
			
		||||
	all \
 | 
			
		||||
	vendor \
 | 
			
		||||
	lint \
 | 
			
		||||
 	lint \
 | 
			
		||||
	vet \
 | 
			
		||||
	fmt \
 | 
			
		||||
	fmtcheck \
 | 
			
		||||
@@ -20,32 +18,28 @@ VERSION := $(shell git describe --tags --abbrev=0)
 | 
			
		||||
REVISION := $(shell git rev-parse --short HEAD)
 | 
			
		||||
BUILDTIME := $(shell date "+%Y%m%d_%H%M%S")
 | 
			
		||||
LDFLAGS := -X 'github.com/future-architect/vuls/config.Version=$(VERSION)' \
 | 
			
		||||
    -X 'github.com/future-architect/vuls/config.Revision=$(BUILDTIME)_$(REVISION)'
 | 
			
		||||
    -X 'github.com/future-architect/vuls/config.Revision=build-$(BUILDTIME)_$(REVISION)'
 | 
			
		||||
GO := GO111MODULE=on go
 | 
			
		||||
GO_OFF := GO111MODULE=off go
 | 
			
		||||
 | 
			
		||||
all: dep build
 | 
			
		||||
 | 
			
		||||
dep:
 | 
			
		||||
	go get -u github.com/golang/dep/...
 | 
			
		||||
	dep ensure -v
 | 
			
		||||
all: build
 | 
			
		||||
 | 
			
		||||
depup:
 | 
			
		||||
	go get -u github.com/golang/dep/...
 | 
			
		||||
	dep ensure -update -v
 | 
			
		||||
build: main.go pretest
 | 
			
		||||
	$(GO) build -a -ldflags "$(LDFLAGS)" -o vuls $<
 | 
			
		||||
 | 
			
		||||
build: main.go dep pretest
 | 
			
		||||
	go build -ldflags "$(LDFLAGS)" -o vuls $<
 | 
			
		||||
 | 
			
		||||
install: main.go dep pretest
 | 
			
		||||
	go install -ldflags "$(LDFLAGS)"
 | 
			
		||||
b: 	main.go pretest
 | 
			
		||||
	$(GO) build -ldflags "$(LDFLAGS)" -o vuls $<
 | 
			
		||||
 | 
			
		||||
install: main.go pretest
 | 
			
		||||
	$(GO) install -ldflags "$(LDFLAGS)"
 | 
			
		||||
 | 
			
		||||
lint:
 | 
			
		||||
	@ go get -v golang.org/x/lint/golint
 | 
			
		||||
	$(GO_OFF) get -u golang.org/x/lint/golint
 | 
			
		||||
	golint $(PKGS)
 | 
			
		||||
 | 
			
		||||
vet:
 | 
			
		||||
	#  @-go get -v golang.org/x/tools/cmd/vet
 | 
			
		||||
	go vet ./... || exit;
 | 
			
		||||
	echo $(PKGS) | xargs env $(GO) vet || exit;
 | 
			
		||||
 | 
			
		||||
fmt:
 | 
			
		||||
	gofmt -s -w $(SRCS)
 | 
			
		||||
@@ -59,7 +53,7 @@ fmtcheck:
 | 
			
		||||
pretest: lint vet fmtcheck
 | 
			
		||||
 | 
			
		||||
test: 
 | 
			
		||||
	echo $(PKGS) | xargs go test -cover -v || exit;
 | 
			
		||||
	$(GO) test -cover -v ./... || exit;
 | 
			
		||||
 | 
			
		||||
unused:
 | 
			
		||||
	$(foreach pkg,$(PKGS),unused $(pkg);)
 | 
			
		||||
@@ -71,4 +65,5 @@ cov:
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
	echo $(PKGS) | xargs go clean || exit;
 | 
			
		||||
	echo $(PKGS) | xargs go clean || exit;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										969
									
								
								Gopkg.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										969
									
								
								Gopkg.lock
									
									
									
										generated
									
									
									
								
							@@ -1,969 +0,0 @@
 | 
			
		||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:b92928b73320648b38c93cacb9082c0fe3f8ac3383ad9bd537eef62c380e0e7a"
 | 
			
		||||
  name = "contrib.go.opencensus.io/exporter/ocagent"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "00af367e65149ff1f2f4b93bbfbb84fd9297170d"
 | 
			
		||||
  version = "v0.2.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:a90dbfc07d4cf47b5f113a2d52227e0b098592b22c0bd7afd731f6cb8bd9d55c"
 | 
			
		||||
  name = "github.com/Azure/azure-sdk-for-go"
 | 
			
		||||
  packages = [
 | 
			
		||||
    "storage",
 | 
			
		||||
    "version",
 | 
			
		||||
  ]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "1951233eb944a49aa5f7278cba8e3e32a8c958af"
 | 
			
		||||
  version = "v24.0.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:e6c692a2dd5f978216331f2ab41b45dfdf01d5b2a74c596a9f69bcef77b7d67b"
 | 
			
		||||
  name = "github.com/Azure/go-autorest"
 | 
			
		||||
  packages = [
 | 
			
		||||
    "autorest",
 | 
			
		||||
    "autorest/adal",
 | 
			
		||||
    "autorest/azure",
 | 
			
		||||
    "autorest/date",
 | 
			
		||||
    "logger",
 | 
			
		||||
    "tracing",
 | 
			
		||||
  ]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "be17756531f50014397912b7aa557ec335e39b98"
 | 
			
		||||
  version = "v11.3.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:9f3b30d9f8e0d7040f729b82dcbc8f0dead820a133b3147ce355fc451f32d761"
 | 
			
		||||
  name = "github.com/BurntSushi/toml"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "3012a1dbe2e4bd1391d42b32f0577cb7bbc7f005"
 | 
			
		||||
  version = "v0.3.1"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  digest = "1:bb6c15391e666c4f44bdc604772301b93102233ed687be6df6d1c2abbde4f15c"
 | 
			
		||||
  name = "github.com/RackSec/srslog"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "a4725f04ec91af1a91b380da679d6e0c2f061e59"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:320e7ead93de9fd2b0e59b50fd92a4d50c1f8ab455d96bc2eb083267453a9709"
 | 
			
		||||
  name = "github.com/asaskevich/govalidator"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "ccb8e960c48f04d6935e72476ae4a51028f9e22f"
 | 
			
		||||
  version = "v9"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:5652bf3ce03ccaeb93cd5e11fcaab25467f78275fd9c4b4d1ffe88aae6faed12"
 | 
			
		||||
  name = "github.com/aws/aws-sdk-go"
 | 
			
		||||
  packages = [
 | 
			
		||||
    "aws",
 | 
			
		||||
    "aws/awserr",
 | 
			
		||||
    "aws/awsutil",
 | 
			
		||||
    "aws/client",
 | 
			
		||||
    "aws/client/metadata",
 | 
			
		||||
    "aws/corehandlers",
 | 
			
		||||
    "aws/credentials",
 | 
			
		||||
    "aws/credentials/ec2rolecreds",
 | 
			
		||||
    "aws/credentials/endpointcreds",
 | 
			
		||||
    "aws/credentials/processcreds",
 | 
			
		||||
    "aws/credentials/stscreds",
 | 
			
		||||
    "aws/csm",
 | 
			
		||||
    "aws/defaults",
 | 
			
		||||
    "aws/ec2metadata",
 | 
			
		||||
    "aws/endpoints",
 | 
			
		||||
    "aws/request",
 | 
			
		||||
    "aws/session",
 | 
			
		||||
    "aws/signer/v4",
 | 
			
		||||
    "internal/ini",
 | 
			
		||||
    "internal/s3err",
 | 
			
		||||
    "internal/sdkio",
 | 
			
		||||
    "internal/sdkrand",
 | 
			
		||||
    "internal/sdkuri",
 | 
			
		||||
    "internal/shareddefaults",
 | 
			
		||||
    "private/protocol",
 | 
			
		||||
    "private/protocol/eventstream",
 | 
			
		||||
    "private/protocol/eventstream/eventstreamapi",
 | 
			
		||||
    "private/protocol/query",
 | 
			
		||||
    "private/protocol/query/queryutil",
 | 
			
		||||
    "private/protocol/rest",
 | 
			
		||||
    "private/protocol/restxml",
 | 
			
		||||
    "private/protocol/xml/xmlutil",
 | 
			
		||||
    "service/s3",
 | 
			
		||||
    "service/sts",
 | 
			
		||||
  ]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "aabf189db35ba7eb5a35afe6d681fc0f70954fca"
 | 
			
		||||
  version = "v1.16.18"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:0f98f59e9a2f4070d66f0c9c39561f68fcd1dc837b22a852d28d0003aebd1b1e"
 | 
			
		||||
  name = "github.com/boltdb/bolt"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "2f1ce7a837dcb8da3ec595b1dac9d0632f0f99e8"
 | 
			
		||||
  version = "v1.3.1"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:166438587ed45ac211dab8a3ecebf4fa0c186d0db63430fb9127bbc2e5fcdc67"
 | 
			
		||||
  name = "github.com/cenkalti/backoff"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "1e4cf3da559842a91afcb6ea6141451e6c30c618"
 | 
			
		||||
  version = "v2.1.1"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:65b0d980b428a6ad4425f2df4cd5410edd81f044cf527bd1c345368444649e58"
 | 
			
		||||
  name = "github.com/census-instrumentation/opencensus-proto"
 | 
			
		||||
  packages = [
 | 
			
		||||
    "gen-go/agent/common/v1",
 | 
			
		||||
    "gen-go/agent/trace/v1",
 | 
			
		||||
    "gen-go/resource/v1",
 | 
			
		||||
    "gen-go/trace/v1",
 | 
			
		||||
  ]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "7f2434bc10da710debe5c4315ed6d4df454b4024"
 | 
			
		||||
  version = "v0.1.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:e04c00d619875ce5fa67180891984a9b1fadcc031af36bcd7a3509cbdad1df15"
 | 
			
		||||
  name = "github.com/cheggaaa/pb"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "c112833d014c77e8bde723fd0158e3156951639f"
 | 
			
		||||
  version = "v2.0.6"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:76dc72490af7174349349838f2fe118996381b31ea83243812a97e5a0fd5ed55"
 | 
			
		||||
  name = "github.com/dgrijalva/jwt-go"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e"
 | 
			
		||||
  version = "v3.2.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:865079840386857c809b72ce300be7580cb50d3d3129ce11bf9aa6ca2bc1934a"
 | 
			
		||||
  name = "github.com/fatih/color"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "5b77d2a35fb0ede96d138fc9a99f5c9b6aef11b4"
 | 
			
		||||
  version = "v1.7.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd"
 | 
			
		||||
  name = "github.com/fsnotify/fsnotify"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
 | 
			
		||||
  version = "v1.4.7"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:ad53d1f710522a38d1f0e5e0a55a194b1c6b2cd8e84313568e43523271f0cf62"
 | 
			
		||||
  name = "github.com/go-redis/redis"
 | 
			
		||||
  packages = [
 | 
			
		||||
    ".",
 | 
			
		||||
    "internal",
 | 
			
		||||
    "internal/consistenthash",
 | 
			
		||||
    "internal/hashtag",
 | 
			
		||||
    "internal/pool",
 | 
			
		||||
    "internal/proto",
 | 
			
		||||
    "internal/util",
 | 
			
		||||
  ]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "22be8a3eaf992c828cecb69dc07348313bf08d2e"
 | 
			
		||||
  version = "v6.15.1"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:ec6f9bf5e274c833c911923c9193867f3f18788c461f76f05f62bb1510e0ae65"
 | 
			
		||||
  name = "github.com/go-sql-driver/mysql"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "72cd26f257d44c1114970e19afddcd812016007e"
 | 
			
		||||
  version = "v1.4.1"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:586ea76dbd0374d6fb649a91d70d652b7fe0ccffb8910a77468e7702e7901f3d"
 | 
			
		||||
  name = "github.com/go-stack/stack"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "2fee6af1a9795aafbe0253a0cfbdf668e1fb8a9a"
 | 
			
		||||
  version = "v1.8.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:8f0705fa33e8957018611cc81c65cb373b626c092d39931bb86882489fc4c3f4"
 | 
			
		||||
  name = "github.com/golang/protobuf"
 | 
			
		||||
  packages = [
 | 
			
		||||
    "proto",
 | 
			
		||||
    "ptypes",
 | 
			
		||||
    "ptypes/any",
 | 
			
		||||
    "ptypes/duration",
 | 
			
		||||
    "ptypes/timestamp",
 | 
			
		||||
    "ptypes/wrappers",
 | 
			
		||||
  ]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5"
 | 
			
		||||
  version = "v1.2.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  digest = "1:df265b7f54410945dad5cf5979d91461b9fa7ff9b397ab58d2d577002a8a0e24"
 | 
			
		||||
  name = "github.com/google/subcommands"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "46f0354f63152e8801bb460d26f5b6c4c878efbb"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:7b5c6e2eeaa9ae5907c391a91c132abfd5c9e8a784a341b5625e750c67e6825d"
 | 
			
		||||
  name = "github.com/gorilla/websocket"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "66b9c49e59c6c48f0ffce28c2d8b8a5678502c6d"
 | 
			
		||||
  version = "v1.4.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  digest = "1:4e08dc2383a46b3107f0b34ca338c4459e8fc8ee90e46a60e728aa8a2b21d558"
 | 
			
		||||
  name = "github.com/gosuri/uitable"
 | 
			
		||||
  packages = [
 | 
			
		||||
    ".",
 | 
			
		||||
    "util/strutil",
 | 
			
		||||
    "util/wordwrap",
 | 
			
		||||
  ]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "36ee7e946282a3fb1cfecd476ddc9b35d8847e42"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  digest = "1:8dbe76014be3c83806abc61befcb5e1789d2d872bc8f98a8fb955405550c63be"
 | 
			
		||||
  name = "github.com/grokify/html-strip-tags-go"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "e9e44961e26f513866063f54bf85070db95600f7"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:950caca7dfcf796419232ba996c9c3539d09f26af27ba848c4508e604c13efbb"
 | 
			
		||||
  name = "github.com/hashicorp/go-version"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "d40cf49b3a77bba84a7afdbd7f1dc295d114efb1"
 | 
			
		||||
  version = "v1.1.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:c0d19ab64b32ce9fe5cf4ddceba78d5bc9807f0016db6b1183599da3dcc24d10"
 | 
			
		||||
  name = "github.com/hashicorp/hcl"
 | 
			
		||||
  packages = [
 | 
			
		||||
    ".",
 | 
			
		||||
    "hcl/ast",
 | 
			
		||||
    "hcl/parser",
 | 
			
		||||
    "hcl/printer",
 | 
			
		||||
    "hcl/scanner",
 | 
			
		||||
    "hcl/strconv",
 | 
			
		||||
    "hcl/token",
 | 
			
		||||
    "json/parser",
 | 
			
		||||
    "json/scanner",
 | 
			
		||||
    "json/token",
 | 
			
		||||
  ]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "8cb6e5b959231cc1119e43259c4a608f9c51a241"
 | 
			
		||||
  version = "v1.0.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  digest = "1:0f8b63af5601a93b6b6a63a420c857819e98a252369262d8faf66f3566ba294e"
 | 
			
		||||
  name = "github.com/hashicorp/uuid"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "ebb0a03e909c9c642a36d2527729104324c44fdb"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  digest = "1:0778dc7fce1b4669a8bfa7ae506ec1f595b6ab0f8989c1c0d22a8ca1144e9972"
 | 
			
		||||
  name = "github.com/howeyc/gopass"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "bf9dde6d0d2c004a008c27aaee91170c786f6db8"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:e96640e5b9ce93e2d7ee18f48048483080fd23e72e3c38bc17e9c8b77062031a"
 | 
			
		||||
  name = "github.com/inconshreveable/log15"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "67afb5ed74ec82fd7ac8f49d27c509ac6f991970"
 | 
			
		||||
  version = "v2.14"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:ff312c4d510c67954a6fc6a11c9ff72a2b2169584776b7419c7b8c729e2b13ac"
 | 
			
		||||
  name = "github.com/jinzhu/gorm"
 | 
			
		||||
  packages = [
 | 
			
		||||
    ".",
 | 
			
		||||
    "dialects/mysql",
 | 
			
		||||
    "dialects/postgres",
 | 
			
		||||
    "dialects/sqlite",
 | 
			
		||||
  ]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "472c70caa40267cb89fd8facb07fe6454b578626"
 | 
			
		||||
  version = "v1.9.2"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  digest = "1:fd97437fbb6b7dce04132cf06775bd258cce305c44add58eb55ca86c6c325160"
 | 
			
		||||
  name = "github.com/jinzhu/inflection"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "04140366298a54a039076d798123ffa108fff46c"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:bb81097a5b62634f3e9fec1014657855610c82d19b9a40c17612e32651e35dca"
 | 
			
		||||
  name = "github.com/jmespath/go-jmespath"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "c2b33e84"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:114ecad51af93a73ae6781fd0d0bc28e52b433c852b84ab4b4c109c15e6c6b6d"
 | 
			
		||||
  name = "github.com/jroimartin/gocui"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "c055c87ae801372cd74a0839b972db4f7697ae5f"
 | 
			
		||||
  version = "v0.4.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:16dd6b893b78a50564cdde1d9f7ea67224dece11bb0886bd882f1dc3dc1d440d"
 | 
			
		||||
  name = "github.com/k0kubun/pp"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "027a6d1765d673d337e687394dbe780dd64e2a1e"
 | 
			
		||||
  version = "v2.3.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  digest = "1:bdf08c9b41c029c60ba5dc99443a3ce74eedad842cf2adf9c255513f432422e2"
 | 
			
		||||
  name = "github.com/knqyf263/go-cpe"
 | 
			
		||||
  packages = [
 | 
			
		||||
    "common",
 | 
			
		||||
    "matching",
 | 
			
		||||
    "naming",
 | 
			
		||||
  ]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "659663f6eca2ff32258e282557e7808115ea498a"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  digest = "1:a9955a589c7f6f28bd5a5f69da3f1e2cc857c23c7605c5fa7b605f065ba8f3fe"
 | 
			
		||||
  name = "github.com/knqyf263/go-deb-version"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "9865fe14d09b1c729188ac810466dde90f897ee3"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  digest = "1:5734c5362ef66c39ddf1b4a11dfe75fa3c1adb70e78059543c83c0c6e89f2bc0"
 | 
			
		||||
  name = "github.com/knqyf263/go-rpm-version"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "74609b86c936dff800c69ec89fcf4bc52d5f13a4"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  digest = "1:acca5c567e76e94e81f8d65893e2a9cd12d914dd688731f461a5ebdb180d4938"
 | 
			
		||||
  name = "github.com/knqyf263/gost"
 | 
			
		||||
  packages = [
 | 
			
		||||
    "config",
 | 
			
		||||
    "db",
 | 
			
		||||
    "models",
 | 
			
		||||
    "util",
 | 
			
		||||
  ]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "5afeda5e8e1f8f3561738d3d6fa6549c88feb31e"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:0a69a1c0db3591fcefb47f115b224592c8dfa4368b7ba9fae509d5e16cdc95c8"
 | 
			
		||||
  name = "github.com/konsorten/go-windows-terminal-sequences"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "5c8c8bd35d3832f5d134ae1e1e375b69a4d25242"
 | 
			
		||||
  version = "v1.0.1"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  digest = "1:fa59fec07121e6190139b5134524c4fc7a6abe5143a775a0e17313b6997e17a7"
 | 
			
		||||
  name = "github.com/kotakanbe/go-cve-dictionary"
 | 
			
		||||
  packages = [
 | 
			
		||||
    "config",
 | 
			
		||||
    "db",
 | 
			
		||||
    "log",
 | 
			
		||||
    "models",
 | 
			
		||||
  ]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "b083bedef97055c27d1f039428ab183eca7c6450"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:54d3c90db1164399906830313a6fce7770917d7e4a12da8f2d8693d18ff5ef27"
 | 
			
		||||
  name = "github.com/kotakanbe/go-pingscanner"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "641dc2cc2d3cbf295dad356667b74c69bcbd6f70"
 | 
			
		||||
  version = "v0.1.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:8fd95e6bab4d09a0f610bd5c02ef6ec7d0d91da5a72b7cfcbfd67254bcb72b75"
 | 
			
		||||
  name = "github.com/kotakanbe/goval-dictionary"
 | 
			
		||||
  packages = [
 | 
			
		||||
    "config",
 | 
			
		||||
    "db",
 | 
			
		||||
    "db/rdb",
 | 
			
		||||
    "models",
 | 
			
		||||
  ]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "5070051ecafdf15cbe2490e71ec038de7d25b71e"
 | 
			
		||||
  version = "v0.1.1"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  digest = "1:0daead102d7ca3af110dfb832e8c14393f197d94e5ffe3f0639f10ea2cc55530"
 | 
			
		||||
  name = "github.com/kotakanbe/logrus-prefixed-formatter"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "928f7356cb964637e2489a6ef37eee55181676c5"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:01eb0269028d3c2e21b5b6cd9b1ba81bc4170ab293fcffa84e3aa3a6138a92e8"
 | 
			
		||||
  name = "github.com/labstack/gommon"
 | 
			
		||||
  packages = [
 | 
			
		||||
    "color",
 | 
			
		||||
    "log",
 | 
			
		||||
  ]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "7fd9f68ece0bcb1a905fac8f1549f0083f71c51b"
 | 
			
		||||
  version = "v0.2.8"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:b18ffc558326ebaed3b4a175617f1e12ed4e3f53d6ebfe5ba372a3de16d22278"
 | 
			
		||||
  name = "github.com/lib/pq"
 | 
			
		||||
  packages = [
 | 
			
		||||
    ".",
 | 
			
		||||
    "hstore",
 | 
			
		||||
    "oid",
 | 
			
		||||
  ]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "4ded0e9383f75c197b3a2aaa6d590ac52df6fd79"
 | 
			
		||||
  version = "v1.0.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:c568d7727aa262c32bdf8a3f7db83614f7af0ed661474b24588de635c20024c7"
 | 
			
		||||
  name = "github.com/magiconair/properties"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "c2353362d570a7bfa228149c62842019201cfb71"
 | 
			
		||||
  version = "v1.8.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:4e878df5f4e9fd625bf9c9aac77ef7cbfa4a74c01265505527c23470c0e40300"
 | 
			
		||||
  name = "github.com/marstr/guid"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "8bd9a64bf37eb297b492a4101fb28e80ac0b290f"
 | 
			
		||||
  version = "v1.1.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:c658e84ad3916da105a761660dcaeb01e63416c8ec7bc62256a9b411a05fcd67"
 | 
			
		||||
  name = "github.com/mattn/go-colorable"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
 | 
			
		||||
  version = "v0.0.9"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:0981502f9816113c9c8c4ac301583841855c8cf4da8c72f696b3ebedf6d0e4e5"
 | 
			
		||||
  name = "github.com/mattn/go-isatty"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "6ca4dbf54d38eea1a992b3c722a76a5d1c4cb25c"
 | 
			
		||||
  version = "v0.0.4"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:0356f3312c9bd1cbeda81505b7fd437501d8e778ab66998ef69f00d7f9b3a0d7"
 | 
			
		||||
  name = "github.com/mattn/go-runewidth"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "3ee7d812e62a0804a7d0a324e0249ca2db3476d3"
 | 
			
		||||
  version = "v0.0.4"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:4a49346ca45376a2bba679ca0e83bec949d780d4e927931317904bad482943ec"
 | 
			
		||||
  name = "github.com/mattn/go-sqlite3"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "c7c4067b79cc51e6dfdcef5c702e74b1e0fa7c75"
 | 
			
		||||
  version = "v1.10.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  digest = "1:2b32af4d2a529083275afc192d1067d8126b578c7a9613b26600e4df9c735155"
 | 
			
		||||
  name = "github.com/mgutz/ansi"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "9520e82c474b0a04dd04f8a40959027271bab992"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:78bbb1ba5b7c3f2ed0ea1eab57bdd3859aec7e177811563edc41198a760b06af"
 | 
			
		||||
  name = "github.com/mitchellh/go-homedir"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "ae18d6b8b3205b561c79e8e5f69bff09736185f4"
 | 
			
		||||
  version = "v1.0.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:53bc4cd4914cd7cd52139990d5170d6dc99067ae31c56530621b18b35fc30318"
 | 
			
		||||
  name = "github.com/mitchellh/mapstructure"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe"
 | 
			
		||||
  version = "v1.1.2"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:7aefb397a53fc437c90f0fdb3e1419c751c5a3a165ced52325d5d797edf1aca6"
 | 
			
		||||
  name = "github.com/moul/http2curl"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "9ac6cf4d929b2fa8fd2d2e6dec5bb0feb4f4911d"
 | 
			
		||||
  version = "v1.0.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  digest = "1:a440c18972e9499a1e8de68915e5a9119008d86efc2a9c6c6edddc323ce6f3ed"
 | 
			
		||||
  name = "github.com/mozqnet/go-exploitdb"
 | 
			
		||||
  packages = [
 | 
			
		||||
    "db",
 | 
			
		||||
    "models",
 | 
			
		||||
    "util",
 | 
			
		||||
  ]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "652ae1f6aa234a4e22c8a6c9566ef4018a1d28c8"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:95d38d218bf2290987c6b0e885a9f0f2d3d3239235acaddca01c3fe36e5e5566"
 | 
			
		||||
  name = "github.com/nlopes/slack"
 | 
			
		||||
  packages = [
 | 
			
		||||
    ".",
 | 
			
		||||
    "slackutilsx",
 | 
			
		||||
  ]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "b9033a72a20bf84563485e86a2adbea4bf265804"
 | 
			
		||||
  version = "v0.4.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  digest = "1:f3fc7efada7606d5abc88372e1f838ed897fa522077957070fbc2207a50d6faa"
 | 
			
		||||
  name = "github.com/nsf/termbox-go"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "0938b5187e61bb8c4dcac2b0a9cf4047d83784fc"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:abcdbf03ca6ca13d3697e2186edc1f33863bbdac2b3a44dfa39015e8903f7409"
 | 
			
		||||
  name = "github.com/olekukonko/tablewriter"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "e6d60cf7ba1f42d86d54cdf5508611c4aafb3970"
 | 
			
		||||
  version = "v0.0.1"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:d776f3e95774a8719f2e57fabbbb33103035fe072dcf6f1864f33abd17b753e5"
 | 
			
		||||
  name = "github.com/parnurzeal/gorequest"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "a578a48e8d6ca8b01a3b18314c43c6716bb5f5a3"
 | 
			
		||||
  version = "v0.2.15"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e"
 | 
			
		||||
  name = "github.com/pelletier/go-toml"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194"
 | 
			
		||||
  version = "v1.2.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:cf31692c14422fa27c83a05292eb5cbe0fb2775972e8f1f8446a71549bd8980b"
 | 
			
		||||
  name = "github.com/pkg/errors"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4"
 | 
			
		||||
  version = "v0.8.1"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:1a23fdd843129ef761ffe7651bc5fe7c5b09fbe933e92783ab06cc11c37b7b37"
 | 
			
		||||
  name = "github.com/rifflock/lfshook"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "b9218ef580f59a2e72dad1aa33d660150445d05a"
 | 
			
		||||
  version = "v2.4"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:274f67cb6fed9588ea2521ecdac05a6d62a8c51c074c1fccc6a49a40ba80e925"
 | 
			
		||||
  name = "github.com/satori/go.uuid"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "f58768cc1a7a7e77a3bd49e98cdd21419399b6a3"
 | 
			
		||||
  version = "v1.2.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  digest = "1:9c3c856c4bd09733de5727aeb85fc484b8b87a6eb9e6a632c47e3f17142757ee"
 | 
			
		||||
  name = "github.com/sirupsen/logrus"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "78fb3852d92683dc28da6cc3d5f965100677c27d"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:d707dbc1330c0ed177d4642d6ae102d5e2c847ebd0eb84562d0dc4f024531cfc"
 | 
			
		||||
  name = "github.com/spf13/afero"
 | 
			
		||||
  packages = [
 | 
			
		||||
    ".",
 | 
			
		||||
    "mem",
 | 
			
		||||
  ]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "a5d6946387efe7d64d09dcba68cdd523dc1273a3"
 | 
			
		||||
  version = "v1.2.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:08d65904057412fc0270fc4812a1c90c594186819243160dc779a402d4b6d0bc"
 | 
			
		||||
  name = "github.com/spf13/cast"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "8c9545af88b134710ab1cd196795e7f2388358d7"
 | 
			
		||||
  version = "v1.3.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:68ea4e23713989dc20b1bded5d9da2c5f9be14ff9885beef481848edd18c26cb"
 | 
			
		||||
  name = "github.com/spf13/jwalterweatherman"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "4a4406e478ca629068e7768fc33f3f044173c0a6"
 | 
			
		||||
  version = "v1.0.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:c1b1102241e7f645bc8e0c22ae352e8f0dc6484b6cb4d132fa9f24174e0119e2"
 | 
			
		||||
  name = "github.com/spf13/pflag"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "298182f68c66c05229eb03ac171abe6e309ee79a"
 | 
			
		||||
  version = "v1.0.3"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:de37e343c64582d7026bf8ab6ac5b22a72eac54f3a57020db31524affed9f423"
 | 
			
		||||
  name = "github.com/spf13/viper"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "6d33b5a963d922d182c91e8a1c88d81fd150cfd4"
 | 
			
		||||
  version = "v1.3.1"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:c468422f334a6b46a19448ad59aaffdfc0a36b08fdcc1c749a0b29b6453d7e59"
 | 
			
		||||
  name = "github.com/valyala/bytebufferpool"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "e746df99fe4a3986f4d4f79e13c1e0117ce9c2f7"
 | 
			
		||||
  version = "v1.0.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  digest = "1:268b8bce0064e8c057d7b913605459f9a26dcab864c0886a56d196540fbf003f"
 | 
			
		||||
  name = "github.com/valyala/fasttemplate"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "dcecefd839c4193db0d35b88ec65b4c12d360ab0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  digest = "1:0792df7c7ff49b81c7a8c5a2a47aee897c5bab31fb348c8e2f80a560d675f941"
 | 
			
		||||
  name = "github.com/ymomoi/goval-parser"
 | 
			
		||||
  packages = ["oval"]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "0a0be1dd9d0855b50be0be5a10ad3085382b6d59"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:2ae8314c44cd413cfdb5b1df082b350116dd8d2fff973e62c01b285b7affd89e"
 | 
			
		||||
  name = "go.opencensus.io"
 | 
			
		||||
  packages = [
 | 
			
		||||
    ".",
 | 
			
		||||
    "exemplar",
 | 
			
		||||
    "internal",
 | 
			
		||||
    "internal/tagencoding",
 | 
			
		||||
    "plugin/ochttp",
 | 
			
		||||
    "plugin/ochttp/propagation/b3",
 | 
			
		||||
    "plugin/ochttp/propagation/tracecontext",
 | 
			
		||||
    "stats",
 | 
			
		||||
    "stats/internal",
 | 
			
		||||
    "stats/view",
 | 
			
		||||
    "tag",
 | 
			
		||||
    "trace",
 | 
			
		||||
    "trace/internal",
 | 
			
		||||
    "trace/propagation",
 | 
			
		||||
    "trace/tracestate",
 | 
			
		||||
  ]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "b7bf3cdb64150a8c8c53b769fdeb2ba581bd4d4b"
 | 
			
		||||
  version = "v0.18.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  digest = "1:30e9f5bea4df0d1a573ed89a85cc680ab05dfc078f6a21e627db236f29650a11"
 | 
			
		||||
  name = "golang.org/x/crypto"
 | 
			
		||||
  packages = [
 | 
			
		||||
    "curve25519",
 | 
			
		||||
    "ed25519",
 | 
			
		||||
    "ed25519/internal/edwards25519",
 | 
			
		||||
    "internal/chacha20",
 | 
			
		||||
    "internal/subtle",
 | 
			
		||||
    "poly1305",
 | 
			
		||||
    "ssh",
 | 
			
		||||
    "ssh/agent",
 | 
			
		||||
    "ssh/terminal",
 | 
			
		||||
  ]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "ff983b9c42bc9fbf91556e191cc8efb585c16908"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  digest = "1:35d68717797810014f26d6a175b0e73bd7fba070c408d443b3fff87a61c8008e"
 | 
			
		||||
  name = "golang.org/x/net"
 | 
			
		||||
  packages = [
 | 
			
		||||
    "context",
 | 
			
		||||
    "http/httpguts",
 | 
			
		||||
    "http2",
 | 
			
		||||
    "http2/hpack",
 | 
			
		||||
    "idna",
 | 
			
		||||
    "internal/timeseries",
 | 
			
		||||
    "publicsuffix",
 | 
			
		||||
    "trace",
 | 
			
		||||
  ]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "915654e7eabcea33ae277abbecf52f0d8b7a9fdc"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  digest = "1:75515eedc0dc2cb0b40372008b616fa2841d831c63eedd403285ff286c593295"
 | 
			
		||||
  name = "golang.org/x/sync"
 | 
			
		||||
  packages = ["semaphore"]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "37e7f081c4d4c64e13b10787722085407fe5d15f"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  digest = "1:5ee4df7ab18e945607ac822de8d10b180baea263b5e8676a1041727543b9c1e4"
 | 
			
		||||
  name = "golang.org/x/sys"
 | 
			
		||||
  packages = [
 | 
			
		||||
    "unix",
 | 
			
		||||
    "windows",
 | 
			
		||||
  ]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "48ac38b7c8cbedd50b1613c0fccacfc7d88dfcdf"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
 | 
			
		||||
  name = "golang.org/x/text"
 | 
			
		||||
  packages = [
 | 
			
		||||
    "collate",
 | 
			
		||||
    "collate/build",
 | 
			
		||||
    "internal/colltab",
 | 
			
		||||
    "internal/gen",
 | 
			
		||||
    "internal/tag",
 | 
			
		||||
    "internal/triegen",
 | 
			
		||||
    "internal/ucd",
 | 
			
		||||
    "language",
 | 
			
		||||
    "secure/bidirule",
 | 
			
		||||
    "transform",
 | 
			
		||||
    "unicode/bidi",
 | 
			
		||||
    "unicode/cldr",
 | 
			
		||||
    "unicode/norm",
 | 
			
		||||
    "unicode/rangetable",
 | 
			
		||||
  ]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
 | 
			
		||||
  version = "v0.3.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:5f003878aabe31d7f6b842d4de32b41c46c214bb629bb485387dbcce1edf5643"
 | 
			
		||||
  name = "google.golang.org/api"
 | 
			
		||||
  packages = ["support/bundler"]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "19e022d8cf43ce81f046bae8cc18c5397cc7732f"
 | 
			
		||||
  version = "v0.1.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:c25289f43ac4a68d88b02245742347c94f1e108c534dda442188015ff80669b3"
 | 
			
		||||
  name = "google.golang.org/appengine"
 | 
			
		||||
  packages = ["cloudsql"]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "e9657d882bb81064595ca3b56cbe2546bbabf7b1"
 | 
			
		||||
  version = "v1.4.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  digest = "1:077c1c599507b3b3e9156d17d36e1e61928ee9b53a5b420f10f28ebd4a0b275c"
 | 
			
		||||
  name = "google.golang.org/genproto"
 | 
			
		||||
  packages = ["googleapis/rpc/status"]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "db91494dd46c1fdcbbde05e5ff5eb56df8f7d79a"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:9edd250a3c46675d0679d87540b30c9ed253b19bd1fd1af08f4f5fb3c79fc487"
 | 
			
		||||
  name = "google.golang.org/grpc"
 | 
			
		||||
  packages = [
 | 
			
		||||
    ".",
 | 
			
		||||
    "balancer",
 | 
			
		||||
    "balancer/base",
 | 
			
		||||
    "balancer/roundrobin",
 | 
			
		||||
    "binarylog/grpc_binarylog_v1",
 | 
			
		||||
    "codes",
 | 
			
		||||
    "connectivity",
 | 
			
		||||
    "credentials",
 | 
			
		||||
    "credentials/internal",
 | 
			
		||||
    "encoding",
 | 
			
		||||
    "encoding/proto",
 | 
			
		||||
    "grpclog",
 | 
			
		||||
    "internal",
 | 
			
		||||
    "internal/backoff",
 | 
			
		||||
    "internal/binarylog",
 | 
			
		||||
    "internal/channelz",
 | 
			
		||||
    "internal/envconfig",
 | 
			
		||||
    "internal/grpcrand",
 | 
			
		||||
    "internal/grpcsync",
 | 
			
		||||
    "internal/syscall",
 | 
			
		||||
    "internal/transport",
 | 
			
		||||
    "keepalive",
 | 
			
		||||
    "metadata",
 | 
			
		||||
    "naming",
 | 
			
		||||
    "peer",
 | 
			
		||||
    "resolver",
 | 
			
		||||
    "resolver/dns",
 | 
			
		||||
    "resolver/passthrough",
 | 
			
		||||
    "stats",
 | 
			
		||||
    "status",
 | 
			
		||||
    "tap",
 | 
			
		||||
  ]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "df014850f6dee74ba2fc94874043a9f3f75fbfd8"
 | 
			
		||||
  version = "v1.17.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:e626376fab8608a972d47e91b3c1bbbddaecaf1d42b82be6dcc52d10a7557893"
 | 
			
		||||
  name = "gopkg.in/VividCortex/ewma.v1"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "b24eb346a94c3ba12c1da1e564dbac1b498a77ce"
 | 
			
		||||
  version = "v1.1.1"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:94cad6e2359d57da6652e689189c5b6ef19f99db6304d2c41de54f6632e15143"
 | 
			
		||||
  name = "gopkg.in/cheggaaa/pb.v1"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "1cc5bbe20449079337944d56292c7383510c534c"
 | 
			
		||||
  version = "v1.0.27"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:256938e7d43c73bd5e7bb97dd281d1ebe294b2928403ee1fbec96249915d1150"
 | 
			
		||||
  name = "gopkg.in/cheggaaa/pb.v2"
 | 
			
		||||
  packages = ["termutil"]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "c112833d014c77e8bde723fd0158e3156951639f"
 | 
			
		||||
  version = "v2.0.6"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:865079840386857c809b72ce300be7580cb50d3d3129ce11bf9aa6ca2bc1934a"
 | 
			
		||||
  name = "gopkg.in/fatih/color.v1"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "5b77d2a35fb0ede96d138fc9a99f5c9b6aef11b4"
 | 
			
		||||
  version = "v1.7.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:c658e84ad3916da105a761660dcaeb01e63416c8ec7bc62256a9b411a05fcd67"
 | 
			
		||||
  name = "gopkg.in/mattn/go-colorable.v0"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
 | 
			
		||||
  version = "v0.0.9"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:0981502f9816113c9c8c4ac301583841855c8cf4da8c72f696b3ebedf6d0e4e5"
 | 
			
		||||
  name = "gopkg.in/mattn/go-isatty.v0"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "6ca4dbf54d38eea1a992b3c722a76a5d1c4cb25c"
 | 
			
		||||
  version = "v0.0.4"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:0356f3312c9bd1cbeda81505b7fd437501d8e778ab66998ef69f00d7f9b3a0d7"
 | 
			
		||||
  name = "gopkg.in/mattn/go-runewidth.v0"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "3ee7d812e62a0804a7d0a324e0249ca2db3476d3"
 | 
			
		||||
  version = "v0.0.4"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  digest = "1:4d2e5a73dc1500038e504a8d78b986630e3626dc027bc030ba5c75da257cdb96"
 | 
			
		||||
  name = "gopkg.in/yaml.v2"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  pruneopts = "UT"
 | 
			
		||||
  revision = "51d6538a90f86fe93ac480b35f37b2be17fef232"
 | 
			
		||||
  version = "v2.2.2"
 | 
			
		||||
 | 
			
		||||
[solve-meta]
 | 
			
		||||
  analyzer-name = "dep"
 | 
			
		||||
  analyzer-version = 1
 | 
			
		||||
  input-imports = [
 | 
			
		||||
    "github.com/Azure/azure-sdk-for-go/storage",
 | 
			
		||||
    "github.com/BurntSushi/toml",
 | 
			
		||||
    "github.com/RackSec/srslog",
 | 
			
		||||
    "github.com/asaskevich/govalidator",
 | 
			
		||||
    "github.com/aws/aws-sdk-go/aws",
 | 
			
		||||
    "github.com/aws/aws-sdk-go/aws/credentials",
 | 
			
		||||
    "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds",
 | 
			
		||||
    "github.com/aws/aws-sdk-go/aws/ec2metadata",
 | 
			
		||||
    "github.com/aws/aws-sdk-go/aws/session",
 | 
			
		||||
    "github.com/aws/aws-sdk-go/service/s3",
 | 
			
		||||
    "github.com/aws/aws-sdk-go/service/sts",
 | 
			
		||||
    "github.com/boltdb/bolt",
 | 
			
		||||
    "github.com/cenkalti/backoff",
 | 
			
		||||
    "github.com/google/subcommands",
 | 
			
		||||
    "github.com/gosuri/uitable",
 | 
			
		||||
    "github.com/hashicorp/uuid",
 | 
			
		||||
    "github.com/howeyc/gopass",
 | 
			
		||||
    "github.com/jroimartin/gocui",
 | 
			
		||||
    "github.com/k0kubun/pp",
 | 
			
		||||
    "github.com/knqyf263/go-cpe/naming",
 | 
			
		||||
    "github.com/knqyf263/go-deb-version",
 | 
			
		||||
    "github.com/knqyf263/go-rpm-version",
 | 
			
		||||
    "github.com/knqyf263/gost/db",
 | 
			
		||||
    "github.com/knqyf263/gost/models",
 | 
			
		||||
    "github.com/kotakanbe/go-cve-dictionary/db",
 | 
			
		||||
    "github.com/kotakanbe/go-cve-dictionary/log",
 | 
			
		||||
    "github.com/kotakanbe/go-cve-dictionary/models",
 | 
			
		||||
    "github.com/kotakanbe/go-pingscanner",
 | 
			
		||||
    "github.com/kotakanbe/goval-dictionary/db",
 | 
			
		||||
    "github.com/kotakanbe/goval-dictionary/models",
 | 
			
		||||
    "github.com/kotakanbe/logrus-prefixed-formatter",
 | 
			
		||||
    "github.com/mitchellh/go-homedir",
 | 
			
		||||
    "github.com/mozqnet/go-exploitdb/db",
 | 
			
		||||
    "github.com/mozqnet/go-exploitdb/models",
 | 
			
		||||
    "github.com/nlopes/slack",
 | 
			
		||||
    "github.com/olekukonko/tablewriter",
 | 
			
		||||
    "github.com/parnurzeal/gorequest",
 | 
			
		||||
    "github.com/pkg/errors",
 | 
			
		||||
    "github.com/rifflock/lfshook",
 | 
			
		||||
    "github.com/sirupsen/logrus",
 | 
			
		||||
    "golang.org/x/crypto/ssh",
 | 
			
		||||
    "golang.org/x/crypto/ssh/agent",
 | 
			
		||||
  ]
 | 
			
		||||
  solver-name = "gps-cdcl"
 | 
			
		||||
  solver-version = 1
 | 
			
		||||
							
								
								
									
										41
									
								
								Gopkg.toml
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								Gopkg.toml
									
									
									
									
									
								
							@@ -1,41 +0,0 @@
 | 
			
		||||
# Gopkg.toml example
 | 
			
		||||
#
 | 
			
		||||
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
 | 
			
		||||
# for detailed Gopkg.toml documentation.
 | 
			
		||||
#
 | 
			
		||||
# required = ["github.com/user/thing/cmd/thing"]
 | 
			
		||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
 | 
			
		||||
#
 | 
			
		||||
# [[constraint]]
 | 
			
		||||
#   name = "github.com/user/project"
 | 
			
		||||
#   version = "1.0.0"
 | 
			
		||||
#
 | 
			
		||||
# [[constraint]]
 | 
			
		||||
#   name = "github.com/user/project2"
 | 
			
		||||
#   branch = "dev"
 | 
			
		||||
#   source = "github.com/myfork/project2"
 | 
			
		||||
#
 | 
			
		||||
# [[override]]
 | 
			
		||||
#   name = "github.com/x/y"
 | 
			
		||||
#   version = "2.4.0"
 | 
			
		||||
#
 | 
			
		||||
# [prune]
 | 
			
		||||
#   non-go = false
 | 
			
		||||
#   go-tests = true
 | 
			
		||||
#   unused-packages = true
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  name = "github.com/knqyf263/gost"
 | 
			
		||||
  branch = "master"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  name = "github.com/kotakanbe/go-cve-dictionary"
 | 
			
		||||
  branch = "master"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  name = "github.com/mozqnet/go-exploitdb"
 | 
			
		||||
  branch = "master"
 | 
			
		||||
 | 
			
		||||
[prune]
 | 
			
		||||
  go-tests = true
 | 
			
		||||
  unused-packages = true
 | 
			
		||||
							
								
								
									
										169
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										169
									
								
								README.md
									
									
									
									
									
								
							@@ -5,12 +5,12 @@
 | 
			
		||||
[](https://github.com/future-architect/vuls/blob/master/LICENSE)
 | 
			
		||||
[](https://travis-ci.org/future-architect/vuls)
 | 
			
		||||
[](https://goreportcard.com/report/github.com/future-architect/vuls)
 | 
			
		||||
[](https://github.com/future-architect/vuls/graphs/contributors)
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
Vulnerability scanner for Linux/FreeBSD, agentless, written in golang.  
 | 
			
		||||
We have a slack team. [Join slack team](http://goo.gl/forms/xm5KFo35tu)  
 | 
			
		||||
Vulnerability scanner for Linux/FreeBSD, agentless, written in golang.
 | 
			
		||||
We have a slack team. [Join slack team](http://goo.gl/forms/xm5KFo35tu)
 | 
			
		||||
Twitter: [@vuls_en](https://twitter.com/vuls_en)
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
@@ -23,135 +23,188 @@ Twitter: [@vuls_en](https://twitter.com/vuls_en)
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
# Abstract
 | 
			
		||||
## NEWS
 | 
			
		||||
 | 
			
		||||
| Version     | Main Feature |  Date |
 | 
			
		||||
|:------------|:---------------------------------|:--------------------|
 | 
			
		||||
| [v0.8.0](https://github.com/future-architect/vuls/releases/tag/v0.8.0) | secret | Coming soon |
 | 
			
		||||
| [v0.7.0](https://github.com/future-architect/vuls/releases/tag/v0.7.0) | WordPress Vulnerability Scan | 2019/Apr/8 |
 | 
			
		||||
| [v0.6.3](https://github.com/future-architect/vuls/releases/tag/v0.6.3) | GitHub Integration | 2019/Feb/20 |
 | 
			
		||||
| [v0.6.2](https://github.com/future-architect/vuls/releases/tag/v0.6.2) | Add US-CERT/JPCERT Alerts as VulnSrc | 2019/Jan/23 |
 | 
			
		||||
| [v0.6.1](https://github.com/future-architect/vuls/releases/tag/v0.6.1) | BugFix | 2018/Nov/16 |
 | 
			
		||||
| [v0.6.0](https://github.com/future-architect/vuls/releases/tag/v0.6.0) | Add ExploitDB as VulnSrc | 2018/Nov/3 |
 | 
			
		||||
| [v0.5.0](https://github.com/future-architect/vuls/releases/tag/v0.5.0) | Scan accuracy improvement | 2018/Aug/27 |
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
## Abstract
 | 
			
		||||
 | 
			
		||||
For a system administrator, having to perform security vulnerability analysis and software update on a daily basis can be a burden.
 | 
			
		||||
To avoid downtime in production environment, it is common for system administrator to choose not to use the automatic update option provided by package manager and to perform update manually.
 | 
			
		||||
To avoid downtime in a production environment, it is common for a system administrator to choose not to use the automatic update option provided by the package manager and to perform update manually.
 | 
			
		||||
This leads to the following problems.
 | 
			
		||||
- System administrator will have to constantly watch out for any new vulnerabilities in NVD(National Vulnerability Database) or similar databases.
 | 
			
		||||
- It might be impossible for the system administrator to monitor all the software if there are a large number of software installed in server.
 | 
			
		||||
 | 
			
		||||
- The system administrator will have to constantly watch out for any new vulnerabilities in NVD (National Vulnerability Database) or similar databases.
 | 
			
		||||
- It might be impossible for the system administrator to monitor all the software if there are a large number of software packages installed in the server.
 | 
			
		||||
- It is expensive to perform analysis to determine the servers affected by new vulnerabilities. The possibility of overlooking a server or two during analysis is there.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Vuls is a tool created to solve the problems listed above. It has the following characteristics.
 | 
			
		||||
 | 
			
		||||
- Informs users of the vulnerabilities that are related to the system.
 | 
			
		||||
- Informs users of the servers that are affected.
 | 
			
		||||
- Vulnerability detection is done automatically to prevent any oversight.
 | 
			
		||||
- Report is generated on regular basis using CRON or other methods. to manage vulnerability.
 | 
			
		||||
- A report is generated on a regular basis using CRON or other methods. to manage vulnerability.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
# Main Features
 | 
			
		||||
## Main Features
 | 
			
		||||
 | 
			
		||||
## Scan for any vulnerabilities in Linux/FreeBSD Server
 | 
			
		||||
### Scan for any vulnerabilities in Linux/FreeBSD Server
 | 
			
		||||
 | 
			
		||||
[Supports major Linux/FreeBSD](https://vuls.io/docs/en/supported-os.html)
 | 
			
		||||
- Alpine, Ubuntu, Debian, CentOS, Amazon Linux, RHEL, Oracle Linux, SUSE Enterprise Linux and Raspbian, FreeBSD
 | 
			
		||||
- Cloud, on-premise, Docker
 | 
			
		||||
 | 
			
		||||
##  High quality scan
 | 
			
		||||
- Alpine, Amazon Linux, CentOS, Debian, Oracle Linux, Raspbian, RHEL, SUSE Enterprise Linux, and Ubuntu
 | 
			
		||||
- FreeBSD
 | 
			
		||||
- Cloud, on-premise, Docker Container and Docker Image
 | 
			
		||||
 | 
			
		||||
### High-quality scan
 | 
			
		||||
 | 
			
		||||
Vuls uses multiple vulnerability databases
 | 
			
		||||
 | 
			
		||||
Vuls uses Multiple vulnerability databases
 | 
			
		||||
- [NVD](https://nvd.nist.gov/)
 | 
			
		||||
- [JVN(Japanese)](http://jvndb.jvn.jp/apis/myjvn/)
 | 
			
		||||
- OVAL
 | 
			
		||||
	- [RedHat](https://www.redhat.com/security/data/oval/)
 | 
			
		||||
	- [Debian](https://www.debian.org/security/oval/)
 | 
			
		||||
	- [Ubuntu](https://people.canonical.com/~ubuntu-security/oval/)
 | 
			
		||||
	- [SUSE](http://ftp.suse.com/pub/projects/security/oval/)
 | 
			
		||||
	- [Oracle Linux](https://linux.oracle.com/security/oval/)
 | 
			
		||||
  - [Debian](https://www.debian.org/security/oval/)
 | 
			
		||||
  - [Oracle Linux](https://linux.oracle.com/security/oval/)
 | 
			
		||||
  - [RedHat](https://www.redhat.com/security/data/oval/)
 | 
			
		||||
  - [SUSE](http://ftp.suse.com/pub/projects/security/oval/)
 | 
			
		||||
  - [Ubuntu](https://people.canonical.com/~ubuntu-security/oval/)
 | 
			
		||||
 | 
			
		||||
- [Alpine-secdb](https://git.alpinelinux.org/cgit/alpine-secdb/)
 | 
			
		||||
- [Red Hat Security Advisories](https://access.redhat.com/security/security-updates/)
 | 
			
		||||
- [Debian Security Bug Tracker](https://security-tracker.debian.org/tracker/)
 | 
			
		||||
- Commands(yum, zypper, pkg-audit)
 | 
			
		||||
	- RHSA/ALAS/ELSA/FreeBSD-SA
 | 
			
		||||
- [Red Hat Security Advisories](https://access.redhat.com/security/security-updates/)
 | 
			
		||||
- Commands (yum, zypper, and pkg-audit)
 | 
			
		||||
  - RHSA/ALAS/ELSA/FreeBSD-SA
 | 
			
		||||
- [Exploit Database](https://www.exploit-db.com/)
 | 
			
		||||
- [US-CERT](https://www.us-cert.gov/ncas/alerts)
 | 
			
		||||
- [JPCERT](http://www.jpcert.or.jp/at/2019.html)
 | 
			
		||||
- [WPVulnDB](https://wpvulndb.com/api)
 | 
			
		||||
- [Node.js Security Working Group](https://github.com/nodejs/security-wg)
 | 
			
		||||
- [Ruby Advisory Database](https://github.com/rubysec/ruby-advisory-db)
 | 
			
		||||
- [Safety DB(Python)](https://github.com/pyupio/safety-db)
 | 
			
		||||
- [PHP Security Advisories Database](https://github.com/FriendsOfPHP/security-advisories)
 | 
			
		||||
- [RustSec Advisory Database](https://github.com/RustSec/advisory-db)
 | 
			
		||||
- Changelog
 | 
			
		||||
 | 
			
		||||
## Fast scan and Deep scan
 | 
			
		||||
### Scan mode
 | 
			
		||||
 | 
			
		||||
[Fast Scan](https://vuls.io/docs/en/architecture-fast-scan.html)
 | 
			
		||||
 | 
			
		||||
- Scan without root privilege, no dependencies
 | 
			
		||||
- Almost no load on the scan target server
 | 
			
		||||
- Offline mode scan with no internet access. (Red Hat, CentOS, OracleLinux, Ubuntu, Debian)
 | 
			
		||||
- Offline mode scan with no internet access. (CentOS, Debian, Oracle Linux, Red Hat, and Ubuntu)
 | 
			
		||||
 | 
			
		||||
[Fast Root Scan](https://vuls.io/docs/en/architecture-fast-root-scan.html)
 | 
			
		||||
 | 
			
		||||
- Scan with root privilege
 | 
			
		||||
- Almost no load on the scan target server
 | 
			
		||||
- Detect processes affected by update using yum-ps (RedHat, CentOS, Oracle Linux and Amazon Linux)
 | 
			
		||||
- Detect processes affected by update using yum-ps (Amazon Linux, CentOS, Oracle Linux, and RedHat)
 | 
			
		||||
- Detect processes which updated before but not restarting yet using checkrestart of debian-goodies (Debian and Ubuntu)
 | 
			
		||||
- Offline mode scan with no internet access. (RedHat, CentOS, OracleLinux, Ubuntu, Debian)
 | 
			
		||||
- Offline mode scan with no internet access. (CentOS, Debian, Oracle Linux, Red Hat, and Ubuntu)
 | 
			
		||||
 | 
			
		||||
[Deep Scan](https://vuls.io/docs/en/architecture-deep-scan.html)
 | 
			
		||||
- Scan with root privilege
 | 
			
		||||
- Parses the Changelog  
 | 
			
		||||
    Changelog has a history of version changes. When a security issue is fixed, the relevant CVE ID is listed.
 | 
			
		||||
    By parsing the changelog and analysing the updates between the installed version of software on the server and the newest version of that software
 | 
			
		||||
    it's possible to create a list of all vulnerabilities that need to be fixed.
 | 
			
		||||
- Sometimes load on the scan target server
 | 
			
		||||
### [Remote, Local scan mode, Server mode](https://vuls.io/docs/en/architecture-remote-local.html)
 | 
			
		||||
 | 
			
		||||
## [Remote scan and Local scan](https://vuls.io/docs/en/architecture-remote-local.html)
 | 
			
		||||
[Remote scan mode](https://vuls.io/docs/en/architecture-remote-scan.html)
 | 
			
		||||
 | 
			
		||||
[Remote Scan](https://vuls.io/docs/en/architecture-remote-scan.html)
 | 
			
		||||
- User is required to only setup one machine that is connected to other target servers via SSH
 | 
			
		||||
- User is required to only set up one machine that is connected to other target servers via SSH
 | 
			
		||||
 | 
			
		||||
[Local scan mode](https://vuls.io/docs/en/architecture-local-scan.html)
 | 
			
		||||
 | 
			
		||||
[Local Scan](https://vuls.io/docs/en/architecture-local-scan.html)
 | 
			
		||||
- If you don't want the central Vuls server to connect to each server by SSH, you can use Vuls in the Local Scan mode.
 | 
			
		||||
 | 
			
		||||
## **Dynamic** Analysis
 | 
			
		||||
[Server mode](https://vuls.io/docs/en/usage-server.html)
 | 
			
		||||
 | 
			
		||||
- It is possible to acquire the state of the server by connecting via SSH and executing the command. 
 | 
			
		||||
- First, start Vuls in server mode and listen as an HTTP server.
 | 
			
		||||
- Next, issue a command on the scan target server to collect software information. Then send the result to Vuls Server via HTTP. You receive the scan results as JSON format.
 | 
			
		||||
- No SSH needed, No Scanner needed. Only issuing Linux commands directory on the scan target server.
 | 
			
		||||
 | 
			
		||||
### **Dynamic** Analysis
 | 
			
		||||
 | 
			
		||||
- It is possible to acquire the state of the server by connecting via SSH and executing the command.
 | 
			
		||||
- Vuls warns when the scan target server was updated the kernel etc. but not restarting it.
 | 
			
		||||
 | 
			
		||||
## [Scan middleware that are not included in OS package management](https://vuls.io/docs/en/usage-scan-non-os-packages.html)
 | 
			
		||||
### **Static** Analysis
 | 
			
		||||
 | 
			
		||||
- Scan middleware, programming language libraries and framework for vulnerability
 | 
			
		||||
- Support software registered in CPE
 | 
			
		||||
Vuls v0.8.0 can scan Docker images using [knqyf263/trivy](https://github.com/knqyf263/trivy).
 | 
			
		||||
Following Registry supported.
 | 
			
		||||
 | 
			
		||||
- ECR
 | 
			
		||||
- GCR
 | 
			
		||||
- Local Image
 | 
			
		||||
 | 
			
		||||
For details, see [Scan docker image](https://vuls.io/docs/en/tutorial-scan-docker-image.html)
 | 
			
		||||
 | 
			
		||||
### Scan vulnerabilities of non-OS-packages
 | 
			
		||||
 | 
			
		||||
- Libraries of programming language
 | 
			
		||||
- Self-compiled software
 | 
			
		||||
- Network Devices
 | 
			
		||||
 | 
			
		||||
Vuls has some options to detect the vulnerabilities
 | 
			
		||||
 | 
			
		||||
- [Lockfile based Scan](https://vuls.io/docs/en/usage-scan-non-os-packages.html#library-vulns-scan)
 | 
			
		||||
- [GitHub Integration](https://vuls.io/docs/en/usage-scan-non-os-packages.html#usage-integrate-with-github-security-alerts)
 | 
			
		||||
- [Common Platform Enumeration (CPE) based Scan](https://vuls.io/docs/en/usage-scan-non-os-packages.html#cpe-scan)
 | 
			
		||||
- [OWASP Dependency Check Integration](https://vuls.io/docs/en/usage-scan-non-os-packages.html#usage-integrate-with-owasp-dependency-check-to-automatic-update-when-the-libraries-are-updated-experimental)
 | 
			
		||||
 | 
			
		||||
## Scan WordPress core, themes, plugins
 | 
			
		||||
 | 
			
		||||
- [Scan WordPress](https://vuls.io/docs/en/usage-scan-wordpress.html)
 | 
			
		||||
 | 
			
		||||
## MISC
 | 
			
		||||
 | 
			
		||||
- Nondestructive testing
 | 
			
		||||
- Pre-authorization is *NOT* necessary before scanning on AWS
 | 
			
		||||
	- Vuls works well with Continuous Integration since tests can be run every day. This allows you to find vulnerabilities very quickly.
 | 
			
		||||
- Auto generation of configuration file template
 | 
			
		||||
	- Auto detection of servers set using CIDR, generate configuration file template
 | 
			
		||||
  - Vuls works well with Continuous Integration since tests can be run every day. This allows you to find vulnerabilities very quickly.
 | 
			
		||||
- Auto-generation of configuration file template
 | 
			
		||||
  - Auto-detection of servers set using CIDR, generate configuration file template
 | 
			
		||||
- Email and Slack notification is possible (supports Japanese language)
 | 
			
		||||
- Scan result is viewable on accessory software, TUI Viewer on terminal or Web UI ([VulsRepo](https://github.com/usiusi360/vulsrepo)).
 | 
			
		||||
- Scan result is viewable on accessory software, TUI Viewer in a terminal or Web UI ([VulsRepo](https://github.com/future-architect/vulsrepo)).
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
# What Vuls Doesn't Do
 | 
			
		||||
## What Vuls Doesn't Do
 | 
			
		||||
 | 
			
		||||
- Vuls doesn't update the vulnerable packages.
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
# Document
 | 
			
		||||
## Document
 | 
			
		||||
 | 
			
		||||
For more information such as Installation, Tutorial, Usage, visit [vuls.io](https://vuls.io/)  
 | 
			
		||||
For more information such as Installation, Tutorial, Usage, visit [vuls.io](https://vuls.io/)
 | 
			
		||||
[日本語翻訳ドキュメント](https://vuls.io/ja/)
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
# Authors
 | 
			
		||||
## Authors
 | 
			
		||||
 | 
			
		||||
kotakanbe ([@kotakanbe](https://twitter.com/kotakanbe)) created vuls and [these fine people](https://github.com/future-architect/vuls/graphs/contributors) have contributed.
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
# Change Log
 | 
			
		||||
## Change Log
 | 
			
		||||
 | 
			
		||||
Please see [CHANGELOG](https://github.com/future-architect/vuls/blob/master/CHANGELOG.md).
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
# Stargazers over time		
 | 
			
		||||
		
 | 
			
		||||
[](https://starcharts.herokuapp.com/future-architect/vuls)		
 | 
			
		||||
 | 
			
		||||
-----
 | 
			
		||||
## Stargazers over time
 | 
			
		||||
 | 
			
		||||
# License
 | 
			
		||||
[](https://starcharts.herokuapp.com/future-architect/vuls)
 | 
			
		||||
 | 
			
		||||
-----;
 | 
			
		||||
 | 
			
		||||
## License
 | 
			
		||||
 | 
			
		||||
Please see [LICENSE](https://github.com/future-architect/vuls/blob/master/LICENSE).
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								cache/bolt.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								cache/bolt.go
									
									
									
									
										vendored
									
									
								
							@@ -19,12 +19,12 @@ package cache
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/boltdb/bolt"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Bolt holds a pointer of bolt.DB
 | 
			
		||||
@@ -69,7 +69,7 @@ func (b *Bolt) createBucketIfNotExists(name string) error {
 | 
			
		||||
	return b.db.Update(func(tx *bolt.Tx) error {
 | 
			
		||||
		_, err := tx.CreateBucketIfNotExists([]byte(name))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("Failed to create bucket: %s", err)
 | 
			
		||||
			return xerrors.Errorf("Failed to create bucket: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
@@ -98,7 +98,7 @@ func (b Bolt) RefreshMeta(meta Meta) error {
 | 
			
		||||
	meta.CreatedAt = time.Now()
 | 
			
		||||
	jsonBytes, err := json.Marshal(meta)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to marshal to JSON: %s", err)
 | 
			
		||||
		return xerrors.Errorf("Failed to marshal to JSON: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	return b.db.Update(func(tx *bolt.Tx) error {
 | 
			
		||||
		bkt := tx.Bucket([]byte(metabucket))
 | 
			
		||||
@@ -114,7 +114,7 @@ func (b Bolt) RefreshMeta(meta Meta) error {
 | 
			
		||||
func (b Bolt) EnsureBuckets(meta Meta) error {
 | 
			
		||||
	jsonBytes, err := json.Marshal(meta)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to marshal to JSON: %s", err)
 | 
			
		||||
		return xerrors.Errorf("Failed to marshal to JSON: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	return b.db.Update(func(tx *bolt.Tx) error {
 | 
			
		||||
		b.Log.Debugf("Put to meta: %s", meta.Name)
 | 
			
		||||
@@ -163,7 +163,7 @@ func (b Bolt) GetChangelog(servername, packName string) (changelog string, err e
 | 
			
		||||
	err = b.db.View(func(tx *bolt.Tx) error {
 | 
			
		||||
		bkt := tx.Bucket([]byte(servername))
 | 
			
		||||
		if bkt == nil {
 | 
			
		||||
			return fmt.Errorf("Failed to get Bucket: %s", servername)
 | 
			
		||||
			return xerrors.Errorf("Failed to get Bucket: %s", servername)
 | 
			
		||||
		}
 | 
			
		||||
		v := bkt.Get([]byte(packName))
 | 
			
		||||
		if v == nil {
 | 
			
		||||
@@ -181,7 +181,7 @@ func (b Bolt) PutChangelog(servername, packName, changelog string) error {
 | 
			
		||||
	return b.db.Update(func(tx *bolt.Tx) error {
 | 
			
		||||
		bkt := tx.Bucket([]byte(servername))
 | 
			
		||||
		if bkt == nil {
 | 
			
		||||
			return fmt.Errorf("Failed to get Bucket: %s", servername)
 | 
			
		||||
			return xerrors.Errorf("Failed to get Bucket: %s", servername)
 | 
			
		||||
		}
 | 
			
		||||
		return bkt.Put([]byte(packName), []byte(changelog))
 | 
			
		||||
	})
 | 
			
		||||
 
 | 
			
		||||
@@ -20,8 +20,10 @@ package commands
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/subcommands"
 | 
			
		||||
 | 
			
		||||
@@ -98,7 +100,7 @@ func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfa
 | 
			
		||||
	util.Log = util.NewCustomLogger(c.ServerInfo{})
 | 
			
		||||
 | 
			
		||||
	if err := mkdirDotVuls(); err != nil {
 | 
			
		||||
		util.Log.Errorf("Failed to create .vuls: %s", err)
 | 
			
		||||
		util.Log.Errorf("Failed to create .vuls. err: %+v", err)
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -114,9 +116,12 @@ func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfa
 | 
			
		||||
 | 
			
		||||
	err = c.Load(p.configPath, keyPass)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		util.Log.Errorf("Error loading %s, %s", p.configPath, err)
 | 
			
		||||
		util.Log.Errorf("If you update Vuls and get this error, there may be incompatible changes in config.toml")
 | 
			
		||||
		util.Log.Errorf("Please check README: https://github.com/future-architect/vuls#configuration")
 | 
			
		||||
		msg := []string{
 | 
			
		||||
			fmt.Sprintf("Error loading %s", p.configPath),
 | 
			
		||||
			"If you update Vuls and get this error, there may be incompatible changes in config.toml",
 | 
			
		||||
			"Please check config.toml template : https://vuls.io/docs/en/usage-settings.html",
 | 
			
		||||
		}
 | 
			
		||||
		util.Log.Errorf("%s\n%+v", strings.Join(msg, "\n"), err)
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -151,13 +156,13 @@ func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfa
 | 
			
		||||
 | 
			
		||||
	util.Log.Info("Detecting Server/Container OS... ")
 | 
			
		||||
	if err := scan.InitServers(p.timeoutSec); err != nil {
 | 
			
		||||
		util.Log.Errorf("Failed to init servers: %s", err)
 | 
			
		||||
		util.Log.Errorf("Failed to init servers. err: %+v", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	util.Log.Info("Checking Scan Modes...")
 | 
			
		||||
	if err := scan.CheckScanModes(); err != nil {
 | 
			
		||||
		util.Log.Errorf("Fix config.toml: %s", err)
 | 
			
		||||
		util.Log.Errorf("Fix config.toml. err: %+v", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -220,10 +220,19 @@ host                = "{{$ip}}"
 | 
			
		||||
#owaspDCXMLPath = "/path/to/dependency-check-report.xml"
 | 
			
		||||
#ignoreCves     = ["CVE-2014-0160"]
 | 
			
		||||
 | 
			
		||||
#[servers.{{index $names $i}}.githubs."owner/repo"]
 | 
			
		||||
#token   = "yourToken"
 | 
			
		||||
 | 
			
		||||
#[servers.{{index $names $i}}.wordpress]
 | 
			
		||||
#cmdPath = "/usr/local/bin/wp"
 | 
			
		||||
#osUser = "wordpress"
 | 
			
		||||
#docRoot = "/path/to/DocumentRoot/"
 | 
			
		||||
#wpVulnDBToken = "xxxxTokenxxxx"
 | 
			
		||||
#ignoreInactive = true
 | 
			
		||||
 | 
			
		||||
#[servers.{{index $names $i}}.optional]
 | 
			
		||||
#key = "value1"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
{{end}}
 | 
			
		||||
 | 
			
		||||
`
 | 
			
		||||
 
 | 
			
		||||
@@ -64,6 +64,7 @@ func (*ReportCmd) Usage() string {
 | 
			
		||||
		[-diff]
 | 
			
		||||
		[-ignore-unscored-cves]
 | 
			
		||||
		[-ignore-unfixed]
 | 
			
		||||
		[-ignore-github-dismissed]
 | 
			
		||||
		[-to-email]
 | 
			
		||||
		[-to-http]
 | 
			
		||||
		[-to-slack]
 | 
			
		||||
@@ -133,10 +134,12 @@ func (p *ReportCmd) SetFlags(f *flag.FlagSet) {
 | 
			
		||||
	f.BoolVar(&c.Conf.IgnoreUnscoredCves, "ignore-unscored-cves", false,
 | 
			
		||||
		"Don't report the unscored CVEs")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(
 | 
			
		||||
		&c.Conf.IgnoreUnfixed, "ignore-unfixed", false,
 | 
			
		||||
	f.BoolVar(&c.Conf.IgnoreUnfixed, "ignore-unfixed", false,
 | 
			
		||||
		"Don't report the unfixed CVEs")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.IgnoreGitHubDismissed, "ignore-github-dismissed", false,
 | 
			
		||||
		"Don't report the dismissed CVEs on GitHub Security Alerts")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(
 | 
			
		||||
		&c.Conf.HTTPProxy, "http-proxy", "",
 | 
			
		||||
		"http://proxy-url:port (default: empty)")
 | 
			
		||||
@@ -206,7 +209,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
 | 
			
		||||
	cvelog.SetLogger(c.Conf.LogDir, false, c.Conf.Debug, false)
 | 
			
		||||
 | 
			
		||||
	if err := c.Load(p.configPath, ""); err != nil {
 | 
			
		||||
		util.Log.Errorf("Error loading %s, %s", p.configPath, err)
 | 
			
		||||
		util.Log.Errorf("Error loading %s, %+v", p.configPath, err)
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -224,7 +227,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
 | 
			
		||||
		dir, err = report.JSONDir(f.Args())
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		util.Log.Errorf("Failed to read from JSON: %s", err)
 | 
			
		||||
		util.Log.Errorf("Failed to read from JSON: %+v", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -273,7 +276,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.ToS3 {
 | 
			
		||||
		if err := report.CheckIfBucketExists(); err != nil {
 | 
			
		||||
			util.Log.Errorf("Check if there is a bucket beforehand: %s, err: %s",
 | 
			
		||||
			util.Log.Errorf("Check if there is a bucket beforehand: %s, err: %+v",
 | 
			
		||||
				c.Conf.AWS.S3Bucket, err)
 | 
			
		||||
			return subcommands.ExitUsageError
 | 
			
		||||
		}
 | 
			
		||||
@@ -294,7 +297,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
 | 
			
		||||
			return subcommands.ExitUsageError
 | 
			
		||||
		}
 | 
			
		||||
		if err := report.CheckIfAzureContainerExists(); err != nil {
 | 
			
		||||
			util.Log.Errorf("Check if there is a container beforehand: %s, err: %s",
 | 
			
		||||
			util.Log.Errorf("Check if there is a container beforehand: %s, err: %+v",
 | 
			
		||||
				c.Conf.Azure.ContainerName, err)
 | 
			
		||||
			return subcommands.ExitUsageError
 | 
			
		||||
		}
 | 
			
		||||
@@ -327,15 +330,21 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
 | 
			
		||||
	util.Log.Infof("Loaded: %s", dir)
 | 
			
		||||
 | 
			
		||||
	var res models.ScanResults
 | 
			
		||||
	hasError := false
 | 
			
		||||
	for _, r := range loaded {
 | 
			
		||||
		if len(r.Errors) == 0 {
 | 
			
		||||
			res = append(res, r)
 | 
			
		||||
		} else {
 | 
			
		||||
			util.Log.Warnf("Ignored since errors occurred during scanning: %s",
 | 
			
		||||
				r.ServerName)
 | 
			
		||||
			util.Log.Errorf("Ignored since errors occurred during scanning: %s, err: %v",
 | 
			
		||||
				r.ServerName, r.Errors)
 | 
			
		||||
			hasError = true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(res) == 0 {
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, r := range res {
 | 
			
		||||
		util.Log.Debugf("%s: %s",
 | 
			
		||||
			r.ServerInfo(),
 | 
			
		||||
@@ -345,7 +354,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
 | 
			
		||||
	if c.Conf.UUID {
 | 
			
		||||
		// Ensure UUIDs of scan target servers in config.toml
 | 
			
		||||
		if err := report.EnsureUUIDs(p.configPath, res); err != nil {
 | 
			
		||||
			util.Log.Errorf("Failed to ensure UUIDs: %s", err)
 | 
			
		||||
			util.Log.Errorf("Failed to ensure UUIDs. err: %+v", err)
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -358,7 +367,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
 | 
			
		||||
 | 
			
		||||
		if c.Conf.CveDict.URL != "" {
 | 
			
		||||
			if err := report.CveClient.CheckHealth(); err != nil {
 | 
			
		||||
				util.Log.Errorf("CVE HTTP server is not running. err: %s", err)
 | 
			
		||||
				util.Log.Errorf("CVE HTTP server is not running. err: %+v", err)
 | 
			
		||||
				util.Log.Errorf("Run go-cve-dictionary as server mode before reporting or run with `-cvedb-type=sqlite3 -cvedb-sqlite3-path` option instead of -cvedb-url")
 | 
			
		||||
				return subcommands.ExitFailure
 | 
			
		||||
			}
 | 
			
		||||
@@ -367,7 +376,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
 | 
			
		||||
		if c.Conf.OvalDict.URL != "" {
 | 
			
		||||
			err := oval.Base{}.CheckHTTPHealth()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				util.Log.Errorf("OVAL HTTP server is not running. err: %s", err)
 | 
			
		||||
				util.Log.Errorf("OVAL HTTP server is not running. err: %+v", err)
 | 
			
		||||
				util.Log.Errorf("Run goval-dictionary as server mode before reporting or run with `-ovaldb-type=sqlite3 -ovaldb-sqlite3-path` option instead of -ovaldb-url")
 | 
			
		||||
				return subcommands.ExitFailure
 | 
			
		||||
			}
 | 
			
		||||
@@ -377,7 +386,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
 | 
			
		||||
			util.Log.Infof("gost: %s", c.Conf.Gost.URL)
 | 
			
		||||
			err := gost.Base{}.CheckHTTPHealth()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				util.Log.Errorf("gost HTTP server is not running. err: %s", err)
 | 
			
		||||
				util.Log.Errorf("gost HTTP server is not running. err: %+v", err)
 | 
			
		||||
				util.Log.Errorf("Run gost as server mode before reporting or run with `-gostdb-type=sqlite3 -gostdb-sqlite3-path` option instead of -gostdb-url")
 | 
			
		||||
				return subcommands.ExitFailure
 | 
			
		||||
			}
 | 
			
		||||
@@ -386,7 +395,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
 | 
			
		||||
		if c.Conf.Exploit.URL != "" {
 | 
			
		||||
			err := exploit.CheckHTTPHealth()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				util.Log.Errorf("exploit HTTP server is not running. err: %s", err)
 | 
			
		||||
				util.Log.Errorf("exploit HTTP server is not running. err: %+v", err)
 | 
			
		||||
				util.Log.Errorf("Run go-exploitdb as server mode before reporting")
 | 
			
		||||
				return subcommands.ExitFailure
 | 
			
		||||
			}
 | 
			
		||||
@@ -399,27 +408,31 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
 | 
			
		||||
			DebugSQL:    c.Conf.DebugSQL,
 | 
			
		||||
		})
 | 
			
		||||
		if locked {
 | 
			
		||||
			util.Log.Errorf("SQLite3 is locked. Close other DB connections and try again: %s", err)
 | 
			
		||||
			util.Log.Errorf("SQLite3 is locked. Close other DB connections and try again. err: %+v", err)
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Errorf("Failed to init DB Clients: %s", err)
 | 
			
		||||
			util.Log.Errorf("Failed to init DB Clients. err: %+v", err)
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
		defer dbclient.CloseDB()
 | 
			
		||||
 | 
			
		||||
		if res, err = report.FillCveInfos(*dbclient, res, dir); err != nil {
 | 
			
		||||
			util.Log.Error(err)
 | 
			
		||||
			util.Log.Errorf("%+v", err)
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, w := range reports {
 | 
			
		||||
		if err := w.Write(res...); err != nil {
 | 
			
		||||
			util.Log.Errorf("Failed to report: %s", err)
 | 
			
		||||
			util.Log.Errorf("Failed to report. err: %+v", err)
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if hasError {
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return subcommands.ExitSuccess
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -96,7 +96,10 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
 | 
			
		||||
		"Use SSH options specified in ssh_config preferentially")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.ContainersOnly, "containers-only", false,
 | 
			
		||||
		"Scan containers only. Default: Scan both of hosts and containers")
 | 
			
		||||
		"Scan running containers only. Default: Scan both of hosts and running containers")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.ImagesOnly, "images-only", false,
 | 
			
		||||
		"Scan container images only. Default: Scan both of hosts and images")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.SkipBroken, "skip-broken", false,
 | 
			
		||||
		"[For CentOS] yum update changelog with --skip-broken option")
 | 
			
		||||
@@ -126,7 +129,7 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
 | 
			
		||||
	util.Log = util.NewCustomLogger(c.ServerInfo{})
 | 
			
		||||
 | 
			
		||||
	if err := mkdirDotVuls(); err != nil {
 | 
			
		||||
		util.Log.Errorf("Failed to create .vuls: %s", err)
 | 
			
		||||
		util.Log.Errorf("Failed to create .vuls. err: %+v", err)
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -142,9 +145,12 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
 | 
			
		||||
 | 
			
		||||
	err = c.Load(p.configPath, keyPass)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		util.Log.Errorf("Error loading %s, %s", p.configPath, err)
 | 
			
		||||
		util.Log.Errorf("If you update Vuls and get this error, there may be incompatible changes in config.toml")
 | 
			
		||||
		util.Log.Errorf("Please check README: https://github.com/future-architect/vuls#configuration")
 | 
			
		||||
		msg := []string{
 | 
			
		||||
			fmt.Sprintf("Error loading %s", p.configPath),
 | 
			
		||||
			"If you update Vuls and get this error, there may be incompatible changes in config.toml",
 | 
			
		||||
			"Please check config.toml template : https://vuls.io/docs/en/usage-settings.html",
 | 
			
		||||
		}
 | 
			
		||||
		util.Log.Errorf("%s\n%+v", strings.Join(msg, "\n"), err)
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -157,7 +163,7 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
 | 
			
		||||
	} else if c.Conf.Pipe {
 | 
			
		||||
		bytes, err := ioutil.ReadAll(os.Stdin)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Errorf("Failed to read stdin: %s", err)
 | 
			
		||||
			util.Log.Errorf("Failed to read stdin. err: %+v", err)
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
		fields := strings.Fields(string(bytes))
 | 
			
		||||
@@ -193,13 +199,13 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
 | 
			
		||||
 | 
			
		||||
	util.Log.Info("Detecting Server/Container OS... ")
 | 
			
		||||
	if err := scan.InitServers(p.timeoutSec); err != nil {
 | 
			
		||||
		util.Log.Errorf("Failed to init servers: %s", err)
 | 
			
		||||
		util.Log.Errorf("Failed to init servers: %+v", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	util.Log.Info("Checking Scan Modes... ")
 | 
			
		||||
	if err := scan.CheckScanModes(); err != nil {
 | 
			
		||||
		util.Log.Errorf("Fix config.toml: %s", err)
 | 
			
		||||
		util.Log.Errorf("Fix config.toml. err: %+v", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -208,7 +214,7 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
 | 
			
		||||
 | 
			
		||||
	util.Log.Info("Scanning vulnerabilities... ")
 | 
			
		||||
	if err := scan.Scan(p.scanTimeoutSec); err != nil {
 | 
			
		||||
		util.Log.Errorf("Failed to scan. err: %s", err)
 | 
			
		||||
		util.Log.Errorf("Failed to scan. err: %+v", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("\n\n\n")
 | 
			
		||||
 
 | 
			
		||||
@@ -152,7 +152,7 @@ func (p *ServerCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
 | 
			
		||||
	cvelog.SetLogger(c.Conf.LogDir, false, c.Conf.Debug, false)
 | 
			
		||||
 | 
			
		||||
	if err := c.Load(p.configPath, ""); err != nil {
 | 
			
		||||
		util.Log.Errorf("Error loading %s, %s", p.configPath, err)
 | 
			
		||||
		util.Log.Errorf("Error loading %s. err: %+v", p.configPath, err)
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -173,7 +173,7 @@ func (p *ServerCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.CveDict.URL != "" {
 | 
			
		||||
		if err := report.CveClient.CheckHealth(); err != nil {
 | 
			
		||||
			util.Log.Errorf("CVE HTTP server is not running. err: %s", err)
 | 
			
		||||
			util.Log.Errorf("CVE HTTP server is not running. err: %+v", err)
 | 
			
		||||
			util.Log.Errorf("Run go-cve-dictionary as server mode before reporting or run with `-cvedb-type=sqlite3 -cvedb-sqlite3-path` option instead of -cvedb-url")
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
@@ -192,7 +192,7 @@ func (p *ServerCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
 | 
			
		||||
		util.Log.Infof("gost: %s", c.Conf.Gost.URL)
 | 
			
		||||
		err := gost.Base{}.CheckHTTPHealth()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Errorf("gost HTTP server is not running. err: %s", err)
 | 
			
		||||
			util.Log.Errorf("gost HTTP server is not running. err: %+v", err)
 | 
			
		||||
			util.Log.Errorf("Run gost as server mode before reporting or run with `-gostdb-type=sqlite3 -gostdb-sqlite3-path` option instead of -gostdb-url")
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
@@ -201,7 +201,7 @@ func (p *ServerCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
 | 
			
		||||
	if c.Conf.Exploit.URL != "" {
 | 
			
		||||
		err := exploit.CheckHTTPHealth()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Errorf("exploit HTTP server is not running. err: %s", err)
 | 
			
		||||
			util.Log.Errorf("exploit HTTP server is not running. err: %+v", err)
 | 
			
		||||
			util.Log.Errorf("Run go-exploitdb as server mode before reporting")
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
@@ -215,12 +215,12 @@ func (p *ServerCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
 | 
			
		||||
		DebugSQL:    c.Conf.DebugSQL,
 | 
			
		||||
	})
 | 
			
		||||
	if locked {
 | 
			
		||||
		util.Log.Errorf("SQLite3 is locked. Close other DB connections and try again: %s", err)
 | 
			
		||||
		util.Log.Errorf("SQLite3 is locked. Close other DB connections and try again: %+v", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		util.Log.Errorf("Failed to init DB Clients: %s", err)
 | 
			
		||||
		util.Log.Errorf("Failed to init DB Clients. err: %+v", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -232,7 +232,7 @@ func (p *ServerCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
 | 
			
		||||
	})
 | 
			
		||||
	util.Log.Infof("Listening on %s", p.listen)
 | 
			
		||||
	if err := http.ListenAndServe(p.listen, nil); err != nil {
 | 
			
		||||
		util.Log.Errorf("Failed to start server: %s", err)
 | 
			
		||||
		util.Log.Errorf("Failed to start server. err: %+v", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
	return subcommands.ExitSuccess
 | 
			
		||||
 
 | 
			
		||||
@@ -149,7 +149,7 @@ func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) s
 | 
			
		||||
	cvelog.SetLogger(c.Conf.LogDir, false, c.Conf.Debug, false)
 | 
			
		||||
 | 
			
		||||
	if err := c.Load(p.configPath, ""); err != nil {
 | 
			
		||||
		util.Log.Errorf("Error loading %s, %s", p.configPath, err)
 | 
			
		||||
		util.Log.Errorf("Error loading %s, err: %+v", p.configPath, err)
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -166,7 +166,7 @@ func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) s
 | 
			
		||||
		dir, err = report.JSONDir(f.Args())
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		util.Log.Errorf("Failed to read from JSON: %s", err)
 | 
			
		||||
		util.Log.Errorf("Failed to read from JSON. err: %+v", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -189,7 +189,7 @@ func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) s
 | 
			
		||||
 | 
			
		||||
	if c.Conf.CveDict.URL != "" {
 | 
			
		||||
		if err := report.CveClient.CheckHealth(); err != nil {
 | 
			
		||||
			util.Log.Errorf("CVE HTTP server is not running. err: %s", err)
 | 
			
		||||
			util.Log.Errorf("CVE HTTP server is not running. err: %+v", err)
 | 
			
		||||
			util.Log.Errorf("Run go-cve-dictionary as server mode before reporting or run with `-cvedb-type=sqlite3 -cvedb-sqlite3-path` option instead of -cvedb-url")
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
@@ -198,7 +198,7 @@ func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) s
 | 
			
		||||
	if c.Conf.OvalDict.URL != "" {
 | 
			
		||||
		err := oval.Base{}.CheckHTTPHealth()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Errorf("OVAL HTTP server is not running. err: %s", err)
 | 
			
		||||
			util.Log.Errorf("OVAL HTTP server is not running. err: %+v", err)
 | 
			
		||||
			util.Log.Errorf("Run goval-dictionary as server mode before reporting or run with `-ovaldb-type=sqlite3 -ovaldb-sqlite3-path` option instead of -ovaldb-url")
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
@@ -208,7 +208,7 @@ func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) s
 | 
			
		||||
		util.Log.Infof("gost: %s", c.Conf.Gost.URL)
 | 
			
		||||
		err := gost.Base{}.CheckHTTPHealth()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Errorf("gost HTTP server is not running. err: %s", err)
 | 
			
		||||
			util.Log.Errorf("gost HTTP server is not running. err: %+v", err)
 | 
			
		||||
			util.Log.Errorf("Run gost as server mode before reporting or run with `-gostdb-type=sqlite3 -gostdb-sqlite3-path` option instead of -gostdb-url")
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
@@ -217,7 +217,7 @@ func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) s
 | 
			
		||||
	if c.Conf.Exploit.URL != "" {
 | 
			
		||||
		err := exploit.CheckHTTPHealth()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Errorf("exploit HTTP server is not running. err: %s", err)
 | 
			
		||||
			util.Log.Errorf("exploit HTTP server is not running. err: %+v", err)
 | 
			
		||||
			util.Log.Errorf("Run go-exploitdb as server mode before reporting")
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
@@ -230,12 +230,12 @@ func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) s
 | 
			
		||||
		DebugSQL:    c.Conf.DebugSQL,
 | 
			
		||||
	})
 | 
			
		||||
	if locked {
 | 
			
		||||
		util.Log.Errorf("SQLite3 is locked. Close other DB connections and try again: %s", err)
 | 
			
		||||
		util.Log.Errorf("SQLite3 is locked. Close other DB connections and try again: %+v", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		util.Log.Errorf("Failed to init DB Clients: %s", err)
 | 
			
		||||
		util.Log.Errorf("Failed to init DB Clients. err: %+v", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -245,5 +245,13 @@ func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) s
 | 
			
		||||
		util.Log.Error(err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, r := range res {
 | 
			
		||||
		if len(r.Warnings) != 0 {
 | 
			
		||||
			util.Log.Warnf("Warning: Some warnings occurred while scanning on %s: %s",
 | 
			
		||||
				r.FormatServerName(), r.Warnings)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return report.RunTui(res)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"github.com/howeyc/gopass"
 | 
			
		||||
	homedir "github.com/mitchellh/go-homedir"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func getPasswd(prompt string) (string, error) {
 | 
			
		||||
@@ -31,7 +32,7 @@ func getPasswd(prompt string) (string, error) {
 | 
			
		||||
		fmt.Print(prompt)
 | 
			
		||||
		pass, err := gopass.GetPasswdMasked()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", fmt.Errorf("Failed to read password")
 | 
			
		||||
			return "", xerrors.New("Failed to read a password")
 | 
			
		||||
		}
 | 
			
		||||
		if 0 < len(pass) {
 | 
			
		||||
			return string(pass), nil
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										234
									
								
								config/config.go
									
									
									
									
									
								
							
							
						
						
									
										234
									
								
								config/config.go
									
									
									
									
									
								
							@@ -27,13 +27,14 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	syslog "github.com/RackSec/srslog"
 | 
			
		||||
 | 
			
		||||
	valid "github.com/asaskevich/govalidator"
 | 
			
		||||
	"github.com/knqyf263/fanal/types"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Version of Vuls
 | 
			
		||||
var Version = "0.6.2"
 | 
			
		||||
var Version = "0.7.0"
 | 
			
		||||
 | 
			
		||||
// Revision of Git
 | 
			
		||||
var Revision string
 | 
			
		||||
@@ -98,31 +99,35 @@ const (
 | 
			
		||||
 | 
			
		||||
//Config is struct of Configuration
 | 
			
		||||
type Config struct {
 | 
			
		||||
	Debug      bool   `json:"debug"`
 | 
			
		||||
	DebugSQL   bool   `json:"debugSQL"`
 | 
			
		||||
	Lang       string `json:"lang"`
 | 
			
		||||
	HTTPProxy  string `valid:"url" json:"httpProxy"`
 | 
			
		||||
	LogDir     string `json:"logDir"`
 | 
			
		||||
	ResultsDir string `json:"resultsDir"`
 | 
			
		||||
	Pipe       bool   `json:"pipe"`
 | 
			
		||||
	Debug      bool   `json:"debug,omitempty"`
 | 
			
		||||
	DebugSQL   bool   `json:"debugSQL,omitempty"`
 | 
			
		||||
	Lang       string `json:"lang,omitempty"`
 | 
			
		||||
	HTTPProxy  string `valid:"url" json:"httpProxy,omitempty"`
 | 
			
		||||
	LogDir     string `json:"logDir,omitempty"`
 | 
			
		||||
	ResultsDir string `json:"resultsDir,omitempty"`
 | 
			
		||||
	Pipe       bool   `json:"pipe,omitempty"`
 | 
			
		||||
 | 
			
		||||
	Default            ServerInfo            `json:"default"`
 | 
			
		||||
	Servers            map[string]ServerInfo `json:"servers"`
 | 
			
		||||
	CvssScoreOver      float64               `json:"cvssScoreOver"`
 | 
			
		||||
	IgnoreUnscoredCves bool                  `json:"ignoreUnscoredCves"`
 | 
			
		||||
	IgnoreUnfixed      bool                  `json:"ignoreUnfixed"`
 | 
			
		||||
	SSHNative          bool                  `json:"sshNative"`
 | 
			
		||||
	SSHConfig          bool                  `json:"sshConfig"`
 | 
			
		||||
	ContainersOnly     bool                  `json:"containersOnly"`
 | 
			
		||||
	SkipBroken         bool                  `json:"skipBroken"`
 | 
			
		||||
	CacheDBPath        string                `json:"cacheDBPath"`
 | 
			
		||||
	Vvv                bool                  `json:"vvv"`
 | 
			
		||||
	UUID               bool                  `json:"uuid"`
 | 
			
		||||
	Default       ServerInfo            `json:"default,omitempty"`
 | 
			
		||||
	Servers       map[string]ServerInfo `json:"servers,omitempty"`
 | 
			
		||||
	CvssScoreOver float64               `json:"cvssScoreOver,omitempty"`
 | 
			
		||||
 | 
			
		||||
	CveDict  GoCveDictConf `json:"cveDict"`
 | 
			
		||||
	OvalDict GovalDictConf `json:"ovalDict"`
 | 
			
		||||
	Gost     GostConf      `json:"gost"`
 | 
			
		||||
	Exploit  ExploitConf   `json:"exploit"`
 | 
			
		||||
	IgnoreUnscoredCves    bool `json:"ignoreUnscoredCves,omitempty"`
 | 
			
		||||
	IgnoreUnfixed         bool `json:"ignoreUnfixed,omitempty"`
 | 
			
		||||
	IgnoreGitHubDismissed bool `json:"ignore_git_hub_dismissed,omitempty"`
 | 
			
		||||
 | 
			
		||||
	SSHNative      bool   `json:"sshNative,omitempty"`
 | 
			
		||||
	SSHConfig      bool   `json:"sshConfig,omitempty"`
 | 
			
		||||
	ContainersOnly bool   `json:"containersOnly,omitempty"`
 | 
			
		||||
	ImagesOnly     bool   `json:"imagesOnly,omitempty"`
 | 
			
		||||
	SkipBroken     bool   `json:"skipBroken,omitempty"`
 | 
			
		||||
	CacheDBPath    string `json:"cacheDBPath,omitempty"`
 | 
			
		||||
	Vvv            bool   `json:"vvv,omitempty"`
 | 
			
		||||
	UUID           bool   `json:"uuid,omitempty"`
 | 
			
		||||
 | 
			
		||||
	CveDict  GoCveDictConf `json:"cveDict,omitempty"`
 | 
			
		||||
	OvalDict GovalDictConf `json:"ovalDict,omitempty"`
 | 
			
		||||
	Gost     GostConf      `json:"gost,omitempty"`
 | 
			
		||||
	Exploit  ExploitConf   `json:"exploit,omitempty"`
 | 
			
		||||
 | 
			
		||||
	Slack    SlackConf    `json:"-"`
 | 
			
		||||
	EMail    SMTPConf     `json:"-"`
 | 
			
		||||
@@ -136,27 +141,27 @@ type Config struct {
 | 
			
		||||
	Telegram TelegramConf `json:"-"`
 | 
			
		||||
	Saas     SaasConf     `json:"-"`
 | 
			
		||||
 | 
			
		||||
	RefreshCve        bool `json:"refreshCve"`
 | 
			
		||||
	ToSlack           bool `json:"toSlack"`
 | 
			
		||||
	ToStride          bool `json:"toStride"`
 | 
			
		||||
	ToHipChat         bool `json:"toHipChat"`
 | 
			
		||||
	ToChatWork        bool `json:"toChatWork"`
 | 
			
		||||
	ToTelegram        bool `json:"ToTelegram"`
 | 
			
		||||
	ToEmail           bool `json:"toEmail"`
 | 
			
		||||
	ToSyslog          bool `json:"toSyslog"`
 | 
			
		||||
	ToLocalFile       bool `json:"toLocalFile"`
 | 
			
		||||
	ToS3              bool `json:"toS3"`
 | 
			
		||||
	ToAzureBlob       bool `json:"toAzureBlob"`
 | 
			
		||||
	ToSaas            bool `json:"toSaas"`
 | 
			
		||||
	ToHTTP            bool `json:"toHTTP"`
 | 
			
		||||
	FormatXML         bool `json:"formatXML"`
 | 
			
		||||
	FormatJSON        bool `json:"formatJSON"`
 | 
			
		||||
	FormatOneEMail    bool `json:"formatOneEMail"`
 | 
			
		||||
	FormatOneLineText bool `json:"formatOneLineText"`
 | 
			
		||||
	FormatList        bool `json:"formatList"`
 | 
			
		||||
	FormatFullText    bool `json:"formatFullText"`
 | 
			
		||||
	GZIP              bool `json:"gzip"`
 | 
			
		||||
	Diff              bool `json:"diff"`
 | 
			
		||||
	RefreshCve        bool `json:"refreshCve,omitempty"`
 | 
			
		||||
	ToSlack           bool `json:"toSlack,omitempty"`
 | 
			
		||||
	ToStride          bool `json:"toStride,omitempty"`
 | 
			
		||||
	ToHipChat         bool `json:"toHipChat,omitempty"`
 | 
			
		||||
	ToChatWork        bool `json:"toChatWork,omitempty"`
 | 
			
		||||
	ToTelegram        bool `json:"ToTelegram,omitempty"`
 | 
			
		||||
	ToEmail           bool `json:"toEmail,omitempty"`
 | 
			
		||||
	ToSyslog          bool `json:"toSyslog,omitempty"`
 | 
			
		||||
	ToLocalFile       bool `json:"toLocalFile,omitempty"`
 | 
			
		||||
	ToS3              bool `json:"toS3,omitempty"`
 | 
			
		||||
	ToAzureBlob       bool `json:"toAzureBlob,omitempty"`
 | 
			
		||||
	ToSaas            bool `json:"toSaas,omitempty"`
 | 
			
		||||
	ToHTTP            bool `json:"toHTTP,omitempty"`
 | 
			
		||||
	FormatXML         bool `json:"formatXML,omitempty"`
 | 
			
		||||
	FormatJSON        bool `json:"formatJSON,omitempty"`
 | 
			
		||||
	FormatOneEMail    bool `json:"formatOneEMail,omitempty"`
 | 
			
		||||
	FormatOneLineText bool `json:"formatOneLineText,omitempty"`
 | 
			
		||||
	FormatList        bool `json:"formatList,omitempty"`
 | 
			
		||||
	FormatFullText    bool `json:"formatFullText,omitempty"`
 | 
			
		||||
	GZIP              bool `json:"gzip,omitempty"`
 | 
			
		||||
	Diff              bool `json:"diff,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ValidateOnConfigtest validates
 | 
			
		||||
@@ -164,7 +169,7 @@ func (c Config) ValidateOnConfigtest() bool {
 | 
			
		||||
	errs := []error{}
 | 
			
		||||
 | 
			
		||||
	if runtime.GOOS == "windows" && !c.SSHNative {
 | 
			
		||||
		errs = append(errs, fmt.Errorf("-ssh-native-insecure is needed on windows"))
 | 
			
		||||
		errs = append(errs, xerrors.New("-ssh-native-insecure is needed on windows"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := valid.ValidateStruct(c)
 | 
			
		||||
@@ -185,25 +190,25 @@ func (c Config) ValidateOnScan() bool {
 | 
			
		||||
 | 
			
		||||
	if len(c.ResultsDir) != 0 {
 | 
			
		||||
		if ok, _ := valid.IsFilePath(c.ResultsDir); !ok {
 | 
			
		||||
			errs = append(errs, fmt.Errorf(
 | 
			
		||||
			errs = append(errs, xerrors.Errorf(
 | 
			
		||||
				"JSON base directory must be a *Absolute* file path. -results-dir: %s", c.ResultsDir))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if runtime.GOOS == "windows" && !c.SSHNative {
 | 
			
		||||
		errs = append(errs, fmt.Errorf("-ssh-native-insecure is needed on windows"))
 | 
			
		||||
		errs = append(errs, xerrors.New("-ssh-native-insecure is needed on windows"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(c.ResultsDir) != 0 {
 | 
			
		||||
		if ok, _ := valid.IsFilePath(c.ResultsDir); !ok {
 | 
			
		||||
			errs = append(errs, fmt.Errorf(
 | 
			
		||||
			errs = append(errs, xerrors.Errorf(
 | 
			
		||||
				"JSON base directory must be a *Absolute* file path. -results-dir: %s", c.ResultsDir))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(c.CacheDBPath) != 0 {
 | 
			
		||||
		if ok, _ := valid.IsFilePath(c.CacheDBPath); !ok {
 | 
			
		||||
			errs = append(errs, fmt.Errorf(
 | 
			
		||||
			errs = append(errs, xerrors.Errorf(
 | 
			
		||||
				"Cache DB path must be a *Absolute* file path. -cache-dbpath: %s",
 | 
			
		||||
				c.CacheDBPath))
 | 
			
		||||
		}
 | 
			
		||||
@@ -230,7 +235,7 @@ func (c Config) ValidateOnReportDB() bool {
 | 
			
		||||
	}
 | 
			
		||||
	if c.CveDict.Type == "sqlite3" {
 | 
			
		||||
		if _, err := os.Stat(c.CveDict.SQLite3Path); os.IsNotExist(err) {
 | 
			
		||||
			errs = append(errs, fmt.Errorf("SQLite3 DB path (%s) is not exist: %s", "cvedb", c.CveDict.SQLite3Path))
 | 
			
		||||
			errs = append(errs, xerrors.Errorf("SQLite3 DB path (%s) is not exist: %s", "cvedb", c.CveDict.SQLite3Path))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -259,7 +264,7 @@ func (c Config) ValidateOnReport() bool {
 | 
			
		||||
 | 
			
		||||
	if len(c.ResultsDir) != 0 {
 | 
			
		||||
		if ok, _ := valid.IsFilePath(c.ResultsDir); !ok {
 | 
			
		||||
			errs = append(errs, fmt.Errorf(
 | 
			
		||||
			errs = append(errs, xerrors.Errorf(
 | 
			
		||||
				"JSON base directory must be a *Absolute* file path. -results-dir: %s", c.ResultsDir))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -318,7 +323,7 @@ func (c Config) ValidateOnTui() bool {
 | 
			
		||||
 | 
			
		||||
	if len(c.ResultsDir) != 0 {
 | 
			
		||||
		if ok, _ := valid.IsFilePath(c.ResultsDir); !ok {
 | 
			
		||||
			errs = append(errs, fmt.Errorf(
 | 
			
		||||
			errs = append(errs, xerrors.Errorf(
 | 
			
		||||
				"JSON base directory must be a *Absolute* file path. -results-dir: %s", c.ResultsDir))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -328,7 +333,7 @@ func (c Config) ValidateOnTui() bool {
 | 
			
		||||
	}
 | 
			
		||||
	if c.CveDict.Type == "sqlite3" {
 | 
			
		||||
		if _, err := os.Stat(c.CveDict.SQLite3Path); os.IsNotExist(err) {
 | 
			
		||||
			errs = append(errs, fmt.Errorf("SQLite3 DB path (%s) is not exist: %s", "cvedb", c.CveDict.SQLite3Path))
 | 
			
		||||
			errs = append(errs, xerrors.Errorf("SQLite3 DB path (%s) is not exist: %s", "cvedb", c.CveDict.SQLite3Path))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -348,35 +353,35 @@ func validateDB(dictionaryDBName, dbType, dbPath, dbURL string) error {
 | 
			
		||||
	switch dbType {
 | 
			
		||||
	case "sqlite3":
 | 
			
		||||
		if dbURL != "" {
 | 
			
		||||
			return fmt.Errorf("To use SQLite3, specify -%s-type=sqlite3 and -%s-path. To use as http server mode, specify -%s-type=http and -%s-url",
 | 
			
		||||
			return xerrors.Errorf("To use SQLite3, specify -%s-type=sqlite3 and -%s-path. To use as http server mode, specify -%s-type=http and -%s-url",
 | 
			
		||||
				dictionaryDBName, dictionaryDBName, dictionaryDBName, dictionaryDBName)
 | 
			
		||||
		}
 | 
			
		||||
		if ok, _ := valid.IsFilePath(dbPath); !ok {
 | 
			
		||||
			return fmt.Errorf("SQLite3 path must be a *Absolute* file path. -%s-path: %s",
 | 
			
		||||
			return xerrors.Errorf("SQLite3 path must be a *Absolute* file path. -%s-path: %s",
 | 
			
		||||
				dictionaryDBName, dbPath)
 | 
			
		||||
		}
 | 
			
		||||
	case "mysql":
 | 
			
		||||
		if dbURL == "" {
 | 
			
		||||
			return fmt.Errorf(`MySQL connection string is needed. -%s-url="user:pass@tcp(localhost:3306)/dbname"`,
 | 
			
		||||
			return xerrors.Errorf(`MySQL connection string is needed. -%s-url="user:pass@tcp(localhost:3306)/dbname"`,
 | 
			
		||||
				dictionaryDBName)
 | 
			
		||||
		}
 | 
			
		||||
	case "postgres":
 | 
			
		||||
		if dbURL == "" {
 | 
			
		||||
			return fmt.Errorf(`PostgreSQL connection string is needed. -%s-url="host=myhost user=user dbname=dbname sslmode=disable password=password"`,
 | 
			
		||||
			return xerrors.Errorf(`PostgreSQL connection string is needed. -%s-url="host=myhost user=user dbname=dbname sslmode=disable password=password"`,
 | 
			
		||||
				dictionaryDBName)
 | 
			
		||||
		}
 | 
			
		||||
	case "redis":
 | 
			
		||||
		if dbURL == "" {
 | 
			
		||||
			return fmt.Errorf(`Redis connection string is needed. -%s-url="redis://localhost/0"`,
 | 
			
		||||
			return xerrors.Errorf(`Redis connection string is needed. -%s-url="redis://localhost/0"`,
 | 
			
		||||
				dictionaryDBName)
 | 
			
		||||
		}
 | 
			
		||||
	case "http":
 | 
			
		||||
		if dbURL == "" {
 | 
			
		||||
			return fmt.Errorf(`URL is needed. -%s-url="http://localhost:1323"`,
 | 
			
		||||
			return xerrors.Errorf(`URL is needed. -%s-url="http://localhost:1323"`,
 | 
			
		||||
				dictionaryDBName)
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		return fmt.Errorf("%s type must be either 'sqlite3', 'mysql', 'postgres', 'redis' or 'http'.  -%s-type: %s",
 | 
			
		||||
		return xerrors.Errorf("%s type must be either 'sqlite3', 'mysql', 'postgres', 'redis' or 'http'.  -%s-type: %s",
 | 
			
		||||
			dictionaryDBName, dictionaryDBName, dbType)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
@@ -400,7 +405,7 @@ func checkEmails(emails []string) (errs []error) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if ok := valid.IsEmail(addr); !ok {
 | 
			
		||||
			errs = append(errs, fmt.Errorf("Invalid email address. email: %s", addr))
 | 
			
		||||
			errs = append(errs, xerrors.Errorf("Invalid email address. email: %s", addr))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
@@ -422,16 +427,16 @@ func (c *SMTPConf) Validate() (errs []error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(c.SMTPAddr) == 0 {
 | 
			
		||||
		errs = append(errs, fmt.Errorf("email.smtpAddr must not be empty"))
 | 
			
		||||
		errs = append(errs, xerrors.New("email.smtpAddr must not be empty"))
 | 
			
		||||
	}
 | 
			
		||||
	if len(c.SMTPPort) == 0 {
 | 
			
		||||
		errs = append(errs, fmt.Errorf("email.smtpPort must not be empty"))
 | 
			
		||||
		errs = append(errs, xerrors.New("email.smtpPort must not be empty"))
 | 
			
		||||
	}
 | 
			
		||||
	if len(c.To) == 0 {
 | 
			
		||||
		errs = append(errs, fmt.Errorf("email.To required at least one address"))
 | 
			
		||||
		errs = append(errs, xerrors.New("email.To required at least one address"))
 | 
			
		||||
	}
 | 
			
		||||
	if len(c.From) == 0 {
 | 
			
		||||
		errs = append(errs, fmt.Errorf("email.From required at least one address"))
 | 
			
		||||
		errs = append(errs, xerrors.New("email.From required at least one address"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := valid.ValidateStruct(c)
 | 
			
		||||
@@ -454,11 +459,11 @@ func (c *StrideConf) Validate() (errs []error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(c.HookURL) == 0 {
 | 
			
		||||
		errs = append(errs, fmt.Errorf("stride.HookURL must not be empty"))
 | 
			
		||||
		errs = append(errs, xerrors.New("stride.HookURL must not be empty"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(c.AuthToken) == 0 {
 | 
			
		||||
		errs = append(errs, fmt.Errorf("stride.AuthToken must not be empty"))
 | 
			
		||||
		errs = append(errs, xerrors.New("stride.AuthToken must not be empty"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := valid.ValidateStruct(c)
 | 
			
		||||
@@ -486,21 +491,21 @@ func (c *SlackConf) Validate() (errs []error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(c.HookURL) == 0 && len(c.LegacyToken) == 0 {
 | 
			
		||||
		errs = append(errs, fmt.Errorf("slack.hookURL or slack.LegacyToken must not be empty"))
 | 
			
		||||
		errs = append(errs, xerrors.New("slack.hookURL or slack.LegacyToken must not be empty"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(c.Channel) == 0 {
 | 
			
		||||
		errs = append(errs, fmt.Errorf("slack.channel must not be empty"))
 | 
			
		||||
		errs = append(errs, xerrors.New("slack.channel must not be empty"))
 | 
			
		||||
	} else {
 | 
			
		||||
		if !(strings.HasPrefix(c.Channel, "#") ||
 | 
			
		||||
			c.Channel == "${servername}") {
 | 
			
		||||
			errs = append(errs, fmt.Errorf(
 | 
			
		||||
			errs = append(errs, xerrors.Errorf(
 | 
			
		||||
				"channel's prefix must be '#', channel: %s", c.Channel))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(c.AuthUser) == 0 {
 | 
			
		||||
		errs = append(errs, fmt.Errorf("slack.authUser must not be empty"))
 | 
			
		||||
		errs = append(errs, xerrors.New("slack.authUser must not be empty"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := valid.ValidateStruct(c)
 | 
			
		||||
@@ -523,11 +528,11 @@ func (c *HipChatConf) Validate() (errs []error) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if len(c.Room) == 0 {
 | 
			
		||||
		errs = append(errs, fmt.Errorf("hipcaht.room must not be empty"))
 | 
			
		||||
		errs = append(errs, xerrors.New("hipcaht.room must not be empty"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(c.AuthToken) == 0 {
 | 
			
		||||
		errs = append(errs, fmt.Errorf("hipcaht.AuthToken must not be empty"))
 | 
			
		||||
		errs = append(errs, xerrors.New("hipcaht.AuthToken must not be empty"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := valid.ValidateStruct(c)
 | 
			
		||||
@@ -549,11 +554,11 @@ func (c *ChatWorkConf) Validate() (errs []error) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if len(c.Room) == 0 {
 | 
			
		||||
		errs = append(errs, fmt.Errorf("chatworkcaht.room must not be empty"))
 | 
			
		||||
		errs = append(errs, xerrors.New("chatworkcaht.room must not be empty"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(c.APIToken) == 0 {
 | 
			
		||||
		errs = append(errs, fmt.Errorf("chatworkcaht.ApiToken must not be empty"))
 | 
			
		||||
		errs = append(errs, xerrors.New("chatworkcaht.ApiToken must not be empty"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := valid.ValidateStruct(c)
 | 
			
		||||
@@ -575,11 +580,11 @@ func (c *TelegramConf) Validate() (errs []error) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if len(c.ChatID) == 0 {
 | 
			
		||||
		errs = append(errs, fmt.Errorf("TelegramConf.ChatID must not be empty"))
 | 
			
		||||
		errs = append(errs, xerrors.New("TelegramConf.ChatID must not be empty"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(c.Token) == 0 {
 | 
			
		||||
		errs = append(errs, fmt.Errorf("TelegramConf.Token must not be empty"))
 | 
			
		||||
		errs = append(errs, xerrors.New("TelegramConf.Token must not be empty"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := valid.ValidateStruct(c)
 | 
			
		||||
@@ -603,15 +608,15 @@ func (c *SaasConf) Validate() (errs []error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.GroupID == 0 {
 | 
			
		||||
		errs = append(errs, fmt.Errorf("saas.GroupID must not be empty"))
 | 
			
		||||
		errs = append(errs, xerrors.New("saas.GroupID must not be empty"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(c.Token) == 0 {
 | 
			
		||||
		errs = append(errs, fmt.Errorf("saas.Token must not be empty"))
 | 
			
		||||
		errs = append(errs, xerrors.New("saas.Token must not be empty"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(c.URL) == 0 {
 | 
			
		||||
		errs = append(errs, fmt.Errorf("saas.URL must not be empty"))
 | 
			
		||||
		errs = append(errs, xerrors.New("saas.URL must not be empty"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := valid.ValidateStruct(c)
 | 
			
		||||
@@ -685,7 +690,7 @@ func (c *SyslogConf) GetSeverity() (syslog.Priority, error) {
 | 
			
		||||
	case "debug":
 | 
			
		||||
		return syslog.LOG_DEBUG, nil
 | 
			
		||||
	default:
 | 
			
		||||
		return -1, fmt.Errorf("Invalid severity: %s", c.Severity)
 | 
			
		||||
		return -1, xerrors.Errorf("Invalid severity: %s", c.Severity)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -737,7 +742,7 @@ func (c *SyslogConf) GetFacility() (syslog.Priority, error) {
 | 
			
		||||
	case "local7":
 | 
			
		||||
		return syslog.LOG_LOCAL7, nil
 | 
			
		||||
	default:
 | 
			
		||||
		return -1, fmt.Errorf("Invalid facility: %s", c.Facility)
 | 
			
		||||
		return -1, xerrors.Errorf("Invalid facility: %s", c.Facility)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1038,33 +1043,41 @@ type Azure struct {
 | 
			
		||||
 | 
			
		||||
// ServerInfo has SSH Info, additional CPE packages to scan.
 | 
			
		||||
type ServerInfo struct {
 | 
			
		||||
	ServerName             string                      `toml:"-" json:"serverName"`
 | 
			
		||||
	User                   string                      `toml:"user,omitempty" json:"user"`
 | 
			
		||||
	Host                   string                      `toml:"host,omitempty" json:"host"`
 | 
			
		||||
	Port                   string                      `toml:"port,omitempty" json:"port"`
 | 
			
		||||
	KeyPath                string                      `toml:"keyPath,omitempty" json:"keyPath"`
 | 
			
		||||
	KeyPassword            string                      `json:"-" toml:"-"`
 | 
			
		||||
	ServerName             string                      `toml:"-" json:"serverName,omitempty"`
 | 
			
		||||
	User                   string                      `toml:"user,omitempty" json:"user,omitempty"`
 | 
			
		||||
	Host                   string                      `toml:"host,omitempty" json:"host,omitempty"`
 | 
			
		||||
	Port                   string                      `toml:"port,omitempty" json:"port,omitempty"`
 | 
			
		||||
	KeyPath                string                      `toml:"keyPath,omitempty" json:"keyPath,omitempty"`
 | 
			
		||||
	KeyPassword            string                      `json:"-,omitempty" toml:"-"`
 | 
			
		||||
	CpeNames               []string                    `toml:"cpeNames,omitempty" json:"cpeNames,omitempty"`
 | 
			
		||||
	ScanMode               []string                    `toml:"scanMode,omitempty" json:"scanMode,omitempty"`
 | 
			
		||||
	DependencyCheckXMLPath string                      `toml:"dependencyCheckXMLPath,omitempty" json:"-"` // TODO Deprecated remove in near future
 | 
			
		||||
	OwaspDCXMLPath         string                      `toml:"owaspDCXMLPath,omitempty" json:"owaspDCXMLPath"`
 | 
			
		||||
	OwaspDCXMLPath         string                      `toml:"owaspDCXMLPath,omitempty" json:"owaspDCXMLPath,omitempty"`
 | 
			
		||||
	ContainersIncluded     []string                    `toml:"containersIncluded,omitempty" json:"containersIncluded,omitempty"`
 | 
			
		||||
	ContainersExcluded     []string                    `toml:"containersExcluded,omitempty" json:"containersExcluded,omitempty"`
 | 
			
		||||
	ContainerType          string                      `toml:"containerType,omitempty" json:"containerType,omitempty"`
 | 
			
		||||
	Containers             map[string]ContainerSetting `toml:"containers" json:"containers,omitempty"`
 | 
			
		||||
	IgnoreCves             []string                    `toml:"ignoreCves,omitempty" json:"ignoreCves,omitempty"`
 | 
			
		||||
	IgnorePkgsRegexp       []string                    `toml:"ignorePkgsRegexp,omitempty" json:"ignorePkgsRegexp,omitempty"`
 | 
			
		||||
	GitHubRepos            map[string]GitHubConf       `toml:"githubs" json:"githubs,omitempty"` // key: owner/repo
 | 
			
		||||
	Images                 map[string]Image            `toml:"images" json:"images,omitempty"`
 | 
			
		||||
	UUIDs                  map[string]string           `toml:"uuids,omitempty" json:"uuids,omitempty"`
 | 
			
		||||
	Memo                   string                      `toml:"memo,omitempty" json:"memo"`
 | 
			
		||||
	Memo                   string                      `toml:"memo,omitempty" json:"memo,omitempty"`
 | 
			
		||||
	Enablerepo             []string                    `toml:"enablerepo,omitempty" json:"enablerepo,omitempty"` // For CentOS, RHEL, Amazon
 | 
			
		||||
	Optional               map[string]interface{}      `toml:"optional,omitempty" json:"optional,omitempty"`     // Optional key-value set that will be outputted to JSON
 | 
			
		||||
	Type                   string                      `toml:"type,omitempty" json:"type"`                       // "pseudo" or ""
 | 
			
		||||
	IPv4Addrs              []string                    `toml:"-" json:"ipv4Addrs,omitempty"`
 | 
			
		||||
	IPv6Addrs              []string                    `toml:"-" json:"ipv6Addrs,omitempty"`
 | 
			
		||||
	Lockfiles              []string                    `toml:"lockfiles,omitempty" json:"lockfiles,omitempty"`   // ie) path/to/package-lock.json
 | 
			
		||||
	FindLock               bool                        `toml:"findLock,omitempty" json:"findLock,omitempty"`
 | 
			
		||||
	Type                   string                      `toml:"type,omitempty" json:"type,omitempty"` // "pseudo" or ""
 | 
			
		||||
 | 
			
		||||
	WordPress WordPressConf `toml:"wordpress,omitempty" json:"wordpress,omitempty"`
 | 
			
		||||
 | 
			
		||||
	// used internal
 | 
			
		||||
	IPv4Addrs []string `toml:"-" json:"ipv4Addrs,omitempty"`
 | 
			
		||||
	IPv6Addrs []string `toml:"-" json:"ipv6Addrs,omitempty"`
 | 
			
		||||
 | 
			
		||||
	LogMsgAnsiColor string    `toml:"-" json:"-"` // DebugLog Color
 | 
			
		||||
	Container       Container `toml:"-" json:"-"`
 | 
			
		||||
	Image           Image     `toml:"-" json:"-"`
 | 
			
		||||
	Distro          Distro    `toml:"-" json:"-"`
 | 
			
		||||
	Mode            ScanMode  `toml:"-" json:"-"`
 | 
			
		||||
}
 | 
			
		||||
@@ -1077,6 +1090,31 @@ type ContainerSetting struct {
 | 
			
		||||
	IgnoreCves       []string `json:"ignoreCves,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WordPressConf used for WordPress Scanning
 | 
			
		||||
type WordPressConf struct {
 | 
			
		||||
	OSUser         string `toml:"osUser" json:"osUser,omitempty"`
 | 
			
		||||
	DocRoot        string `toml:"docRoot" json:"docRoot,omitempty"`
 | 
			
		||||
	CmdPath        string `toml:"cmdPath" json:"cmdPath,omitempty"`
 | 
			
		||||
	WPVulnDBToken  string `toml:"wpVulnDBToken" json:"-,omitempty"`
 | 
			
		||||
	IgnoreInactive bool   `json:"ignoreInactive,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Image is a scan container image info
 | 
			
		||||
type Image struct {
 | 
			
		||||
	Name             string             `json:"name"`
 | 
			
		||||
	Tag              string             `json:"tag"`
 | 
			
		||||
	DockerOption     types.DockerOption `json:"dockerOption,omitempty"`
 | 
			
		||||
	Cpes             []string           `json:"cpes,omitempty"`
 | 
			
		||||
	OwaspDCXMLPath   string             `json:"owaspDCXMLPath"`
 | 
			
		||||
	IgnorePkgsRegexp []string           `json:"ignorePkgsRegexp,omitempty"`
 | 
			
		||||
	IgnoreCves       []string           `json:"ignoreCves,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GitHubConf is used for GitHub integration
 | 
			
		||||
type GitHubConf struct {
 | 
			
		||||
	Token string `json:"-"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ScanMode has a type of scan mode. fast, fast-root, deep and offline
 | 
			
		||||
type ScanMode struct {
 | 
			
		||||
	flag byte
 | 
			
		||||
@@ -1117,9 +1155,9 @@ func (s ScanMode) validate() error {
 | 
			
		||||
	if numTrue == 0 {
 | 
			
		||||
		s.Set(Fast)
 | 
			
		||||
	} else if s.IsDeep() && s.IsOffline() {
 | 
			
		||||
		return fmt.Errorf("Don't specify both of -deep and offline")
 | 
			
		||||
		return xerrors.New("Don't specify both of -deep and offline")
 | 
			
		||||
	} else if numTrue != 1 {
 | 
			
		||||
		return fmt.Errorf("Specify only one of -fast, -fast-root or -deep")
 | 
			
		||||
		return xerrors.New("Specify only one of -fast, -fast-root or -deep")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@@ -1182,7 +1220,7 @@ func (l Distro) MajorVersion() (ver int, err error) {
 | 
			
		||||
	if 0 < len(l.Release) {
 | 
			
		||||
		ver, err = strconv.Atoi(strings.Split(l.Release, ".")[0])
 | 
			
		||||
	} else {
 | 
			
		||||
		err = fmt.Errorf("Release is empty")
 | 
			
		||||
		err = xerrors.New("Release is empty")
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import "fmt"
 | 
			
		||||
import "golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
// JSONLoader loads configuration
 | 
			
		||||
type JSONLoader struct {
 | 
			
		||||
@@ -25,5 +25,5 @@ type JSONLoader struct {
 | 
			
		||||
 | 
			
		||||
// Load load the configuration JSON file specified by path arg.
 | 
			
		||||
func (c JSONLoader) Load(path, sudoPass, keyPass string) (err error) {
 | 
			
		||||
	return fmt.Errorf("Not implement yet")
 | 
			
		||||
	return xerrors.New("Not implement yet")
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -18,13 +18,13 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/BurntSushi/toml"
 | 
			
		||||
	"github.com/knqyf263/go-cpe/naming"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// TOMLLoader loads config
 | 
			
		||||
@@ -65,14 +65,24 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
 | 
			
		||||
	i := 0
 | 
			
		||||
	for serverName, v := range conf.Servers {
 | 
			
		||||
		if 0 < len(v.KeyPassword) {
 | 
			
		||||
			return fmt.Errorf("[Deprecated] KEYPASSWORD IN CONFIG FILE ARE UNSECURE. REMOVE THEM IMMEDIATELY FOR A SECURITY REASONS. THEY WILL BE REMOVED IN A FUTURE RELEASE: %s", serverName)
 | 
			
		||||
			return xerrors.Errorf("[Deprecated] KEYPASSWORD IN CONFIG FILE ARE UNSECURE. REMOVE THEM IMMEDIATELY FOR A SECURITY REASONS. THEY WILL BE REMOVED IN A FUTURE RELEASE: %s", serverName)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s := ServerInfo{ServerName: serverName}
 | 
			
		||||
		s.Images = make(map[string]Image)
 | 
			
		||||
 | 
			
		||||
		// image are able to set any server type
 | 
			
		||||
		for name, image := range v.Images {
 | 
			
		||||
			if err := IsValidImage(image); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			s.Images[name] = image
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if v.Type != ServerTypePseudo {
 | 
			
		||||
			s.Host = v.Host
 | 
			
		||||
			if len(s.Host) == 0 {
 | 
			
		||||
				return fmt.Errorf("%s is invalid. host is empty", serverName)
 | 
			
		||||
				return xerrors.Errorf("%s is invalid. host is empty", serverName)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			switch {
 | 
			
		||||
@@ -91,7 +101,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
 | 
			
		||||
				s.User = d.User
 | 
			
		||||
			default:
 | 
			
		||||
				if s.Port != "local" {
 | 
			
		||||
					return fmt.Errorf("%s is invalid. User is empty", serverName)
 | 
			
		||||
					return xerrors.Errorf("%s is invalid. User is empty", serverName)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
@@ -101,7 +111,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
 | 
			
		||||
			}
 | 
			
		||||
			if s.KeyPath != "" {
 | 
			
		||||
				if _, err := os.Stat(s.KeyPath); err != nil {
 | 
			
		||||
					return fmt.Errorf(
 | 
			
		||||
					return xerrors.Errorf(
 | 
			
		||||
						"%s is invalid. keypath: %s not exists", serverName, s.KeyPath)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
@@ -130,11 +140,11 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
 | 
			
		||||
			case "offline":
 | 
			
		||||
				s.Mode.Set(Offline)
 | 
			
		||||
			default:
 | 
			
		||||
				return fmt.Errorf("scanMode: %s of %s is invalie. Specify -fast, -fast-root, -deep or offline", m, serverName)
 | 
			
		||||
				return xerrors.Errorf("scanMode: %s of %s is invalie. Specify -fast, -fast-root, -deep or offline", m, serverName)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if err := s.Mode.validate(); err != nil {
 | 
			
		||||
			return fmt.Errorf("%s in %s", err, serverName)
 | 
			
		||||
			return xerrors.Errorf("%s in %s", err, serverName)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.CpeNames = v.CpeNames
 | 
			
		||||
@@ -142,10 +152,17 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
 | 
			
		||||
			s.CpeNames = d.CpeNames
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.Lockfiles = v.Lockfiles
 | 
			
		||||
		if len(s.Lockfiles) == 0 {
 | 
			
		||||
			s.Lockfiles = d.Lockfiles
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.FindLock = v.FindLock
 | 
			
		||||
 | 
			
		||||
		for i, n := range s.CpeNames {
 | 
			
		||||
			uri, err := toCpeURI(n)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return fmt.Errorf("Failed to parse CPENames %s in %s: %s", n, serverName, err)
 | 
			
		||||
				return xerrors.Errorf("Failed to parse CPENames %s in %s, err: %w", n, serverName, err)
 | 
			
		||||
			}
 | 
			
		||||
			s.CpeNames[i] = uri
 | 
			
		||||
		}
 | 
			
		||||
@@ -172,7 +189,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(v.DependencyCheckXMLPath) != 0 || len(d.DependencyCheckXMLPath) != 0 {
 | 
			
		||||
			return fmt.Errorf("[DEPRECATED] dependencyCheckXMLPath IS DEPRECATED. USE owaspDCXMLPath INSTEAD: %s", serverName)
 | 
			
		||||
			return xerrors.Errorf("[DEPRECATED] dependencyCheckXMLPath IS DEPRECATED. USE owaspDCXMLPath INSTEAD: %s", serverName)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.OwaspDCXMLPath = v.OwaspDCXMLPath
 | 
			
		||||
@@ -215,14 +232,14 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
 | 
			
		||||
		for _, reg := range s.IgnorePkgsRegexp {
 | 
			
		||||
			_, err := regexp.Compile(reg)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return fmt.Errorf("Faild to parse %s in %s. err: %s", reg, serverName, err)
 | 
			
		||||
				return xerrors.Errorf("Faild to parse %s in %s. err: %w", reg, serverName, err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		for contName, cont := range s.Containers {
 | 
			
		||||
			for _, reg := range cont.IgnorePkgsRegexp {
 | 
			
		||||
				_, err := regexp.Compile(reg)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return fmt.Errorf("Faild to parse %s in %s@%s. err: %s",
 | 
			
		||||
					return xerrors.Errorf("Faild to parse %s in %s@%s. err: %w",
 | 
			
		||||
						reg, contName, serverName, err)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
@@ -247,16 +264,34 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
 | 
			
		||||
				case "base", "updates":
 | 
			
		||||
					// nop
 | 
			
		||||
				default:
 | 
			
		||||
					return fmt.Errorf(
 | 
			
		||||
					return xerrors.Errorf(
 | 
			
		||||
						"For now, enablerepo have to be base or updates: %s, servername: %s",
 | 
			
		||||
						s.Enablerepo, serverName)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.GitHubRepos = v.GitHubRepos
 | 
			
		||||
		for ownerRepo, githubSetting := range s.GitHubRepos {
 | 
			
		||||
			if ss := strings.Split(ownerRepo, "/"); len(ss) != 2 {
 | 
			
		||||
				return xerrors.Errorf("Failed to parse GitHub owner/repo: %s in %s",
 | 
			
		||||
					ownerRepo, serverName)
 | 
			
		||||
			}
 | 
			
		||||
			if githubSetting.Token == "" {
 | 
			
		||||
				return xerrors.Errorf("GitHub owner/repo: %s in %s token is empty",
 | 
			
		||||
					ownerRepo, serverName)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.UUIDs = v.UUIDs
 | 
			
		||||
		s.Type = v.Type
 | 
			
		||||
 | 
			
		||||
		s.WordPress.WPVulnDBToken = v.WordPress.WPVulnDBToken
 | 
			
		||||
		s.WordPress.CmdPath = v.WordPress.CmdPath
 | 
			
		||||
		s.WordPress.DocRoot = v.WordPress.DocRoot
 | 
			
		||||
		s.WordPress.OSUser = v.WordPress.OSUser
 | 
			
		||||
		s.WordPress.IgnoreInactive = v.WordPress.IgnoreInactive
 | 
			
		||||
 | 
			
		||||
		s.LogMsgAnsiColor = Colors[i%len(Colors)]
 | 
			
		||||
		i++
 | 
			
		||||
 | 
			
		||||
@@ -280,5 +315,16 @@ func toCpeURI(cpename string) (string, error) {
 | 
			
		||||
		}
 | 
			
		||||
		return naming.BindToURI(wfn), nil
 | 
			
		||||
	}
 | 
			
		||||
	return "", fmt.Errorf("Unknow CPE format: %s", cpename)
 | 
			
		||||
	return "", xerrors.Errorf("Unknow CPE format: %s", cpename)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsValidImage checks a container configuration
 | 
			
		||||
func IsValidImage(c Image) error {
 | 
			
		||||
	if c.Name == "" {
 | 
			
		||||
		return xerrors.New("Invalid arguments : no image name")
 | 
			
		||||
	}
 | 
			
		||||
	if c.Tag == "" {
 | 
			
		||||
		return xerrors.New("Invalid arguments : no image tag")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,12 +2,12 @@ package parser
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/xml"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type analysis struct {
 | 
			
		||||
@@ -49,7 +49,7 @@ func Parse(path string) ([]string, error) {
 | 
			
		||||
 | 
			
		||||
	var anal analysis
 | 
			
		||||
	if err := xml.Unmarshal(b, &anal); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("Failed to unmarshal: %s", err)
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to unmarshal: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cpes := []string{}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										27
									
								
								errof/errof.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								errof/errof.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
package errof
 | 
			
		||||
 | 
			
		||||
// ErrorCode is vuls error code
 | 
			
		||||
type ErrorCode string
 | 
			
		||||
 | 
			
		||||
// Error is vuls error
 | 
			
		||||
type Error struct {
 | 
			
		||||
	Code    ErrorCode
 | 
			
		||||
	Message string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e Error) Error() string {
 | 
			
		||||
	return e.Message
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// ErrFailedToAccessGithubAPI is error of github alert's api access
 | 
			
		||||
	ErrFailedToAccessGithubAPI ErrorCode = "ErrFailedToAccessGithubAPI"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// New :
 | 
			
		||||
func New(code ErrorCode, msg string) Error {
 | 
			
		||||
	return Error{
 | 
			
		||||
		Code:    code,
 | 
			
		||||
		Message: msg,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -28,6 +28,7 @@ import (
 | 
			
		||||
	"github.com/mozqnet/go-exploitdb/db"
 | 
			
		||||
	exploitmodels "github.com/mozqnet/go-exploitdb/models"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// FillWithExploit fills exploit information that has in Exploit
 | 
			
		||||
@@ -112,8 +113,7 @@ func CheckHTTPHealth() error {
 | 
			
		||||
	//  resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
 | 
			
		||||
	//  resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
 | 
			
		||||
	if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
		return fmt.Errorf("Failed to connect to exploit server. url: %s, errs: %v",
 | 
			
		||||
			url, errs)
 | 
			
		||||
		return xerrors.Errorf("Failed to connect to exploit server. url: %s, errs: %w", url, errs)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -18,13 +18,13 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
package exploit
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cenkalti/backoff"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type response struct {
 | 
			
		||||
@@ -79,11 +79,11 @@ func getCvesViaHTTP(cveIDs []string, urlPrefix string) (
 | 
			
		||||
		case err := <-errChan:
 | 
			
		||||
			errs = append(errs, err)
 | 
			
		||||
		case <-timeout:
 | 
			
		||||
			return nil, fmt.Errorf("Timeout Fetching OVAL")
 | 
			
		||||
			return nil, xerrors.New("Timeout Fetching OVAL")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(errs) != 0 {
 | 
			
		||||
		return nil, fmt.Errorf("Failed to fetch OVAL. err: %v", errs)
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to fetch OVAL. err: %w", errs)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
@@ -108,8 +108,7 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er
 | 
			
		||||
			if count == retryMax {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			return fmt.Errorf("HTTP GET error: %v, url: %s, resp: %v",
 | 
			
		||||
				errs, url, resp)
 | 
			
		||||
			return xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %w", url, resp, errs)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -118,11 +117,11 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er
 | 
			
		||||
	}
 | 
			
		||||
	err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		errChan <- fmt.Errorf("HTTP Error %s", err)
 | 
			
		||||
		errChan <- xerrors.Errorf("HTTP Error %w", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if count == retryMax {
 | 
			
		||||
		errChan <- fmt.Errorf("HRetry count exceeded")
 | 
			
		||||
		errChan <- xerrors.New("Retry count exceeded")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										151
									
								
								github/github.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								github/github.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,151 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Corporation , Japan.
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package github
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"context"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/errof"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/k0kubun/pp"
 | 
			
		||||
	"golang.org/x/oauth2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// FillGitHubSecurityAlerts access to owner/repo on GitHub and fetch scurity alerts of the repository via GitHub API v4 GraphQL and then set to the given ScanResult.
 | 
			
		||||
// https://help.github.com/articles/about-security-alerts-for-vulnerable-dependencies/
 | 
			
		||||
func FillGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string) (nCVEs int, err error) {
 | 
			
		||||
	src := oauth2.StaticTokenSource(
 | 
			
		||||
		&oauth2.Token{AccessToken: token},
 | 
			
		||||
	)
 | 
			
		||||
	httpClient := oauth2.NewClient(context.Background(), src)
 | 
			
		||||
 | 
			
		||||
	// TODO Use `https://github.com/shurcooL/githubv4` if the tool supports vulnerabilityAlerts Endpoint
 | 
			
		||||
	const jsonfmt = `{"query":
 | 
			
		||||
	"query { repository(owner:\"%s\", name:\"%s\") { url, vulnerabilityAlerts(first: %d, %s) { pageInfo{ endCursor, hasNextPage, startCursor}, edges { node { id, externalIdentifier, externalReference, fixedIn, packageName,  dismissReason, dismissedAt } } } } }"}`
 | 
			
		||||
	after := ""
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		jsonStr := fmt.Sprintf(jsonfmt, owner, repo, 100, after)
 | 
			
		||||
		req, err := http.NewRequest("POST",
 | 
			
		||||
			"https://api.github.com/graphql",
 | 
			
		||||
			bytes.NewBuffer([]byte(jsonStr)),
 | 
			
		||||
		)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// https://developer.github.com/v4/previews/#repository-vulnerability-alerts
 | 
			
		||||
		// To toggle this preview and access data, need to provide a custom media type in the Accept header:
 | 
			
		||||
		// MEMO: I tried to get the affected version via GitHub API. Bit it seems difficult to determin the affected version if there are multiple dependency files such as package.json.
 | 
			
		||||
		// TODO remove this header if it is no longer preview status in the future.
 | 
			
		||||
		req.Header.Set("Accept", "application/vnd.github.vixen-preview+json")
 | 
			
		||||
		req.Header.Set("Content-Type", "application/json")
 | 
			
		||||
 | 
			
		||||
		resp, err := httpClient.Do(req)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		defer resp.Body.Close()
 | 
			
		||||
		alerts := SecurityAlerts{}
 | 
			
		||||
		if json.NewDecoder(resp.Body).Decode(&alerts); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		util.Log.Debugf("%s", pp.Sprint(alerts))
 | 
			
		||||
		if alerts.Data.Repository.URL == "" {
 | 
			
		||||
			return 0, errof.New(
 | 
			
		||||
				errof.ErrFailedToAccessGithubAPI,
 | 
			
		||||
				fmt.Sprintf("Failed to access to GitHub API. Response: %#v", alerts),
 | 
			
		||||
			)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, v := range alerts.Data.Repository.VulnerabilityAlerts.Edges {
 | 
			
		||||
			if config.Conf.IgnoreGitHubDismissed && v.Node.DismissReason != "" {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			pkgName := fmt.Sprintf("%s %s",
 | 
			
		||||
				alerts.Data.Repository.URL, v.Node.PackageName)
 | 
			
		||||
 | 
			
		||||
			m := models.GitHubSecurityAlert{
 | 
			
		||||
				PackageName:   pkgName,
 | 
			
		||||
				FixedIn:       v.Node.FixedIn,
 | 
			
		||||
				AffectedRange: v.Node.AffectedRange,
 | 
			
		||||
				Dismissed:     len(v.Node.DismissReason) != 0,
 | 
			
		||||
				DismissedAt:   v.Node.DismissedAt,
 | 
			
		||||
				DismissReason: v.Node.DismissReason,
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			cveID := v.Node.ExternalIdentifier
 | 
			
		||||
 | 
			
		||||
			if val, ok := r.ScannedCves[cveID]; ok {
 | 
			
		||||
				val.GitHubSecurityAlerts = val.GitHubSecurityAlerts.Add(m)
 | 
			
		||||
				r.ScannedCves[cveID] = val
 | 
			
		||||
				nCVEs++
 | 
			
		||||
			} else {
 | 
			
		||||
				v := models.VulnInfo{
 | 
			
		||||
					CveID:                cveID,
 | 
			
		||||
					Confidences:          models.Confidences{models.GitHubMatch},
 | 
			
		||||
					GitHubSecurityAlerts: models.GitHubSecurityAlerts{m},
 | 
			
		||||
				}
 | 
			
		||||
				r.ScannedCves[cveID] = v
 | 
			
		||||
				nCVEs++
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if !alerts.Data.Repository.VulnerabilityAlerts.PageInfo.HasNextPage {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		after = fmt.Sprintf(`after: \"%s\"`, alerts.Data.Repository.VulnerabilityAlerts.PageInfo.EndCursor)
 | 
			
		||||
	}
 | 
			
		||||
	return nCVEs, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//SecurityAlerts has detected CVE-IDs, PackageNames, Refs
 | 
			
		||||
type SecurityAlerts struct {
 | 
			
		||||
	Data struct {
 | 
			
		||||
		Repository struct {
 | 
			
		||||
			URL                 string `json:"url,omitempty"`
 | 
			
		||||
			VulnerabilityAlerts struct {
 | 
			
		||||
				PageInfo struct {
 | 
			
		||||
					EndCursor   string `json:"endCursor,omitempty"`
 | 
			
		||||
					HasNextPage bool   `json:"hasNextPage,omitempty"`
 | 
			
		||||
					StartCursor string `json:"startCursor,omitempty"`
 | 
			
		||||
				} `json:"pageInfo,omitempty"`
 | 
			
		||||
				Edges []struct {
 | 
			
		||||
					Node struct {
 | 
			
		||||
						ID                 string    `json:"id,omitempty"`
 | 
			
		||||
						ExternalIdentifier string    `json:"externalIdentifier,omitempty"`
 | 
			
		||||
						ExternalReference  string    `json:"externalReference,omitempty"`
 | 
			
		||||
						FixedIn            string    `json:"fixedIn,omitempty"`
 | 
			
		||||
						AffectedRange      string    `json:"affectedRange,omitempty"`
 | 
			
		||||
						PackageName        string    `json:"packageName,omitempty"`
 | 
			
		||||
						DismissReason      string    `json:"dismissReason,omitempty"`
 | 
			
		||||
						DismissedAt        time.Time `json:"dismissedAt,omitempty"`
 | 
			
		||||
					} `json:"node,omitempty"`
 | 
			
		||||
				} `json:"edges,omitempty"`
 | 
			
		||||
			} `json:"vulnerabilityAlerts,omitempty"`
 | 
			
		||||
		} `json:"repository,omitempty"`
 | 
			
		||||
	} `json:"data,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										110
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,110 @@
 | 
			
		||||
module github.com/future-architect/vuls
 | 
			
		||||
 | 
			
		||||
go 1.12
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	cloud.google.com/go v0.40.0 // indirect
 | 
			
		||||
	contrib.go.opencensus.io/exporter/ocagent v0.4.12 // indirect
 | 
			
		||||
	github.com/Azure/azure-sdk-for-go v28.1.0+incompatible
 | 
			
		||||
	github.com/Azure/go-autorest v12.0.0+incompatible // indirect
 | 
			
		||||
	github.com/BurntSushi/toml v0.3.1
 | 
			
		||||
	github.com/Microsoft/go-winio v0.4.12 // indirect
 | 
			
		||||
	github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91
 | 
			
		||||
	github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a
 | 
			
		||||
	github.com/aws/aws-sdk-go v1.19.24
 | 
			
		||||
	github.com/beorn7/perks v1.0.0 // indirect
 | 
			
		||||
	github.com/boltdb/bolt v1.3.1
 | 
			
		||||
	github.com/cenkalti/backoff v2.1.1+incompatible
 | 
			
		||||
	github.com/cheggaaa/pb v2.0.6+incompatible // indirect
 | 
			
		||||
	github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc // indirect
 | 
			
		||||
	github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3 // indirect
 | 
			
		||||
	github.com/dnaeon/go-vcr v1.0.1 // indirect
 | 
			
		||||
	github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f // indirect
 | 
			
		||||
	github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f // indirect
 | 
			
		||||
	github.com/genuinetools/reg v0.16.1 // indirect
 | 
			
		||||
	github.com/go-redis/redis v6.15.2+incompatible // indirect
 | 
			
		||||
	github.com/gogo/protobuf v1.2.1 // indirect
 | 
			
		||||
	github.com/golang/mock v1.3.1 // indirect
 | 
			
		||||
	github.com/google/btree v1.0.0 // indirect
 | 
			
		||||
	github.com/google/pprof v0.0.0-20190515194954-54271f7e092f // indirect
 | 
			
		||||
	github.com/google/subcommands v1.0.1
 | 
			
		||||
	github.com/gorilla/mux v1.7.1 // indirect
 | 
			
		||||
	github.com/gorilla/websocket v1.4.0 // indirect
 | 
			
		||||
	github.com/gosuri/uitable v0.0.1
 | 
			
		||||
	github.com/grokify/html-strip-tags-go v0.0.0-20190424092004-025bd760b278 // indirect
 | 
			
		||||
	github.com/hashicorp/go-version v1.2.0
 | 
			
		||||
	github.com/hashicorp/uuid v0.0.0-20160311170451-ebb0a03e909c
 | 
			
		||||
	github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c
 | 
			
		||||
	github.com/jinzhu/gorm v1.9.8 // indirect
 | 
			
		||||
	github.com/jinzhu/inflection v0.0.0-20190603042836-f5c5f50e6090 // indirect
 | 
			
		||||
	github.com/jroimartin/gocui v0.4.0
 | 
			
		||||
	github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect
 | 
			
		||||
	github.com/k0kubun/pp v3.0.1+incompatible
 | 
			
		||||
	github.com/knqyf263/fanal v0.0.0-20190528042547-07e27879b658
 | 
			
		||||
	github.com/knqyf263/go-cpe v0.0.0-20180327054844-659663f6eca2
 | 
			
		||||
	github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d
 | 
			
		||||
	github.com/knqyf263/go-dep-parser v0.0.0-20190521150559-1ef8521d17a0
 | 
			
		||||
	github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936
 | 
			
		||||
	github.com/knqyf263/go-version v1.1.1
 | 
			
		||||
	github.com/knqyf263/gost v0.0.0-20190326022014-39175c0da9e3
 | 
			
		||||
	github.com/knqyf263/trivy v0.1.1
 | 
			
		||||
	github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
 | 
			
		||||
	github.com/kotakanbe/go-cve-dictionary v0.0.0-20190327053454-5fe52611f0b8
 | 
			
		||||
	github.com/kotakanbe/go-pingscanner v0.1.0
 | 
			
		||||
	github.com/kotakanbe/goval-dictionary v0.1.3-0.20190613053258-078b163b76ec
 | 
			
		||||
	github.com/kotakanbe/logrus-prefixed-formatter v0.0.0-20180123152602-928f7356cb96
 | 
			
		||||
	github.com/kr/pty v1.1.5 // indirect
 | 
			
		||||
	github.com/labstack/echo v3.3.10+incompatible // indirect
 | 
			
		||||
	github.com/labstack/gommon v0.2.9 // indirect
 | 
			
		||||
	github.com/lib/pq v1.1.1 // indirect
 | 
			
		||||
	github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5 // indirect
 | 
			
		||||
	github.com/lusis/slack-test v0.0.0-20190426140909-c40012f20018 // indirect
 | 
			
		||||
	github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
 | 
			
		||||
	github.com/mitchellh/go-homedir v1.1.0
 | 
			
		||||
	github.com/moul/http2curl v1.0.0 // indirect
 | 
			
		||||
	github.com/mozqnet/go-exploitdb v0.0.0-20190426034301-a055cc2c195d
 | 
			
		||||
	github.com/nlopes/slack v0.4.0
 | 
			
		||||
	github.com/nsf/termbox-go v0.0.0-20190325093121-288510b9734e // indirect
 | 
			
		||||
	github.com/olekukonko/tablewriter v0.0.1
 | 
			
		||||
	github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
 | 
			
		||||
	github.com/parnurzeal/gorequest v0.2.15
 | 
			
		||||
	github.com/pelletier/go-toml v1.4.0 // indirect
 | 
			
		||||
	github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 // indirect
 | 
			
		||||
	github.com/prometheus/common v0.3.0 // indirect
 | 
			
		||||
	github.com/prometheus/procfs v0.0.0-20190503130316-740c07785007 // indirect
 | 
			
		||||
	github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
 | 
			
		||||
	github.com/satori/go.uuid v1.2.0 // indirect
 | 
			
		||||
	github.com/sirupsen/logrus v1.2.0
 | 
			
		||||
	github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a // indirect
 | 
			
		||||
	github.com/spf13/afero v1.2.2 // indirect
 | 
			
		||||
	github.com/spf13/jwalterweatherman v1.1.0 // indirect
 | 
			
		||||
	github.com/spf13/viper v1.3.2 // indirect
 | 
			
		||||
	go.opencensus.io v0.22.0 // indirect
 | 
			
		||||
	golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8
 | 
			
		||||
	golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522 // indirect
 | 
			
		||||
	golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff // indirect
 | 
			
		||||
	golang.org/x/mobile v0.0.0-20190607214518-6fa95d984e88 // indirect
 | 
			
		||||
	golang.org/x/mod v0.1.0 // indirect
 | 
			
		||||
	golang.org/x/net v0.0.0-20190611141213-3f473d35a33a // indirect
 | 
			
		||||
	golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
 | 
			
		||||
	golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae // indirect
 | 
			
		||||
	golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
 | 
			
		||||
	golang.org/x/tools v0.0.0-20190612232758-d4e310b4a8a5 // indirect
 | 
			
		||||
	golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373
 | 
			
		||||
	google.golang.org/appengine v1.6.1 // indirect
 | 
			
		||||
	google.golang.org/genproto v0.0.0-20190611190212-a7e196e89fd3 // indirect
 | 
			
		||||
	google.golang.org/grpc v1.21.1 // indirect
 | 
			
		||||
	gopkg.in/VividCortex/ewma.v1 v1.1.1 // indirect
 | 
			
		||||
	gopkg.in/cheggaaa/pb.v2 v2.0.6 // indirect
 | 
			
		||||
	gopkg.in/fatih/color.v1 v1.7.0 // indirect
 | 
			
		||||
	gopkg.in/mattn/go-colorable.v0 v0.0.0-00010101000000-000000000000 // indirect
 | 
			
		||||
	gopkg.in/mattn/go-isatty.v0 v0.0.0-00010101000000-000000000000 // indirect
 | 
			
		||||
	gopkg.in/mattn/go-runewidth.v0 v0.0.4 // indirect
 | 
			
		||||
	honnef.co/go/tools v0.0.0-20190607181801-497c8f037f5a // indirect
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
replace github.com/genuinetools/reg => github.com/tomoyamachi/reg v0.16.2-0.20190418055600-c6010b917a55
 | 
			
		||||
 | 
			
		||||
replace gopkg.in/mattn/go-colorable.v0 => github.com/mattn/go-colorable v0.1.0
 | 
			
		||||
 | 
			
		||||
replace gopkg.in/mattn/go-isatty.v0 => github.com/mattn/go-isatty v0.0.6
 | 
			
		||||
							
								
								
									
										648
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										648
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,648 @@
 | 
			
		||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 | 
			
		||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 | 
			
		||||
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
 | 
			
		||||
cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo=
 | 
			
		||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
 | 
			
		||||
cloud.google.com/go v0.40.0 h1:FjSY7bOj+WzJe6TZRVtXI2b9kAYvtNg4lMbcH2+MUkk=
 | 
			
		||||
cloud.google.com/go v0.40.0/go.mod h1:Tk58MuI9rbLMKlAjeO/bDnteAx7tX2gJIXw4T5Jwlro=
 | 
			
		||||
contrib.go.opencensus.io/exporter/ocagent v0.4.12 h1:jGFvw3l57ViIVEPKKEUXPcLYIXJmQxLUh6ey1eJhwyc=
 | 
			
		||||
contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA=
 | 
			
		||||
github.com/Azure/azure-sdk-for-go v28.1.0+incompatible h1:uApF+FNMxRibKyoWxLatbrBJse505r7UVdrOm3dEtfk=
 | 
			
		||||
github.com/Azure/azure-sdk-for-go v28.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
 | 
			
		||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
 | 
			
		||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
 | 
			
		||||
github.com/Azure/go-autorest v12.0.0+incompatible h1:N+VqClcomLGD/sHb3smbSYYtNMgKpVV3Cd5r5i8z6bQ=
 | 
			
		||||
github.com/Azure/go-autorest v12.0.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
 | 
			
		||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 | 
			
		||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 | 
			
		||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 | 
			
		||||
github.com/DataDog/zstd v1.3.6-0.20190409195224-796139022798/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
 | 
			
		||||
github.com/DataDog/zstd v1.4.0/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
 | 
			
		||||
github.com/GoogleCloudPlatform/docker-credential-gcr v1.5.0 h1:wykTgKwhVr2t2qs+xI020s6W5dt614QqCHV+7W9dg64=
 | 
			
		||||
github.com/GoogleCloudPlatform/docker-credential-gcr v1.5.0/go.mod h1:BB1eHdMLYEFuFdBlRMb0N7YGVdM5s6Pt0njxgvfbGGs=
 | 
			
		||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
 | 
			
		||||
github.com/Microsoft/go-winio v0.4.12 h1:xAfWHN1IrQ0NJ9TBC0KBZoqLjzDTr1ML+4MywiUOryc=
 | 
			
		||||
github.com/Microsoft/go-winio v0.4.12/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
 | 
			
		||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
 | 
			
		||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
 | 
			
		||||
github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91 h1:vX+gnvBc56EbWYrmlhYbFYRaeikAke1GL84N4BEYOFE=
 | 
			
		||||
github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91/go.mod h1:cDLGBht23g0XQdLjzn6xOGXDkLK182YfINAaZEQLCHQ=
 | 
			
		||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
 | 
			
		||||
github.com/Shopify/sarama v1.22.1/go.mod h1:FRzlvRpMFO/639zY1SDxUxkqH97Y0ndM5CbGj6oG3As=
 | 
			
		||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
 | 
			
		||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
 | 
			
		||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
 | 
			
		||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 | 
			
		||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 | 
			
		||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
 | 
			
		||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
 | 
			
		||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
 | 
			
		||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
 | 
			
		||||
github.com/asaskevich/govalidator v0.0.0-20180315120708-ccb8e960c48f h1:y2hSFdXeA1y5z5f0vfNO0Dg5qVY036qzlz3Pds0B92o=
 | 
			
		||||
github.com/asaskevich/govalidator v0.0.0-20180315120708-ccb8e960c48f/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 | 
			
		||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
 | 
			
		||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 | 
			
		||||
github.com/aws/aws-sdk-go v1.19.11/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
 | 
			
		||||
github.com/aws/aws-sdk-go v1.19.24 h1:qOIYaFxcFg07Vdn799ERpGiuUUIEi5MQ2vYib3CNMp4=
 | 
			
		||||
github.com/aws/aws-sdk-go v1.19.24/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
 | 
			
		||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 | 
			
		||||
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
 | 
			
		||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 | 
			
		||||
github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
 | 
			
		||||
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
 | 
			
		||||
github.com/briandowns/spinner v0.0.0-20190319032542-ac46072a5a91 h1:GMmnK0dvr0Sf0gx3DvTbln0c8DE07B7sPVD9dgHOqo4=
 | 
			
		||||
github.com/briandowns/spinner v0.0.0-20190319032542-ac46072a5a91/go.mod h1:hw/JEQBIE+c/BLI4aKM8UU8v+ZqrD3h7HC27kKt8JQU=
 | 
			
		||||
github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY=
 | 
			
		||||
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
 | 
			
		||||
github.com/census-instrumentation/opencensus-proto v0.2.0 h1:LzQXZOgg4CQfE6bFvXGM30YZL1WW/M337pXml+GrcZ4=
 | 
			
		||||
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 | 
			
		||||
github.com/cheggaaa/pb v2.0.6+incompatible h1:sutSx+mRaNbeJUMCAtyqNWU/tQ0B/xBm+hyb1JQmQYs=
 | 
			
		||||
github.com/cheggaaa/pb v2.0.6+incompatible/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
 | 
			
		||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 | 
			
		||||
github.com/containerd/continuity v0.0.0-20180921161001-7f53d412b9eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
 | 
			
		||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc h1:TP+534wVlf61smEIq1nwLLAjQVEK2EADoW3CX9AuT+8=
 | 
			
		||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
 | 
			
		||||
github.com/coreos/clair v0.0.0-20180919182544-44ae4bc9590a/go.mod h1:uXhHPWAoRqw0jJc2f8RrPCwRhIo9otQ8OEWUFtpCiwA=
 | 
			
		||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 | 
			
		||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
 | 
			
		||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ=
 | 
			
		||||
github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
 | 
			
		||||
github.com/denisenkom/go-mssqldb v0.0.0-20190423183735-731ef375ac02 h1:PS3xfVPa8N84AzoWZHFCbA0+ikz4f4skktfjQoNMsgk=
 | 
			
		||||
github.com/denisenkom/go-mssqldb v0.0.0-20190423183735-731ef375ac02/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
 | 
			
		||||
github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
 | 
			
		||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
 | 
			
		||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 | 
			
		||||
github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY=
 | 
			
		||||
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
 | 
			
		||||
github.com/docker/cli v0.0.0-20180920165730-54c19e67f69c h1:QlAVcyoF7QQVN7zV+xYBjgwtRVlRU3WCTCpb2mcqQrM=
 | 
			
		||||
github.com/docker/cli v0.0.0-20180920165730-54c19e67f69c/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
 | 
			
		||||
github.com/docker/distribution v0.0.0-20180920194744-16128bbac47f h1:hYf+mPizfvpH6VgIxdntnOmQHd1F1mQUc1oG+j3Ol2g=
 | 
			
		||||
github.com/docker/distribution v0.0.0-20180920194744-16128bbac47f/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
 | 
			
		||||
github.com/docker/docker v0.0.0-20180924202107-a9c061deec0f h1:W4fbqg0JUwy6lLesoJaV/rE0fwAmtdtinMa64X1CEh0=
 | 
			
		||||
github.com/docker/docker v0.0.0-20180924202107-a9c061deec0f/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 | 
			
		||||
github.com/docker/docker-ce v0.0.0-20180924210327-f53bd8bb8e43 h1:gZ4lWixV821UVbYtr+oz1ZPCHkbtE+ivfmHyZRgyl2Y=
 | 
			
		||||
github.com/docker/docker-ce v0.0.0-20180924210327-f53bd8bb8e43/go.mod h1:l1FUGRYBvbjnZ8MS6A2xOji4aZFlY/Qmgz7p4oXH7ac=
 | 
			
		||||
github.com/docker/docker-credential-helpers v0.6.1 h1:Dq4iIfcM7cNtddhLVWe9h4QDjsi4OER3Z8voPu/I52g=
 | 
			
		||||
github.com/docker/docker-credential-helpers v0.6.1/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
 | 
			
		||||
github.com/docker/go-connections v0.0.0-20180821093606-97c2040d34df/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
 | 
			
		||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
 | 
			
		||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
 | 
			
		||||
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916 h1:yWHOI+vFjEsAakUTSrtqc/SAHrhSkmn48pqjidZX3QA=
 | 
			
		||||
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
 | 
			
		||||
github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk=
 | 
			
		||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 | 
			
		||||
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4=
 | 
			
		||||
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
 | 
			
		||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
 | 
			
		||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
 | 
			
		||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
 | 
			
		||||
github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f h1:8GDPb0tCY8LQ+OJ3dbHb5sA6YZWXFORQYZx5sdsTlMs=
 | 
			
		||||
github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
 | 
			
		||||
github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f h1:AUj1VoZUfhPhOPHULCQQDnGhRelpFWHMLhQVWDsS0v4=
 | 
			
		||||
github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
 | 
			
		||||
github.com/emirpasic/gods v1.9.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
 | 
			
		||||
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
 | 
			
		||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
 | 
			
		||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
 | 
			
		||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
 | 
			
		||||
github.com/etcd-io/bbolt v1.3.2 h1:RLRQ0TKLX7DlBRXAJHvbmXL17Q3KNnTBtZ9B6Qo+/Y0=
 | 
			
		||||
github.com/etcd-io/bbolt v1.3.2/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
 | 
			
		||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
 | 
			
		||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 | 
			
		||||
github.com/fernet/fernet-go v0.0.0-20180830025343-9eac43b88a5e/go.mod h1:2H9hjfbpSMHwY503FclkV/lZTBh2YlOmLLSda12uL8c=
 | 
			
		||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
 | 
			
		||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
 | 
			
		||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
 | 
			
		||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 | 
			
		||||
github.com/genuinetools/pkg v0.0.0-20180910213200-1c141f661797/go.mod h1:XTcrCYlXPxnxL2UpnwuRn7tcaTn9HAhxFoFJucootk8=
 | 
			
		||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 | 
			
		||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
 | 
			
		||||
github.com/gliderlabs/ssh v0.1.3 h1:cBU46h1lYQk5f2Z+jZbewFKy+1zzE2aUX/ilcPDAm9M=
 | 
			
		||||
github.com/gliderlabs/ssh v0.1.3/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
 | 
			
		||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 | 
			
		||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 | 
			
		||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 | 
			
		||||
github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
 | 
			
		||||
github.com/go-redis/redis v6.15.2+incompatible h1:9SpNVG76gr6InJGxoZ6IuuxaCOQwDAhzyXg+Bs+0Sb4=
 | 
			
		||||
github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
 | 
			
		||||
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
 | 
			
		||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
 | 
			
		||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
 | 
			
		||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 | 
			
		||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 | 
			
		||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 | 
			
		||||
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
 | 
			
		||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
 | 
			
		||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
 | 
			
		||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 | 
			
		||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
 | 
			
		||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 | 
			
		||||
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
 | 
			
		||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 | 
			
		||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
 | 
			
		||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
			
		||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
 | 
			
		||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
			
		||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 | 
			
		||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 | 
			
		||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 | 
			
		||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 | 
			
		||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
 | 
			
		||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 | 
			
		||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 | 
			
		||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 | 
			
		||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 | 
			
		||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 | 
			
		||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 | 
			
		||||
github.com/google/subcommands v0.0.0-20181012225330-46f0354f6315/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
 | 
			
		||||
github.com/google/subcommands v1.0.1 h1:/eqq+otEXm5vhfBrbREPCSVQbvofip6kIz+mX5TUH7k=
 | 
			
		||||
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
 | 
			
		||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 | 
			
		||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
 | 
			
		||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 | 
			
		||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
 | 
			
		||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 | 
			
		||||
github.com/gorilla/mux v1.7.1 h1:Dw4jY2nghMMRsh1ol8dv1axHkDwMQK2DHerMNJsIpJU=
 | 
			
		||||
github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 | 
			
		||||
github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 | 
			
		||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
 | 
			
		||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 | 
			
		||||
github.com/gosuri/uitable v0.0.1 h1:M9sMNgSZPyAu1FJZJLpJ16ofL8q5ko2EDUkICsynvlY=
 | 
			
		||||
github.com/gosuri/uitable v0.0.1/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo=
 | 
			
		||||
github.com/grokify/html-strip-tags-go v0.0.0-20190424092004-025bd760b278 h1:DZo48DQFIDo/YWjUeFip1dfJztBhRuaxfUnPd+gAfcs=
 | 
			
		||||
github.com/grokify/html-strip-tags-go v0.0.0-20190424092004-025bd760b278/go.mod h1:Xk7G0nwBiIloTMbLddk4WWJOqi4i/JLhadLd0HUXO30=
 | 
			
		||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
 | 
			
		||||
github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJlb8Kqsd41CTE=
 | 
			
		||||
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 | 
			
		||||
github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E=
 | 
			
		||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
 | 
			
		||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 | 
			
		||||
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
 | 
			
		||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 | 
			
		||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
 | 
			
		||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 | 
			
		||||
github.com/hashicorp/uuid v0.0.0-20160311170451-ebb0a03e909c h1:nQcv325vxv2fFHJsOt53eSRf1eINt6vOdYUFfXs4rgk=
 | 
			
		||||
github.com/hashicorp/uuid v0.0.0-20160311170451-ebb0a03e909c/go.mod h1:fHzc09UnyJyqyW+bFuq864eh+wC7dj65aXmXLRe5to0=
 | 
			
		||||
github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c h1:kQWxfPIHVLbgLzphqk3QUflDy9QdksZR4ygR807bpy0=
 | 
			
		||||
github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs=
 | 
			
		||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 | 
			
		||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 | 
			
		||||
github.com/htcat/htcat v1.0.2 h1:zro95dGwkKDeZOgq9ei+9szd5qurGxBGfHY8hRehA7k=
 | 
			
		||||
github.com/htcat/htcat v1.0.2/go.mod h1:i8ViQbjSi2+lJzM6Lx20FIxHENCz6mzJglK3HH06W3s=
 | 
			
		||||
github.com/inconshreveable/log15 v0.0.0-20180818164646-67afb5ed74ec h1:CGkYB1Q7DSsH/ku+to+foV4agt2F2miquaLUgF6L178=
 | 
			
		||||
github.com/inconshreveable/log15 v0.0.0-20180818164646-67afb5ed74ec/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
 | 
			
		||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
 | 
			
		||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
 | 
			
		||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
 | 
			
		||||
github.com/jinzhu/gorm v1.9.1/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
 | 
			
		||||
github.com/jinzhu/gorm v1.9.5 h1:sc+tBaUPibSnfkb6xezGWjUp45CtSwt4wsYt+LJan6w=
 | 
			
		||||
github.com/jinzhu/gorm v1.9.5/go.mod h1:bdqTT3q6dhSph2K3pWxrHP6nqxuAp2yQ3KFtc3U3F84=
 | 
			
		||||
github.com/jinzhu/gorm v1.9.8 h1:n5uvxqLepIP2R1XF7pudpt9Rv8I3m7G9trGxJVjLZ5k=
 | 
			
		||||
github.com/jinzhu/gorm v1.9.8/go.mod h1:bdqTT3q6dhSph2K3pWxrHP6nqxuAp2yQ3KFtc3U3F84=
 | 
			
		||||
github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a h1:eeaG9XMUvRBYXJi4pg1ZKM7nxc5AfXfojeLLW7O5J3k=
 | 
			
		||||
github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
 | 
			
		||||
github.com/jinzhu/inflection v0.0.0-20190603042836-f5c5f50e6090 h1:LIwA5USOJ9W/0hwiRH1MugeThGBHGqv+USXcDKWHIVY=
 | 
			
		||||
github.com/jinzhu/inflection v0.0.0-20190603042836-f5c5f50e6090/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
 | 
			
		||||
github.com/jinzhu/now v1.0.0 h1:6WV8LvwPpDhKjo5U9O6b4+xdG/jTXNPwlDme/MTo8Ns=
 | 
			
		||||
github.com/jinzhu/now v1.0.0/go.mod h1:oHTiXerJ20+SfYcrdlBO7rzZRJWGwSTQ0iUY2jI6Gfc=
 | 
			
		||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
 | 
			
		||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
 | 
			
		||||
github.com/jroimartin/gocui v0.4.0 h1:52jnalstgmc25FmtGcWqa0tcbMEWS6RpFLsOIO+I+E8=
 | 
			
		||||
github.com/jroimartin/gocui v0.4.0/go.mod h1:7i7bbj99OgFHzo7kB2zPb8pXLqMBSQegY7azfqXMkyY=
 | 
			
		||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 | 
			
		||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
 | 
			
		||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
 | 
			
		||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 | 
			
		||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
 | 
			
		||||
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM=
 | 
			
		||||
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
 | 
			
		||||
github.com/k0kubun/pp v2.3.0+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg=
 | 
			
		||||
github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40=
 | 
			
		||||
github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg=
 | 
			
		||||
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e h1:RgQk53JHp/Cjunrr1WlsXSZpqXn+uREuHvUVcK82CV8=
 | 
			
		||||
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
 | 
			
		||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
 | 
			
		||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
 | 
			
		||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 | 
			
		||||
github.com/knqyf263/berkeleydb v0.0.0-20190501065933-fafe01fb9662 h1:UGS0RbPHwXJkq8tcba8OD0nvVUWLf2h7uUJznuHPPB0=
 | 
			
		||||
github.com/knqyf263/berkeleydb v0.0.0-20190501065933-fafe01fb9662/go.mod h1:bu1CcN4tUtoRcI/B/RFHhxMNKFHVq/c3SV+UTyduoXg=
 | 
			
		||||
github.com/knqyf263/fanal v0.0.0-20190521154631-a2dde7e171c6/go.mod h1:guPOH3Sfj5M4j/LvCOoWmuYCXnjReDIwJO+S89Fje1E=
 | 
			
		||||
github.com/knqyf263/fanal v0.0.0-20190528042547-07e27879b658 h1:m0FCzKmngHBMqrbBpxadWR8Py3/jWYnWiWxmOW7ovVU=
 | 
			
		||||
github.com/knqyf263/fanal v0.0.0-20190528042547-07e27879b658/go.mod h1:guPOH3Sfj5M4j/LvCOoWmuYCXnjReDIwJO+S89Fje1E=
 | 
			
		||||
github.com/knqyf263/go-cpe v0.0.0-20180327054844-659663f6eca2 h1:9CYbtr3i56D/rD6u6jJ/Aocsic9G+MupyVu7gb+QHF4=
 | 
			
		||||
github.com/knqyf263/go-cpe v0.0.0-20180327054844-659663f6eca2/go.mod h1:XM58Cg7dN+g0J9UPVmKjiXWlGi55lx+9IMs0IMoFWQo=
 | 
			
		||||
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d h1:X4cedH4Kn3JPupAwwWuo4AzYp16P0OyLO9d7OnMZc/c=
 | 
			
		||||
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d/go.mod h1:o8sgWoz3JADecfc/cTYD92/Et1yMqMy0utV1z+VaZao=
 | 
			
		||||
github.com/knqyf263/go-dep-parser v0.0.0-20190521150559-1ef8521d17a0 h1:DOQ2UbTciy48dV9vpZ25BOiShrWIWZwBdMOy7SD1Wow=
 | 
			
		||||
github.com/knqyf263/go-dep-parser v0.0.0-20190521150559-1ef8521d17a0/go.mod h1:gSiqSkOFPstUZu/qZ4wnNJS69PtQQnPl397vxKHJ5mQ=
 | 
			
		||||
github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936 h1:HDjRqotkViMNcGMGicb7cgxklx8OwnjtCBmyWEqrRvM=
 | 
			
		||||
github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936/go.mod h1:i4sF0l1fFnY1aiw08QQSwVAFxHEm311Me3WsU/X7nL0=
 | 
			
		||||
github.com/knqyf263/go-rpmdb v0.0.0-20190501070121-10a1c42a10dc h1:pumO9pqmRAjvic6oove22RGh9wDZQnj96XQjJSbSEPs=
 | 
			
		||||
github.com/knqyf263/go-rpmdb v0.0.0-20190501070121-10a1c42a10dc/go.mod h1:MrSSvdMpTSymaQWk1yFr9sxFSyQmKMj6jkbvGrchBV8=
 | 
			
		||||
github.com/knqyf263/go-version v1.1.1 h1:+MpcBC9b7rk5ihag8Y/FLG8get1H2GjniwKQ+9DxI2o=
 | 
			
		||||
github.com/knqyf263/go-version v1.1.1/go.mod h1:0tBvHvOBSf5TqGNcY+/ih9o8qo3R16iZCpB9rP0D3VM=
 | 
			
		||||
github.com/knqyf263/gost v0.0.0-20190326022014-39175c0da9e3 h1:TPlz2V0Hpgg3Ecw5hozTSBUDZF286CQ21P4QFSYPJvo=
 | 
			
		||||
github.com/knqyf263/gost v0.0.0-20190326022014-39175c0da9e3/go.mod h1:tktdrQ3uwKVTxlSF9kAgxjW2xkuaY8IrIHmjVQoslOc=
 | 
			
		||||
github.com/knqyf263/nested v0.0.1 h1:Sv26CegUMhjt19zqbBKntjwESdxe5hxVPSk0+AKjdUc=
 | 
			
		||||
github.com/knqyf263/nested v0.0.1/go.mod h1:zwhsIhMkBg90DTOJQvxPkKIypEHPYkgWHs4gybdlUmk=
 | 
			
		||||
github.com/knqyf263/trivy v0.1.1 h1:K7k9TsKTdN0rdSsL9103G71WC9NWCZQF6QhfsByOnHg=
 | 
			
		||||
github.com/knqyf263/trivy v0.1.1/go.mod h1:AjYD8rbiW7vw2KOv9urd0BqCVfyNNjRNBfVfevikqj8=
 | 
			
		||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 | 
			
		||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
 | 
			
		||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 | 
			
		||||
github.com/kotakanbe/go-cve-dictionary v0.0.0-20190327053454-5fe52611f0b8 h1:0zo7jVQn8KjV0xT5AOHHNIzABmYBDJ2WWKVeqyOdTKc=
 | 
			
		||||
github.com/kotakanbe/go-cve-dictionary v0.0.0-20190327053454-5fe52611f0b8/go.mod h1:CNVaCVSeqjxCFQm93uCWPT8mR+a0514XHiiBJx9yrkQ=
 | 
			
		||||
github.com/kotakanbe/go-pingscanner v0.1.0 h1:VG4/9l0i8WeToXclj7bIGoAZAu7a07Z3qmQiIfU0gT0=
 | 
			
		||||
github.com/kotakanbe/go-pingscanner v0.1.0/go.mod h1:/761QZzuZFcfN8h/1QuawUA+pKukp3qcNj5mxJCOiAk=
 | 
			
		||||
github.com/kotakanbe/goval-dictionary v0.1.2 h1:XnninBr9KJcP3557PcR8qkUq9zlQbCukVIkU7AHWxd4=
 | 
			
		||||
github.com/kotakanbe/goval-dictionary v0.1.2/go.mod h1:lzsw634rJIxLteds6RAACIKZCoXKT06o/xHsTWf6v5o=
 | 
			
		||||
github.com/kotakanbe/goval-dictionary v0.1.3-0.20190612145907-3fbb67115698 h1:5/4vBHQiXPIejJSZEqRRUpd0HVqFMQrFZUZtSDMvMzc=
 | 
			
		||||
github.com/kotakanbe/goval-dictionary v0.1.3-0.20190612145907-3fbb67115698/go.mod h1:D0FzzGCYCJCgPy5+wGgEOvWTb8fxUxqdxkWM2JDwguA=
 | 
			
		||||
github.com/kotakanbe/goval-dictionary v0.1.3-0.20190613041505-2362c088a437 h1:gnwqfC+G78bmvVHETLvZOUKopUD/ljQAdwcvHiLKMKA=
 | 
			
		||||
github.com/kotakanbe/goval-dictionary v0.1.3-0.20190613041505-2362c088a437/go.mod h1:VupP39J8370MdBkmvQQVmuYf98VrcQzhiGo+UiNW4rs=
 | 
			
		||||
github.com/kotakanbe/goval-dictionary v0.1.3-0.20190613053258-078b163b76ec h1:gMji7JMOrnUYUorYUTM7TRlvy8D613WkQhayEQhBsFI=
 | 
			
		||||
github.com/kotakanbe/goval-dictionary v0.1.3-0.20190613053258-078b163b76ec/go.mod h1:VupP39J8370MdBkmvQQVmuYf98VrcQzhiGo+UiNW4rs=
 | 
			
		||||
github.com/kotakanbe/goval-dictionary v0.1.3-0.20190613053258-8b98657de17d h1:S2hGRg/3mxi8eR7DROKT9kqTEjGLgm4dDHm72/DIJrQ=
 | 
			
		||||
github.com/kotakanbe/goval-dictionary v0.1.3-0.20190613053258-8b98657de17d/go.mod h1:VupP39J8370MdBkmvQQVmuYf98VrcQzhiGo+UiNW4rs=
 | 
			
		||||
github.com/kotakanbe/logrus-prefixed-formatter v0.0.0-20180123152602-928f7356cb96 h1:xNVK0mQJdQjw+QYeaMM4G6fvucWr8rTGGIhlPakx1wU=
 | 
			
		||||
github.com/kotakanbe/logrus-prefixed-formatter v0.0.0-20180123152602-928f7356cb96/go.mod h1:ljq48H1V+0Vh0u7ucA3LjR4AfkAeCpxrf7LaaCk8Vmo=
 | 
			
		||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 | 
			
		||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 | 
			
		||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 | 
			
		||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 | 
			
		||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
 | 
			
		||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 | 
			
		||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 | 
			
		||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4=
 | 
			
		||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
 | 
			
		||||
github.com/labstack/echo v2.2.0+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
 | 
			
		||||
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
 | 
			
		||||
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
 | 
			
		||||
github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0=
 | 
			
		||||
github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=
 | 
			
		||||
github.com/labstack/gommon v0.2.9 h1:heVeuAYtevIQVYkGj6A41dtfT91LrvFG220lavpWhrU=
 | 
			
		||||
github.com/labstack/gommon v0.2.9/go.mod h1:E8ZTmW9vw5az5/ZyHWCp0Lw4OH2ecsaBP1C/NKavGG4=
 | 
			
		||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 | 
			
		||||
github.com/lib/pq v1.1.0 h1:/5u4a+KGJptBRqGzPvYQL9p0d/tPR4S31+Tnzj9lEO4=
 | 
			
		||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 | 
			
		||||
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
 | 
			
		||||
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 | 
			
		||||
github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5 h1:AsEBgzv3DhuYHI/GiQh2HxvTP71HCCE9E/tzGUzGdtU=
 | 
			
		||||
github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5/go.mod h1:c2mYKRyMb1BPkO5St0c/ps62L4S0W2NAkaTXj9qEI+0=
 | 
			
		||||
github.com/lusis/slack-test v0.0.0-20190426140909-c40012f20018 h1:MNApn+Z+fIT4NPZopPfCc1obT6aY3SVM6DOctz1A9ZU=
 | 
			
		||||
github.com/lusis/slack-test v0.0.0-20190426140909-c40012f20018/go.mod h1:sFlOUpQL1YcjhFVXhg1CG8ZASEs/Mf1oVb6H75JL/zg=
 | 
			
		||||
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
 | 
			
		||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 | 
			
		||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.0 h1:v2XXALHHh6zHfYTJ+cSkwtyffnaOyR1MXaA91mTrb8o=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.6 h1:SrwhHcpV4nWrMGdNcC2kXpMfcBVYGDuTArqyhocJgvA=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 | 
			
		||||
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
 | 
			
		||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 | 
			
		||||
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
 | 
			
		||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
 | 
			
		||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
 | 
			
		||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 | 
			
		||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
 | 
			
		||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
 | 
			
		||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 | 
			
		||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
 | 
			
		||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 | 
			
		||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
 | 
			
		||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
 | 
			
		||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 | 
			
		||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
			
		||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 | 
			
		||||
github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs=
 | 
			
		||||
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
 | 
			
		||||
github.com/mozqnet/go-exploitdb v0.0.0-20190426034301-a055cc2c195d h1:ujS/a5AnCh6CNKIBfvrisDw2edwFa0TmHQHEQ6g5COg=
 | 
			
		||||
github.com/mozqnet/go-exploitdb v0.0.0-20190426034301-a055cc2c195d/go.mod h1:tqVnRPFR/8bkvCzGsGjwq+vb5dS6jwFFa+sEAbWPbDI=
 | 
			
		||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 | 
			
		||||
github.com/nlopes/slack v0.4.0 h1:OVnHm7lv5gGT5gkcHsZAyw++oHVFihbjWbL3UceUpiA=
 | 
			
		||||
github.com/nlopes/slack v0.4.0/go.mod h1:jVI4BBK3lSktibKahxBF74txcK2vyvkza1z/+rRnVAM=
 | 
			
		||||
github.com/nsf/termbox-go v0.0.0-20190325093121-288510b9734e h1:Vbib8wJAaMEF9jusI/kMSYMr/LtRzM7+F9MJgt/nH8k=
 | 
			
		||||
github.com/nsf/termbox-go v0.0.0-20190325093121-288510b9734e/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
 | 
			
		||||
github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88=
 | 
			
		||||
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
 | 
			
		||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
			
		||||
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
 | 
			
		||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
			
		||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
			
		||||
github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 | 
			
		||||
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
 | 
			
		||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 | 
			
		||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 | 
			
		||||
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
 | 
			
		||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
 | 
			
		||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
 | 
			
		||||
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
 | 
			
		||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
 | 
			
		||||
github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
 | 
			
		||||
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
 | 
			
		||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
 | 
			
		||||
github.com/parnurzeal/gorequest v0.2.15 h1:oPjDCsF5IkD4gUk6vIgsxYNaSgvAnIh1EJeROn3HdJU=
 | 
			
		||||
github.com/parnurzeal/gorequest v0.2.15/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE=
 | 
			
		||||
github.com/pelletier/go-buffruneio v0.2.0 h1:U4t4R6YkofJ5xHm3dJzuRpPZ0mr5MMCoAWooScCR7aA=
 | 
			
		||||
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
 | 
			
		||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 | 
			
		||||
github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg=
 | 
			
		||||
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
 | 
			
		||||
github.com/peterhellberg/link v1.0.0 h1:mUWkiegowUXEcmlb+ybF75Q/8D2Y0BjZtR8cxoKhaQo=
 | 
			
		||||
github.com/peterhellberg/link v1.0.0/go.mod h1:gtSlOT4jmkY8P47hbTc8PTgiDDWpdPbFYl75keYyBB8=
 | 
			
		||||
github.com/pierrec/lz4 v0.0.0-20190327172049-315a67e90e41/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
 | 
			
		||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
 | 
			
		||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
			
		||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
 | 
			
		||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
			
		||||
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
 | 
			
		||||
github.com/pkg/profile v1.3.0/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
 | 
			
		||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
			
		||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
			
		||||
github.com/prometheus/client_golang v0.0.0-20180924113449-f69c853d21c1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 | 
			
		||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 | 
			
		||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 h1:D+CiwcpGTW6pL6bv6KI3KbyEyCKyS+1JWS2h8PNDnGA=
 | 
			
		||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
 | 
			
		||||
github.com/prometheus/client_golang v0.9.4/go.mod h1:oCXIBxdI62A4cR6aTRJCgetEjecSIYzOEaeAn4iYEpM=
 | 
			
		||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 | 
			
		||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 | 
			
		||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
 | 
			
		||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 | 
			
		||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
 | 
			
		||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 | 
			
		||||
github.com/prometheus/common v0.3.0 h1:taZ4h8Tkxv2kNyoSctBvfXEHmBmxrwmIidZTIaHons4=
 | 
			
		||||
github.com/prometheus/common v0.3.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 | 
			
		||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 | 
			
		||||
github.com/prometheus/procfs v0.0.0-20180920065004-418d78d0b9a7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 | 
			
		||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 | 
			
		||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 | 
			
		||||
github.com/prometheus/procfs v0.0.0-20190503130316-740c07785007 h1:gT4PpkbWSQM4J8fup/aXeQhY5jLDyHuPq8y2dHspqFw=
 | 
			
		||||
github.com/prometheus/procfs v0.0.0-20190503130316-740c07785007/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 | 
			
		||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 | 
			
		||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
 | 
			
		||||
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 h1:mZHayPoR0lNmnHyvtYjDeq0zlVHn9K/ZXoy17ylucdo=
 | 
			
		||||
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5/go.mod h1:GEXHk5HgEKCvEIIrSpFI3ozzG5xOKA2DVlEX/gGnewM=
 | 
			
		||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 | 
			
		||||
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
 | 
			
		||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 | 
			
		||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
 | 
			
		||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
 | 
			
		||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
 | 
			
		||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 | 
			
		||||
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
 | 
			
		||||
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
 | 
			
		||||
github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
 | 
			
		||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 | 
			
		||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 | 
			
		||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
 | 
			
		||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 | 
			
		||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=
 | 
			
		||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 | 
			
		||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 | 
			
		||||
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
 | 
			
		||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
 | 
			
		||||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
 | 
			
		||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 | 
			
		||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 | 
			
		||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
 | 
			
		||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
 | 
			
		||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
 | 
			
		||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 | 
			
		||||
github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M=
 | 
			
		||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
 | 
			
		||||
github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4=
 | 
			
		||||
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
 | 
			
		||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
			
		||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
			
		||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
 | 
			
		||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 | 
			
		||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
 | 
			
		||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 | 
			
		||||
github.com/tomoyamachi/reg v0.16.2-0.20190418055600-c6010b917a55 h1:O7Xl4zpk6zjYnwxUd7lubrx7xdzQ+PqfTgaxLE9nF+o=
 | 
			
		||||
github.com/tomoyamachi/reg v0.16.2-0.20190418055600-c6010b917a55/go.mod h1:12Fe9EIvK3dG/qWhNk5e9O96I8SGmCKLsJ8GsXUbk+Y=
 | 
			
		||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
 | 
			
		||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
 | 
			
		||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
 | 
			
		||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
 | 
			
		||||
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4/go.mod h1:50wTf68f99/Zt14pr046Tgt3Lp2vLyFZKzbFXTOabXw=
 | 
			
		||||
github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8=
 | 
			
		||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
 | 
			
		||||
github.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8=
 | 
			
		||||
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
 | 
			
		||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
 | 
			
		||||
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
 | 
			
		||||
github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
 | 
			
		||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
 | 
			
		||||
github.com/ymomoi/goval-parser v0.0.0-20170813122243-0a0be1dd9d08 h1:OsHsjWw5m3P0r+RJITvigJu9dn6L8812S54x42jxeII=
 | 
			
		||||
github.com/ymomoi/goval-parser v0.0.0-20170813122243-0a0be1dd9d08/go.mod h1:ox1Nt/rGgWuhVrNg+jKYonAs4BiQG1tRJwj4ue91iy4=
 | 
			
		||||
go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk=
 | 
			
		||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 | 
			
		||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
 | 
			
		||||
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
 | 
			
		||||
go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
 | 
			
		||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 | 
			
		||||
go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
 | 
			
		||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
 | 
			
		||||
go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4=
 | 
			
		||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 | 
			
		||||
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
 | 
			
		||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
 | 
			
		||||
go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o=
 | 
			
		||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 h1:p/H982KKEjUnLJkM3tt/LemDnOc1GiZL5FCVlORJ5zo=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
 | 
			
		||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 | 
			
		||||
golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 | 
			
		||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 | 
			
		||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 | 
			
		||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 | 
			
		||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 | 
			
		||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 | 
			
		||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 | 
			
		||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
 | 
			
		||||
golang.org/x/mobile v0.0.0-20190607214518-6fa95d984e88/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
 | 
			
		||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
 | 
			
		||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
 | 
			
		||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20180925072008-f04abc6bdfa7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190502183928-7f726cade0ab h1:9RfW3ktsOZxgo9YNbBAjq1FWzc/igwEcUzZz8IXgSbk=
 | 
			
		||||
golang.org/x/net v0.0.0-20190502183928-7f726cade0ab/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 | 
			
		||||
golang.org/x/net v0.0.0-20190611141213-3f473d35a33a h1:+KkCgOMgnKSgenxTBoiwkMqTiouMIy/3o8RLdmSbGoY=
 | 
			
		||||
golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
			
		||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
 | 
			
		||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180925112736-b09afc3d579e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 h1:vsphBvatvfbhlb4PO1BYSr9dzugGxJ/SQHoNufZJq1w=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae h1:xiXzMMEQdQcric9hXtr1QU98MHunKK7OTtsoU6bYWs4=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 | 
			
		||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 | 
			
		||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
			
		||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg=
 | 
			
		||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
			
		||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190530171427-2b03ca6e44eb/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190612180059-59534d075a87/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190612232758-d4e310b4a8a5/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373 h1:PPwnA7z1Pjf7XYaBP9GL1VAMZmcIWyFz7QCMSIIa3Bg=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
 | 
			
		||||
google.golang.org/api v0.4.0 h1:KKgc1aqhV8wDPbDzlDtpvyjZFY3vjz85FP7p4wcQUyI=
 | 
			
		||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
 | 
			
		||||
google.golang.org/api v0.6.0 h1:2tJEkRfnZL5g1GeBUlITh/rqT5HG3sFcoVCUUxmgJ2g=
 | 
			
		||||
google.golang.org/api v0.6.0/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4=
 | 
			
		||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 | 
			
		||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 | 
			
		||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 | 
			
		||||
google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
 | 
			
		||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 | 
			
		||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20180924164928-221a8d4f7494/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873 h1:nfPFGzJkUDX6uBmpN/pSw7MbOAWegH5QDQuoXFHedLg=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190611190212-a7e196e89fd3 h1:0LGHEA/u5XLibPOx6D7D8FBT/ax6wT57vNKY0QckCwo=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190611190212-a7e196e89fd3/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
 | 
			
		||||
google.golang.org/grpc v1.15.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
 | 
			
		||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
 | 
			
		||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 | 
			
		||||
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 | 
			
		||||
google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU=
 | 
			
		||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 | 
			
		||||
google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8=
 | 
			
		||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 | 
			
		||||
gopkg.in/VividCortex/ewma.v1 v1.1.1 h1:tWHEKkKq802K/JT9RiqGCBU5fW3raAPnJGTE9ostZvg=
 | 
			
		||||
gopkg.in/VividCortex/ewma.v1 v1.1.1/go.mod h1:TekXuFipeiHWiAlO1+wSS23vTcyFau5u3rxXUSXj710=
 | 
			
		||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
 | 
			
		||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 | 
			
		||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk=
 | 
			
		||||
gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
 | 
			
		||||
gopkg.in/cheggaaa/pb.v2 v2.0.6 h1:L2KAo2l2ZQTzxmh8b9RdQpzgLpK2mX3paGCMJSUugBk=
 | 
			
		||||
gopkg.in/cheggaaa/pb.v2 v2.0.6/go.mod h1:0CiZ1p8pvtxBlQpLXkHuUTpdJ1shm3OqCF1QugkjHL4=
 | 
			
		||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 | 
			
		||||
gopkg.in/fatih/color.v1 v1.7.0 h1:bYGjb+HezBM6j/QmgBfgm1adxHpzzrss6bj4r9ROppk=
 | 
			
		||||
gopkg.in/fatih/color.v1 v1.7.0/go.mod h1:P7yosIhqIl/sX8J8UypY5M+dDpD2KmyfP5IRs5v/fo0=
 | 
			
		||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
 | 
			
		||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 | 
			
		||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
 | 
			
		||||
gopkg.in/mattn/go-runewidth.v0 v0.0.4 h1:r0P71TnzQDlNIcizCqvPSSANoFa3WVGtcNJf3TWurcY=
 | 
			
		||||
gopkg.in/mattn/go-runewidth.v0 v0.0.4/go.mod h1:BmXejnxvhwdaATwiJbB1vZ2dtXkQKZGu9yLFCZb4msQ=
 | 
			
		||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 | 
			
		||||
gopkg.in/src-d/go-billy.v4 v4.2.1/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
 | 
			
		||||
gopkg.in/src-d/go-billy.v4 v4.3.0 h1:KtlZ4c1OWbIs4jCv5ZXrTqG8EQocr0g/d4DjNg70aek=
 | 
			
		||||
gopkg.in/src-d/go-billy.v4 v4.3.0/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
 | 
			
		||||
gopkg.in/src-d/go-git-fixtures.v3 v3.1.1/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
 | 
			
		||||
gopkg.in/src-d/go-git-fixtures.v3 v3.4.0 h1:KFpaNTUcLHLoP/OkdcRXR+MA5p55MhA41YVb7Wd8EfM=
 | 
			
		||||
gopkg.in/src-d/go-git-fixtures.v3 v3.4.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
 | 
			
		||||
gopkg.in/src-d/go-git.v4 v4.10.0 h1:NWjTJTQnk8UpIGlssuefyDZ6JruEjo5s88vm88uASbw=
 | 
			
		||||
gopkg.in/src-d/go-git.v4 v4.10.0/go.mod h1:Vtut8izDyrM8BUVQnzJ+YvmNcem2J89EmfZYCkLokZk=
 | 
			
		||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 | 
			
		||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 | 
			
		||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
 | 
			
		||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gotest.tools v2.1.0+incompatible h1:5USw7CrJBYKqjg9R7QlA6jzqZKEAtvW82aNmsxxGPxw=
 | 
			
		||||
gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20190607181801-497c8f037f5a/go.mod h1:JlmFZigtG9vBVR3QGIQ9g/Usz4BzH+Xm6Z8iHQWRYUw=
 | 
			
		||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 | 
			
		||||
@@ -147,7 +147,7 @@ func (deb Debian) FillWithGost(driver db.DB, r *models.ScanResult) (nCVEs int, e
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, name := range names {
 | 
			
		||||
				v.AffectedPackages = v.AffectedPackages.Store(models.PackageStatus{
 | 
			
		||||
				v.AffectedPackages = v.AffectedPackages.Store(models.PackageFixStatus{
 | 
			
		||||
					Name:        name,
 | 
			
		||||
					FixState:    "open",
 | 
			
		||||
					NotFixedYet: true,
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,7 @@ import (
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/knqyf263/gost/db"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Client is the interface of OVAL client.
 | 
			
		||||
@@ -71,8 +72,7 @@ func (b Base) CheckHTTPHealth() error {
 | 
			
		||||
	//  resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
 | 
			
		||||
	//  resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
 | 
			
		||||
	if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
		return fmt.Errorf("Failed to connect to gost server. url: %s, errs: %v",
 | 
			
		||||
			url, errs)
 | 
			
		||||
		return xerrors.Errorf("Failed to connect to gost server. url: %s, errs: %w", url, errs)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ func TestSetPackageStates(t *testing.T) {
 | 
			
		||||
		installed models.Packages
 | 
			
		||||
		release   string
 | 
			
		||||
		in        models.VulnInfo
 | 
			
		||||
		out       models.PackageStatuses
 | 
			
		||||
		out       models.PackageFixStatuses
 | 
			
		||||
	}{
 | 
			
		||||
 | 
			
		||||
		//0 one
 | 
			
		||||
@@ -31,7 +31,7 @@ func TestSetPackageStates(t *testing.T) {
 | 
			
		||||
			},
 | 
			
		||||
			release: "7",
 | 
			
		||||
			in:      models.VulnInfo{},
 | 
			
		||||
			out: []models.PackageStatus{
 | 
			
		||||
			out: []models.PackageFixStatus{
 | 
			
		||||
				{
 | 
			
		||||
					Name:        "bouncycastle",
 | 
			
		||||
					FixState:    "Will not fix",
 | 
			
		||||
@@ -66,7 +66,7 @@ func TestSetPackageStates(t *testing.T) {
 | 
			
		||||
			},
 | 
			
		||||
			release: "7",
 | 
			
		||||
			in:      models.VulnInfo{},
 | 
			
		||||
			out: []models.PackageStatus{
 | 
			
		||||
			out: []models.PackageFixStatus{
 | 
			
		||||
				{
 | 
			
		||||
					Name:        "bouncycastle",
 | 
			
		||||
					FixState:    "Will not fix",
 | 
			
		||||
@@ -94,9 +94,9 @@ func TestSetPackageStates(t *testing.T) {
 | 
			
		||||
			},
 | 
			
		||||
			release: "7",
 | 
			
		||||
			in: models.VulnInfo{
 | 
			
		||||
				AffectedPackages: models.PackageStatuses{},
 | 
			
		||||
				AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
			},
 | 
			
		||||
			out: models.PackageStatuses{},
 | 
			
		||||
			out: models.PackageFixStatuses{},
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		//3 look only the same os release.
 | 
			
		||||
@@ -113,9 +113,9 @@ func TestSetPackageStates(t *testing.T) {
 | 
			
		||||
			},
 | 
			
		||||
			release: "7",
 | 
			
		||||
			in: models.VulnInfo{
 | 
			
		||||
				AffectedPackages: models.PackageStatuses{},
 | 
			
		||||
				AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
			},
 | 
			
		||||
			out: models.PackageStatuses{},
 | 
			
		||||
			out: models.PackageFixStatuses{},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -191,7 +191,7 @@ func (red RedHat) fillUnfixed(driver db.DB, r *models.ScanResult) (nCVEs int, er
 | 
			
		||||
	return nCVEs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (red RedHat) mergePackageStates(v models.VulnInfo, ps []gostmodels.RedhatPackageState, installed models.Packages, release string) (pkgStats models.PackageStatuses) {
 | 
			
		||||
func (red RedHat) mergePackageStates(v models.VulnInfo, ps []gostmodels.RedhatPackageState, installed models.Packages, release string) (pkgStats models.PackageFixStatuses) {
 | 
			
		||||
	pkgStats = v.AffectedPackages
 | 
			
		||||
	for _, pstate := range ps {
 | 
			
		||||
		if pstate.Cpe !=
 | 
			
		||||
@@ -200,7 +200,8 @@ func (red RedHat) mergePackageStates(v models.VulnInfo, ps []gostmodels.RedhatPa
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !(pstate.FixState == "Will not fix" ||
 | 
			
		||||
			pstate.FixState == "Fix deferred") {
 | 
			
		||||
			pstate.FixState == "Fix deferred" ||
 | 
			
		||||
			pstate.FixState == "Affected") {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -210,11 +211,11 @@ func (red RedHat) mergePackageStates(v models.VulnInfo, ps []gostmodels.RedhatPa
 | 
			
		||||
 | 
			
		||||
		notFixedYet := false
 | 
			
		||||
		switch pstate.FixState {
 | 
			
		||||
		case "Will not fix", "Fix deferred":
 | 
			
		||||
		case "Will not fix", "Fix deferred", "Affected":
 | 
			
		||||
			notFixedYet = true
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pkgStats = pkgStats.Store(models.PackageStatus{
 | 
			
		||||
		pkgStats = pkgStats.Store(models.PackageFixStatus{
 | 
			
		||||
			Name:        pstate.PackageName,
 | 
			
		||||
			FixState:    pstate.FixState,
 | 
			
		||||
			NotFixedYet: notFixedYet,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								gost/util.go
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								gost/util.go
									
									
									
									
									
								
							@@ -18,7 +18,6 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
package gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
@@ -26,6 +25,7 @@ import (
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type response struct {
 | 
			
		||||
@@ -80,11 +80,11 @@ func getCvesViaHTTP(cveIDs []string, urlPrefix string) (
 | 
			
		||||
		case err := <-errChan:
 | 
			
		||||
			errs = append(errs, err)
 | 
			
		||||
		case <-timeout:
 | 
			
		||||
			return nil, fmt.Errorf("Timeout Fetching OVAL")
 | 
			
		||||
			return nil, xerrors.New("Timeout Fetching OVAL")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(errs) != 0 {
 | 
			
		||||
		return nil, fmt.Errorf("Failed to fetch OVAL. err: %v", errs)
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to fetch OVAL. err: %w", errs)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
@@ -154,11 +154,11 @@ func getAllUnfixedCvesViaHTTP(r *models.ScanResult, urlPrefix string) (
 | 
			
		||||
		case err := <-errChan:
 | 
			
		||||
			errs = append(errs, err)
 | 
			
		||||
		case <-timeout:
 | 
			
		||||
			return nil, fmt.Errorf("Timeout Fetching OVAL")
 | 
			
		||||
			return nil, xerrors.New("Timeout Fetching OVAL")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(errs) != 0 {
 | 
			
		||||
		return nil, fmt.Errorf("Failed to fetch OVAL. err: %v", errs)
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to fetch OVAL. err: %w", errs)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
@@ -176,8 +176,7 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er
 | 
			
		||||
			if count == retryMax {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			return fmt.Errorf("HTTP GET error: %v, url: %s, resp: %v",
 | 
			
		||||
				errs, url, resp)
 | 
			
		||||
			return xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %w", url, resp, errs)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -186,11 +185,11 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er
 | 
			
		||||
	}
 | 
			
		||||
	err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		errChan <- fmt.Errorf("HTTP Error %s", err)
 | 
			
		||||
		errChan <- xerrors.Errorf("HTTP Error %w", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if count == retryMax {
 | 
			
		||||
		errChan <- fmt.Errorf("HRetry count exceeded")
 | 
			
		||||
		errChan <- xerrors.New("Retry count exceeded")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 297 KiB  | 
							
								
								
									
										33
									
								
								libmanager/libManager.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								libmanager/libManager.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
package libmanager
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/knqyf263/trivy/pkg/db"
 | 
			
		||||
	"github.com/knqyf263/trivy/pkg/log"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// FillLibrary fills LibraryScanner informations
 | 
			
		||||
func FillLibrary(r *models.ScanResult) (totalCnt int, err error) {
 | 
			
		||||
	// initialize trivy's logger and db
 | 
			
		||||
	err = log.InitLogger(false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := db.Init(); err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	for _, lib := range r.LibraryScanners {
 | 
			
		||||
		vinfos, err := lib.Scan()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		for _, vinfo := range vinfos {
 | 
			
		||||
			r.ScannedCves[vinfo.CveID] = vinfo
 | 
			
		||||
		}
 | 
			
		||||
		totalCnt += len(vinfos)
 | 
			
		||||
	}
 | 
			
		||||
	db.Close()
 | 
			
		||||
 | 
			
		||||
	return totalCnt, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -19,6 +19,8 @@ package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/knqyf263/trivy/pkg/vulnsrc/vulnerability"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CveContents has CveContent
 | 
			
		||||
@@ -68,6 +70,9 @@ func (v CveContents) SourceLinks(lang, myFamily, cveID string) (values []CveCont
 | 
			
		||||
	order := CveContentTypes{Nvd, NvdXML, NewCveContentType(myFamily)}
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if cont, found := v[ctype]; found {
 | 
			
		||||
			if cont.SourceLink == "" {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			values = append(values, CveContentStr{ctype, cont.SourceLink})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -108,7 +113,7 @@ type CveContentCpes struct {
 | 
			
		||||
// Cpes returns affected CPEs of this Vulnerability
 | 
			
		||||
func (v CveContents) Cpes(myFamily string) (values []CveContentCpes) {
 | 
			
		||||
	order := CveContentTypes{NewCveContentType(myFamily)}
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(append(order)...)...)
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(order...)...)
 | 
			
		||||
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if cont, found := v[ctype]; found && 0 < len(cont.Cpes) {
 | 
			
		||||
@@ -130,7 +135,7 @@ type CveContentRefs struct {
 | 
			
		||||
// References returns References
 | 
			
		||||
func (v CveContents) References(myFamily string) (values []CveContentRefs) {
 | 
			
		||||
	order := CveContentTypes{NewCveContentType(myFamily)}
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(append(order)...)...)
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(order...)...)
 | 
			
		||||
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if cont, found := v[ctype]; found && 0 < len(cont.References) {
 | 
			
		||||
@@ -140,13 +145,14 @@ func (v CveContents) References(myFamily string) (values []CveContentRefs) {
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CweIDs returns related CweIDs of the vulnerability
 | 
			
		||||
func (v CveContents) CweIDs(myFamily string) (values []CveContentStr) {
 | 
			
		||||
	order := CveContentTypes{NewCveContentType(myFamily)}
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(append(order)...)...)
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(order...)...)
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if cont, found := v[ctype]; found && 0 < len(cont.CweIDs) {
 | 
			
		||||
			for _, cweID := range cont.CweIDs {
 | 
			
		||||
@@ -222,7 +228,7 @@ func NewCveContentType(name string) CveContentType {
 | 
			
		||||
		return Oracle
 | 
			
		||||
	case "ubuntu":
 | 
			
		||||
		return Ubuntu
 | 
			
		||||
	case "debian":
 | 
			
		||||
	case "debian", vulnerability.DebianOVAL:
 | 
			
		||||
		return Debian
 | 
			
		||||
	case "redhat_api":
 | 
			
		||||
		return RedHatAPI
 | 
			
		||||
@@ -230,6 +236,20 @@ func NewCveContentType(name string) CveContentType {
 | 
			
		||||
		return DebianSecurityTracker
 | 
			
		||||
	case "microsoft":
 | 
			
		||||
		return Microsoft
 | 
			
		||||
	case "wordpress":
 | 
			
		||||
		return WPVulnDB
 | 
			
		||||
	case "amazon":
 | 
			
		||||
		return Amazon
 | 
			
		||||
	case vulnerability.NodejsSecurityWg:
 | 
			
		||||
		return NodeSec
 | 
			
		||||
	case vulnerability.PythonSafetyDB:
 | 
			
		||||
		return PythonSec
 | 
			
		||||
	case vulnerability.RustSec:
 | 
			
		||||
		return RustSec
 | 
			
		||||
	case vulnerability.PhpSecurityAdvisories:
 | 
			
		||||
		return PhpSec
 | 
			
		||||
	case vulnerability.RubySec:
 | 
			
		||||
		return RubySec
 | 
			
		||||
	default:
 | 
			
		||||
		return Unknown
 | 
			
		||||
	}
 | 
			
		||||
@@ -263,12 +283,33 @@ const (
 | 
			
		||||
	// Oracle is Oracle Linux
 | 
			
		||||
	Oracle CveContentType = "oracle"
 | 
			
		||||
 | 
			
		||||
	// Amazon is Amazon Linux
 | 
			
		||||
	Amazon CveContentType = "amazon"
 | 
			
		||||
 | 
			
		||||
	// SUSE is SUSE Linux
 | 
			
		||||
	SUSE CveContentType = "suse"
 | 
			
		||||
 | 
			
		||||
	// Microsoft is Microsoft
 | 
			
		||||
	Microsoft CveContentType = "microsoft"
 | 
			
		||||
 | 
			
		||||
	// WPVulnDB is WordPress
 | 
			
		||||
	WPVulnDB CveContentType = "wpvulndb"
 | 
			
		||||
 | 
			
		||||
	// NodeSec : for JS
 | 
			
		||||
	NodeSec CveContentType = "node"
 | 
			
		||||
 | 
			
		||||
	// PythonSec : for PHP
 | 
			
		||||
	PythonSec CveContentType = "python"
 | 
			
		||||
 | 
			
		||||
	// PhpSec : for PHP
 | 
			
		||||
	PhpSec CveContentType = "php"
 | 
			
		||||
 | 
			
		||||
	// RubySec : for Ruby
 | 
			
		||||
	RubySec CveContentType = "ruby"
 | 
			
		||||
 | 
			
		||||
	// RustSec : for Rust
 | 
			
		||||
	RustSec CveContentType = "rust"
 | 
			
		||||
 | 
			
		||||
	// Unknown is Unknown
 | 
			
		||||
	Unknown CveContentType = "unknown"
 | 
			
		||||
)
 | 
			
		||||
@@ -282,10 +323,18 @@ var AllCveContetTypes = CveContentTypes{
 | 
			
		||||
	NvdXML,
 | 
			
		||||
	Jvn,
 | 
			
		||||
	RedHat,
 | 
			
		||||
	RedHatAPI,
 | 
			
		||||
	Debian,
 | 
			
		||||
	Ubuntu,
 | 
			
		||||
	RedHatAPI,
 | 
			
		||||
	Amazon,
 | 
			
		||||
	SUSE,
 | 
			
		||||
	DebianSecurityTracker,
 | 
			
		||||
	WPVulnDB,
 | 
			
		||||
	NodeSec,
 | 
			
		||||
	PythonSec,
 | 
			
		||||
	PhpSec,
 | 
			
		||||
	RubySec,
 | 
			
		||||
	RustSec,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Except returns CveContentTypes except for given args
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										141
									
								
								models/library.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								models/library.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,141 @@
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/knqyf263/trivy/pkg/scanner/library"
 | 
			
		||||
	"github.com/knqyf263/trivy/pkg/vulnsrc/vulnerability"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/knqyf263/go-dep-parser/pkg/types"
 | 
			
		||||
	"github.com/knqyf263/go-version"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// LibraryScanner has libraries information
 | 
			
		||||
type LibraryScanner struct {
 | 
			
		||||
	Path string
 | 
			
		||||
	Libs []types.Library
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Scan : scan target library
 | 
			
		||||
func (s LibraryScanner) Scan() ([]VulnInfo, error) {
 | 
			
		||||
	scanner := library.NewScanner(filepath.Base(string(s.Path)))
 | 
			
		||||
	if scanner == nil {
 | 
			
		||||
		return nil, xerrors.New("unknown file type")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	util.Log.Info("Updating library db...")
 | 
			
		||||
	err := scanner.UpdateDB()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("failed to update %s advisories: %w", scanner.Type(), err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var vulnerabilities []VulnInfo
 | 
			
		||||
	for _, pkg := range s.Libs {
 | 
			
		||||
		v, err := version.NewVersion(pkg.Version)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Debugf("new version cant detected %s@%s", pkg.Name, pkg.Version)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		tvulns, err := scanner.Detect(pkg.Name, v)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("failed to detect %s vulnerabilities: %w", scanner.Type(), err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		vulns := s.convertFanalToVuln(tvulns)
 | 
			
		||||
		vulnerabilities = append(vulnerabilities, vulns...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return vulnerabilities, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s LibraryScanner) convertFanalToVuln(tvulns []vulnerability.DetectedVulnerability) (vulns []VulnInfo) {
 | 
			
		||||
	for _, tvuln := range tvulns {
 | 
			
		||||
		vinfo, _ := s.getVulnDetail(tvuln)
 | 
			
		||||
		vulns = append(vulns, vinfo)
 | 
			
		||||
	}
 | 
			
		||||
	return vulns
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s LibraryScanner) getVulnDetail(tvuln vulnerability.DetectedVulnerability) (vinfo VulnInfo, err error) {
 | 
			
		||||
	details, err := vulnerability.Get(tvuln.VulnerabilityID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return vinfo, err
 | 
			
		||||
	} else if len(details) == 0 {
 | 
			
		||||
		return vinfo, xerrors.Errorf("Unknown vulnID : %s", tvuln.VulnerabilityID)
 | 
			
		||||
	}
 | 
			
		||||
	vinfo.CveID = tvuln.VulnerabilityID
 | 
			
		||||
	vinfo.CveContents = getCveContents(details)
 | 
			
		||||
	if tvuln.FixedVersion != "" {
 | 
			
		||||
 | 
			
		||||
		vinfo.LibraryFixedIns = []LibraryFixedIn{
 | 
			
		||||
			{
 | 
			
		||||
				Key:     s.GetLibraryKey(),
 | 
			
		||||
				Name:    tvuln.PkgName,
 | 
			
		||||
				FixedIn: tvuln.FixedVersion,
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return vinfo, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getCveContents(details map[string]vulnerability.Vulnerability) (contents map[CveContentType]CveContent) {
 | 
			
		||||
	contents = map[CveContentType]CveContent{}
 | 
			
		||||
	for source, detail := range details {
 | 
			
		||||
		refs := []Reference{}
 | 
			
		||||
		for _, refURL := range detail.References {
 | 
			
		||||
			refs = append(refs, Reference{Source: refURL, Link: refURL})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		content := CveContent{
 | 
			
		||||
			Type:          NewCveContentType(source),
 | 
			
		||||
			CveID:         detail.ID,
 | 
			
		||||
			Title:         detail.Title,
 | 
			
		||||
			Summary:       detail.Description,
 | 
			
		||||
			Cvss3Score:    detail.CvssScoreV3,
 | 
			
		||||
			Cvss3Severity: string(detail.SeverityV3),
 | 
			
		||||
			Cvss2Score:    detail.CvssScore,
 | 
			
		||||
			Cvss2Severity: string(detail.Severity),
 | 
			
		||||
			References:    refs,
 | 
			
		||||
 | 
			
		||||
			//SourceLink    string            `json:"sourceLink"`
 | 
			
		||||
			//Cvss2Vector   string            `json:"cvss2Vector"`
 | 
			
		||||
			//Cvss3Vector   string            `json:"cvss3Vector"`
 | 
			
		||||
			//Cvss3Severity string            `json:"cvss3Severity"`
 | 
			
		||||
			//Cpes          []Cpe             `json:"cpes,omitempty"`
 | 
			
		||||
			//CweIDs        []string          `json:"cweIDs,omitempty"`
 | 
			
		||||
			//Published     time.Time         `json:"published"`
 | 
			
		||||
			//LastModified  time.Time         `json:"lastModified"`
 | 
			
		||||
			//Mitigation    string            `json:"mitigation"` // RedHat API
 | 
			
		||||
			//Optional      map[string]string `json:"optional,omitempty"`
 | 
			
		||||
		}
 | 
			
		||||
		contents[NewCveContentType(source)] = content
 | 
			
		||||
	}
 | 
			
		||||
	return contents
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LibraryMap is filename and library type
 | 
			
		||||
var LibraryMap = map[string]string{
 | 
			
		||||
	"package-lock.json": "node",
 | 
			
		||||
	"yarn.lock":         "node",
 | 
			
		||||
	"Gemfile.lock":      "ruby",
 | 
			
		||||
	"Cargo.lock":        "rust",
 | 
			
		||||
	"composer.json":     "php",
 | 
			
		||||
	"Pipfile.lock":      "python",
 | 
			
		||||
	"poetry.lock":       "python",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetLibraryKey returns target library key
 | 
			
		||||
func (s LibraryScanner) GetLibraryKey() string {
 | 
			
		||||
	fileName := filepath.Base(s.Path)
 | 
			
		||||
	return LibraryMap[fileName]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LibraryFixedIn has library fixed information
 | 
			
		||||
type LibraryFixedIn struct {
 | 
			
		||||
	Key     string `json:"key,omitempty"`
 | 
			
		||||
	Name    string `json:"name,omitempty"`
 | 
			
		||||
	FixedIn string `json:"fixedIn,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										52
									
								
								models/library_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								models/library_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	godeptypes "github.com/knqyf263/go-dep-parser/pkg/types"
 | 
			
		||||
	"github.com/knqyf263/trivy/pkg/db"
 | 
			
		||||
	"github.com/knqyf263/trivy/pkg/log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestScan(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		path string
 | 
			
		||||
		pkgs []godeptypes.Library
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			path: "app/package-lock.json",
 | 
			
		||||
			pkgs: []godeptypes.Library{
 | 
			
		||||
				{
 | 
			
		||||
					Name:    "jquery",
 | 
			
		||||
					Version: "2.2.4",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Name:    "@babel/traverse",
 | 
			
		||||
					Version: "7.4.4",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := log.InitLogger(false); err != nil {
 | 
			
		||||
		t.Errorf("trivy logger failed")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := db.Init(); err != nil {
 | 
			
		||||
		t.Errorf("trivy db.Init failed")
 | 
			
		||||
	}
 | 
			
		||||
	for _, v := range tests {
 | 
			
		||||
		lib := LibraryScanner{
 | 
			
		||||
			Path: v.path,
 | 
			
		||||
			Libs: v.pkgs,
 | 
			
		||||
		}
 | 
			
		||||
		actual, err := lib.Scan()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("error occurred")
 | 
			
		||||
		}
 | 
			
		||||
		if len(actual) == 0 {
 | 
			
		||||
			t.Errorf("no vuln found : actual: %v\n", actual)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	db.Close()
 | 
			
		||||
}
 | 
			
		||||
@@ -21,6 +21,8 @@ import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Packages is Map of Package
 | 
			
		||||
@@ -83,7 +85,7 @@ func (ps Packages) FindByFQPN(nameVerRelArc string) (*Package, error) {
 | 
			
		||||
			return &p, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil, fmt.Errorf("Failed to find the package: %s", nameVerRelArc)
 | 
			
		||||
	return nil, xerrors.Errorf("Failed to find the package: %s", nameVerRelArc)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Package has installed binary packages.
 | 
			
		||||
 
 | 
			
		||||
@@ -36,35 +36,41 @@ type ScanResults []ScanResult
 | 
			
		||||
 | 
			
		||||
// ScanResult has the result of scanned CVE information.
 | 
			
		||||
type ScanResult struct {
 | 
			
		||||
	JSONVersion      int                    `json:"jsonVersion"`
 | 
			
		||||
	Lang             string                 `json:"lang"`
 | 
			
		||||
	ServerUUID       string                 `json:"serverUUID"`
 | 
			
		||||
	ServerName       string                 `json:"serverName"` // TOML Section key
 | 
			
		||||
	Family           string                 `json:"family"`
 | 
			
		||||
	Release          string                 `json:"release"`
 | 
			
		||||
	Container        Container              `json:"container"`
 | 
			
		||||
	Platform         Platform               `json:"platform"`
 | 
			
		||||
	IPv4Addrs        []string               `json:"ipv4Addrs,omitempty"` // only global unicast address (https://golang.org/pkg/net/#IP.IsGlobalUnicast)
 | 
			
		||||
	IPv6Addrs        []string               `json:"ipv6Addrs,omitempty"` // only global unicast address (https://golang.org/pkg/net/#IP.IsGlobalUnicast)
 | 
			
		||||
	ScannedAt        time.Time              `json:"scannedAt"`
 | 
			
		||||
	ScanMode         string                 `json:"scanMode"`
 | 
			
		||||
	ScannedVersion   string                 `json:"scannedVersion"`
 | 
			
		||||
	ScannedRevision  string                 `json:"scannedRevision"`
 | 
			
		||||
	ScannedBy        string                 `json:"scannedBy"`
 | 
			
		||||
	ScannedIPv4Addrs []string               `json:"scannedIpv4Addrs"`
 | 
			
		||||
	ScannedIPv6Addrs []string               `json:"scannedIpv6Addrs"`
 | 
			
		||||
	ReportedAt       time.Time              `json:"reportedAt"`
 | 
			
		||||
	ReportedVersion  string                 `json:"reportedVersion"`
 | 
			
		||||
	ReportedRevision string                 `json:"reportedRevision"`
 | 
			
		||||
	ReportedBy       string                 `json:"reportedBy"`
 | 
			
		||||
	ScannedCves      VulnInfos              `json:"scannedCves"`
 | 
			
		||||
	RunningKernel    Kernel                 `json:"runningKernel"`
 | 
			
		||||
	Packages         Packages               `json:"packages"`
 | 
			
		||||
	CweDict          CweDict                `json:"cweDict"`
 | 
			
		||||
	Optional         map[string]interface{} `json:",omitempty"`
 | 
			
		||||
	SrcPackages      SrcPackages            `json:",omitempty"`
 | 
			
		||||
	Errors           []string               `json:"errors"`
 | 
			
		||||
	Config           struct {
 | 
			
		||||
	JSONVersion      int       `json:"jsonVersion"`
 | 
			
		||||
	Lang             string    `json:"lang"`
 | 
			
		||||
	ServerUUID       string    `json:"serverUUID"`
 | 
			
		||||
	ServerName       string    `json:"serverName"` // TOML Section key
 | 
			
		||||
	Family           string    `json:"family"`
 | 
			
		||||
	Release          string    `json:"release"`
 | 
			
		||||
	Container        Container `json:"container"`
 | 
			
		||||
	Image            Image     `json:"image"`
 | 
			
		||||
	Platform         Platform  `json:"platform"`
 | 
			
		||||
	IPv4Addrs        []string  `json:"ipv4Addrs,omitempty"` // only global unicast address (https://golang.org/pkg/net/#IP.IsGlobalUnicast)
 | 
			
		||||
	IPv6Addrs        []string  `json:"ipv6Addrs,omitempty"` // only global unicast address (https://golang.org/pkg/net/#IP.IsGlobalUnicast)
 | 
			
		||||
	ScannedAt        time.Time `json:"scannedAt"`
 | 
			
		||||
	ScanMode         string    `json:"scanMode"`
 | 
			
		||||
	ScannedVersion   string    `json:"scannedVersion"`
 | 
			
		||||
	ScannedRevision  string    `json:"scannedRevision"`
 | 
			
		||||
	ScannedBy        string    `json:"scannedBy"`
 | 
			
		||||
	ScannedVia       string    `json:"scannedVia"`
 | 
			
		||||
	ScannedIPv4Addrs []string  `json:"scannedIpv4Addrs,omitempty"`
 | 
			
		||||
	ScannedIPv6Addrs []string  `json:"scannedIpv6Addrs,omitempty"`
 | 
			
		||||
	ReportedAt       time.Time `json:"reportedAt"`
 | 
			
		||||
	ReportedVersion  string    `json:"reportedVersion"`
 | 
			
		||||
	ReportedRevision string    `json:"reportedRevision"`
 | 
			
		||||
	ReportedBy       string    `json:"reportedBy"`
 | 
			
		||||
	Errors           []string  `json:"errors"`
 | 
			
		||||
	Warnings         []string  `json:"warnings"`
 | 
			
		||||
 | 
			
		||||
	ScannedCves       VulnInfos              `json:"scannedCves"`
 | 
			
		||||
	RunningKernel     Kernel                 `json:"runningKernel"`
 | 
			
		||||
	Packages          Packages               `json:"packages"`
 | 
			
		||||
	SrcPackages       SrcPackages            `json:",omitempty"`
 | 
			
		||||
	WordPressPackages *WordPressPackages     `json:",omitempty"`
 | 
			
		||||
	LibraryScanners   []LibraryScanner       `json:"libScanners"`
 | 
			
		||||
	CweDict           CweDict                `json:"cweDict,omitempty"`
 | 
			
		||||
	Optional          map[string]interface{} `json:",omitempty"`
 | 
			
		||||
	Config            struct {
 | 
			
		||||
		Scan   config.Config `json:"scan"`
 | 
			
		||||
		Report config.Config `json:"report"`
 | 
			
		||||
	} `json:"config"`
 | 
			
		||||
@@ -153,8 +159,6 @@ func (r ScanResult) FilterIgnoreCves() ScanResult {
 | 
			
		||||
			if con, ok := s.Containers[r.Container.Name]; ok {
 | 
			
		||||
				ignoreCves = con.IgnoreCves
 | 
			
		||||
			} else {
 | 
			
		||||
				util.Log.Errorf("%s is not found in config.toml",
 | 
			
		||||
					r.Container.Name)
 | 
			
		||||
				return r
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
@@ -182,6 +186,7 @@ func (r ScanResult) FilterUnfixed() ScanResult {
 | 
			
		||||
		return r
 | 
			
		||||
	}
 | 
			
		||||
	filtered := r.ScannedCves.Find(func(v VulnInfo) bool {
 | 
			
		||||
		// Report cves detected by CPE because Vuls can't know 'fixed' or 'unfixed'
 | 
			
		||||
		if len(v.CpeURIs) != 0 {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
@@ -205,8 +210,6 @@ func (r ScanResult) FilterIgnorePkgs() ScanResult {
 | 
			
		||||
			if con, ok := s.Containers[r.Container.Name]; ok {
 | 
			
		||||
				ignorePkgsRegexps = con.IgnorePkgsRegexp
 | 
			
		||||
			} else {
 | 
			
		||||
				util.Log.Errorf("%s is not found in config.toml",
 | 
			
		||||
					r.Container.Name)
 | 
			
		||||
				return r
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
@@ -220,7 +223,7 @@ func (r ScanResult) FilterIgnorePkgs() ScanResult {
 | 
			
		||||
	for _, pkgRegexp := range ignorePkgsRegexps {
 | 
			
		||||
		re, err := regexp.Compile(pkgRegexp)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Errorf("Faild to parse %s, %s", pkgRegexp, err)
 | 
			
		||||
			util.Log.Errorf("Faild to parse %s. err: %+v", pkgRegexp, err)
 | 
			
		||||
			continue
 | 
			
		||||
		} else {
 | 
			
		||||
			regexps = append(regexps, re)
 | 
			
		||||
@@ -252,6 +255,30 @@ func (r ScanResult) FilterIgnorePkgs() ScanResult {
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FilterInactiveWordPressLibs is filter function.
 | 
			
		||||
func (r ScanResult) FilterInactiveWordPressLibs() ScanResult {
 | 
			
		||||
	if !config.Conf.Servers[r.ServerName].WordPress.IgnoreInactive {
 | 
			
		||||
		return r
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	filtered := r.ScannedCves.Find(func(v VulnInfo) bool {
 | 
			
		||||
		if len(v.WpPackageFixStats) == 0 {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
		// Ignore if all libs in this vulnInfo inactive
 | 
			
		||||
		for _, wp := range v.WpPackageFixStats {
 | 
			
		||||
			if p, ok := r.WordPressPackages.Find(wp.Name); ok {
 | 
			
		||||
				if p.Status != Inactive {
 | 
			
		||||
					return true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return false
 | 
			
		||||
	})
 | 
			
		||||
	r.ScannedCves = filtered
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReportFileName returns the filename on localhost without extention
 | 
			
		||||
func (r ScanResult) ReportFileName() (name string) {
 | 
			
		||||
	if len(r.Container.ContainerID) == 0 {
 | 
			
		||||
@@ -289,6 +316,9 @@ func (r ScanResult) ServerInfoTui() string {
 | 
			
		||||
	if len(r.Container.ContainerID) == 0 {
 | 
			
		||||
		line := fmt.Sprintf("%s (%s%s)",
 | 
			
		||||
			r.ServerName, r.Family, r.Release)
 | 
			
		||||
		if len(r.Warnings) != 0 {
 | 
			
		||||
			line = "[Warn] " + line
 | 
			
		||||
		}
 | 
			
		||||
		if r.RunningKernel.RebootRequired {
 | 
			
		||||
			return "[Reboot] " + line
 | 
			
		||||
		}
 | 
			
		||||
@@ -411,6 +441,11 @@ func (r ScanResult) IsContainer() bool {
 | 
			
		||||
	return 0 < len(r.Container.ContainerID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsImage returns whether this ServerInfo is about container
 | 
			
		||||
func (r ScanResult) IsImage() bool {
 | 
			
		||||
	return 0 < len(r.Image.Name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsDeepScanMode checks if the scan mode is deep scan mode.
 | 
			
		||||
func (r ScanResult) IsDeepScanMode() bool {
 | 
			
		||||
	for _, s := range r.Config.Scan.Servers {
 | 
			
		||||
@@ -432,6 +467,12 @@ type Container struct {
 | 
			
		||||
	UUID        string `json:"uuid"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Image has Container information
 | 
			
		||||
type Image struct {
 | 
			
		||||
	Name string `json:"name"`
 | 
			
		||||
	Tag  string `json:"tag"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Platform has platform information
 | 
			
		||||
type Platform struct {
 | 
			
		||||
	Name       string `json:"name"` // aws or azure or gcp or other...
 | 
			
		||||
 
 | 
			
		||||
@@ -348,7 +348,7 @@ func TestFilterUnfixed(t *testing.T) {
 | 
			
		||||
				ScannedCves: VulnInfos{
 | 
			
		||||
					"CVE-2017-0001": {
 | 
			
		||||
						CveID: "CVE-2017-0001",
 | 
			
		||||
						AffectedPackages: PackageStatuses{
 | 
			
		||||
						AffectedPackages: PackageFixStatuses{
 | 
			
		||||
							{
 | 
			
		||||
								Name:        "a",
 | 
			
		||||
								NotFixedYet: true,
 | 
			
		||||
@@ -357,7 +357,7 @@ func TestFilterUnfixed(t *testing.T) {
 | 
			
		||||
					},
 | 
			
		||||
					"CVE-2017-0002": {
 | 
			
		||||
						CveID: "CVE-2017-0002",
 | 
			
		||||
						AffectedPackages: PackageStatuses{
 | 
			
		||||
						AffectedPackages: PackageFixStatuses{
 | 
			
		||||
							{
 | 
			
		||||
								Name:        "b",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
@@ -366,7 +366,7 @@ func TestFilterUnfixed(t *testing.T) {
 | 
			
		||||
					},
 | 
			
		||||
					"CVE-2017-0003": {
 | 
			
		||||
						CveID: "CVE-2017-0003",
 | 
			
		||||
						AffectedPackages: PackageStatuses{
 | 
			
		||||
						AffectedPackages: PackageFixStatuses{
 | 
			
		||||
							{
 | 
			
		||||
								Name:        "c",
 | 
			
		||||
								NotFixedYet: true,
 | 
			
		||||
@@ -383,7 +383,7 @@ func TestFilterUnfixed(t *testing.T) {
 | 
			
		||||
				ScannedCves: VulnInfos{
 | 
			
		||||
					"CVE-2017-0002": {
 | 
			
		||||
						CveID: "CVE-2017-0002",
 | 
			
		||||
						AffectedPackages: PackageStatuses{
 | 
			
		||||
						AffectedPackages: PackageFixStatuses{
 | 
			
		||||
							{
 | 
			
		||||
								Name:        "b",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
@@ -392,7 +392,7 @@ func TestFilterUnfixed(t *testing.T) {
 | 
			
		||||
					},
 | 
			
		||||
					"CVE-2017-0003": {
 | 
			
		||||
						CveID: "CVE-2017-0003",
 | 
			
		||||
						AffectedPackages: PackageStatuses{
 | 
			
		||||
						AffectedPackages: PackageFixStatuses{
 | 
			
		||||
							{
 | 
			
		||||
								Name:        "c",
 | 
			
		||||
								NotFixedYet: true,
 | 
			
		||||
@@ -435,7 +435,7 @@ func TestFilterIgnorePkgs(t *testing.T) {
 | 
			
		||||
					ScannedCves: VulnInfos{
 | 
			
		||||
						"CVE-2017-0001": {
 | 
			
		||||
							CveID: "CVE-2017-0001",
 | 
			
		||||
							AffectedPackages: PackageStatuses{
 | 
			
		||||
							AffectedPackages: PackageFixStatuses{
 | 
			
		||||
								{Name: "kernel"},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
@@ -462,7 +462,7 @@ func TestFilterIgnorePkgs(t *testing.T) {
 | 
			
		||||
					ScannedCves: VulnInfos{
 | 
			
		||||
						"CVE-2017-0001": {
 | 
			
		||||
							CveID: "CVE-2017-0001",
 | 
			
		||||
							AffectedPackages: PackageStatuses{
 | 
			
		||||
							AffectedPackages: PackageFixStatuses{
 | 
			
		||||
								{Name: "kernel"},
 | 
			
		||||
								{Name: "vim"},
 | 
			
		||||
							},
 | 
			
		||||
@@ -475,7 +475,7 @@ func TestFilterIgnorePkgs(t *testing.T) {
 | 
			
		||||
				ScannedCves: VulnInfos{
 | 
			
		||||
					"CVE-2017-0001": {
 | 
			
		||||
						CveID: "CVE-2017-0001",
 | 
			
		||||
						AffectedPackages: PackageStatuses{
 | 
			
		||||
						AffectedPackages: PackageFixStatuses{
 | 
			
		||||
							{Name: "kernel"},
 | 
			
		||||
							{Name: "vim"},
 | 
			
		||||
						},
 | 
			
		||||
@@ -491,7 +491,7 @@ func TestFilterIgnorePkgs(t *testing.T) {
 | 
			
		||||
					ScannedCves: VulnInfos{
 | 
			
		||||
						"CVE-2017-0001": {
 | 
			
		||||
							CveID: "CVE-2017-0001",
 | 
			
		||||
							AffectedPackages: PackageStatuses{
 | 
			
		||||
							AffectedPackages: PackageFixStatuses{
 | 
			
		||||
								{Name: "kernel"},
 | 
			
		||||
								{Name: "vim"},
 | 
			
		||||
							},
 | 
			
		||||
@@ -545,7 +545,7 @@ func TestFilterIgnorePkgsContainer(t *testing.T) {
 | 
			
		||||
					ScannedCves: VulnInfos{
 | 
			
		||||
						"CVE-2017-0001": {
 | 
			
		||||
							CveID: "CVE-2017-0001",
 | 
			
		||||
							AffectedPackages: PackageStatuses{
 | 
			
		||||
							AffectedPackages: PackageFixStatuses{
 | 
			
		||||
								{Name: "kernel"},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
@@ -574,7 +574,7 @@ func TestFilterIgnorePkgsContainer(t *testing.T) {
 | 
			
		||||
					ScannedCves: VulnInfos{
 | 
			
		||||
						"CVE-2017-0001": {
 | 
			
		||||
							CveID: "CVE-2017-0001",
 | 
			
		||||
							AffectedPackages: PackageStatuses{
 | 
			
		||||
							AffectedPackages: PackageFixStatuses{
 | 
			
		||||
								{Name: "kernel"},
 | 
			
		||||
								{Name: "vim"},
 | 
			
		||||
							},
 | 
			
		||||
@@ -588,7 +588,7 @@ func TestFilterIgnorePkgsContainer(t *testing.T) {
 | 
			
		||||
				ScannedCves: VulnInfos{
 | 
			
		||||
					"CVE-2017-0001": {
 | 
			
		||||
						CveID: "CVE-2017-0001",
 | 
			
		||||
						AffectedPackages: PackageStatuses{
 | 
			
		||||
						AffectedPackages: PackageFixStatuses{
 | 
			
		||||
							{Name: "kernel"},
 | 
			
		||||
							{Name: "vim"},
 | 
			
		||||
						},
 | 
			
		||||
@@ -605,7 +605,7 @@ func TestFilterIgnorePkgsContainer(t *testing.T) {
 | 
			
		||||
					ScannedCves: VulnInfos{
 | 
			
		||||
						"CVE-2017-0001": {
 | 
			
		||||
							CveID: "CVE-2017-0001",
 | 
			
		||||
							AffectedPackages: PackageStatuses{
 | 
			
		||||
							AffectedPackages: PackageFixStatuses{
 | 
			
		||||
								{Name: "kernel"},
 | 
			
		||||
								{Name: "vim"},
 | 
			
		||||
							},
 | 
			
		||||
 
 | 
			
		||||
@@ -115,27 +115,26 @@ func (v VulnInfos) FormatFixedStatus(packs Packages) string {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		total++
 | 
			
		||||
		if vInfo.PatchStatus(packs) == "Fixed" {
 | 
			
		||||
		if vInfo.PatchStatus(packs) == "fixed" {
 | 
			
		||||
			fixed++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%d/%d Fixed", fixed, total)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PackageStatuses is a list of PackageStatus
 | 
			
		||||
type PackageStatuses []PackageStatus
 | 
			
		||||
// PackageFixStatuses is a list of PackageStatus
 | 
			
		||||
type PackageFixStatuses []PackageFixStatus
 | 
			
		||||
 | 
			
		||||
// FormatTuiSummary format packname to show TUI summary
 | 
			
		||||
func (ps PackageStatuses) FormatTuiSummary() string {
 | 
			
		||||
	names := []string{}
 | 
			
		||||
// Names return a slice of package names
 | 
			
		||||
func (ps PackageFixStatuses) Names() (names []string) {
 | 
			
		||||
	for _, p := range ps {
 | 
			
		||||
		names = append(names, p.Name)
 | 
			
		||||
	}
 | 
			
		||||
	return strings.Join(names, ", ")
 | 
			
		||||
	return names
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Store insert given pkg if missing, update pkg if exists
 | 
			
		||||
func (ps PackageStatuses) Store(pkg PackageStatus) PackageStatuses {
 | 
			
		||||
func (ps PackageFixStatuses) Store(pkg PackageFixStatus) PackageFixStatuses {
 | 
			
		||||
	for i, p := range ps {
 | 
			
		||||
		if p.Name == pkg.Name {
 | 
			
		||||
			ps[i] = pkg
 | 
			
		||||
@@ -147,15 +146,15 @@ func (ps PackageStatuses) Store(pkg PackageStatus) PackageStatuses {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sort by Name
 | 
			
		||||
func (ps PackageStatuses) Sort() {
 | 
			
		||||
func (ps PackageFixStatuses) Sort() {
 | 
			
		||||
	sort.Slice(ps, func(i, j int) bool {
 | 
			
		||||
		return ps[i].Name < ps[j].Name
 | 
			
		||||
	})
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PackageStatus has name and other status abount the package
 | 
			
		||||
type PackageStatus struct {
 | 
			
		||||
// PackageFixStatus has name and other status abount the package
 | 
			
		||||
type PackageFixStatus struct {
 | 
			
		||||
	Name        string `json:"name"`
 | 
			
		||||
	NotFixedYet bool   `json:"notFixedYet"`
 | 
			
		||||
	FixState    string `json:"fixState"`
 | 
			
		||||
@@ -163,14 +162,77 @@ type PackageStatus struct {
 | 
			
		||||
 | 
			
		||||
// VulnInfo has a vulnerability information and unsecure packages
 | 
			
		||||
type VulnInfo struct {
 | 
			
		||||
	CveID            string           `json:"cveID"`
 | 
			
		||||
	Confidences      Confidences      `json:"confidences"`
 | 
			
		||||
	AffectedPackages PackageStatuses  `json:"affectedPackages"`
 | 
			
		||||
	DistroAdvisories []DistroAdvisory `json:"distroAdvisories,omitempty"` // for Aamazon, RHEL, FreeBSD
 | 
			
		||||
	CpeURIs          []string         `json:"cpeURIs,omitempty"`          // CpeURIs related to this CVE defined in config.toml
 | 
			
		||||
	CveContents      CveContents      `json:"cveContents"`
 | 
			
		||||
	Exploits         []Exploit        `json:"exploits"`
 | 
			
		||||
	AlertDict        AlertDict        `json:"alertDict"`
 | 
			
		||||
	CveID                string               `json:"cveID,omitempty"`
 | 
			
		||||
	Confidences          Confidences          `json:"confidences,omitempty"`
 | 
			
		||||
	AffectedPackages     PackageFixStatuses   `json:"affectedPackages,omitempty"`
 | 
			
		||||
	DistroAdvisories     DistroAdvisories     `json:"distroAdvisories,omitempty"` // for Aamazon, RHEL, FreeBSD
 | 
			
		||||
	CveContents          CveContents          `json:"cveContents,omitempty"`
 | 
			
		||||
	Exploits             []Exploit            `json:"exploits,omitempty"`
 | 
			
		||||
	AlertDict            AlertDict            `json:"alertDict,omitempty"`
 | 
			
		||||
	CpeURIs              []string             `json:"cpeURIs,omitempty"` // CpeURIs related to this CVE defined in config.toml
 | 
			
		||||
	GitHubSecurityAlerts GitHubSecurityAlerts `json:"gitHubSecurityAlerts,omitempty"`
 | 
			
		||||
	WpPackageFixStats    WpPackageFixStats    `json:"wpPackageFixStats,omitempty"`
 | 
			
		||||
	LibraryFixedIns      LibraryFixedIns      `json:"libraryFixedIns,omitempty"`
 | 
			
		||||
 | 
			
		||||
	VulnType string `json:"vulnType,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GitHubSecurityAlerts is a list of GitHubSecurityAlert
 | 
			
		||||
type GitHubSecurityAlerts []GitHubSecurityAlert
 | 
			
		||||
 | 
			
		||||
// Add adds given arg to the slice and return the slice (immutable)
 | 
			
		||||
func (g GitHubSecurityAlerts) Add(alert GitHubSecurityAlert) GitHubSecurityAlerts {
 | 
			
		||||
	for _, a := range g {
 | 
			
		||||
		if a.PackageName == alert.PackageName {
 | 
			
		||||
			return g
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return append(g, alert)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Names return a slice of lib names
 | 
			
		||||
func (g GitHubSecurityAlerts) Names() (names []string) {
 | 
			
		||||
	for _, a := range g {
 | 
			
		||||
		names = append(names, a.PackageName)
 | 
			
		||||
	}
 | 
			
		||||
	return names
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GitHubSecurityAlert has detected CVE-ID, PackageName, Status fetched via GitHub API
 | 
			
		||||
type GitHubSecurityAlert struct {
 | 
			
		||||
	PackageName   string    `json:"packageName"`
 | 
			
		||||
	FixedIn       string    `json:"fixedIn"`
 | 
			
		||||
	AffectedRange string    `json:"affectedRange"`
 | 
			
		||||
	Dismissed     bool      `json:"dismissed"`
 | 
			
		||||
	DismissedAt   time.Time `json:"dismissedAt"`
 | 
			
		||||
	DismissReason string    `json:"dismissReason"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LibraryFixedIns is a list of Library's FixedIn
 | 
			
		||||
type LibraryFixedIns []LibraryFixedIn
 | 
			
		||||
 | 
			
		||||
// WpPackageFixStats is a list of WpPackageFixStatus
 | 
			
		||||
type WpPackageFixStats []WpPackageFixStatus
 | 
			
		||||
 | 
			
		||||
// Names return a slice of names
 | 
			
		||||
func (ws WpPackageFixStats) Names() (names []string) {
 | 
			
		||||
	for _, w := range ws {
 | 
			
		||||
		names = append(names, w.Name)
 | 
			
		||||
	}
 | 
			
		||||
	return names
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WpPackages has a list of WpPackage
 | 
			
		||||
type WpPackages []WpPackage
 | 
			
		||||
 | 
			
		||||
// Add adds given arg to the slice and return the slice (immutable)
 | 
			
		||||
func (g WpPackages) Add(pkg WpPackage) WpPackages {
 | 
			
		||||
	for _, a := range g {
 | 
			
		||||
		if a.Name == pkg.Name {
 | 
			
		||||
			return g
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return append(g, pkg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Titles returns tilte (TUI)
 | 
			
		||||
@@ -245,6 +307,13 @@ func (v VulnInfo) Summaries(lang, myFamily string) (values []CveContentStr) {
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v, ok := v.CveContents[WPVulnDB]; ok {
 | 
			
		||||
		values = append(values, CveContentStr{
 | 
			
		||||
			Type:  "WPVDB",
 | 
			
		||||
			Value: v.Title,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(values) == 0 {
 | 
			
		||||
		return []CveContentStr{{
 | 
			
		||||
			Type:  Unknown,
 | 
			
		||||
@@ -284,7 +353,7 @@ func (v VulnInfo) Cvss2Scores(myFamily string) (values []CveContentCvss) {
 | 
			
		||||
	}
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if cont, found := v.CveContents[ctype]; found {
 | 
			
		||||
			if cont.Cvss2Score == 0 && cont.Cvss2Severity == "" {
 | 
			
		||||
			if cont.Cvss2Score == 0 || cont.Cvss2Severity == "" {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			// https://nvd.nist.gov/vuln-metrics/cvss
 | 
			
		||||
@@ -478,15 +547,15 @@ func (v VulnInfo) AttackVector() string {
 | 
			
		||||
	for _, cnt := range v.CveContents {
 | 
			
		||||
		if strings.HasPrefix(cnt.Cvss2Vector, "AV:N") ||
 | 
			
		||||
			strings.HasPrefix(cnt.Cvss3Vector, "CVSS:3.0/AV:N") {
 | 
			
		||||
			return "Network"
 | 
			
		||||
			return "N"
 | 
			
		||||
		} else if strings.HasPrefix(cnt.Cvss2Vector, "AV:A") ||
 | 
			
		||||
			strings.HasPrefix(cnt.Cvss3Vector, "CVSS:3.0/AV:A") {
 | 
			
		||||
			return "Adjacent"
 | 
			
		||||
			return "A"
 | 
			
		||||
		} else if strings.HasPrefix(cnt.Cvss2Vector, "AV:L") ||
 | 
			
		||||
			strings.HasPrefix(cnt.Cvss3Vector, "CVSS:3.0/AV:L") {
 | 
			
		||||
			return "Local"
 | 
			
		||||
			return "L"
 | 
			
		||||
		} else if strings.HasPrefix(cnt.Cvss3Vector, "CVSS:3.0/AV:P") {
 | 
			
		||||
			return "Physical"
 | 
			
		||||
			return "P"
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if cont, found := v.CveContents[DebianSecurityTracker]; found {
 | 
			
		||||
@@ -497,7 +566,7 @@ func (v VulnInfo) AttackVector() string {
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PatchStatus returns attack vector string
 | 
			
		||||
// PatchStatus returns fixed or unfixed string
 | 
			
		||||
func (v VulnInfo) PatchStatus(packs Packages) string {
 | 
			
		||||
	// Vuls don't know patch status of the CPE
 | 
			
		||||
	if len(v.CpeURIs) != 0 {
 | 
			
		||||
@@ -505,17 +574,17 @@ func (v VulnInfo) PatchStatus(packs Packages) string {
 | 
			
		||||
	}
 | 
			
		||||
	for _, p := range v.AffectedPackages {
 | 
			
		||||
		if p.NotFixedYet {
 | 
			
		||||
			return "Unfixed"
 | 
			
		||||
			return "unfixed"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// fast, offline mode doesn't have new version
 | 
			
		||||
		if pack, ok := packs[p.Name]; ok {
 | 
			
		||||
			if pack.NewVersion == "" {
 | 
			
		||||
				return "Unknown"
 | 
			
		||||
				return "unknown"
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return "Fixed"
 | 
			
		||||
	return "fixed"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CveContentCvss has CVSS information
 | 
			
		||||
@@ -615,6 +684,12 @@ func (v VulnInfo) Cvss3CalcURL() string {
 | 
			
		||||
// VendorLinks returns links of vendor support's URL
 | 
			
		||||
func (v VulnInfo) VendorLinks(family string) map[string]string {
 | 
			
		||||
	links := map[string]string{}
 | 
			
		||||
	if strings.HasPrefix(v.CveID, "WPVDBID") {
 | 
			
		||||
		links["WPVulnDB"] = fmt.Sprintf("https://wpvulndb.com/vulnerabilities/%s",
 | 
			
		||||
			strings.TrimPrefix(v.CveID, "WPVDBID-"))
 | 
			
		||||
		return links
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch family {
 | 
			
		||||
	case config.RedHat, config.CentOS:
 | 
			
		||||
		links["RHEL-CVE"] = "https://access.redhat.com/security/cve/" + v.CveID
 | 
			
		||||
@@ -633,8 +708,14 @@ func (v VulnInfo) VendorLinks(family string) map[string]string {
 | 
			
		||||
	case config.Amazon:
 | 
			
		||||
		links["RHEL-CVE"] = "https://access.redhat.com/security/cve/" + v.CveID
 | 
			
		||||
		for _, advisory := range v.DistroAdvisories {
 | 
			
		||||
			links[advisory.AdvisoryID] =
 | 
			
		||||
				fmt.Sprintf("https://alas.aws.amazon.com/%s.html", advisory.AdvisoryID)
 | 
			
		||||
			if strings.HasPrefix(advisory.AdvisoryID, "ALAS2") {
 | 
			
		||||
				links[advisory.AdvisoryID] =
 | 
			
		||||
					fmt.Sprintf("https://alas.aws.amazon.com/AL2/%s.html",
 | 
			
		||||
						strings.Replace(advisory.AdvisoryID, "ALAS2", "ALAS", -1))
 | 
			
		||||
			} else {
 | 
			
		||||
				links[advisory.AdvisoryID] =
 | 
			
		||||
					fmt.Sprintf("https://alas.aws.amazon.com/%s.html", advisory.AdvisoryID)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return links
 | 
			
		||||
	case config.Ubuntu:
 | 
			
		||||
@@ -654,6 +735,20 @@ func (v VulnInfo) VendorLinks(family string) map[string]string {
 | 
			
		||||
	return links
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DistroAdvisories is a list of DistroAdvisory
 | 
			
		||||
type DistroAdvisories []DistroAdvisory
 | 
			
		||||
 | 
			
		||||
// AppendIfMissing appends if missing
 | 
			
		||||
func (advs *DistroAdvisories) AppendIfMissing(adv *DistroAdvisory) bool {
 | 
			
		||||
	for _, a := range *advs {
 | 
			
		||||
		if a.AdvisoryID == adv.AdvisoryID {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	*advs = append(*advs, *adv)
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DistroAdvisory has Amazon Linux, RHEL, FreeBSD Security Advisory information.
 | 
			
		||||
type DistroAdvisory struct {
 | 
			
		||||
	AdvisoryID  string    `json:"advisoryID"`
 | 
			
		||||
@@ -774,6 +869,12 @@ const (
 | 
			
		||||
	// ChangelogLenientMatchStr is a String representation of ChangelogLenientMatch
 | 
			
		||||
	ChangelogLenientMatchStr = "ChangelogLenientMatch"
 | 
			
		||||
 | 
			
		||||
	// GitHubMatchStr is a String representation of GitHubMatch
 | 
			
		||||
	GitHubMatchStr = "GitHubMatch"
 | 
			
		||||
 | 
			
		||||
	// WPVulnDBMatchStr is a String representation of WordPress VulnDB scanning
 | 
			
		||||
	WPVulnDBMatchStr = "WPVulnDBMatch"
 | 
			
		||||
 | 
			
		||||
	// FailedToGetChangelog is a String representation of FailedToGetChangelog
 | 
			
		||||
	FailedToGetChangelog = "FailedToGetChangelog"
 | 
			
		||||
 | 
			
		||||
@@ -805,4 +906,10 @@ var (
 | 
			
		||||
 | 
			
		||||
	// ChangelogLenientMatch is a ranking how confident the CVE-ID was deteted correctly
 | 
			
		||||
	ChangelogLenientMatch = Confidence{50, ChangelogLenientMatchStr, 4}
 | 
			
		||||
 | 
			
		||||
	// GitHubMatch is a ranking how confident the CVE-ID was deteted correctly
 | 
			
		||||
	GitHubMatch = Confidence{97, GitHubMatchStr, 2}
 | 
			
		||||
 | 
			
		||||
	// WPVulnDBMatch is a ranking how confident the CVE-ID was deteted correctly
 | 
			
		||||
	WPVulnDBMatch = Confidence{100, WPVulnDBMatchStr, 0}
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -916,15 +916,15 @@ func TestFormatMaxCvssScore(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
func TestSortPackageStatues(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in  PackageStatuses
 | 
			
		||||
		out PackageStatuses
 | 
			
		||||
		in  PackageFixStatuses
 | 
			
		||||
		out PackageFixStatuses
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			in: PackageStatuses{
 | 
			
		||||
			in: PackageFixStatuses{
 | 
			
		||||
				{Name: "b"},
 | 
			
		||||
				{Name: "a"},
 | 
			
		||||
			},
 | 
			
		||||
			out: PackageStatuses{
 | 
			
		||||
			out: PackageFixStatuses{
 | 
			
		||||
				{Name: "a"},
 | 
			
		||||
				{Name: "b"},
 | 
			
		||||
			},
 | 
			
		||||
@@ -940,19 +940,19 @@ func TestSortPackageStatues(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
func TestStorePackageStatueses(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		pkgstats PackageStatuses
 | 
			
		||||
		in       PackageStatus
 | 
			
		||||
		out      PackageStatuses
 | 
			
		||||
		pkgstats PackageFixStatuses
 | 
			
		||||
		in       PackageFixStatus
 | 
			
		||||
		out      PackageFixStatuses
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			pkgstats: PackageStatuses{
 | 
			
		||||
			pkgstats: PackageFixStatuses{
 | 
			
		||||
				{Name: "a"},
 | 
			
		||||
				{Name: "b"},
 | 
			
		||||
			},
 | 
			
		||||
			in: PackageStatus{
 | 
			
		||||
			in: PackageFixStatus{
 | 
			
		||||
				Name: "c",
 | 
			
		||||
			},
 | 
			
		||||
			out: PackageStatuses{
 | 
			
		||||
			out: PackageFixStatuses{
 | 
			
		||||
				{Name: "a"},
 | 
			
		||||
				{Name: "b"},
 | 
			
		||||
				{Name: "c"},
 | 
			
		||||
@@ -1034,3 +1034,65 @@ func TestSortByConfiden(t *testing.T) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestDistroAdvisories_AppendIfMissing(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		adv *DistroAdvisory
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name  string
 | 
			
		||||
		advs  DistroAdvisories
 | 
			
		||||
		args  args
 | 
			
		||||
		want  bool
 | 
			
		||||
		after DistroAdvisories
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "duplicate no append",
 | 
			
		||||
			advs: DistroAdvisories{
 | 
			
		||||
				DistroAdvisory{
 | 
			
		||||
					AdvisoryID: "ALASs-2019-1214",
 | 
			
		||||
				}},
 | 
			
		||||
			args: args{
 | 
			
		||||
				adv: &DistroAdvisory{
 | 
			
		||||
					AdvisoryID: "ALASs-2019-1214",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			want: false,
 | 
			
		||||
			after: DistroAdvisories{
 | 
			
		||||
				DistroAdvisory{
 | 
			
		||||
					AdvisoryID: "ALASs-2019-1214",
 | 
			
		||||
				}},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "append",
 | 
			
		||||
			advs: DistroAdvisories{
 | 
			
		||||
				DistroAdvisory{
 | 
			
		||||
					AdvisoryID: "ALASs-2019-1214",
 | 
			
		||||
				}},
 | 
			
		||||
			args: args{
 | 
			
		||||
				adv: &DistroAdvisory{
 | 
			
		||||
					AdvisoryID: "ALASs-2019-1215",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			want: true,
 | 
			
		||||
			after: DistroAdvisories{
 | 
			
		||||
				{
 | 
			
		||||
					AdvisoryID: "ALASs-2019-1214",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					AdvisoryID: "ALASs-2019-1215",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			if got := tt.advs.AppendIfMissing(tt.args.adv); got != tt.want {
 | 
			
		||||
				t.Errorf("DistroAdvisories.AppendIfMissing() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
			if !reflect.DeepEqual(tt.advs, tt.after) {
 | 
			
		||||
				t.Errorf("\nexpected: %v\n  actual: %v\n", tt.after, tt.advs)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										88
									
								
								models/wordpress.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								models/wordpress.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,88 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Corporation , Japan.
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
// WordPressPackages has Core version, plugins and themes.
 | 
			
		||||
type WordPressPackages []WpPackage
 | 
			
		||||
 | 
			
		||||
// CoreVersion returns the core version of the installed WordPress
 | 
			
		||||
func (w WordPressPackages) CoreVersion() string {
 | 
			
		||||
	for _, p := range w {
 | 
			
		||||
		if p.Type == WPCore {
 | 
			
		||||
			return p.Version
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Plugins returns a slice of plugins of the installed WordPress
 | 
			
		||||
func (w WordPressPackages) Plugins() (ps []WpPackage) {
 | 
			
		||||
	for _, p := range w {
 | 
			
		||||
		if p.Type == WPPlugin {
 | 
			
		||||
			ps = append(ps, p)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Themes returns a slice of themes of the installed WordPress
 | 
			
		||||
func (w WordPressPackages) Themes() (ps []WpPackage) {
 | 
			
		||||
	for _, p := range w {
 | 
			
		||||
		if p.Type == WPTheme {
 | 
			
		||||
			ps = append(ps, p)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Find searches by specified name
 | 
			
		||||
func (w WordPressPackages) Find(name string) (ps *WpPackage, found bool) {
 | 
			
		||||
	for _, p := range w {
 | 
			
		||||
		if p.Name == name {
 | 
			
		||||
			return &p, true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// WPCore is a type `core` in WPPackage struct
 | 
			
		||||
	WPCore = "core"
 | 
			
		||||
	// WPPlugin is a type `plugin` in WPPackage struct
 | 
			
		||||
	WPPlugin = "plugin"
 | 
			
		||||
	// WPTheme is a type `theme` in WPPackage struct
 | 
			
		||||
	WPTheme = "theme"
 | 
			
		||||
 | 
			
		||||
	// Inactive is a inactive status in WPPackage struct
 | 
			
		||||
	Inactive = "inactive"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// WpPackage has a details of plugin and theme
 | 
			
		||||
type WpPackage struct {
 | 
			
		||||
	Name    string `json:"name,omitempty"`
 | 
			
		||||
	Status  string `json:"status,omitempty"` // active, inactive or must-use
 | 
			
		||||
	Update  string `json:"update,omitempty"` // available or none
 | 
			
		||||
	Version string `json:"version,omitempty"`
 | 
			
		||||
	Type    string `json:"type,omitempty"` // core, plugin, theme
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WpPackageFixStatus is used in Vulninfo.WordPress
 | 
			
		||||
type WpPackageFixStatus struct {
 | 
			
		||||
	Name    string `json:"name,omitempty"`
 | 
			
		||||
	FixedIn string `json:"fixedIn,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										163
									
								
								oval/debian.go
									
									
									
									
									
								
							
							
						
						
									
										163
									
								
								oval/debian.go
									
									
									
									
									
								
							@@ -18,6 +18,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
package oval
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
@@ -93,6 +95,7 @@ func (o DebianBase) convertToModel(def *ovalmodels.Definition) *models.CveConten
 | 
			
		||||
		Title:         def.Title,
 | 
			
		||||
		Summary:       def.Description,
 | 
			
		||||
		Cvss2Severity: def.Advisory.Severity,
 | 
			
		||||
		Cvss3Severity: def.Advisory.Severity,
 | 
			
		||||
		References:    refs,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -189,55 +192,117 @@ func NewUbuntu() Ubuntu {
 | 
			
		||||
 | 
			
		||||
// FillWithOval returns scan result after updating CVE info by OVAL
 | 
			
		||||
func (o Ubuntu) FillWithOval(driver db.DB, r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
	ovalKernelImageNames := []string{
 | 
			
		||||
		"linux-aws",
 | 
			
		||||
		"linux-azure",
 | 
			
		||||
		"linux-flo",
 | 
			
		||||
		"linux-gcp",
 | 
			
		||||
		"linux-gke",
 | 
			
		||||
		"linux-goldfish",
 | 
			
		||||
		"linux-hwe",
 | 
			
		||||
		"linux-hwe-edge",
 | 
			
		||||
		"linux-kvm",
 | 
			
		||||
		"linux-mako",
 | 
			
		||||
		"linux-raspi2",
 | 
			
		||||
		"linux-snapdragon",
 | 
			
		||||
	switch major(r.Release) {
 | 
			
		||||
	case "14":
 | 
			
		||||
		kernelNamesInOval := []string{
 | 
			
		||||
			"linux",
 | 
			
		||||
			"linux-aws",
 | 
			
		||||
			"linux-azure",
 | 
			
		||||
			"linux-firmware",
 | 
			
		||||
			"linux-lts-utopic",
 | 
			
		||||
			"linux-lts-vivid",
 | 
			
		||||
			"linux-lts-wily",
 | 
			
		||||
			"linux-lts-xenial",
 | 
			
		||||
		}
 | 
			
		||||
		return o.fillWithOval(driver, r, kernelNamesInOval)
 | 
			
		||||
	case "16":
 | 
			
		||||
		kernelNamesInOval := []string{
 | 
			
		||||
			"linux-image-aws",
 | 
			
		||||
			"linux-image-aws-hwe",
 | 
			
		||||
			"linux-image-azure",
 | 
			
		||||
			"linux-image-extra-virtual",
 | 
			
		||||
			"linux-image-extra-virtual-lts-utopic",
 | 
			
		||||
			"linux-image-extra-virtual-lts-vivid",
 | 
			
		||||
			"linux-image-extra-virtual-lts-wily",
 | 
			
		||||
			"linux-image-extra-virtual-lts-xenial",
 | 
			
		||||
			"linux-image-gcp",
 | 
			
		||||
			"linux-image-generic-lpae",
 | 
			
		||||
			"linux-image-generic-lpae-hwe-16.04",
 | 
			
		||||
			"linux-image-generic-lpae-lts-utopic",
 | 
			
		||||
			"linux-image-generic-lpae-lts-vivid",
 | 
			
		||||
			"linux-image-generic-lpae-lts-wily",
 | 
			
		||||
			"linux-image-generic-lpae-lts-xenial",
 | 
			
		||||
			"linux-image-generic-lts-utopic",
 | 
			
		||||
			"linux-image-generic-lts-vivid",
 | 
			
		||||
			"linux-image-generic-lts-wily",
 | 
			
		||||
			"linux-image-generic-lts-xenial",
 | 
			
		||||
			"linux-image-gke",
 | 
			
		||||
			"linux-image-hwe-generic-trusty",
 | 
			
		||||
			"linux-image-hwe-virtual-trusty",
 | 
			
		||||
			"linux-image-kvm",
 | 
			
		||||
			"linux-image-lowlatency",
 | 
			
		||||
			"linux-image-lowlatency-lts-utopic",
 | 
			
		||||
			"linux-image-lowlatency-lts-vivid",
 | 
			
		||||
			"linux-image-lowlatency-lts-wily",
 | 
			
		||||
		}
 | 
			
		||||
		return o.fillWithOval(driver, r, kernelNamesInOval)
 | 
			
		||||
	case "18":
 | 
			
		||||
		kernelNamesInOval := []string{
 | 
			
		||||
			"linux-image-aws",
 | 
			
		||||
			"linux-image-azure",
 | 
			
		||||
			"linux-image-extra-virtual",
 | 
			
		||||
			"linux-image-gcp",
 | 
			
		||||
			"linux-image-generic-lpae",
 | 
			
		||||
			"linux-image-kvm",
 | 
			
		||||
			"linux-image-lowlatency",
 | 
			
		||||
			"linux-image-oem",
 | 
			
		||||
			"linux-image-oracle",
 | 
			
		||||
			"linux-image-raspi2",
 | 
			
		||||
			"linux-image-snapdragon",
 | 
			
		||||
			"linux-image-virtual",
 | 
			
		||||
		}
 | 
			
		||||
		return o.fillWithOval(driver, r, kernelNamesInOval)
 | 
			
		||||
	}
 | 
			
		||||
	linuxImage := "linux-image-" + r.RunningKernel.Release
 | 
			
		||||
	return 0, fmt.Errorf("Ubuntu %s is not support for now", r.Release)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o Ubuntu) fillWithOval(driver db.DB, r *models.ScanResult, kernelNamesInOval []string) (nCVEs int, err error) {
 | 
			
		||||
	// kernel names in OVAL except for linux-image-generic
 | 
			
		||||
	linuxImage := "linux-image-" + r.RunningKernel.Release
 | 
			
		||||
	runningKernelVersion := ""
 | 
			
		||||
	kernelPkgInOVAL := ""
 | 
			
		||||
	isOVALKernelPkgAdded := true
 | 
			
		||||
	unusedKernels := []models.Package{}
 | 
			
		||||
 | 
			
		||||
	found := false
 | 
			
		||||
	if r.Container.ContainerID == "" {
 | 
			
		||||
		for _, n := range ovalKernelImageNames {
 | 
			
		||||
			if _, ok := r.Packages[n]; ok {
 | 
			
		||||
				v, ok := r.Packages[linuxImage]
 | 
			
		||||
				if ok {
 | 
			
		||||
					// Set running kernel version
 | 
			
		||||
					p := r.Packages[n]
 | 
			
		||||
					p.Version = v.Version
 | 
			
		||||
					p.NewVersion = v.NewVersion
 | 
			
		||||
					r.Packages[n] = p
 | 
			
		||||
				} else {
 | 
			
		||||
					util.Log.Warnf("Running kernel image %s is not found: %s",
 | 
			
		||||
						linuxImage, r.RunningKernel.Version)
 | 
			
		||||
				}
 | 
			
		||||
				found = true
 | 
			
		||||
		if v, ok := r.Packages[linuxImage]; ok {
 | 
			
		||||
			runningKernelVersion = v.Version
 | 
			
		||||
		} else {
 | 
			
		||||
			util.Log.Warnf("Unable to detect vulns of running kernel because the version of the runnning kernel is unknown. server: %s",
 | 
			
		||||
				r.ServerName)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, n := range kernelNamesInOval {
 | 
			
		||||
			if p, ok := r.Packages[n]; ok {
 | 
			
		||||
				kernelPkgInOVAL = p.Name
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !found {
 | 
			
		||||
			// linux-generic is described as "linux" in Ubuntu's oval.
 | 
			
		||||
			// Add "linux" and set the version of running kernel to search OVAL.
 | 
			
		||||
			v, ok := r.Packages[linuxImage]
 | 
			
		||||
			if ok {
 | 
			
		||||
				r.Packages["linux"] = models.Package{
 | 
			
		||||
					Name:       "linux",
 | 
			
		||||
					Version:    v.Version,
 | 
			
		||||
					NewVersion: v.NewVersion,
 | 
			
		||||
				}
 | 
			
		||||
		// remove unused kernels from packages to prevent detecting vulns of unused kernel
 | 
			
		||||
		for _, n := range kernelNamesInOval {
 | 
			
		||||
			if v, ok := r.Packages[n]; ok {
 | 
			
		||||
				unusedKernels = append(unusedKernels, v)
 | 
			
		||||
				delete(r.Packages, n)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if kernelPkgInOVAL == "" {
 | 
			
		||||
			if r.Release == "14" {
 | 
			
		||||
				kernelPkgInOVAL = "linux"
 | 
			
		||||
			} else if _, ok := r.Packages["linux-image-generic"]; !ok {
 | 
			
		||||
				util.Log.Warnf("The OVAL name of the running kernel image %s is not found. So vulns of linux-image-generic wll be detected. server: %s",
 | 
			
		||||
					r.RunningKernel.Version, r.ServerName)
 | 
			
		||||
				kernelPkgInOVAL = "linux-image-generic"
 | 
			
		||||
			} else {
 | 
			
		||||
				util.Log.Warnf("%s is not found. Running: %s",
 | 
			
		||||
					linuxImage, r.RunningKernel.Release)
 | 
			
		||||
				isOVALKernelPkgAdded = false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if runningKernelVersion != "" {
 | 
			
		||||
			r.Packages[kernelPkgInOVAL] = models.Package{
 | 
			
		||||
				Name:    kernelPkgInOVAL,
 | 
			
		||||
				Version: runningKernelVersion,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -253,24 +318,26 @@ func (o Ubuntu) FillWithOval(driver db.DB, r *models.ScanResult) (nCVEs int, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !found {
 | 
			
		||||
		delete(r.Packages, "linux")
 | 
			
		||||
	if isOVALKernelPkgAdded {
 | 
			
		||||
		delete(r.Packages, kernelPkgInOVAL)
 | 
			
		||||
	}
 | 
			
		||||
	for _, p := range unusedKernels {
 | 
			
		||||
		r.Packages[p.Name] = p
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, defPacks := range relatedDefs.entries {
 | 
			
		||||
		// Remove "linux" added above to search for oval
 | 
			
		||||
		// "linux" is not a real package name (key of affected packages in OVAL)
 | 
			
		||||
		if _, ok := defPacks.actuallyAffectedPackNames["linux"]; !found && ok {
 | 
			
		||||
			defPacks.actuallyAffectedPackNames[linuxImage] = true
 | 
			
		||||
			delete(defPacks.actuallyAffectedPackNames, "linux")
 | 
			
		||||
		if nfy, ok := defPacks.actuallyAffectedPackNames[kernelPkgInOVAL]; isOVALKernelPkgAdded && ok {
 | 
			
		||||
			defPacks.actuallyAffectedPackNames[linuxImage] = nfy
 | 
			
		||||
			delete(defPacks.actuallyAffectedPackNames, kernelPkgInOVAL)
 | 
			
		||||
			for i, p := range defPacks.def.AffectedPacks {
 | 
			
		||||
				if p.Name == "linux" {
 | 
			
		||||
				if p.Name == kernelPkgInOVAL {
 | 
			
		||||
					p.Name = linuxImage
 | 
			
		||||
					defPacks.def.AffectedPacks[i] = p
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		o.update(r, defPacks)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,7 @@ func TestPackNamesOfUpdateDebian(t *testing.T) {
 | 
			
		||||
			in: models.ScanResult{
 | 
			
		||||
				ScannedCves: models.VulnInfos{
 | 
			
		||||
					"CVE-2000-1000": models.VulnInfo{
 | 
			
		||||
						AffectedPackages: models.PackageStatuses{
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							{Name: "packA"},
 | 
			
		||||
							{Name: "packC"},
 | 
			
		||||
						},
 | 
			
		||||
@@ -56,7 +56,7 @@ func TestPackNamesOfUpdateDebian(t *testing.T) {
 | 
			
		||||
			out: models.ScanResult{
 | 
			
		||||
				ScannedCves: models.VulnInfos{
 | 
			
		||||
					"CVE-2000-1000": models.VulnInfo{
 | 
			
		||||
						AffectedPackages: models.PackageStatuses{
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							{Name: "packA"},
 | 
			
		||||
							{Name: "packB", NotFixedYet: true},
 | 
			
		||||
							{Name: "packC"},
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								oval/oval.go
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								oval/oval.go
									
									
									
									
									
								
							@@ -28,6 +28,7 @@ import (
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/kotakanbe/goval-dictionary/db"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Client is the interface of OVAL client.
 | 
			
		||||
@@ -58,7 +59,7 @@ func (b Base) CheckHTTPHealth() error {
 | 
			
		||||
	//  resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
 | 
			
		||||
	//  resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
 | 
			
		||||
	if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
		return fmt.Errorf("Failed to request to OVAL server. url: %s, errs: %v",
 | 
			
		||||
		return xerrors.Errorf("Failed to request to OVAL server. url: %s, errs: %w",
 | 
			
		||||
			url, errs)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
@@ -69,8 +70,7 @@ func (b Base) CheckIfOvalFetched(driver db.DB, osFamily, release string) (fetche
 | 
			
		||||
	if !cnf.Conf.OvalDict.IsFetchViaHTTP() {
 | 
			
		||||
		count, err := driver.CountDefs(osFamily, release)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, fmt.Errorf("Failed to count OVAL defs: %s, %s, %v",
 | 
			
		||||
				osFamily, release, err)
 | 
			
		||||
			return false, xerrors.Errorf("Failed to count OVAL defs: %s, %s, %w", osFamily, release, err)
 | 
			
		||||
		}
 | 
			
		||||
		return 0 < count, nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -78,13 +78,11 @@ func (b Base) CheckIfOvalFetched(driver db.DB, osFamily, release string) (fetche
 | 
			
		||||
	url, _ := util.URLPathJoin(cnf.Conf.OvalDict.URL, "count", osFamily, release)
 | 
			
		||||
	resp, body, errs := gorequest.New().Get(url).End()
 | 
			
		||||
	if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
		return false, fmt.Errorf("HTTP GET error: %v, url: %s, resp: %v",
 | 
			
		||||
			errs, url, resp)
 | 
			
		||||
		return false, xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %w", url, resp, errs)
 | 
			
		||||
	}
 | 
			
		||||
	count := 0
 | 
			
		||||
	if err := json.Unmarshal([]byte(body), &count); err != nil {
 | 
			
		||||
		return false, fmt.Errorf("Failed to Unmarshall. body: %s, err: %s",
 | 
			
		||||
			body, err)
 | 
			
		||||
		return false, xerrors.Errorf("Failed to Unmarshall. body: %s, err: %w", body, err)
 | 
			
		||||
	}
 | 
			
		||||
	return 0 < count, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -98,13 +96,11 @@ func (b Base) CheckIfOvalFresh(driver db.DB, osFamily, release string) (ok bool,
 | 
			
		||||
		url, _ := util.URLPathJoin(cnf.Conf.OvalDict.URL, "lastmodified", osFamily, release)
 | 
			
		||||
		resp, body, errs := gorequest.New().Get(url).End()
 | 
			
		||||
		if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
			return false, fmt.Errorf("HTTP GET error: %v, url: %s, resp: %v",
 | 
			
		||||
				errs, url, resp)
 | 
			
		||||
			return false, xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %w", url, resp, errs)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := json.Unmarshal([]byte(body), &lastModified); err != nil {
 | 
			
		||||
			return false, fmt.Errorf("Failed to Unmarshall. body: %s, err: %s",
 | 
			
		||||
				body, err)
 | 
			
		||||
			return false, xerrors.Errorf("Failed to Unmarshall. body: %s, err: %w", body, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -119,7 +119,6 @@ func (o RedHatBase) update(r *models.ScanResult, defPacks defPacks) (nCVEs int)
 | 
			
		||||
				if v.LastModified.After(ovalContent.LastModified) {
 | 
			
		||||
					util.Log.Debugf("%s, OvalID: %d ignroed: ",
 | 
			
		||||
						cve.CveID, defPacks.def.ID)
 | 
			
		||||
					continue
 | 
			
		||||
				} else {
 | 
			
		||||
					util.Log.Debugf("%s OVAL will be overwritten", cve.CveID)
 | 
			
		||||
				}
 | 
			
		||||
@@ -133,6 +132,9 @@ func (o RedHatBase) update(r *models.ScanResult, defPacks defPacks) (nCVEs int)
 | 
			
		||||
			vinfo.CveContents = cveContents
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		vinfo.DistroAdvisories.AppendIfMissing(
 | 
			
		||||
			o.convertToDistroAdvisory(&defPacks.def))
 | 
			
		||||
 | 
			
		||||
		// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
 | 
			
		||||
		for _, pack := range vinfo.AffectedPackages {
 | 
			
		||||
			if nfy, ok := defPacks.actuallyAffectedPackNames[pack.Name]; !ok {
 | 
			
		||||
@@ -148,6 +150,21 @@ func (o RedHatBase) update(r *models.ScanResult, defPacks defPacks) (nCVEs int)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o RedHatBase) convertToDistroAdvisory(def *ovalmodels.Definition) *models.DistroAdvisory {
 | 
			
		||||
	advisoryID := def.Title
 | 
			
		||||
	if o.family == config.RedHat || o.family == config.CentOS {
 | 
			
		||||
		ss := strings.Fields(def.Title)
 | 
			
		||||
		advisoryID = strings.TrimSuffix(ss[0], ":")
 | 
			
		||||
	}
 | 
			
		||||
	return &models.DistroAdvisory{
 | 
			
		||||
		AdvisoryID:  advisoryID,
 | 
			
		||||
		Severity:    def.Advisory.Severity,
 | 
			
		||||
		Issued:      def.Advisory.Issued,
 | 
			
		||||
		Updated:     def.Advisory.Updated,
 | 
			
		||||
		Description: def.Description,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o RedHatBase) convertToModel(cveID string, def *ovalmodels.Definition) *models.CveContent {
 | 
			
		||||
	for _, cve := range def.Advisory.Cves {
 | 
			
		||||
		if cve.CveID != cveID {
 | 
			
		||||
@@ -171,10 +188,10 @@ func (o RedHatBase) convertToModel(cveID string, def *ovalmodels.Definition) *mo
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		sev2, sev3 := "", ""
 | 
			
		||||
		if score2 != 0 {
 | 
			
		||||
		if score2 == 0 {
 | 
			
		||||
			sev2 = severity
 | 
			
		||||
		}
 | 
			
		||||
		if score3 != 0 {
 | 
			
		||||
		if score3 == 0 {
 | 
			
		||||
			sev3 = severity
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -276,3 +293,20 @@ func NewOracle() Oracle {
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Amazon is the interface for RedhatBase OVAL
 | 
			
		||||
type Amazon struct {
 | 
			
		||||
	// Base
 | 
			
		||||
	RedHatBase
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewAmazon creates OVAL client for Amazon Linux
 | 
			
		||||
func NewAmazon() Amazon {
 | 
			
		||||
	return Amazon{
 | 
			
		||||
		RedHatBase{
 | 
			
		||||
			Base{
 | 
			
		||||
				family: config.Amazon,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -102,7 +102,7 @@ func TestPackNamesOfUpdate(t *testing.T) {
 | 
			
		||||
			in: models.ScanResult{
 | 
			
		||||
				ScannedCves: models.VulnInfos{
 | 
			
		||||
					"CVE-2000-1000": models.VulnInfo{
 | 
			
		||||
						AffectedPackages: models.PackageStatuses{
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							{Name: "packA"},
 | 
			
		||||
							{Name: "packB", NotFixedYet: false},
 | 
			
		||||
						},
 | 
			
		||||
@@ -126,7 +126,7 @@ func TestPackNamesOfUpdate(t *testing.T) {
 | 
			
		||||
			out: models.ScanResult{
 | 
			
		||||
				ScannedCves: models.VulnInfos{
 | 
			
		||||
					"CVE-2000-1000": models.VulnInfo{
 | 
			
		||||
						AffectedPackages: models.PackageStatuses{
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							{Name: "packA"},
 | 
			
		||||
							{Name: "packB", NotFixedYet: true},
 | 
			
		||||
						},
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										81
									
								
								oval/util.go
									
									
									
									
									
								
							
							
						
						
									
										81
									
								
								oval/util.go
									
									
									
									
									
								
							@@ -19,7 +19,6 @@ package oval
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
@@ -34,6 +33,7 @@ import (
 | 
			
		||||
	"github.com/kotakanbe/goval-dictionary/db"
 | 
			
		||||
	ovalmodels "github.com/kotakanbe/goval-dictionary/models"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ovalResult struct {
 | 
			
		||||
@@ -47,9 +47,9 @@ type defPacks struct {
 | 
			
		||||
	actuallyAffectedPackNames map[string]bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e defPacks) toPackStatuses() (ps models.PackageStatuses) {
 | 
			
		||||
func (e defPacks) toPackStatuses() (ps models.PackageFixStatuses) {
 | 
			
		||||
	for name, notFixedYet := range e.actuallyAffectedPackNames {
 | 
			
		||||
		ps = append(ps, models.PackageStatus{
 | 
			
		||||
		ps = append(ps, models.PackageFixStatus{
 | 
			
		||||
			Name:        name,
 | 
			
		||||
			NotFixedYet: notFixedYet,
 | 
			
		||||
		})
 | 
			
		||||
@@ -78,7 +78,8 @@ func (e *ovalResult) upsert(def ovalmodels.Definition, packName string, notFixed
 | 
			
		||||
type request struct {
 | 
			
		||||
	packName          string
 | 
			
		||||
	versionRelease    string
 | 
			
		||||
	NewVersionRelease string
 | 
			
		||||
	newVersionRelease string
 | 
			
		||||
	arch              string
 | 
			
		||||
	binaryPackNames   []string
 | 
			
		||||
	isSrcPack         bool
 | 
			
		||||
}
 | 
			
		||||
@@ -105,8 +106,9 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult) (
 | 
			
		||||
			reqChan <- request{
 | 
			
		||||
				packName:          pack.Name,
 | 
			
		||||
				versionRelease:    pack.FormatVer(),
 | 
			
		||||
				NewVersionRelease: pack.FormatVer(),
 | 
			
		||||
				newVersionRelease: pack.FormatVer(),
 | 
			
		||||
				isSrcPack:         false,
 | 
			
		||||
				arch:              pack.Arch,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		for _, pack := range r.SrcPackages {
 | 
			
		||||
@@ -115,6 +117,7 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult) (
 | 
			
		||||
				binaryPackNames: pack.BinaryNames,
 | 
			
		||||
				versionRelease:  pack.Version,
 | 
			
		||||
				isSrcPack:       true,
 | 
			
		||||
				// arch:            pack.Arch,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
@@ -164,11 +167,11 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult) (
 | 
			
		||||
		case err := <-errChan:
 | 
			
		||||
			errs = append(errs, err)
 | 
			
		||||
		case <-timeout:
 | 
			
		||||
			return relatedDefs, fmt.Errorf("Timeout Fetching OVAL")
 | 
			
		||||
			return relatedDefs, xerrors.New("Timeout Fetching OVAL")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(errs) != 0 {
 | 
			
		||||
		return relatedDefs, fmt.Errorf("Failed to fetch OVAL. err: %v", errs)
 | 
			
		||||
		return relatedDefs, xerrors.Errorf("Failed to fetch OVAL. err: %w", errs)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
@@ -186,8 +189,7 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er
 | 
			
		||||
			if count == retryMax {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			return fmt.Errorf("HTTP GET error: %v, url: %s, resp: %v",
 | 
			
		||||
				errs, url, resp)
 | 
			
		||||
			return xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %w", url, resp, errs)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -196,18 +198,17 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er
 | 
			
		||||
	}
 | 
			
		||||
	err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		errChan <- fmt.Errorf("HTTP Error %s", err)
 | 
			
		||||
		errChan <- xerrors.Errorf("HTTP Error %w", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if count == retryMax {
 | 
			
		||||
		errChan <- fmt.Errorf("HRetry count exceeded")
 | 
			
		||||
		errChan <- xerrors.New("HRetry count exceeded")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defs := []ovalmodels.Definition{}
 | 
			
		||||
	if err := json.Unmarshal([]byte(body), &defs); err != nil {
 | 
			
		||||
		errChan <- fmt.Errorf("Failed to Unmarshall. body: %s, err: %s",
 | 
			
		||||
			body, err)
 | 
			
		||||
		errChan <- xerrors.Errorf("Failed to Unmarshall. body: %s, err: %w", body, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	resChan <- response{
 | 
			
		||||
@@ -222,7 +223,8 @@ func getDefsByPackNameFromOvalDB(driver db.DB, r *models.ScanResult) (relatedDef
 | 
			
		||||
		requests = append(requests, request{
 | 
			
		||||
			packName:          pack.Name,
 | 
			
		||||
			versionRelease:    pack.FormatVer(),
 | 
			
		||||
			NewVersionRelease: pack.FormatNewVer(),
 | 
			
		||||
			newVersionRelease: pack.FormatNewVer(),
 | 
			
		||||
			arch:              pack.Arch,
 | 
			
		||||
			isSrcPack:         false,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
@@ -236,9 +238,9 @@ func getDefsByPackNameFromOvalDB(driver db.DB, r *models.ScanResult) (relatedDef
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, req := range requests {
 | 
			
		||||
		definitions, err := driver.GetByPackName(r.Release, req.packName)
 | 
			
		||||
		definitions, err := driver.GetByPackName(r.Release, req.packName, req.arch)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return relatedDefs, fmt.Errorf("Failed to get %s OVAL info by package: %#v, err: %s", r.Family, req, err)
 | 
			
		||||
			return relatedDefs, xerrors.Errorf("Failed to get %s OVAL info by package: %#v, err: %w", r.Family, req, err)
 | 
			
		||||
		}
 | 
			
		||||
		for _, def := range definitions {
 | 
			
		||||
			affected, notFixedYet := isOvalDefAffected(def, req, r.Family, r.RunningKernel)
 | 
			
		||||
@@ -291,31 +293,41 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family string, ru
 | 
			
		||||
			return true, true
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Compare between the installed version vs the version in OVAL
 | 
			
		||||
		less, err := lessThan(family, req.versionRelease, ovalPack)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Debugf("Failed to parse versions: %s, Ver: %#v, OVAL: %#v, DefID: %s",
 | 
			
		||||
				err, req.versionRelease, ovalPack, def.DefinitionID)
 | 
			
		||||
			return false, false
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if less {
 | 
			
		||||
			if req.isSrcPack {
 | 
			
		||||
				// Unable to judge whether fixed or not fixed of src package(Ubuntu, Debian)
 | 
			
		||||
			// If the version of installed is less than in OVAL
 | 
			
		||||
			switch family {
 | 
			
		||||
			case config.RedHat,
 | 
			
		||||
				config.Amazon,
 | 
			
		||||
				config.SUSEEnterpriseServer,
 | 
			
		||||
				config.Debian,
 | 
			
		||||
				config.Ubuntu:
 | 
			
		||||
				// Use fixed state in OVAL for these distros.
 | 
			
		||||
				return true, false
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// But CentOS can't judge whether fixed or unfixed.
 | 
			
		||||
			// Because fixed state in RHEL's OVAL is different.
 | 
			
		||||
			// So, it have to be judged version comparison.
 | 
			
		||||
 | 
			
		||||
			// `offline` or `fast` scan mode can't get a updatable version.
 | 
			
		||||
			// In these mode, the blow field was set empty.
 | 
			
		||||
			// Vuls can not judge fixed or unfixed.
 | 
			
		||||
			if req.NewVersionRelease == "" {
 | 
			
		||||
			if req.newVersionRelease == "" {
 | 
			
		||||
				return true, false
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// compare version: newVer vs oval
 | 
			
		||||
			less, err := lessThan(family, req.NewVersionRelease, ovalPack)
 | 
			
		||||
			less, err := lessThan(family, req.newVersionRelease, ovalPack)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				util.Log.Debugf("Failed to parse versions: %s, NewVer: %#v, OVAL: %#v, DefID: %s",
 | 
			
		||||
					err, req.NewVersionRelease, ovalPack, def.DefinitionID)
 | 
			
		||||
					err, req.newVersionRelease, ovalPack, def.DefinitionID)
 | 
			
		||||
				return false, false
 | 
			
		||||
			}
 | 
			
		||||
			return true, less
 | 
			
		||||
@@ -324,9 +336,13 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family string, ru
 | 
			
		||||
	return false, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var centosVerPattern = regexp.MustCompile(`\.[es]l(\d+)(?:_\d+)?(?:\.centos)?`)
 | 
			
		||||
var esVerPattern = regexp.MustCompile(`\.el(\d+)(?:_\d+)?`)
 | 
			
		||||
 | 
			
		||||
func lessThan(family, versionRelease string, packB ovalmodels.Package) (bool, error) {
 | 
			
		||||
	switch family {
 | 
			
		||||
	case config.Debian, config.Ubuntu:
 | 
			
		||||
	case config.Debian,
 | 
			
		||||
		config.Ubuntu:
 | 
			
		||||
		vera, err := debver.NewVersion(versionRelease)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, err
 | 
			
		||||
@@ -336,18 +352,23 @@ func lessThan(family, versionRelease string, packB ovalmodels.Package) (bool, er
 | 
			
		||||
			return false, err
 | 
			
		||||
		}
 | 
			
		||||
		return vera.LessThan(verb), nil
 | 
			
		||||
	case config.Oracle, config.SUSEEnterpriseServer, config.Alpine:
 | 
			
		||||
 | 
			
		||||
	case config.Oracle,
 | 
			
		||||
		config.SUSEEnterpriseServer,
 | 
			
		||||
		config.Alpine,
 | 
			
		||||
		config.Amazon:
 | 
			
		||||
		vera := rpmver.NewVersion(versionRelease)
 | 
			
		||||
		verb := rpmver.NewVersion(packB.Version)
 | 
			
		||||
		return vera.LessThan(verb), nil
 | 
			
		||||
	case config.RedHat, config.CentOS: // TODO: Suport config.Scientific
 | 
			
		||||
		rea := regexp.MustCompile(`\.[es]l(\d+)(?:_\d+)?(?:\.centos)?`)
 | 
			
		||||
		reb := regexp.MustCompile(`\.el(\d+)(?:_\d+)?`)
 | 
			
		||||
		vera := rpmver.NewVersion(rea.ReplaceAllString(versionRelease, ".el$1"))
 | 
			
		||||
		verb := rpmver.NewVersion(reb.ReplaceAllString(packB.Version, ".el$1"))
 | 
			
		||||
 | 
			
		||||
	case config.RedHat,
 | 
			
		||||
		config.CentOS:
 | 
			
		||||
		vera := rpmver.NewVersion(centosVerPattern.ReplaceAllString(versionRelease, ".el$1"))
 | 
			
		||||
		verb := rpmver.NewVersion(esVerPattern.ReplaceAllString(packB.Version, ".el$1"))
 | 
			
		||||
		return vera.LessThan(verb), nil
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		util.Log.Errorf("Not implemented yet: %s", family)
 | 
			
		||||
	}
 | 
			
		||||
	return false, fmt.Errorf("Package version comparison not supported: %s", family)
 | 
			
		||||
	return false, xerrors.Errorf("Package version comparison not supported: %s", family)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -110,7 +110,7 @@ func TestDefpacksToPackStatuses(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in  in
 | 
			
		||||
		out models.PackageStatuses
 | 
			
		||||
		out models.PackageFixStatuses
 | 
			
		||||
	}{
 | 
			
		||||
		// Ubuntu
 | 
			
		||||
		{
 | 
			
		||||
@@ -135,7 +135,7 @@ func TestDefpacksToPackStatuses(t *testing.T) {
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: models.PackageStatuses{
 | 
			
		||||
			out: models.PackageFixStatuses{
 | 
			
		||||
				{
 | 
			
		||||
					Name:        "a",
 | 
			
		||||
					NotFixedYet: true,
 | 
			
		||||
@@ -281,11 +281,11 @@ func TestIsOvalDefAffected(t *testing.T) {
 | 
			
		||||
					packName:          "b",
 | 
			
		||||
					isSrcPack:         false,
 | 
			
		||||
					versionRelease:    "1.0.0-0",
 | 
			
		||||
					NewVersionRelease: "1.0.0-2",
 | 
			
		||||
					newVersionRelease: "1.0.0-2",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    true,
 | 
			
		||||
			notFixedYet: true,
 | 
			
		||||
			notFixedYet: false,
 | 
			
		||||
		},
 | 
			
		||||
		// 4. Ubuntu
 | 
			
		||||
		//   ovalpack.NotFixedYet == false
 | 
			
		||||
@@ -313,7 +313,7 @@ func TestIsOvalDefAffected(t *testing.T) {
 | 
			
		||||
					packName:          "b",
 | 
			
		||||
					isSrcPack:         false,
 | 
			
		||||
					versionRelease:    "1.0.0-0",
 | 
			
		||||
					NewVersionRelease: "1.0.0-3",
 | 
			
		||||
					newVersionRelease: "1.0.0-3",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    true,
 | 
			
		||||
@@ -340,7 +340,7 @@ func TestIsOvalDefAffected(t *testing.T) {
 | 
			
		||||
					packName:          "b",
 | 
			
		||||
					isSrcPack:         false,
 | 
			
		||||
					versionRelease:    "0:1.2.3-45.el6_7.7",
 | 
			
		||||
					NewVersionRelease: "",
 | 
			
		||||
					newVersionRelease: "",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    true,
 | 
			
		||||
@@ -367,11 +367,11 @@ func TestIsOvalDefAffected(t *testing.T) {
 | 
			
		||||
					packName:          "b",
 | 
			
		||||
					isSrcPack:         false,
 | 
			
		||||
					versionRelease:    "0:1.2.3-45.el6_7.6",
 | 
			
		||||
					NewVersionRelease: "0:1.2.3-45.el6_7.7",
 | 
			
		||||
					newVersionRelease: "0:1.2.3-45.el6_7.7",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    true,
 | 
			
		||||
			notFixedYet: true,
 | 
			
		||||
			notFixedYet: false,
 | 
			
		||||
		},
 | 
			
		||||
		// 7 RedHat
 | 
			
		||||
		{
 | 
			
		||||
@@ -446,11 +446,11 @@ func TestIsOvalDefAffected(t *testing.T) {
 | 
			
		||||
					packName:          "b",
 | 
			
		||||
					isSrcPack:         false,
 | 
			
		||||
					versionRelease:    "0:1.2.3-45.el6_7.6",
 | 
			
		||||
					NewVersionRelease: "0:1.2.3-45.el6_7.7",
 | 
			
		||||
					newVersionRelease: "0:1.2.3-45.el6_7.7",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    true,
 | 
			
		||||
			notFixedYet: true,
 | 
			
		||||
			notFixedYet: false,
 | 
			
		||||
		},
 | 
			
		||||
		// 10 RedHat
 | 
			
		||||
		{
 | 
			
		||||
@@ -473,7 +473,7 @@ func TestIsOvalDefAffected(t *testing.T) {
 | 
			
		||||
					packName:          "b",
 | 
			
		||||
					isSrcPack:         false,
 | 
			
		||||
					versionRelease:    "0:1.2.3-45.el6_7.6",
 | 
			
		||||
					NewVersionRelease: "0:1.2.3-45.el6_7.8",
 | 
			
		||||
					newVersionRelease: "0:1.2.3-45.el6_7.8",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    true,
 | 
			
		||||
@@ -499,7 +499,7 @@ func TestIsOvalDefAffected(t *testing.T) {
 | 
			
		||||
					packName:          "b",
 | 
			
		||||
					isSrcPack:         false,
 | 
			
		||||
					versionRelease:    "0:1.2.3-45.el6_7.6",
 | 
			
		||||
					NewVersionRelease: "0:1.2.3-45.el6_7.9",
 | 
			
		||||
					newVersionRelease: "0:1.2.3-45.el6_7.9",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    true,
 | 
			
		||||
@@ -578,7 +578,7 @@ func TestIsOvalDefAffected(t *testing.T) {
 | 
			
		||||
					packName:          "b",
 | 
			
		||||
					isSrcPack:         false,
 | 
			
		||||
					versionRelease:    "0:1.2.3-45.el6.centos.7",
 | 
			
		||||
					NewVersionRelease: "",
 | 
			
		||||
					newVersionRelease: "",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    true,
 | 
			
		||||
@@ -657,7 +657,7 @@ func TestIsOvalDefAffected(t *testing.T) {
 | 
			
		||||
					packName:          "b",
 | 
			
		||||
					isSrcPack:         false,
 | 
			
		||||
					versionRelease:    "0:1.2.3-45.el6.centos.6",
 | 
			
		||||
					NewVersionRelease: "0:1.2.3-45.el6.centos.7",
 | 
			
		||||
					newVersionRelease: "0:1.2.3-45.el6.centos.7",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    true,
 | 
			
		||||
@@ -684,7 +684,7 @@ func TestIsOvalDefAffected(t *testing.T) {
 | 
			
		||||
					packName:          "b",
 | 
			
		||||
					isSrcPack:         false,
 | 
			
		||||
					versionRelease:    "0:1.2.3-45.el6.centos.6",
 | 
			
		||||
					NewVersionRelease: "0:1.2.3-45.el6.centos.8",
 | 
			
		||||
					newVersionRelease: "0:1.2.3-45.el6.centos.8",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    true,
 | 
			
		||||
@@ -711,7 +711,7 @@ func TestIsOvalDefAffected(t *testing.T) {
 | 
			
		||||
					packName:          "b",
 | 
			
		||||
					isSrcPack:         false,
 | 
			
		||||
					versionRelease:    "0:1.2.3-45.el6.centos.6",
 | 
			
		||||
					NewVersionRelease: "0:1.2.3-45.el6.centos.9",
 | 
			
		||||
					newVersionRelease: "0:1.2.3-45.el6.centos.9",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    true,
 | 
			
		||||
@@ -865,7 +865,7 @@ func TestIsOvalDefAffected(t *testing.T) {
 | 
			
		||||
					packName:          "b",
 | 
			
		||||
					isSrcPack:         false,
 | 
			
		||||
					versionRelease:    "0:1.2.3-45.sl6.6",
 | 
			
		||||
					NewVersionRelease: "0:1.2.3-45.sl6.7",
 | 
			
		||||
					newVersionRelease: "0:1.2.3-45.sl6.7",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    true,
 | 
			
		||||
@@ -891,7 +891,7 @@ func TestIsOvalDefAffected(t *testing.T) {
 | 
			
		||||
					packName:          "b",
 | 
			
		||||
					isSrcPack:         false,
 | 
			
		||||
					versionRelease:    "0:1.2.3-45.sl6.6",
 | 
			
		||||
					NewVersionRelease: "0:1.2.3-45.sl6.8",
 | 
			
		||||
					newVersionRelease: "0:1.2.3-45.sl6.8",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    true,
 | 
			
		||||
@@ -917,7 +917,7 @@ func TestIsOvalDefAffected(t *testing.T) {
 | 
			
		||||
					packName:          "b",
 | 
			
		||||
					isSrcPack:         false,
 | 
			
		||||
					versionRelease:    "0:1.2.3-45.sl6.6",
 | 
			
		||||
					NewVersionRelease: "0:1.2.3-45.sl6.9",
 | 
			
		||||
					newVersionRelease: "0:1.2.3-45.sl6.9",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    true,
 | 
			
		||||
@@ -989,7 +989,7 @@ func TestIsOvalDefAffected(t *testing.T) {
 | 
			
		||||
				req: request{
 | 
			
		||||
					packName:          "kernel",
 | 
			
		||||
					versionRelease:    "3.0.0",
 | 
			
		||||
					NewVersionRelease: "3.2.0",
 | 
			
		||||
					newVersionRelease: "3.2.0",
 | 
			
		||||
				},
 | 
			
		||||
				kernel: models.Kernel{
 | 
			
		||||
					Release: "3.0.0",
 | 
			
		||||
@@ -1013,7 +1013,7 @@ func TestIsOvalDefAffected(t *testing.T) {
 | 
			
		||||
				req: request{
 | 
			
		||||
					packName:          "kernel",
 | 
			
		||||
					versionRelease:    "3.0.0",
 | 
			
		||||
					NewVersionRelease: "3.2.0",
 | 
			
		||||
					newVersionRelease: "3.2.0",
 | 
			
		||||
				},
 | 
			
		||||
				kernel: models.Kernel{
 | 
			
		||||
					Release: "3.0.0",
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,7 @@ import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	storage "github.com/Azure/azure-sdk-for-go/storage"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	c "github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
@@ -60,7 +61,7 @@ func (w AzureBlobWriter) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
			k := key + ".json"
 | 
			
		||||
			var b []byte
 | 
			
		||||
			if b, err = json.Marshal(r); err != nil {
 | 
			
		||||
				return fmt.Errorf("Failed to Marshal to JSON: %s", err)
 | 
			
		||||
				return xerrors.Errorf("Failed to Marshal to JSON: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			if err := createBlockBlob(cli, k, b); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
@@ -87,7 +88,7 @@ func (w AzureBlobWriter) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
			k := key + ".xml"
 | 
			
		||||
			var b []byte
 | 
			
		||||
			if b, err = xml.Marshal(r); err != nil {
 | 
			
		||||
				return fmt.Errorf("Failed to Marshal to XML: %s", err)
 | 
			
		||||
				return xerrors.Errorf("Failed to Marshal to XML: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			allBytes := bytes.Join([][]byte{[]byte(xml.Header + vulsOpenTag), b, []byte(vulsCloseTag)}, []byte{})
 | 
			
		||||
			if err := createBlockBlob(cli, k, allBytes); err != nil {
 | 
			
		||||
@@ -117,7 +118,7 @@ func CheckIfAzureContainerExists() error {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if !found {
 | 
			
		||||
		return fmt.Errorf("Container not found. Container: %s", c.Conf.Azure.ContainerName)
 | 
			
		||||
		return xerrors.Errorf("Container not found. Container: %s", c.Conf.Azure.ContainerName)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@@ -142,7 +143,7 @@ func createBlockBlob(cli storage.BlobStorageClient, k string, b []byte) error {
 | 
			
		||||
	ref := cli.GetContainerReference(c.Conf.Azure.ContainerName)
 | 
			
		||||
	blob := ref.GetBlobReference(k)
 | 
			
		||||
	if err := blob.CreateBlockBlobFromReader(bytes.NewReader(b), nil); err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to upload data to %s/%s, %s",
 | 
			
		||||
		return xerrors.Errorf("Failed to upload data to %s/%s, err: %w",
 | 
			
		||||
			c.Conf.Azure.ContainerName, k, err)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"github.com/cenkalti/backoff"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
@@ -57,7 +58,7 @@ func (api cvedictClient) CheckHealth() error {
 | 
			
		||||
	resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
 | 
			
		||||
	//  resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
 | 
			
		||||
	if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
		return fmt.Errorf("Failed to request to CVE server. url: %s, errs: %v",
 | 
			
		||||
		return xerrors.Errorf("Failed to request to CVE server. url: %s, errs: %w",
 | 
			
		||||
			url, errs)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
@@ -73,7 +74,7 @@ func (api cvedictClient) FetchCveDetails(driver cvedb.DB, cveIDs []string) (cveD
 | 
			
		||||
		for _, cveID := range cveIDs {
 | 
			
		||||
			cveDetail, err := driver.Get(cveID)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, fmt.Errorf("Failed to fetch CVE. err: %s", err)
 | 
			
		||||
				return nil, xerrors.Errorf("Failed to fetch CVE. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			if len(cveDetail.CveID) == 0 {
 | 
			
		||||
				cveDetails = append(cveDetails, cve.CveDetail{
 | 
			
		||||
@@ -132,12 +133,12 @@ func (api cvedictClient) FetchCveDetails(driver cvedb.DB, cveIDs []string) (cveD
 | 
			
		||||
		case err := <-errChan:
 | 
			
		||||
			errs = append(errs, err)
 | 
			
		||||
		case <-timeout:
 | 
			
		||||
			return nil, fmt.Errorf("Timeout Fetching CVE")
 | 
			
		||||
			return nil, xerrors.New("Timeout Fetching CVE")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(errs) != 0 {
 | 
			
		||||
		return nil,
 | 
			
		||||
			fmt.Errorf("Failed to fetch CVE. err: %v", errs)
 | 
			
		||||
			xerrors.Errorf("Failed to fetch CVE. err: %w", errs)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
@@ -150,8 +151,8 @@ func (api cvedictClient) httpGet(key, url string, resChan chan<- response, errCh
 | 
			
		||||
		//  resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
 | 
			
		||||
		resp, body, errs = gorequest.New().Get(url).End()
 | 
			
		||||
		if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
			return fmt.Errorf("HTTP GET error: %v, url: %s, resp: %v",
 | 
			
		||||
				errs, url, resp)
 | 
			
		||||
			return xerrors.Errorf("HTTP GET Error, url: %s, resp: %v, err: %w",
 | 
			
		||||
				url, resp, errs)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -161,13 +162,12 @@ func (api cvedictClient) httpGet(key, url string, resChan chan<- response, errCh
 | 
			
		||||
	}
 | 
			
		||||
	err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		errChan <- fmt.Errorf("HTTP Error %s", err)
 | 
			
		||||
		errChan <- xerrors.Errorf("HTTP Error: %w", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	cveDetail := cve.CveDetail{}
 | 
			
		||||
	if err := json.Unmarshal([]byte(body), &cveDetail); err != nil {
 | 
			
		||||
		errChan <- fmt.Errorf("Failed to Unmarshall. body: %s, err: %s",
 | 
			
		||||
			body, err)
 | 
			
		||||
		errChan <- xerrors.Errorf("Failed to Unmarshall. body: %s, err: %w", body, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	resChan <- response{
 | 
			
		||||
@@ -203,7 +203,7 @@ func (api cvedictClient) httpPost(key, url string, query map[string]string) ([]c
 | 
			
		||||
		}
 | 
			
		||||
		resp, body, errs = req.End()
 | 
			
		||||
		if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
			return fmt.Errorf("HTTP POST error: %v, url: %s, resp: %v", errs, url, resp)
 | 
			
		||||
			return xerrors.Errorf("HTTP POST error. url: %s, resp: %v, err: %w", url, resp, errs)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -212,13 +212,13 @@ func (api cvedictClient) httpPost(key, url string, query map[string]string) ([]c
 | 
			
		||||
	}
 | 
			
		||||
	err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("HTTP Error %s", err)
 | 
			
		||||
		return nil, xerrors.Errorf("HTTP Error: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cveDetails := []cve.CveDetail{}
 | 
			
		||||
	if err := json.Unmarshal([]byte(body), &cveDetails); err != nil {
 | 
			
		||||
		return nil,
 | 
			
		||||
			fmt.Errorf("Failed to Unmarshall. body: %s, err: %s", body, err)
 | 
			
		||||
			xerrors.Errorf("Failed to Unmarshall. body: %s, err: %w", body, err)
 | 
			
		||||
	}
 | 
			
		||||
	return cveDetails, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
package report
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
@@ -10,6 +9,7 @@ import (
 | 
			
		||||
	cvedb "github.com/kotakanbe/go-cve-dictionary/db"
 | 
			
		||||
	ovaldb "github.com/kotakanbe/goval-dictionary/db"
 | 
			
		||||
	exploitdb "github.com/mozqnet/go-exploitdb/db"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DBClient is a dictionarie's db client for reporting
 | 
			
		||||
@@ -33,7 +33,7 @@ type DBClientConf struct {
 | 
			
		||||
func NewDBClient(cnf DBClientConf) (dbclient *DBClient, locked bool, err error) {
 | 
			
		||||
	cveDriver, locked, err := NewCveDB(cnf)
 | 
			
		||||
	if locked {
 | 
			
		||||
		return nil, true, fmt.Errorf("CveDB is locked: %s",
 | 
			
		||||
		return nil, true, xerrors.Errorf("CveDB is locked: %s",
 | 
			
		||||
			cnf.OvalDictCnf.SQLite3Path)
 | 
			
		||||
	} else if err != nil {
 | 
			
		||||
		return nil, locked, err
 | 
			
		||||
@@ -41,7 +41,7 @@ func NewDBClient(cnf DBClientConf) (dbclient *DBClient, locked bool, err error)
 | 
			
		||||
 | 
			
		||||
	ovaldb, locked, err := NewOvalDB(cnf)
 | 
			
		||||
	if locked {
 | 
			
		||||
		return nil, true, fmt.Errorf("OvalDB is locked: %s",
 | 
			
		||||
		return nil, true, xerrors.Errorf("OvalDB is locked: %s",
 | 
			
		||||
			cnf.OvalDictCnf.SQLite3Path)
 | 
			
		||||
	} else if err != nil {
 | 
			
		||||
		util.Log.Warnf("Unable to use OvalDB: %s, err: %s",
 | 
			
		||||
@@ -50,7 +50,7 @@ func NewDBClient(cnf DBClientConf) (dbclient *DBClient, locked bool, err error)
 | 
			
		||||
 | 
			
		||||
	gostdb, locked, err := NewGostDB(cnf)
 | 
			
		||||
	if locked {
 | 
			
		||||
		return nil, true, fmt.Errorf("gostDB is locked: %s",
 | 
			
		||||
		return nil, true, xerrors.Errorf("gostDB is locked: %s",
 | 
			
		||||
			cnf.GostCnf.SQLite3Path)
 | 
			
		||||
	} else if err != nil {
 | 
			
		||||
		util.Log.Warnf("Unable to use gostDB: %s, err: %s",
 | 
			
		||||
@@ -59,7 +59,7 @@ func NewDBClient(cnf DBClientConf) (dbclient *DBClient, locked bool, err error)
 | 
			
		||||
 | 
			
		||||
	exploitdb, locked, err := NewExploitDB(cnf)
 | 
			
		||||
	if locked {
 | 
			
		||||
		return nil, true, fmt.Errorf("exploitDB is locked: %s",
 | 
			
		||||
		return nil, true, xerrors.Errorf("exploitDB is locked: %s",
 | 
			
		||||
			cnf.ExploitCnf.SQLite3Path)
 | 
			
		||||
	} else if err != nil {
 | 
			
		||||
		util.Log.Warnf("Unable to use exploitDB: %s, err: %s",
 | 
			
		||||
@@ -88,7 +88,7 @@ func NewCveDB(cnf DBClientConf) (driver cvedb.DB, locked bool, err error) {
 | 
			
		||||
	util.Log.Debugf("Open cve-dictionary db (%s): %s", cnf.CveDictCnf.Type, path)
 | 
			
		||||
	driver, locked, err = cvedb.NewDB(cnf.CveDictCnf.Type, path, cnf.DebugSQL)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		err = fmt.Errorf("Failed to init CVE DB. err: %s, path: %s", err, path)
 | 
			
		||||
		err = xerrors.Errorf("Failed to init CVE DB. err: %w, path: %s", err, path)
 | 
			
		||||
		return nil, locked, err
 | 
			
		||||
	}
 | 
			
		||||
	return driver, false, nil
 | 
			
		||||
@@ -112,7 +112,7 @@ func NewOvalDB(cnf DBClientConf) (driver ovaldb.DB, locked bool, err error) {
 | 
			
		||||
	util.Log.Debugf("Open oval-dictionary db (%s): %s", cnf.OvalDictCnf.Type, path)
 | 
			
		||||
	driver, locked, err = ovaldb.NewDB("", cnf.OvalDictCnf.Type, path, cnf.DebugSQL)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		err = fmt.Errorf("Failed to new OVAL DB. err: %s", err)
 | 
			
		||||
		err = xerrors.Errorf("Failed to new OVAL DB. err: %w", err)
 | 
			
		||||
		if locked {
 | 
			
		||||
			return nil, true, err
 | 
			
		||||
		}
 | 
			
		||||
@@ -139,7 +139,7 @@ func NewGostDB(cnf DBClientConf) (driver gostdb.DB, locked bool, err error) {
 | 
			
		||||
	util.Log.Debugf("Open gost db (%s): %s", cnf.GostCnf.Type, path)
 | 
			
		||||
	if driver, locked, err = gostdb.NewDB(cnf.GostCnf.Type, path, cnf.DebugSQL); err != nil {
 | 
			
		||||
		if locked {
 | 
			
		||||
			util.Log.Errorf("gostDB is locked: %s", err)
 | 
			
		||||
			util.Log.Errorf("gostDB is locked. err: %+v", err)
 | 
			
		||||
			return nil, true, err
 | 
			
		||||
		}
 | 
			
		||||
		return nil, false, err
 | 
			
		||||
@@ -165,7 +165,7 @@ func NewExploitDB(cnf DBClientConf) (driver exploitdb.DB, locked bool, err error
 | 
			
		||||
	util.Log.Debugf("Open exploit db (%s): %s", cnf.ExploitCnf.Type, path)
 | 
			
		||||
	if driver, locked, err = exploitdb.NewDB(cnf.ExploitCnf.Type, path, cnf.DebugSQL); err != nil {
 | 
			
		||||
		if locked {
 | 
			
		||||
			util.Log.Errorf("exploitDB is locked: %s", err)
 | 
			
		||||
			util.Log.Errorf("exploitDB is locked. err: %+v", err)
 | 
			
		||||
			return nil, true, err
 | 
			
		||||
		}
 | 
			
		||||
		return nil, false, err
 | 
			
		||||
@@ -177,12 +177,12 @@ func NewExploitDB(cnf DBClientConf) (driver exploitdb.DB, locked bool, err error
 | 
			
		||||
func (d DBClient) CloseDB() {
 | 
			
		||||
	if d.CveDB != nil {
 | 
			
		||||
		if err := d.CveDB.CloseDB(); err != nil {
 | 
			
		||||
			util.Log.Errorf("Failed to close DB: %s", err)
 | 
			
		||||
			util.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if d.OvalDB != nil {
 | 
			
		||||
		if err := d.OvalDB.CloseDB(); err != nil {
 | 
			
		||||
			util.Log.Errorf("Failed to close DB: %s", err)
 | 
			
		||||
			util.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// EMailWriter send mail
 | 
			
		||||
@@ -41,7 +42,6 @@ func (w EMailWriter) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
	for _, r := range rs {
 | 
			
		||||
		if conf.FormatOneEMail {
 | 
			
		||||
			message += formatFullPlainText(r) + "\r\n\r\n"
 | 
			
		||||
 | 
			
		||||
			mm := r.ScannedCves.CountGroupBySeverity()
 | 
			
		||||
			keys := []string{"High", "Medium", "Low", "Unknown"}
 | 
			
		||||
			for _, k := range keys {
 | 
			
		||||
@@ -63,12 +63,14 @@ func (w EMailWriter) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
			} else {
 | 
			
		||||
				message = formatFullPlainText(r)
 | 
			
		||||
			}
 | 
			
		||||
			if conf.FormatOneLineText {
 | 
			
		||||
				message = fmt.Sprintf("One Line Summary\r\n================\r\n%s", formatOneLineSummary(r))
 | 
			
		||||
			}
 | 
			
		||||
			if err := sender.Send(subject, message); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	summary := ""
 | 
			
		||||
	if config.Conf.IgnoreUnscoredCves {
 | 
			
		||||
		summary = fmt.Sprintf("Total: %d (High:%d Medium:%d Low:%d)",
 | 
			
		||||
@@ -77,17 +79,12 @@ func (w EMailWriter) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
	summary = fmt.Sprintf("Total: %d (High:%d Medium:%d Low:%d ?:%d)",
 | 
			
		||||
		m["High"]+m["Medium"]+m["Low"]+m["Unknown"],
 | 
			
		||||
		m["High"], m["Medium"], m["Low"], m["Unknown"])
 | 
			
		||||
 | 
			
		||||
	origmessage := message
 | 
			
		||||
	if conf.FormatOneEMail {
 | 
			
		||||
		message = fmt.Sprintf(
 | 
			
		||||
			`
 | 
			
		||||
One Line Summary
 | 
			
		||||
================
 | 
			
		||||
%s
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
%s`,
 | 
			
		||||
			formatOneLineSummary(rs...), message)
 | 
			
		||||
		message = fmt.Sprintf("One Line Summary\r\n================\r\n%s", formatOneLineSummary(rs...))
 | 
			
		||||
		if !conf.FormatOneLineText {
 | 
			
		||||
			message += fmt.Sprintf("\r\n\r\n%s", origmessage)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		subject := fmt.Sprintf("%s %s",
 | 
			
		||||
			conf.EMail.SubjectPrefix, summary)
 | 
			
		||||
@@ -112,7 +109,7 @@ func (e *emailSender) Send(subject, body string) (err error) {
 | 
			
		||||
	cc := strings.Join(emailConf.Cc[:], ", ")
 | 
			
		||||
	mailAddresses := append(emailConf.To, emailConf.Cc...)
 | 
			
		||||
	if _, err := mail.ParseAddressList(strings.Join(mailAddresses[:], ", ")); err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to parse email addresses: %s", err)
 | 
			
		||||
		return xerrors.Errorf("Failed to parse email addresses: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	headers := make(map[string]string)
 | 
			
		||||
@@ -130,20 +127,34 @@ func (e *emailSender) Send(subject, body string) (err error) {
 | 
			
		||||
	message := fmt.Sprintf("%s\r\n%s", header, body)
 | 
			
		||||
 | 
			
		||||
	smtpServer := net.JoinHostPort(emailConf.SMTPAddr, emailConf.SMTPPort)
 | 
			
		||||
 | 
			
		||||
	if emailConf.User != "" && emailConf.Password != "" {
 | 
			
		||||
		err = e.send(
 | 
			
		||||
			smtpServer,
 | 
			
		||||
			smtp.PlainAuth(
 | 
			
		||||
				"",
 | 
			
		||||
				emailConf.User,
 | 
			
		||||
				emailConf.Password,
 | 
			
		||||
				emailConf.SMTPAddr,
 | 
			
		||||
			),
 | 
			
		||||
			emailConf.From,
 | 
			
		||||
			mailAddresses,
 | 
			
		||||
			[]byte(message),
 | 
			
		||||
		)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to send emails: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	err = e.send(
 | 
			
		||||
		smtpServer,
 | 
			
		||||
		smtp.PlainAuth(
 | 
			
		||||
			"",
 | 
			
		||||
			emailConf.User,
 | 
			
		||||
			emailConf.Password,
 | 
			
		||||
			emailConf.SMTPAddr,
 | 
			
		||||
		),
 | 
			
		||||
		nil,
 | 
			
		||||
		emailConf.From,
 | 
			
		||||
		mailAddresses,
 | 
			
		||||
		[]byte(message),
 | 
			
		||||
	)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to send emails: %s", err)
 | 
			
		||||
		return xerrors.Errorf("Failed to send emails: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -100,7 +100,7 @@ func TestSend(t *testing.T) {
 | 
			
		||||
			t.Errorf("#%d: wrong 'addr' field.\r\nexpected: %s\n got: %s", i, test.out.addr, r.addr)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !reflect.DeepEqual(r.auth, test.out.auth) {
 | 
			
		||||
		if !reflect.DeepEqual(r.auth, test.out.auth) && r.auth != nil {
 | 
			
		||||
			t.Errorf("#%d: wrong 'auth' field.\r\nexpected: %v\n got: %v", i, test.out.auth, r.auth)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -22,10 +22,9 @@ import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
 | 
			
		||||
	c "github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// HTTPRequestWriter writes results to HTTP request
 | 
			
		||||
@@ -53,7 +52,7 @@ type HTTPResponseWriter struct {
 | 
			
		||||
func (w HTTPResponseWriter) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
	res, err := json.Marshal(rs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.Wrap(err, "Failed to marshal scah results")
 | 
			
		||||
		return xerrors.Errorf("Failed to marshal scah results: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	w.Writer.Header().Set("Content-Type", "application/json")
 | 
			
		||||
	_, err = w.Writer.Write(res)
 | 
			
		||||
 
 | 
			
		||||
@@ -21,13 +21,13 @@ import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"encoding/xml"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	c "github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// LocalFileWriter writes results to a local file.
 | 
			
		||||
@@ -40,8 +40,8 @@ func (w LocalFileWriter) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
		path := filepath.Join(w.CurrentDir, "summary.txt")
 | 
			
		||||
		text := formatOneLineSummary(rs...)
 | 
			
		||||
		if err := writeFile(path, []byte(text), 0600); err != nil {
 | 
			
		||||
			return fmt.Errorf(
 | 
			
		||||
				"Failed to write to file. path: %s, err: %s",
 | 
			
		||||
			return xerrors.Errorf(
 | 
			
		||||
				"Failed to write to file. path: %s, err: %w",
 | 
			
		||||
				path, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -60,15 +60,15 @@ func (w LocalFileWriter) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
			var b []byte
 | 
			
		||||
			if c.Conf.Debug {
 | 
			
		||||
				if b, err = json.MarshalIndent(r, "", "    "); err != nil {
 | 
			
		||||
					return fmt.Errorf("Failed to Marshal to JSON: %s", err)
 | 
			
		||||
					return xerrors.Errorf("Failed to Marshal to JSON: %w", err)
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				if b, err = json.Marshal(r); err != nil {
 | 
			
		||||
					return fmt.Errorf("Failed to Marshal to JSON: %s", err)
 | 
			
		||||
					return xerrors.Errorf("Failed to Marshal to JSON: %w", err)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if err := writeFile(p, b, 0600); err != nil {
 | 
			
		||||
				return fmt.Errorf("Failed to write JSON. path: %s, err: %s", p, err)
 | 
			
		||||
				return xerrors.Errorf("Failed to write JSON. path: %s, err: %w", p, err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -82,8 +82,8 @@ func (w LocalFileWriter) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
 | 
			
		||||
			if err := writeFile(
 | 
			
		||||
				p, []byte(formatList(r)), 0600); err != nil {
 | 
			
		||||
				return fmt.Errorf(
 | 
			
		||||
					"Failed to write text files. path: %s, err: %s", p, err)
 | 
			
		||||
				return xerrors.Errorf(
 | 
			
		||||
					"Failed to write text files. path: %s, err: %w", p, err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -97,8 +97,8 @@ func (w LocalFileWriter) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
 | 
			
		||||
			if err := writeFile(
 | 
			
		||||
				p, []byte(formatFullPlainText(r)), 0600); err != nil {
 | 
			
		||||
				return fmt.Errorf(
 | 
			
		||||
					"Failed to write text files. path: %s, err: %s", p, err)
 | 
			
		||||
				return xerrors.Errorf(
 | 
			
		||||
					"Failed to write text files. path: %s, err: %w", p, err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -112,11 +112,11 @@ func (w LocalFileWriter) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
 | 
			
		||||
			var b []byte
 | 
			
		||||
			if b, err = xml.Marshal(r); err != nil {
 | 
			
		||||
				return fmt.Errorf("Failed to Marshal to XML: %s", err)
 | 
			
		||||
				return xerrors.Errorf("Failed to Marshal to XML: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			allBytes := bytes.Join([][]byte{[]byte(xml.Header + vulsOpenTag), b, []byte(vulsCloseTag)}, []byte{})
 | 
			
		||||
			if err := writeFile(p, allBytes, 0600); err != nil {
 | 
			
		||||
				return fmt.Errorf("Failed to write XML. path: %s, err: %s", p, err)
 | 
			
		||||
				return xerrors.Errorf("Failed to write XML. path: %s, err: %w", p, err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										173
									
								
								report/report.go
									
									
									
									
									
								
							
							
						
						
									
										173
									
								
								report/report.go
									
									
									
									
									
								
							@@ -28,20 +28,26 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/libmanager"
 | 
			
		||||
 | 
			
		||||
	"github.com/BurntSushi/toml"
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	c "github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/contrib/owasp-dependency-check/parser"
 | 
			
		||||
	"github.com/future-architect/vuls/cwe"
 | 
			
		||||
	"github.com/future-architect/vuls/exploit"
 | 
			
		||||
	"github.com/future-architect/vuls/github"
 | 
			
		||||
	"github.com/future-architect/vuls/gost"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/oval"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/future-architect/vuls/wordpress"
 | 
			
		||||
	"github.com/hashicorp/uuid"
 | 
			
		||||
	gostdb "github.com/knqyf263/gost/db"
 | 
			
		||||
	cvedb "github.com/kotakanbe/go-cve-dictionary/db"
 | 
			
		||||
	ovaldb "github.com/kotakanbe/goval-dictionary/db"
 | 
			
		||||
	exploitdb "github.com/mozqnet/go-exploitdb/db"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
@@ -56,20 +62,24 @@ func FillCveInfos(dbclient DBClient, rs []models.ScanResult, dir string) ([]mode
 | 
			
		||||
	hostname, _ := os.Hostname()
 | 
			
		||||
	for _, r := range rs {
 | 
			
		||||
		if c.Conf.RefreshCve || needToRefreshCve(r) {
 | 
			
		||||
			r.ScannedCves = models.VulnInfos{}
 | 
			
		||||
			if ovalSupported(&r) {
 | 
			
		||||
				r.ScannedCves = models.VulnInfos{}
 | 
			
		||||
			}
 | 
			
		||||
			cpeURIs := []string{}
 | 
			
		||||
 | 
			
		||||
			if len(r.Container.ContainerID) == 0 {
 | 
			
		||||
				cpeURIs = c.Conf.Servers[r.ServerName].CpeNames
 | 
			
		||||
				owaspDCXMLPath := c.Conf.Servers[r.ServerName].OwaspDCXMLPath
 | 
			
		||||
				if owaspDCXMLPath != "" {
 | 
			
		||||
					cpes, err := parser.Parse(owaspDCXMLPath)
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						return nil, fmt.Errorf("Failed to read OWASP Dependency Check XML: %s, %s, %s",
 | 
			
		||||
						return nil, xerrors.Errorf("Failed to read OWASP Dependency Check XML on %s, `%s`, err: %w",
 | 
			
		||||
							r.ServerName, owaspDCXMLPath, err)
 | 
			
		||||
					}
 | 
			
		||||
					cpeURIs = append(cpeURIs, cpes...)
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				// runningContainer
 | 
			
		||||
				if s, ok := c.Conf.Servers[r.ServerName]; ok {
 | 
			
		||||
					if con, ok := s.Containers[r.Container.Name]; ok {
 | 
			
		||||
						cpeURIs = con.Cpes
 | 
			
		||||
@@ -77,7 +87,7 @@ func FillCveInfos(dbclient DBClient, rs []models.ScanResult, dir string) ([]mode
 | 
			
		||||
						if owaspDCXMLPath != "" {
 | 
			
		||||
							cpes, err := parser.Parse(owaspDCXMLPath)
 | 
			
		||||
							if err != nil {
 | 
			
		||||
								return nil, fmt.Errorf("Failed to read OWASP Dependency Check XML: %s, %s, %s",
 | 
			
		||||
								return nil, xerrors.Errorf("Failed to read OWASP Dependency Check XML on %s, `%s`, err: %w",
 | 
			
		||||
									r.ServerInfo(), owaspDCXMLPath, err)
 | 
			
		||||
							}
 | 
			
		||||
							cpeURIs = append(cpeURIs, cpes...)
 | 
			
		||||
@@ -86,7 +96,16 @@ func FillCveInfos(dbclient DBClient, rs []models.ScanResult, dir string) ([]mode
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if err := FillCveInfo(dbclient, &r, cpeURIs); err != nil {
 | 
			
		||||
			// Integrations
 | 
			
		||||
			githubInts := GithubSecurityAlerts(c.Conf.Servers[r.ServerName].GitHubRepos)
 | 
			
		||||
 | 
			
		||||
			wpOpt := WordPressOption{c.Conf.Servers[r.ServerName].WordPress.WPVulnDBToken}
 | 
			
		||||
 | 
			
		||||
			if err := FillCveInfo(dbclient,
 | 
			
		||||
				&r,
 | 
			
		||||
				cpeURIs,
 | 
			
		||||
				githubInts,
 | 
			
		||||
				wpOpt); err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			r.Lang = c.Conf.Lang
 | 
			
		||||
@@ -99,7 +118,7 @@ func FillCveInfos(dbclient DBClient, rs []models.ScanResult, dir string) ([]mode
 | 
			
		||||
				r.ServerName: c.Conf.Servers[r.ServerName],
 | 
			
		||||
			}
 | 
			
		||||
			if err := overwriteJSONFile(dir, r); err != nil {
 | 
			
		||||
				return nil, fmt.Errorf("Failed to write JSON: %s", err)
 | 
			
		||||
				return nil, xerrors.Errorf("Failed to write JSON: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			filledResults = append(filledResults, r)
 | 
			
		||||
		} else {
 | 
			
		||||
@@ -133,6 +152,7 @@ func FillCveInfos(dbclient DBClient, rs []models.ScanResult, dir string) ([]mode
 | 
			
		||||
		r = r.FilterIgnoreCves()
 | 
			
		||||
		r = r.FilterUnfixed()
 | 
			
		||||
		r = r.FilterIgnorePkgs()
 | 
			
		||||
		r = r.FilterInactiveWordPressLibs()
 | 
			
		||||
		if c.Conf.IgnoreUnscoredCves {
 | 
			
		||||
			r.ScannedCves = r.ScannedCves.FindScoredVulns()
 | 
			
		||||
		}
 | 
			
		||||
@@ -142,12 +162,20 @@ func FillCveInfos(dbclient DBClient, rs []models.ScanResult, dir string) ([]mode
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillCveInfo fill scanResult with cve info.
 | 
			
		||||
func FillCveInfo(dbclient DBClient, r *models.ScanResult, cpeURIs []string) error {
 | 
			
		||||
func FillCveInfo(dbclient DBClient, r *models.ScanResult, cpeURIs []string, integrations ...Integration) error {
 | 
			
		||||
	util.Log.Debugf("need to refresh")
 | 
			
		||||
 | 
			
		||||
	nCVEs, err := FillWithOval(dbclient.OvalDB, r)
 | 
			
		||||
	nCVEs, err := libmanager.FillLibrary(r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to fill with OVAL: %s", err)
 | 
			
		||||
		return xerrors.Errorf("Failed to fill with Library dependency: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	util.Log.Infof("%s: %d CVEs are detected with Library",
 | 
			
		||||
		r.FormatServerName(), nCVEs)
 | 
			
		||||
 | 
			
		||||
	nCVEs, err = FillWithOval(dbclient.OvalDB, r)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to fill with OVAL: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	util.Log.Infof("%s: %d CVEs are detected with OVAL",
 | 
			
		||||
		r.FormatServerName(), nCVEs)
 | 
			
		||||
@@ -163,26 +191,34 @@ func FillCveInfo(dbclient DBClient, r *models.ScanResult, cpeURIs []string) erro
 | 
			
		||||
 | 
			
		||||
	nCVEs, err = fillVulnByCpeURIs(dbclient.CveDB, r, cpeURIs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to detect vulns of %s: %s", cpeURIs, err)
 | 
			
		||||
		return xerrors.Errorf("Failed to detect vulns of `%s`: %w", cpeURIs, err)
 | 
			
		||||
	}
 | 
			
		||||
	util.Log.Infof("%s: %d CVEs are detected with CPE", r.FormatServerName(), nCVEs)
 | 
			
		||||
 | 
			
		||||
	ints := &integrationResults{}
 | 
			
		||||
	for _, o := range integrations {
 | 
			
		||||
		if err = o.apply(r, ints); err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to fill with integration: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	util.Log.Infof("%s: %d CVEs are detected with GitHub Security Alerts", r.FormatServerName(), ints.GithubAlertsCveCounts)
 | 
			
		||||
 | 
			
		||||
	nCVEs, err = FillWithGost(dbclient.GostDB, r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to fill with gost: %s", err)
 | 
			
		||||
		return xerrors.Errorf("Failed to fill with gost: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	util.Log.Infof("%s: %d unfixed CVEs are detected with gost",
 | 
			
		||||
		r.FormatServerName(), nCVEs)
 | 
			
		||||
 | 
			
		||||
	util.Log.Infof("Fill CVE detailed information with CVE-DB")
 | 
			
		||||
	if err := fillCveDetail(dbclient.CveDB, r); err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to fill with CVE: %s", err)
 | 
			
		||||
		return xerrors.Errorf("Failed to fill with CVE: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	util.Log.Infof("Fill exploit information with Exploit-DB")
 | 
			
		||||
	nExploitCve, err := FillWithExploit(dbclient.ExploitDB, r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to fill with exploit: %s", err)
 | 
			
		||||
		return xerrors.Errorf("Failed to fill with exploit: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	util.Log.Infof("%s: %d exploits are detected",
 | 
			
		||||
		r.FormatServerName(), nExploitCve)
 | 
			
		||||
@@ -260,15 +296,18 @@ func FillWithOval(driver ovaldb.DB, r *models.ScanResult) (nCVEs int, err error)
 | 
			
		||||
	case c.Alpine:
 | 
			
		||||
		ovalClient = oval.NewAlpine()
 | 
			
		||||
		ovalFamily = c.Alpine
 | 
			
		||||
	case c.Amazon, c.Raspbian, c.FreeBSD, c.Windows:
 | 
			
		||||
	case c.Amazon:
 | 
			
		||||
		ovalClient = oval.NewAmazon()
 | 
			
		||||
		ovalFamily = c.Amazon
 | 
			
		||||
	case c.Raspbian, c.FreeBSD, c.Windows:
 | 
			
		||||
		return 0, nil
 | 
			
		||||
	case c.ServerTypePseudo:
 | 
			
		||||
		return 0, nil
 | 
			
		||||
	default:
 | 
			
		||||
		if r.Family == "" {
 | 
			
		||||
			return 0, fmt.Errorf("Probably an error occurred during scanning. Check the error message")
 | 
			
		||||
			return 0, xerrors.New("Probably an error occurred during scanning. Check the error message")
 | 
			
		||||
		}
 | 
			
		||||
		return 0, fmt.Errorf("OVAL for %s is not implemented yet", r.Family)
 | 
			
		||||
		return 0, xerrors.Errorf("OVAL for %s is not implemented yet", r.Family)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !c.Conf.OvalDict.IsFetchViaHTTP() {
 | 
			
		||||
@@ -276,7 +315,7 @@ func FillWithOval(driver ovaldb.DB, r *models.ScanResult) (nCVEs int, err error)
 | 
			
		||||
			return 0, nil
 | 
			
		||||
		}
 | 
			
		||||
		if err = driver.NewOvalDB(ovalFamily); err != nil {
 | 
			
		||||
			return 0, fmt.Errorf("Failed to New Oval DB. err: %s", err)
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to New Oval DB. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -286,8 +325,7 @@ func FillWithOval(driver ovaldb.DB, r *models.ScanResult) (nCVEs int, err error)
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	if !ok {
 | 
			
		||||
		util.Log.Warnf("OVAL entries of %s %s are not found. It's recommended to use OVAL to improve scanning accuracy. For details, see https://github.com/kotakanbe/goval-dictionary#usage , Then report with --ovaldb-path or --ovaldb-url flag", ovalFamily, r.Release)
 | 
			
		||||
		return 0, nil
 | 
			
		||||
		return 0, xerrors.Errorf("OVAL entries of %s %s are not found. Fetch OVAL before reporting. For details, see https://github.com/kotakanbe/goval-dictionary#usage", ovalFamily, r.Release)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err = ovalClient.CheckIfOvalFresh(driver, ovalFamily, r.Release)
 | 
			
		||||
@@ -342,6 +380,61 @@ func fillVulnByCpeURIs(driver cvedb.DB, r *models.ScanResult, cpeURIs []string)
 | 
			
		||||
	return nCVEs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type integrationResults struct {
 | 
			
		||||
	GithubAlertsCveCounts int
 | 
			
		||||
	WordPressCveCounts    int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Integration is integration of vuls report
 | 
			
		||||
type Integration interface {
 | 
			
		||||
	apply(*models.ScanResult, *integrationResults) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GithubSecurityAlerts :
 | 
			
		||||
func GithubSecurityAlerts(githubConfs map[string]config.GitHubConf) Integration {
 | 
			
		||||
	return GithubSecurityAlertOption{
 | 
			
		||||
		GithubConfs: githubConfs,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GithubSecurityAlertOption :
 | 
			
		||||
type GithubSecurityAlertOption struct {
 | 
			
		||||
	GithubConfs map[string]config.GitHubConf
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// https://help.github.com/articles/about-security-alerts-for-vulnerable-dependencies/
 | 
			
		||||
func (g GithubSecurityAlertOption) apply(r *models.ScanResult, ints *integrationResults) (err error) {
 | 
			
		||||
	var nCVEs int
 | 
			
		||||
	for ownerRepo, setting := range g.GithubConfs {
 | 
			
		||||
		ss := strings.Split(ownerRepo, "/")
 | 
			
		||||
		owner, repo := ss[0], ss[1]
 | 
			
		||||
		n, err := github.FillGitHubSecurityAlerts(r, owner, repo, setting.Token)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to access GitHub Security Alerts: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		nCVEs += n
 | 
			
		||||
	}
 | 
			
		||||
	ints.GithubAlertsCveCounts = nCVEs
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WordPressOption :
 | 
			
		||||
type WordPressOption struct {
 | 
			
		||||
	token string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g WordPressOption) apply(r *models.ScanResult, ints *integrationResults) (err error) {
 | 
			
		||||
	if g.token == "" {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	n, err := wordpress.FillWordPress(r, g.token)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to fetch from WPVulnDB. Check the WPVulnDBToken in config.toml. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	ints.WordPressCveCounts = n
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fillCweDict(r *models.ScanResult) {
 | 
			
		||||
	uniqCweIDMap := map[string]bool{}
 | 
			
		||||
	for _, vinfo := range r.ScannedCves {
 | 
			
		||||
@@ -355,9 +448,6 @@ func fillCweDict(r *models.ScanResult) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// TODO check the format of CWEID, clean CWEID
 | 
			
		||||
	// JVN, NVD XML, JSON, OVALs
 | 
			
		||||
 | 
			
		||||
	dict := map[string]models.CweDictEntry{}
 | 
			
		||||
	for id := range uniqCweIDMap {
 | 
			
		||||
		entry := models.CweDictEntry{}
 | 
			
		||||
@@ -404,6 +494,20 @@ func fillAlerts(r *models.ScanResult) (enCnt int, jaCnt int) {
 | 
			
		||||
 | 
			
		||||
const reUUID = "[\\da-f]{8}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{12}"
 | 
			
		||||
 | 
			
		||||
// Scanning with the -containers-only, -images-only flag at scan time, the UUID of Container Host may not be generated,
 | 
			
		||||
// so check it. Otherwise create a UUID of the Container Host and set it.
 | 
			
		||||
func getOrCreateServerUUID(r models.ScanResult, server c.ServerInfo) (serverUUID string) {
 | 
			
		||||
	if id, ok := server.UUIDs[r.ServerName]; !ok {
 | 
			
		||||
		serverUUID = uuid.GenerateUUID()
 | 
			
		||||
	} else {
 | 
			
		||||
		matched, err := regexp.MatchString(reUUID, id)
 | 
			
		||||
		if !matched || err != nil {
 | 
			
		||||
			serverUUID = uuid.GenerateUUID()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return serverUUID
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EnsureUUIDs generate a new UUID of the scan target server if UUID is not assigned yet.
 | 
			
		||||
// And then set the generated UUID to config.toml and scan results.
 | 
			
		||||
func EnsureUUIDs(configPath string, results models.ScanResults) error {
 | 
			
		||||
@@ -424,20 +528,13 @@ func EnsureUUIDs(configPath string, results models.ScanResults) error {
 | 
			
		||||
		name := ""
 | 
			
		||||
		if r.IsContainer() {
 | 
			
		||||
			name = fmt.Sprintf("%s@%s", r.Container.Name, r.ServerName)
 | 
			
		||||
 | 
			
		||||
			// Scanning with the -containers-only flag at scan time, the UUID of Container Host may not be generated,
 | 
			
		||||
			// so check it. Otherwise create a UUID of the Container Host and set it.
 | 
			
		||||
			serverUUID := ""
 | 
			
		||||
			if id, ok := server.UUIDs[r.ServerName]; !ok {
 | 
			
		||||
				serverUUID = uuid.GenerateUUID()
 | 
			
		||||
			} else {
 | 
			
		||||
				matched, err := regexp.MatchString(reUUID, id)
 | 
			
		||||
				if !matched || err != nil {
 | 
			
		||||
					serverUUID = uuid.GenerateUUID()
 | 
			
		||||
				}
 | 
			
		||||
			if uuid := getOrCreateServerUUID(r, server); uuid != "" {
 | 
			
		||||
				server.UUIDs[r.ServerName] = uuid
 | 
			
		||||
			}
 | 
			
		||||
			if serverUUID != "" {
 | 
			
		||||
				server.UUIDs[r.ServerName] = serverUUID
 | 
			
		||||
		} else if r.IsImage() {
 | 
			
		||||
			name = fmt.Sprintf("%s:%s@%s", r.Image.Name, r.Image.Tag, r.ServerName)
 | 
			
		||||
			if uuid := getOrCreateServerUUID(r, server); uuid != "" {
 | 
			
		||||
				server.UUIDs[r.ServerName] = uuid
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			name = r.ServerName
 | 
			
		||||
@@ -573,21 +670,21 @@ func EnsureUUIDs(configPath string, results models.ScanResults) error {
 | 
			
		||||
	// rename the current config.toml to config.toml.bak
 | 
			
		||||
	info, err := os.Lstat(configPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to lstat %s: %s", configPath, err)
 | 
			
		||||
		return xerrors.Errorf("Failed to lstat %s: %w", configPath, err)
 | 
			
		||||
	}
 | 
			
		||||
	realPath := configPath
 | 
			
		||||
	if info.Mode()&os.ModeSymlink == os.ModeSymlink {
 | 
			
		||||
		if realPath, err = os.Readlink(configPath); err != nil {
 | 
			
		||||
			return fmt.Errorf("Failed to Read link %s: %s", configPath, err)
 | 
			
		||||
			return xerrors.Errorf("Failed to Read link %s: %w", configPath, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if err := os.Rename(realPath, realPath+".bak"); err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to rename %s: %s", configPath, err)
 | 
			
		||||
		return xerrors.Errorf("Failed to rename %s: %w", configPath, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	if err := toml.NewEncoder(&buf).Encode(c); err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to encode to toml: %s", err)
 | 
			
		||||
		return xerrors.Errorf("Failed to encode to toml: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	str := strings.Replace(buf.String(), "\n  [", "\n\n  [", -1)
 | 
			
		||||
	str = fmt.Sprintf("%s\n\n%s",
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +1,51 @@
 | 
			
		||||
package report
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const defaultUUID = "11111111-1111-1111-1111-111111111111"
 | 
			
		||||
 | 
			
		||||
func TestGetOrCreateServerUUID(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
	cases := map[string]struct {
 | 
			
		||||
		scanResult models.ScanResult
 | 
			
		||||
		server     config.ServerInfo
 | 
			
		||||
		isDefault  bool
 | 
			
		||||
	}{
 | 
			
		||||
		"baseServer": {
 | 
			
		||||
			scanResult: models.ScanResult{
 | 
			
		||||
				ServerName: "hoge",
 | 
			
		||||
			},
 | 
			
		||||
			server: config.ServerInfo{
 | 
			
		||||
				UUIDs: map[string]string{
 | 
			
		||||
					"hoge": defaultUUID,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			isDefault: false,
 | 
			
		||||
		},
 | 
			
		||||
		"onlyContainers": {
 | 
			
		||||
			scanResult: models.ScanResult{
 | 
			
		||||
				ServerName: "hoge",
 | 
			
		||||
			},
 | 
			
		||||
			server: config.ServerInfo{
 | 
			
		||||
				UUIDs: map[string]string{
 | 
			
		||||
					"fuga": defaultUUID,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			isDefault: false,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for testcase, v := range cases {
 | 
			
		||||
		uuid := getOrCreateServerUUID(v.scanResult, v.server)
 | 
			
		||||
		if (uuid == defaultUUID) != v.isDefault {
 | 
			
		||||
			t.Errorf("%s : expected isDefault %t got %s", testcase, v.isDefault, uuid)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								report/s3.go
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								report/s3.go
									
									
									
									
									
								
							@@ -31,6 +31,7 @@ import (
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws/ec2metadata"
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws/session"
 | 
			
		||||
	"github.com/aws/aws-sdk-go/service/s3"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	c "github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
@@ -75,7 +76,7 @@ func (w S3Writer) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
			k := key + ".json"
 | 
			
		||||
			var b []byte
 | 
			
		||||
			if b, err = json.Marshal(r); err != nil {
 | 
			
		||||
				return fmt.Errorf("Failed to Marshal to JSON: %s", err)
 | 
			
		||||
				return xerrors.Errorf("Failed to Marshal to JSON: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			if err := putObject(svc, k, b); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
@@ -102,7 +103,7 @@ func (w S3Writer) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
			k := key + ".xml"
 | 
			
		||||
			var b []byte
 | 
			
		||||
			if b, err = xml.Marshal(r); err != nil {
 | 
			
		||||
				return fmt.Errorf("Failed to Marshal to XML: %s", err)
 | 
			
		||||
				return xerrors.Errorf("Failed to Marshal to XML: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			allBytes := bytes.Join([][]byte{[]byte(xml.Header + vulsOpenTag), b, []byte(vulsCloseTag)}, []byte{})
 | 
			
		||||
			if err := putObject(svc, k, allBytes); err != nil {
 | 
			
		||||
@@ -118,8 +119,8 @@ func CheckIfBucketExists() error {
 | 
			
		||||
	svc := getS3()
 | 
			
		||||
	result, err := svc.ListBuckets(&s3.ListBucketsInput{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf(
 | 
			
		||||
			"Failed to list buckets. err: %s, profile: %s, region: %s",
 | 
			
		||||
		return xerrors.Errorf(
 | 
			
		||||
			"Failed to list buckets. err: %w, profile: %s, region: %s",
 | 
			
		||||
			err, c.Conf.AWS.Profile, c.Conf.AWS.Region)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -131,7 +132,7 @@ func CheckIfBucketExists() error {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if !found {
 | 
			
		||||
		return fmt.Errorf(
 | 
			
		||||
		return xerrors.Errorf(
 | 
			
		||||
			"Failed to find the buckets. profile: %s, region: %s, bucket: %s",
 | 
			
		||||
			c.Conf.AWS.Profile, c.Conf.AWS.Region, c.Conf.AWS.S3Bucket)
 | 
			
		||||
	}
 | 
			
		||||
@@ -158,7 +159,7 @@ func putObject(svc *s3.S3, k string, b []byte) error {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, err := svc.PutObject(putObjectInput); err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to upload data to %s/%s, %s",
 | 
			
		||||
		return xerrors.Errorf("Failed to upload data to %s/%s, err: %w",
 | 
			
		||||
			c.Conf.AWS.S3Bucket, k, err)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,9 @@ import (
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws"
 | 
			
		||||
@@ -35,6 +37,7 @@ import (
 | 
			
		||||
	c "github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SaasWriter writes results to SaaS
 | 
			
		||||
@@ -48,8 +51,11 @@ type TempCredential struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type payload struct {
 | 
			
		||||
	GroupID int    `json:"GroupID"`
 | 
			
		||||
	Token   string `json:"Token"`
 | 
			
		||||
	GroupID      int    `json:"GroupID"`
 | 
			
		||||
	Token        string `json:"Token"`
 | 
			
		||||
	ScannedBy    string `json:"ScannedBy"`
 | 
			
		||||
	ScannedIPv4s string `json:"ScannedIPv4s"`
 | 
			
		||||
	ScannedIPv6s string `json:"ScannedIPv6s"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UploadSaas : UploadSaas
 | 
			
		||||
@@ -59,14 +65,23 @@ func (w SaasWriter) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ipv4s, ipv6s, err := util.IP()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		util.Log.Errorf("Failed to fetch scannedIPs. err: %+v", err)
 | 
			
		||||
	}
 | 
			
		||||
	hostname, _ := os.Hostname()
 | 
			
		||||
 | 
			
		||||
	payload := payload{
 | 
			
		||||
		GroupID: c.Conf.Saas.GroupID,
 | 
			
		||||
		Token:   c.Conf.Saas.Token,
 | 
			
		||||
		GroupID:      c.Conf.Saas.GroupID,
 | 
			
		||||
		Token:        c.Conf.Saas.Token,
 | 
			
		||||
		ScannedBy:    hostname,
 | 
			
		||||
		ScannedIPv4s: strings.Join(ipv4s, ", "),
 | 
			
		||||
		ScannedIPv6s: strings.Join(ipv6s, ", "),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var body []byte
 | 
			
		||||
	if body, err = json.Marshal(payload); err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to Marshal to JSON: %s", err)
 | 
			
		||||
		return xerrors.Errorf("Failed to Marshal to JSON: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var req *http.Request
 | 
			
		||||
@@ -95,7 +110,7 @@ func (w SaasWriter) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
	}
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
	if resp.StatusCode != 200 {
 | 
			
		||||
		return fmt.Errorf("Failed to get Credential. Request JSON : %s,", string(body))
 | 
			
		||||
		return xerrors.Errorf("Failed to get Credential. Request JSON : %s,", string(body))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var t []byte
 | 
			
		||||
@@ -105,7 +120,7 @@ func (w SaasWriter) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
 | 
			
		||||
	var tempCredential TempCredential
 | 
			
		||||
	if err = json.Unmarshal(t, &tempCredential); err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to unmarshal saas credential file. err : %s", err)
 | 
			
		||||
		return xerrors.Errorf("Failed to unmarshal saas credential file. err : %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	credential := credentials.NewStaticCredentialsFromCreds(credentials.Value{
 | 
			
		||||
@@ -119,7 +134,7 @@ func (w SaasWriter) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
		Credentials: credential,
 | 
			
		||||
		Region:      aws.String("ap-northeast-1"),
 | 
			
		||||
	}); err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to new aws session. err : %s", err)
 | 
			
		||||
		return xerrors.Errorf("Failed to new aws session. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	svc := s3.New(sess)
 | 
			
		||||
@@ -127,7 +142,7 @@ func (w SaasWriter) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
		s3Key := renameKeyNameUTC(r.ScannedAt, r.ServerUUID, r.Container)
 | 
			
		||||
		var b []byte
 | 
			
		||||
		if b, err = json.Marshal(r); err != nil {
 | 
			
		||||
			return fmt.Errorf("Failed to Marshal to JSON: %s", err)
 | 
			
		||||
			return xerrors.Errorf("Failed to Marshal to JSON: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		util.Log.Infof("Uploading...: ServerName: %s, ", r.ServerName)
 | 
			
		||||
		putObjectInput := &s3.PutObjectInput{
 | 
			
		||||
@@ -137,7 +152,7 @@ func (w SaasWriter) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if _, err := svc.PutObject(putObjectInput); err != nil {
 | 
			
		||||
			return fmt.Errorf("Failed to upload data to %s/%s, %s",
 | 
			
		||||
			return xerrors.Errorf("Failed to upload data to %s/%s, err: %w",
 | 
			
		||||
				tempCredential.S3Bucket, s3Key, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										135
									
								
								report/slack.go
									
									
									
									
									
								
							
							
						
						
									
										135
									
								
								report/slack.go
									
									
									
									
									
								
							@@ -30,6 +30,7 @@ import (
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type field struct {
 | 
			
		||||
@@ -59,19 +60,6 @@ func (w SlackWriter) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
			channel = fmt.Sprintf("#%s", r.ServerName)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if 0 < len(r.Errors) {
 | 
			
		||||
			msg := message{
 | 
			
		||||
				Text:      msgText(r),
 | 
			
		||||
				Username:  conf.AuthUser,
 | 
			
		||||
				IconEmoji: conf.IconEmoji,
 | 
			
		||||
				Channel:   channel,
 | 
			
		||||
			}
 | 
			
		||||
			if err = send(msg); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// A maximum of 100 attachments are allowed on a message.
 | 
			
		||||
		// Split into chunks with 100 elements
 | 
			
		||||
		// https://api.slack.com/methods/chat.postMessage
 | 
			
		||||
@@ -86,38 +74,60 @@ func (w SlackWriter) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
		}
 | 
			
		||||
		sort.Ints(chunkKeys)
 | 
			
		||||
 | 
			
		||||
		summary := fmt.Sprintf("%s\n%s",
 | 
			
		||||
			getNotifyUsers(config.Conf.Slack.NotifyUsers),
 | 
			
		||||
			formatOneLineSummary(r))
 | 
			
		||||
 | 
			
		||||
		// Send slack by API
 | 
			
		||||
		if 0 < len(token) {
 | 
			
		||||
			api := slack.New(token)
 | 
			
		||||
			ParentMsg := slack.PostMessageParameters{
 | 
			
		||||
				//			Text:      msgText(r),
 | 
			
		||||
			msgPrms := slack.PostMessageParameters{
 | 
			
		||||
				Username:  conf.AuthUser,
 | 
			
		||||
				IconEmoji: conf.IconEmoji,
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var ts string
 | 
			
		||||
			if _, ts, err = api.PostMessage(channel, msgText(r), ParentMsg); err != nil {
 | 
			
		||||
			if _, ts, err = api.PostMessage(channel,
 | 
			
		||||
				summary, msgPrms); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if config.Conf.FormatOneLineText || 0 < len(r.Errors) {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, k := range chunkKeys {
 | 
			
		||||
				params := slack.PostMessageParameters{
 | 
			
		||||
					//		Text:            msgText(r),
 | 
			
		||||
					Username:        conf.AuthUser,
 | 
			
		||||
					IconEmoji:       conf.IconEmoji,
 | 
			
		||||
					Attachments:     m[k],
 | 
			
		||||
					ThreadTimestamp: ts,
 | 
			
		||||
				}
 | 
			
		||||
				if _, _, err = api.PostMessage(channel, msgText(r), params); err != nil {
 | 
			
		||||
				if _, _, err = api.PostMessage(channel, "", params); err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			for i, k := range chunkKeys {
 | 
			
		||||
				txt := ""
 | 
			
		||||
				if i == 0 {
 | 
			
		||||
					txt = msgText(r)
 | 
			
		||||
				}
 | 
			
		||||
			msg := message{
 | 
			
		||||
				Text:      summary,
 | 
			
		||||
				Username:  conf.AuthUser,
 | 
			
		||||
				IconEmoji: conf.IconEmoji,
 | 
			
		||||
				Channel:   channel,
 | 
			
		||||
			}
 | 
			
		||||
			if err := send(msg); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if config.Conf.FormatOneLineText || 0 < len(r.Errors) {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, k := range chunkKeys {
 | 
			
		||||
				txt := fmt.Sprintf("%d/%d for %s",
 | 
			
		||||
					k+1,
 | 
			
		||||
					len(chunkKeys),
 | 
			
		||||
					r.FormatServerName())
 | 
			
		||||
 | 
			
		||||
				msg := message{
 | 
			
		||||
					Text:        txt,
 | 
			
		||||
					Username:    conf.AuthUser,
 | 
			
		||||
@@ -148,9 +158,9 @@ func send(msg message) error {
 | 
			
		||||
			if count == retryMax {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			return fmt.Errorf(
 | 
			
		||||
				"HTTP POST error: %v, url: %s, resp: %v, body: %s",
 | 
			
		||||
				errs, conf.HookURL, resp, body)
 | 
			
		||||
			return xerrors.Errorf(
 | 
			
		||||
				"HTTP POST error. url: %s, resp: %v, body: %s, err: %w",
 | 
			
		||||
				conf.HookURL, resp, body, errs)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -160,68 +170,55 @@ func send(msg message) error {
 | 
			
		||||
	}
 | 
			
		||||
	boff := backoff.NewExponentialBackOff()
 | 
			
		||||
	if err := backoff.RetryNotify(f, boff, notify); err != nil {
 | 
			
		||||
		return fmt.Errorf("HTTP error: %s", err)
 | 
			
		||||
		return xerrors.Errorf("HTTP error: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	if count == retryMax {
 | 
			
		||||
		return fmt.Errorf("Retry count exceeded")
 | 
			
		||||
		return xerrors.New("Retry count exceeded")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func msgText(r models.ScanResult) string {
 | 
			
		||||
	notifyUsers := ""
 | 
			
		||||
	if 0 < len(r.ScannedCves) {
 | 
			
		||||
		notifyUsers = getNotifyUsers(config.Conf.Slack.NotifyUsers)
 | 
			
		||||
	}
 | 
			
		||||
	serverInfo := fmt.Sprintf("*%s*", r.ServerInfo())
 | 
			
		||||
 | 
			
		||||
	if 0 < len(r.Errors) {
 | 
			
		||||
		return fmt.Sprintf("%s\n%s\n%s\n%s\n%s\nError: %s",
 | 
			
		||||
			notifyUsers,
 | 
			
		||||
			serverInfo,
 | 
			
		||||
			r.ScannedCves.FormatCveSummary(),
 | 
			
		||||
			r.ScannedCves.FormatFixedStatus(r.Packages),
 | 
			
		||||
			r.FormatUpdatablePacksSummary(),
 | 
			
		||||
			r.Errors)
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%s\n%s\n%s\n%s\n%s",
 | 
			
		||||
		notifyUsers,
 | 
			
		||||
		serverInfo,
 | 
			
		||||
		r.ScannedCves.FormatCveSummary(),
 | 
			
		||||
		r.ScannedCves.FormatFixedStatus(r.Packages),
 | 
			
		||||
		r.FormatUpdatablePacksSummary())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func toSlackAttachments(r models.ScanResult) (attaches []slack.Attachment) {
 | 
			
		||||
	vinfos := r.ScannedCves.ToSortedSlice()
 | 
			
		||||
	for _, vinfo := range vinfos {
 | 
			
		||||
		curent := []string{}
 | 
			
		||||
 | 
			
		||||
		installed, candidate := []string{}, []string{}
 | 
			
		||||
		for _, affected := range vinfo.AffectedPackages {
 | 
			
		||||
			if p, ok := r.Packages[affected.Name]; ok {
 | 
			
		||||
				curent = append(curent,
 | 
			
		||||
				installed = append(installed,
 | 
			
		||||
					fmt.Sprintf("%s-%s", p.Name, p.FormatVer()))
 | 
			
		||||
			} else {
 | 
			
		||||
				curent = append(curent, affected.Name)
 | 
			
		||||
				installed = append(installed, affected.Name)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		for _, n := range vinfo.CpeURIs {
 | 
			
		||||
			curent = append(curent, n)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		new := []string{}
 | 
			
		||||
		for _, affected := range vinfo.AffectedPackages {
 | 
			
		||||
			if p, ok := r.Packages[affected.Name]; ok {
 | 
			
		||||
				if affected.NotFixedYet {
 | 
			
		||||
					new = append(new, "Not Fixed Yet")
 | 
			
		||||
					candidate = append(candidate, "Not Fixed Yet")
 | 
			
		||||
				} else {
 | 
			
		||||
					new = append(new, p.FormatNewVer())
 | 
			
		||||
					candidate = append(candidate, p.FormatNewVer())
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				new = append(new, "?")
 | 
			
		||||
				candidate = append(candidate, "?")
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		for range vinfo.CpeURIs {
 | 
			
		||||
			new = append(new, "?")
 | 
			
		||||
 | 
			
		||||
		for _, n := range vinfo.CpeURIs {
 | 
			
		||||
			installed = append(installed, n)
 | 
			
		||||
			candidate = append(candidate, "?")
 | 
			
		||||
		}
 | 
			
		||||
		for _, n := range vinfo.GitHubSecurityAlerts {
 | 
			
		||||
			installed = append(installed, n.PackageName)
 | 
			
		||||
			candidate = append(candidate, "?")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, wp := range vinfo.WpPackageFixStats {
 | 
			
		||||
			if p, ok := r.WordPressPackages.Find(wp.Name); ok {
 | 
			
		||||
				installed = append(installed, fmt.Sprintf("%s-%s", wp.Name, p.Version))
 | 
			
		||||
				candidate = append(candidate, wp.FixedIn)
 | 
			
		||||
			} else {
 | 
			
		||||
				installed = append(installed, wp.Name)
 | 
			
		||||
				candidate = append(candidate, "?")
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		a := slack.Attachment{
 | 
			
		||||
@@ -233,12 +230,12 @@ func toSlackAttachments(r models.ScanResult) (attaches []slack.Attachment) {
 | 
			
		||||
				{
 | 
			
		||||
					// Title: "Current Package/CPE",
 | 
			
		||||
					Title: "Installed",
 | 
			
		||||
					Value: strings.Join(curent, "\n"),
 | 
			
		||||
					Value: strings.Join(installed, "\n"),
 | 
			
		||||
					Short: true,
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Title: "Candidate",
 | 
			
		||||
					Value: strings.Join(new, "\n"),
 | 
			
		||||
					Value: strings.Join(candidate, "\n"),
 | 
			
		||||
					Short: true,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
@@ -256,7 +253,7 @@ func cvssColor(cvssScore float64) string {
 | 
			
		||||
		return "danger"
 | 
			
		||||
	case 4 <= cvssScore && cvssScore < 7:
 | 
			
		||||
		return "warning"
 | 
			
		||||
	case cvssScore < 0:
 | 
			
		||||
	case cvssScore == 0:
 | 
			
		||||
		return "#C0C0C0"
 | 
			
		||||
	default:
 | 
			
		||||
		return "good"
 | 
			
		||||
 
 | 
			
		||||
@@ -22,8 +22,7 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	syslog "github.com/RackSec/srslog"
 | 
			
		||||
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
@@ -40,7 +39,7 @@ func (w SyslogWriter) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
 | 
			
		||||
	sysLog, err := syslog.Dial(conf.Protocol, raddr, severity|facility, conf.Tag)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.Wrap(err, "Failed to initialize syslog client")
 | 
			
		||||
		return xerrors.Errorf("Failed to initialize syslog client: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, r := range rs {
 | 
			
		||||
 
 | 
			
		||||
@@ -22,15 +22,15 @@ func TestSyslogWriterEncodeSyslog(t *testing.T) {
 | 
			
		||||
				IPv4Addrs:  []string{"192.168.0.1", "10.0.2.15"},
 | 
			
		||||
				ScannedCves: models.VulnInfos{
 | 
			
		||||
					"CVE-2017-0001": models.VulnInfo{
 | 
			
		||||
						AffectedPackages: models.PackageStatuses{
 | 
			
		||||
							models.PackageStatus{Name: "pkg1"},
 | 
			
		||||
							models.PackageStatus{Name: "pkg2"},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							models.PackageFixStatus{Name: "pkg1"},
 | 
			
		||||
							models.PackageFixStatus{Name: "pkg2"},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"CVE-2017-0002": models.VulnInfo{
 | 
			
		||||
						AffectedPackages: models.PackageStatuses{
 | 
			
		||||
							models.PackageStatus{Name: "pkg3"},
 | 
			
		||||
							models.PackageStatus{Name: "pkg4"},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							models.PackageFixStatus{Name: "pkg3"},
 | 
			
		||||
							models.PackageFixStatus{Name: "pkg4"},
 | 
			
		||||
						},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							models.NvdXML: models.CveContent{
 | 
			
		||||
@@ -57,8 +57,8 @@ func TestSyslogWriterEncodeSyslog(t *testing.T) {
 | 
			
		||||
				IPv6Addrs:  []string{"2001:0DB8::1"},
 | 
			
		||||
				ScannedCves: models.VulnInfos{
 | 
			
		||||
					"CVE-2017-0003": models.VulnInfo{
 | 
			
		||||
						AffectedPackages: models.PackageStatuses{
 | 
			
		||||
							models.PackageStatus{Name: "pkg5"},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							models.PackageFixStatus{Name: "pkg5"},
 | 
			
		||||
						},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							models.RedHat: models.CveContent{
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// TelegramWriter sends report to Telegram
 | 
			
		||||
@@ -73,5 +74,5 @@ func checkResponse(r *http.Response) error {
 | 
			
		||||
	if c := r.StatusCode; 200 <= c && c <= 299 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Errorf("API call to %s failed: %s", r.Request.URL.String(), r.Status)
 | 
			
		||||
	return xerrors.Errorf("API call to %s failed: %s", r.Request.URL.String(), r.Status)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,7 @@ import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/alert"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
@@ -55,14 +56,14 @@ func RunTui(results models.ScanResults) subcommands.ExitStatus {
 | 
			
		||||
 | 
			
		||||
	g, err := gocui.NewGui(gocui.OutputNormal)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		util.Log.Errorf("%s", err)
 | 
			
		||||
		util.Log.Errorf("%+v", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
	defer g.Close()
 | 
			
		||||
 | 
			
		||||
	g.SetManagerFunc(layout)
 | 
			
		||||
	if err := keybindings(g); err != nil {
 | 
			
		||||
		util.Log.Errorf("%s", err)
 | 
			
		||||
		util.Log.Errorf("%+v", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
	g.SelBgColor = gocui.ColorGreen
 | 
			
		||||
@@ -71,7 +72,7 @@ func RunTui(results models.ScanResults) subcommands.ExitStatus {
 | 
			
		||||
 | 
			
		||||
	if err := g.MainLoop(); err != nil {
 | 
			
		||||
		g.Close()
 | 
			
		||||
		util.Log.Errorf("%s", err)
 | 
			
		||||
		util.Log.Errorf("%+v", err)
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
	return subcommands.ExitSuccess
 | 
			
		||||
@@ -581,7 +582,7 @@ func setSideLayout(g *gocui.Gui) error {
 | 
			
		||||
			fmt.Fprintln(v, result.ServerInfoTui())
 | 
			
		||||
		}
 | 
			
		||||
		if len(scanResults) == 0 {
 | 
			
		||||
			return fmt.Errorf("No scan results")
 | 
			
		||||
			return xerrors.New("No scan results")
 | 
			
		||||
		}
 | 
			
		||||
		currentScanResult = scanResults[0]
 | 
			
		||||
		vinfos = scanResults[0].ScannedCves.ToSortedSlice()
 | 
			
		||||
@@ -634,8 +635,10 @@ func summaryLines(r models.ScanResult) string {
 | 
			
		||||
			cvssScore = fmt.Sprintf("| %4.1f", max)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		packname := vinfo.AffectedPackages.FormatTuiSummary()
 | 
			
		||||
		packname += strings.Join(vinfo.CpeURIs, ", ")
 | 
			
		||||
		pkgNames := vinfo.AffectedPackages.Names()
 | 
			
		||||
		pkgNames = append(pkgNames, vinfo.CpeURIs...)
 | 
			
		||||
		pkgNames = append(pkgNames, vinfo.GitHubSecurityAlerts.Names()...)
 | 
			
		||||
		pkgNames = append(pkgNames, vinfo.WpPackageFixStats.Names()...)
 | 
			
		||||
 | 
			
		||||
		alert := "  "
 | 
			
		||||
		if vinfo.AlertDict.HasAlert() {
 | 
			
		||||
@@ -647,9 +650,9 @@ func summaryLines(r models.ScanResult) string {
 | 
			
		||||
			fmt.Sprintf(indexFormat, i+1),
 | 
			
		||||
			alert + vinfo.CveID,
 | 
			
		||||
			cvssScore + " |",
 | 
			
		||||
			fmt.Sprintf("%8s |", vinfo.AttackVector()),
 | 
			
		||||
			fmt.Sprintf("%1s |", vinfo.AttackVector()),
 | 
			
		||||
			fmt.Sprintf("%7s |", vinfo.PatchStatus(r.Packages)),
 | 
			
		||||
			packname,
 | 
			
		||||
			strings.Join(pkgNames, ", "),
 | 
			
		||||
		}
 | 
			
		||||
		icols := make([]interface{}, len(cols))
 | 
			
		||||
		for j := range cols {
 | 
			
		||||
@@ -742,6 +745,43 @@ func setChangelogLayout(g *gocui.Gui) error {
 | 
			
		||||
			lines = append(lines, "* "+uri)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, alert := range vinfo.GitHubSecurityAlerts {
 | 
			
		||||
			lines = append(lines, "* "+alert.PackageName)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		r := currentScanResult
 | 
			
		||||
		// check wordpress fixedin
 | 
			
		||||
		if r.WordPressPackages != nil {
 | 
			
		||||
			for _, wp := range vinfo.WpPackageFixStats {
 | 
			
		||||
				if p, ok := r.WordPressPackages.Find(wp.Name); ok {
 | 
			
		||||
					if p.Type == models.WPCore {
 | 
			
		||||
						lines = append(lines, fmt.Sprintf("* %s-%s, FixedIn: %s",
 | 
			
		||||
							wp.Name, p.Version, wp.FixedIn))
 | 
			
		||||
					} else {
 | 
			
		||||
						lines = append(lines,
 | 
			
		||||
							fmt.Sprintf("* %s-%s, Update: %s, FixedIn: %s, %s",
 | 
			
		||||
								wp.Name, p.Version, p.Update, wp.FixedIn, p.Status))
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					lines = append(lines, fmt.Sprintf("* %s", wp.Name))
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// check library fixedin
 | 
			
		||||
		for _, scanner := range r.LibraryScanners {
 | 
			
		||||
			key := scanner.GetLibraryKey()
 | 
			
		||||
			for _, fixedin := range vinfo.LibraryFixedIns {
 | 
			
		||||
				for _, lib := range scanner.Libs {
 | 
			
		||||
					if fixedin.Key == key && lib.Name == fixedin.Name {
 | 
			
		||||
						lines = append(lines, fmt.Sprintf("* %s-%s, FixedIn: %s",
 | 
			
		||||
							lib.Name, lib.Version, fixedin.FixedIn))
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, adv := range vinfo.DistroAdvisories {
 | 
			
		||||
			lines = append(lines, "\n",
 | 
			
		||||
				"Advisories",
 | 
			
		||||
@@ -841,10 +881,13 @@ func detailLines() (string, error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vinfo := vinfos[currentVinfo]
 | 
			
		||||
	links := []string{vinfo.CveContents.SourceLinks(
 | 
			
		||||
		config.Conf.Lang, r.Family, vinfo.CveID)[0].Value,
 | 
			
		||||
		vinfo.Cvss2CalcURL(),
 | 
			
		||||
		vinfo.Cvss3CalcURL()}
 | 
			
		||||
	links := []string{}
 | 
			
		||||
	if strings.HasPrefix(vinfo.CveID, "CVE-") {
 | 
			
		||||
		links = append(links, vinfo.CveContents.SourceLinks(
 | 
			
		||||
			config.Conf.Lang, r.Family, vinfo.CveID)[0].Value,
 | 
			
		||||
			vinfo.Cvss2CalcURL(),
 | 
			
		||||
			vinfo.Cvss3CalcURL())
 | 
			
		||||
	}
 | 
			
		||||
	for _, url := range vinfo.VendorLinks(r.Family) {
 | 
			
		||||
		links = append(links, url)
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										137
									
								
								report/util.go
									
									
									
									
									
								
							
							
						
						
									
										137
									
								
								report/util.go
									
									
									
									
									
								
							@@ -35,6 +35,7 @@ import (
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/gosuri/uitable"
 | 
			
		||||
	"github.com/olekukonko/tablewriter"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const maxColWidth = 100
 | 
			
		||||
@@ -43,6 +44,8 @@ func formatScanSummary(rs ...models.ScanResult) string {
 | 
			
		||||
	table := uitable.New()
 | 
			
		||||
	table.MaxColWidth = maxColWidth
 | 
			
		||||
	table.Wrap = true
 | 
			
		||||
 | 
			
		||||
	warnMsgs := []string{}
 | 
			
		||||
	for _, r := range rs {
 | 
			
		||||
		var cols []interface{}
 | 
			
		||||
		if len(r.Errors) == 0 {
 | 
			
		||||
@@ -56,18 +59,26 @@ func formatScanSummary(rs ...models.ScanResult) string {
 | 
			
		||||
				r.FormatServerName(),
 | 
			
		||||
				"Error",
 | 
			
		||||
				"",
 | 
			
		||||
				"Run with --debug to view the details",
 | 
			
		||||
				"Use configtest subcommand or scan with --debug to view the details",
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		table.AddRow(cols...)
 | 
			
		||||
 | 
			
		||||
		if len(r.Warnings) != 0 {
 | 
			
		||||
			warnMsgs = append(warnMsgs, fmt.Sprintf("Warning for %s: %s",
 | 
			
		||||
				r.FormatServerName(), r.Warnings))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%s\n", table)
 | 
			
		||||
	return fmt.Sprintf("%s\n\n%s", table, strings.Join(
 | 
			
		||||
		warnMsgs, "\n\n"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func formatOneLineSummary(rs ...models.ScanResult) string {
 | 
			
		||||
	table := uitable.New()
 | 
			
		||||
	table.MaxColWidth = maxColWidth
 | 
			
		||||
	table.Wrap = true
 | 
			
		||||
 | 
			
		||||
	warnMsgs := []string{}
 | 
			
		||||
	for _, r := range rs {
 | 
			
		||||
		var cols []interface{}
 | 
			
		||||
		if len(r.Errors) == 0 {
 | 
			
		||||
@@ -82,29 +93,40 @@ func formatOneLineSummary(rs ...models.ScanResult) string {
 | 
			
		||||
		} else {
 | 
			
		||||
			cols = []interface{}{
 | 
			
		||||
				r.FormatServerName(),
 | 
			
		||||
				"Error: Scan with --debug to view the details",
 | 
			
		||||
				"Use configtest subcommand or scan with --debug to view the details",
 | 
			
		||||
				"",
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		table.AddRow(cols...)
 | 
			
		||||
 | 
			
		||||
		if len(r.Warnings) != 0 {
 | 
			
		||||
			warnMsgs = append(warnMsgs, fmt.Sprintf("Warning for %s: %s",
 | 
			
		||||
				r.FormatServerName(), r.Warnings))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%s\n", table)
 | 
			
		||||
	return fmt.Sprintf("%s\n\n%s", table, strings.Join(
 | 
			
		||||
		warnMsgs, "\n\n"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func formatList(r models.ScanResult) string {
 | 
			
		||||
	header := r.FormatTextReportHeadedr()
 | 
			
		||||
	if len(r.Errors) != 0 {
 | 
			
		||||
		return fmt.Sprintf(
 | 
			
		||||
			"%s\nError: Scan with --debug to view the details\n%s\n\n",
 | 
			
		||||
			"%s\nError: Use configtest subcommand or scan with --debug to view the details\n%s\n\n",
 | 
			
		||||
			header, r.Errors)
 | 
			
		||||
	}
 | 
			
		||||
	if len(r.Warnings) != 0 {
 | 
			
		||||
		header += fmt.Sprintf(
 | 
			
		||||
			"\nWarning: Some warnings occurred.\n%s\n\n",
 | 
			
		||||
			r.Warnings)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(r.ScannedCves) == 0 {
 | 
			
		||||
		return fmt.Sprintf(`
 | 
			
		||||
%s
 | 
			
		||||
No CVE-IDs are found in updatable packages.
 | 
			
		||||
%s
 | 
			
		||||
	 `, header, r.FormatUpdatablePacksSummary())
 | 
			
		||||
`, header, r.FormatUpdatablePacksSummary())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	data := [][]string{}
 | 
			
		||||
@@ -121,17 +143,23 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
			exploits = "   Y"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		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://wpvulndb.com/vulnerabilities/%s", strings.TrimPrefix(vinfo.CveID, "WPVDBID-"))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		data = append(data, []string{
 | 
			
		||||
			vinfo.CveID,
 | 
			
		||||
			fmt.Sprintf("%7s", vinfo.PatchStatus(r.Packages)),
 | 
			
		||||
			vinfo.AlertDict.FormatSource(),
 | 
			
		||||
			fmt.Sprintf("%4.1f", max),
 | 
			
		||||
			// fmt.Sprintf("%4.1f", v2max),
 | 
			
		||||
			// fmt.Sprintf("%4.1f", v3max),
 | 
			
		||||
			fmt.Sprintf("%8s", vinfo.AttackVector()),
 | 
			
		||||
			fmt.Sprintf("%7s", vinfo.PatchStatus(r.Packages)),
 | 
			
		||||
			// packname,
 | 
			
		||||
			fmt.Sprintf("https://nvd.nist.gov/vuln/detail/%s", vinfo.CveID),
 | 
			
		||||
			fmt.Sprintf("%2s", vinfo.AttackVector()),
 | 
			
		||||
			exploits,
 | 
			
		||||
			link,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -139,15 +167,14 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
	table := tablewriter.NewWriter(&b)
 | 
			
		||||
	table.SetHeader([]string{
 | 
			
		||||
		"CVE-ID",
 | 
			
		||||
		"Fixed",
 | 
			
		||||
		"CERT",
 | 
			
		||||
		"CVSS",
 | 
			
		||||
		// "v3",
 | 
			
		||||
		// "v2",
 | 
			
		||||
		"Attack",
 | 
			
		||||
		"Fixed",
 | 
			
		||||
		// "Pkg",
 | 
			
		||||
		"AV",
 | 
			
		||||
		"PoC",
 | 
			
		||||
		"NVD",
 | 
			
		||||
		"Exploit",
 | 
			
		||||
	})
 | 
			
		||||
	table.SetBorder(true)
 | 
			
		||||
	table.AppendBulk(data)
 | 
			
		||||
@@ -159,16 +186,22 @@ func formatFullPlainText(r models.ScanResult) (lines string) {
 | 
			
		||||
	header := r.FormatTextReportHeadedr()
 | 
			
		||||
	if len(r.Errors) != 0 {
 | 
			
		||||
		return fmt.Sprintf(
 | 
			
		||||
			"%s\nError: Scan with --debug to view the details\n%s\n\n",
 | 
			
		||||
			"%s\nError: Use configtest subcommand or scan with --debug to view the details\n%s\n\n",
 | 
			
		||||
			header, r.Errors)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(r.Warnings) != 0 {
 | 
			
		||||
		header += fmt.Sprintf(
 | 
			
		||||
			"\nWarning: Some warnings occurred.\n%s\n\n",
 | 
			
		||||
			r.Warnings)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(r.ScannedCves) == 0 {
 | 
			
		||||
		return fmt.Sprintf(`
 | 
			
		||||
%s
 | 
			
		||||
No CVE-IDs are found in updatable packages.
 | 
			
		||||
%s
 | 
			
		||||
	 `, header, r.FormatUpdatablePacksSummary())
 | 
			
		||||
`, header, r.FormatUpdatablePacksSummary())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	lines = header + "\n"
 | 
			
		||||
@@ -239,19 +272,41 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
			data = append(data, []string{"CPE", name})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, alert := range vuln.GitHubSecurityAlerts {
 | 
			
		||||
			data = append(data, []string{"GitHub", alert.PackageName})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, wp := range vuln.WpPackageFixStats {
 | 
			
		||||
			if p, ok := r.WordPressPackages.Find(wp.Name); ok {
 | 
			
		||||
				if p.Type == models.WPCore {
 | 
			
		||||
					data = append(data, []string{"WordPress",
 | 
			
		||||
						fmt.Sprintf("%s-%s, FixedIn: %s", wp.Name, p.Version, wp.FixedIn)})
 | 
			
		||||
				} else {
 | 
			
		||||
					data = append(data, []string{"WordPress",
 | 
			
		||||
						fmt.Sprintf("%s-%s, Update: %s, FixedIn: %s, %s",
 | 
			
		||||
							wp.Name, p.Version, p.Update, wp.FixedIn, p.Status)})
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				data = append(data, []string{"WordPress",
 | 
			
		||||
					fmt.Sprintf("%s", wp.Name)})
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, confidence := range vuln.Confidences {
 | 
			
		||||
			data = append(data, []string{"Confidence", confidence.String()})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		links := vuln.CveContents.SourceLinks(
 | 
			
		||||
			config.Conf.Lang, r.Family, vuln.CveID)
 | 
			
		||||
		data = append(data, []string{"Source", links[0].Value})
 | 
			
		||||
		if strings.HasPrefix(vuln.CveID, "CVE-") {
 | 
			
		||||
			links := vuln.CveContents.SourceLinks(
 | 
			
		||||
				config.Conf.Lang, r.Family, vuln.CveID)
 | 
			
		||||
			data = append(data, []string{"Source", links[0].Value})
 | 
			
		||||
 | 
			
		||||
		if 0 < len(vuln.Cvss2Scores(r.Family)) {
 | 
			
		||||
			data = append(data, []string{"CVSSv2 Calc", vuln.Cvss2CalcURL()})
 | 
			
		||||
		}
 | 
			
		||||
		if 0 < len(vuln.Cvss3Scores()) {
 | 
			
		||||
			data = append(data, []string{"CVSSv3 Calc", vuln.Cvss3CalcURL()})
 | 
			
		||||
			if 0 < len(vuln.Cvss2Scores(r.Family)) {
 | 
			
		||||
				data = append(data, []string{"CVSSv2 Calc", vuln.Cvss2CalcURL()})
 | 
			
		||||
			}
 | 
			
		||||
			if 0 < len(vuln.Cvss3Scores()) {
 | 
			
		||||
				data = append(data, []string{"CVSSv3 Calc", vuln.Cvss3CalcURL()})
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		vlinks := vuln.VendorLinks(r.Family)
 | 
			
		||||
@@ -288,7 +343,7 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
		table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
 | 
			
		||||
		table.SetHeader([]string{
 | 
			
		||||
			vuln.CveID,
 | 
			
		||||
			"",
 | 
			
		||||
			vuln.PatchStatus(r.Packages),
 | 
			
		||||
		})
 | 
			
		||||
		table.SetBorder(true)
 | 
			
		||||
		table.AppendBulk(data)
 | 
			
		||||
@@ -318,6 +373,16 @@ func formatChangelogs(r models.ScanResult) string {
 | 
			
		||||
	}
 | 
			
		||||
	return strings.Join(buf, "\n")
 | 
			
		||||
}
 | 
			
		||||
func ovalSupported(r *models.ScanResult) bool {
 | 
			
		||||
	switch r.Family {
 | 
			
		||||
	case
 | 
			
		||||
		config.Amazon,
 | 
			
		||||
		config.FreeBSD,
 | 
			
		||||
		config.Raspbian:
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func needToRefreshCve(r models.ScanResult) bool {
 | 
			
		||||
	if r.Lang != config.Conf.Lang {
 | 
			
		||||
@@ -339,7 +404,7 @@ func overwriteJSONFile(dir string, r models.ScanResult) error {
 | 
			
		||||
	config.Conf.Diff = false
 | 
			
		||||
	w := LocalFileWriter{CurrentDir: dir}
 | 
			
		||||
	if err := w.Write(r); err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to write summary report: %s", err)
 | 
			
		||||
		return xerrors.Errorf("Failed to write summary report: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	config.Conf.FormatJSON = before
 | 
			
		||||
	config.Conf.Diff = beforeDiff
 | 
			
		||||
@@ -361,7 +426,7 @@ func loadPrevious(currs models.ScanResults) (prevs models.ScanResults, err error
 | 
			
		||||
			path := filepath.Join(dir, filename)
 | 
			
		||||
			r, err := loadOneServerScanResult(path)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				util.Log.Errorf("%s", err)
 | 
			
		||||
				util.Log.Errorf("%+v", err)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if r.Family == result.Family && r.Release == result.Release {
 | 
			
		||||
@@ -507,7 +572,7 @@ var jsonDirPattern = regexp.MustCompile(
 | 
			
		||||
func ListValidJSONDirs() (dirs []string, err error) {
 | 
			
		||||
	var dirInfo []os.FileInfo
 | 
			
		||||
	if dirInfo, err = ioutil.ReadDir(config.Conf.ResultsDir); err != nil {
 | 
			
		||||
		err = fmt.Errorf("Failed to read %s: %s",
 | 
			
		||||
		err = xerrors.Errorf("Failed to read %s: %w",
 | 
			
		||||
			config.Conf.ResultsDir, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
@@ -545,20 +610,20 @@ func JSONDir(args []string) (string, error) {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return "", fmt.Errorf("Invalid path: %s", path)
 | 
			
		||||
		return "", xerrors.Errorf("Invalid path: %s", path)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// PIPE
 | 
			
		||||
	if config.Conf.Pipe {
 | 
			
		||||
		bytes, err := ioutil.ReadAll(os.Stdin)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", fmt.Errorf("Failed to read stdin: %s", err)
 | 
			
		||||
			return "", xerrors.Errorf("Failed to read stdin: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		fields := strings.Fields(string(bytes))
 | 
			
		||||
		if 0 < len(fields) {
 | 
			
		||||
			return filepath.Join(config.Conf.ResultsDir, fields[0]), nil
 | 
			
		||||
		}
 | 
			
		||||
		return "", fmt.Errorf("Stdin is invalid: %s", string(bytes))
 | 
			
		||||
		return "", xerrors.Errorf("Stdin is invalid: %s", string(bytes))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// returns latest dir when no args or no PIPE
 | 
			
		||||
@@ -566,7 +631,7 @@ func JSONDir(args []string) (string, error) {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	if len(dirs) == 0 {
 | 
			
		||||
		return "", fmt.Errorf("No results under %s",
 | 
			
		||||
		return "", xerrors.Errorf("No results under %s",
 | 
			
		||||
			config.Conf.ResultsDir)
 | 
			
		||||
	}
 | 
			
		||||
	return dirs[0], nil
 | 
			
		||||
@@ -576,7 +641,7 @@ func JSONDir(args []string) (string, error) {
 | 
			
		||||
func LoadScanResults(jsonDir string) (results models.ScanResults, err error) {
 | 
			
		||||
	var files []os.FileInfo
 | 
			
		||||
	if files, err = ioutil.ReadDir(jsonDir); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("Failed to read %s: %s", jsonDir, err)
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to read %s: %w", jsonDir, err)
 | 
			
		||||
	}
 | 
			
		||||
	for _, f := range files {
 | 
			
		||||
		if filepath.Ext(f.Name()) != ".json" || strings.HasSuffix(f.Name(), "_diff.json") {
 | 
			
		||||
@@ -591,7 +656,7 @@ func LoadScanResults(jsonDir string) (results models.ScanResults, err error) {
 | 
			
		||||
		results = append(results, *r)
 | 
			
		||||
	}
 | 
			
		||||
	if len(results) == 0 {
 | 
			
		||||
		return nil, fmt.Errorf("There is no json file under %s", jsonDir)
 | 
			
		||||
		return nil, xerrors.Errorf("There is no json file under %s", jsonDir)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
@@ -603,11 +668,11 @@ func loadOneServerScanResult(jsonFile string) (*models.ScanResult, error) {
 | 
			
		||||
		err  error
 | 
			
		||||
	)
 | 
			
		||||
	if data, err = ioutil.ReadFile(jsonFile); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("Failed to read %s: %s", jsonFile, err)
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to read %s: %w", jsonFile, err)
 | 
			
		||||
	}
 | 
			
		||||
	result := &models.ScanResult{}
 | 
			
		||||
	if err := json.Unmarshal(data, result); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("Failed to parse %s: %s", jsonFile, err)
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to parse %s: %w", jsonFile, err)
 | 
			
		||||
	}
 | 
			
		||||
	return result, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -192,13 +192,13 @@ func TestDiff(t *testing.T) {
 | 
			
		||||
					ScannedCves: models.VulnInfos{
 | 
			
		||||
						"CVE-2012-6702": {
 | 
			
		||||
							CveID:            "CVE-2012-6702",
 | 
			
		||||
							AffectedPackages: models.PackageStatuses{{Name: "libexpat1"}},
 | 
			
		||||
							AffectedPackages: models.PackageFixStatuses{{Name: "libexpat1"}},
 | 
			
		||||
							DistroAdvisories: []models.DistroAdvisory{},
 | 
			
		||||
							CpeURIs:          []string{},
 | 
			
		||||
						},
 | 
			
		||||
						"CVE-2014-9761": {
 | 
			
		||||
							CveID:            "CVE-2014-9761",
 | 
			
		||||
							AffectedPackages: models.PackageStatuses{{Name: "libc-bin"}},
 | 
			
		||||
							AffectedPackages: models.PackageFixStatuses{{Name: "libc-bin"}},
 | 
			
		||||
							DistroAdvisories: []models.DistroAdvisory{},
 | 
			
		||||
							CpeURIs:          []string{},
 | 
			
		||||
						},
 | 
			
		||||
@@ -217,13 +217,13 @@ func TestDiff(t *testing.T) {
 | 
			
		||||
					ScannedCves: models.VulnInfos{
 | 
			
		||||
						"CVE-2012-6702": {
 | 
			
		||||
							CveID:            "CVE-2012-6702",
 | 
			
		||||
							AffectedPackages: models.PackageStatuses{{Name: "libexpat1"}},
 | 
			
		||||
							AffectedPackages: models.PackageFixStatuses{{Name: "libexpat1"}},
 | 
			
		||||
							DistroAdvisories: []models.DistroAdvisory{},
 | 
			
		||||
							CpeURIs:          []string{},
 | 
			
		||||
						},
 | 
			
		||||
						"CVE-2014-9761": {
 | 
			
		||||
							CveID:            "CVE-2014-9761",
 | 
			
		||||
							AffectedPackages: models.PackageStatuses{{Name: "libc-bin"}},
 | 
			
		||||
							AffectedPackages: models.PackageFixStatuses{{Name: "libc-bin"}},
 | 
			
		||||
							DistroAdvisories: []models.DistroAdvisory{},
 | 
			
		||||
							CpeURIs:          []string{},
 | 
			
		||||
						},
 | 
			
		||||
@@ -254,7 +254,7 @@ func TestDiff(t *testing.T) {
 | 
			
		||||
					ScannedCves: models.VulnInfos{
 | 
			
		||||
						"CVE-2016-6662": {
 | 
			
		||||
							CveID:            "CVE-2016-6662",
 | 
			
		||||
							AffectedPackages: models.PackageStatuses{{Name: "mysql-libs"}},
 | 
			
		||||
							AffectedPackages: models.PackageFixStatuses{{Name: "mysql-libs"}},
 | 
			
		||||
							DistroAdvisories: []models.DistroAdvisory{},
 | 
			
		||||
							CpeURIs:          []string{},
 | 
			
		||||
						},
 | 
			
		||||
@@ -292,7 +292,7 @@ func TestDiff(t *testing.T) {
 | 
			
		||||
				ScannedCves: models.VulnInfos{
 | 
			
		||||
					"CVE-2016-6662": {
 | 
			
		||||
						CveID:            "CVE-2016-6662",
 | 
			
		||||
						AffectedPackages: models.PackageStatuses{{Name: "mysql-libs"}},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{{Name: "mysql-libs"}},
 | 
			
		||||
						DistroAdvisories: []models.DistroAdvisory{},
 | 
			
		||||
						CpeURIs:          []string{},
 | 
			
		||||
					},
 | 
			
		||||
@@ -348,7 +348,7 @@ func TestIsCveFixed(t *testing.T) {
 | 
			
		||||
			in: In{
 | 
			
		||||
				v: models.VulnInfo{
 | 
			
		||||
					CveID: "CVE-2016-6662",
 | 
			
		||||
					AffectedPackages: models.PackageStatuses{
 | 
			
		||||
					AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "mysql-libs",
 | 
			
		||||
							NotFixedYet: false,
 | 
			
		||||
@@ -366,7 +366,7 @@ func TestIsCveFixed(t *testing.T) {
 | 
			
		||||
					ScannedCves: models.VulnInfos{
 | 
			
		||||
						"CVE-2016-6662": {
 | 
			
		||||
							CveID: "CVE-2016-6662",
 | 
			
		||||
							AffectedPackages: models.PackageStatuses{
 | 
			
		||||
							AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
								{
 | 
			
		||||
									Name:        "mysql-libs",
 | 
			
		||||
									NotFixedYet: true,
 | 
			
		||||
@@ -389,7 +389,7 @@ func TestIsCveFixed(t *testing.T) {
 | 
			
		||||
			in: In{
 | 
			
		||||
				v: models.VulnInfo{
 | 
			
		||||
					CveID: "CVE-2016-6662",
 | 
			
		||||
					AffectedPackages: models.PackageStatuses{
 | 
			
		||||
					AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
						{
 | 
			
		||||
							Name:        "mysql-libs",
 | 
			
		||||
							NotFixedYet: true,
 | 
			
		||||
@@ -407,7 +407,7 @@ func TestIsCveFixed(t *testing.T) {
 | 
			
		||||
					ScannedCves: models.VulnInfos{
 | 
			
		||||
						"CVE-2016-6662": {
 | 
			
		||||
							CveID: "CVE-2016-6662",
 | 
			
		||||
							AffectedPackages: models.PackageStatuses{
 | 
			
		||||
							AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
								{
 | 
			
		||||
									Name:        "mysql-libs",
 | 
			
		||||
									NotFixedYet: true,
 | 
			
		||||
 
 | 
			
		||||
@@ -19,12 +19,12 @@ package scan
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// inherit OsTypeInterface
 | 
			
		||||
@@ -66,7 +66,7 @@ func detectAlpine(c config.ServerInfo) (itsMe bool, os osTypeInterface) {
 | 
			
		||||
 | 
			
		||||
func (o *alpine) checkScanMode() error {
 | 
			
		||||
	if o.getServerInfo().Mode.IsOffline() {
 | 
			
		||||
		return fmt.Errorf("Remove offline scan mode, Alpine needs internet connection")
 | 
			
		||||
		return xerrors.New("Remove offline scan mode, Alpine needs internet connection")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@@ -84,7 +84,7 @@ func (o *alpine) checkIfSudoNoPasswd() error {
 | 
			
		||||
func (o *alpine) apkUpdate() error {
 | 
			
		||||
	r := o.exec("apk update", noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return fmt.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
		return xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@@ -130,11 +130,14 @@ func (o *alpine) scanPackages() error {
 | 
			
		||||
 | 
			
		||||
	updatable, err := o.scanUpdatablePackages()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		o.log.Errorf("Failed to scan installed packages: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
		err = xerrors.Errorf("Failed to scan updatable packages: %w", err)
 | 
			
		||||
		o.log.Warnf("err: %+v", err)
 | 
			
		||||
		o.warns = append(o.warns, err)
 | 
			
		||||
		// Only warning this error
 | 
			
		||||
	} else {
 | 
			
		||||
		installed.MergeNewVersion(updatable)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	installed.MergeNewVersion(updatable)
 | 
			
		||||
	o.Packages = installed
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@@ -143,7 +146,7 @@ func (o *alpine) scanInstalledPackages() (models.Packages, error) {
 | 
			
		||||
	cmd := util.PrependProxyEnv("apk info -v")
 | 
			
		||||
	r := o.exec(cmd, noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return nil, fmt.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	return o.parseApkInfo(r.Stdout)
 | 
			
		||||
}
 | 
			
		||||
@@ -160,7 +163,7 @@ func (o *alpine) parseApkInfo(stdout string) (models.Packages, error) {
 | 
			
		||||
		line := scanner.Text()
 | 
			
		||||
		ss := strings.Split(line, "-")
 | 
			
		||||
		if len(ss) < 3 {
 | 
			
		||||
			return nil, fmt.Errorf("Failed to parse apk info -v: %s", line)
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to parse apk info -v: %s", line)
 | 
			
		||||
		}
 | 
			
		||||
		name := strings.Join(ss[:len(ss)-2], "-")
 | 
			
		||||
		packs[name] = models.Package{
 | 
			
		||||
@@ -175,7 +178,7 @@ func (o *alpine) scanUpdatablePackages() (models.Packages, error) {
 | 
			
		||||
	cmd := util.PrependProxyEnv("apk version")
 | 
			
		||||
	r := o.exec(cmd, noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return nil, fmt.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	return o.parseApkVersion(r.Stdout)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,10 @@
 | 
			
		||||
package scan
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// inherit OsTypeInterface
 | 
			
		||||
@@ -32,9 +31,6 @@ func newAmazon(c config.ServerInfo) *amazon {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *amazon) checkScanMode() error {
 | 
			
		||||
	if o.getServerInfo().Mode.IsOffline() {
 | 
			
		||||
		return fmt.Errorf("Remove offline scan mode, Amazon needs internet connection")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -46,7 +42,7 @@ func (o *amazon) checkDeps() error {
 | 
			
		||||
	} else if o.getServerInfo().Mode.IsDeep() {
 | 
			
		||||
		return o.execCheckDeps(o.depsDeep())
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Errorf("Unknown scan mode")
 | 
			
		||||
	return xerrors.New("Unknown scan mode")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *amazon) depsFast() []string {
 | 
			
		||||
@@ -101,14 +97,6 @@ func (o rootPrivAmazon) repoquery() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o rootPrivAmazon) yumRepolist() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o rootPrivAmazon) yumUpdateInfo() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o rootPrivAmazon) yumChangelog() bool {
 | 
			
		||||
func (o rootPrivAmazon) yumMakeCache() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										289
									
								
								scan/base.go
									
									
									
									
									
								
							
							
						
						
									
										289
									
								
								scan/base.go
									
									
									
									
									
								
							@@ -19,15 +19,30 @@ package scan
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/knqyf263/fanal/analyzer"
 | 
			
		||||
 | 
			
		||||
	"github.com/knqyf263/fanal/extractor"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	// Import library scanner
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/library/bundler"
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/library/cargo"
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/library/composer"
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/library/npm"
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/library/pipenv"
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/library/poetry"
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/library/yarn"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type base struct {
 | 
			
		||||
@@ -35,9 +50,12 @@ type base struct {
 | 
			
		||||
	Distro     config.Distro
 | 
			
		||||
	Platform   models.Platform
 | 
			
		||||
	osPackages
 | 
			
		||||
	LibraryScanners []models.LibraryScanner
 | 
			
		||||
	WordPress       *models.WordPressPackages
 | 
			
		||||
 | 
			
		||||
	log  *logrus.Entry
 | 
			
		||||
	errs []error
 | 
			
		||||
	log   *logrus.Entry
 | 
			
		||||
	errs  []error
 | 
			
		||||
	warns []error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *base) exec(cmd string, sudo bool) execResult {
 | 
			
		||||
@@ -79,7 +97,7 @@ func (l *base) getPlatform() models.Platform {
 | 
			
		||||
func (l *base) runningKernel() (release, version string, err error) {
 | 
			
		||||
	r := l.exec("uname -r", noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return "", "", fmt.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
		return "", "", xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	release = strings.TrimSpace(r.Stdout)
 | 
			
		||||
 | 
			
		||||
@@ -87,7 +105,7 @@ func (l *base) runningKernel() (release, version string, err error) {
 | 
			
		||||
	case config.Debian:
 | 
			
		||||
		r := l.exec("uname -a", noSudo)
 | 
			
		||||
		if !r.isSuccess() {
 | 
			
		||||
			return "", "", fmt.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
			return "", "", xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
		}
 | 
			
		||||
		ss := strings.Fields(r.Stdout)
 | 
			
		||||
		if 6 < len(ss) {
 | 
			
		||||
@@ -118,7 +136,7 @@ func (l *base) allContainers() (containers []config.Container, err error) {
 | 
			
		||||
		}
 | 
			
		||||
		return l.parseLxcPs(stdout)
 | 
			
		||||
	default:
 | 
			
		||||
		return containers, fmt.Errorf(
 | 
			
		||||
		return containers, xerrors.Errorf(
 | 
			
		||||
			"Not supported yet: %s", l.ServerInfo.ContainerType)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -144,7 +162,7 @@ func (l *base) runningContainers() (containers []config.Container, err error) {
 | 
			
		||||
		}
 | 
			
		||||
		return l.parseLxcPs(stdout)
 | 
			
		||||
	default:
 | 
			
		||||
		return containers, fmt.Errorf(
 | 
			
		||||
		return containers, xerrors.Errorf(
 | 
			
		||||
			"Not supported yet: %s", l.ServerInfo.ContainerType)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -170,7 +188,7 @@ func (l *base) exitedContainers() (containers []config.Container, err error) {
 | 
			
		||||
		}
 | 
			
		||||
		return l.parseLxcPs(stdout)
 | 
			
		||||
	default:
 | 
			
		||||
		return containers, fmt.Errorf(
 | 
			
		||||
		return containers, xerrors.Errorf(
 | 
			
		||||
			"Not supported yet: %s", l.ServerInfo.ContainerType)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -179,7 +197,7 @@ func (l *base) dockerPs(option string) (string, error) {
 | 
			
		||||
	cmd := fmt.Sprintf("docker ps %s", option)
 | 
			
		||||
	r := l.exec(cmd, noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return "", fmt.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
		return "", xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	return r.Stdout, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -188,7 +206,7 @@ func (l *base) lxdPs(option string) (string, error) {
 | 
			
		||||
	cmd := fmt.Sprintf("lxc list %s", option)
 | 
			
		||||
	r := l.exec(cmd, noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return "", fmt.Errorf("failed to SSH: %s", r)
 | 
			
		||||
		return "", xerrors.Errorf("failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	return r.Stdout, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -197,7 +215,7 @@ func (l *base) lxcPs(option string) (string, error) {
 | 
			
		||||
	cmd := fmt.Sprintf("lxc-ls %s 2>/dev/null", option)
 | 
			
		||||
	r := l.exec(cmd, sudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return "", fmt.Errorf("failed to SSH: %s", r)
 | 
			
		||||
		return "", xerrors.Errorf("failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	return r.Stdout, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -210,7 +228,7 @@ func (l *base) parseDockerPs(stdout string) (containers []config.Container, err
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if len(fields) != 3 {
 | 
			
		||||
			return containers, fmt.Errorf("Unknown format: %s", line)
 | 
			
		||||
			return containers, xerrors.Errorf("Unknown format: %s", line)
 | 
			
		||||
		}
 | 
			
		||||
		containers = append(containers, config.Container{
 | 
			
		||||
			ContainerID: fields[0],
 | 
			
		||||
@@ -232,7 +250,7 @@ func (l *base) parseLxdPs(stdout string) (containers []config.Container, err err
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if len(fields) != 1 {
 | 
			
		||||
			return containers, fmt.Errorf("Unknown format: %s", line)
 | 
			
		||||
			return containers, xerrors.Errorf("Unknown format: %s", line)
 | 
			
		||||
		}
 | 
			
		||||
		containers = append(containers, config.Container{
 | 
			
		||||
			ContainerID: fields[0],
 | 
			
		||||
@@ -265,7 +283,7 @@ func (l *base) ip() ([]string, []string, error) {
 | 
			
		||||
	// 2: eth0    inet6 fe80::5054:ff:fe2a:864c/64 scope link \       valid_lft forever preferred_lft forever
 | 
			
		||||
	r := l.exec("/sbin/ip -o addr", noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return nil, nil, fmt.Errorf("Failed to detect IP address: %v", r)
 | 
			
		||||
		return nil, nil, xerrors.Errorf("Failed to detect IP address: %v", r)
 | 
			
		||||
	}
 | 
			
		||||
	ipv4Addrs, ipv6Addrs := l.parseIP(r.Stdout)
 | 
			
		||||
	return ipv4Addrs, ipv6Addrs, nil
 | 
			
		||||
@@ -358,7 +376,7 @@ func (l *base) detectRunningOnAws() (ok bool, instanceID string, err error) {
 | 
			
		||||
			return false, "", nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false, "", fmt.Errorf(
 | 
			
		||||
	return false, "", xerrors.Errorf(
 | 
			
		||||
		"Failed to curl or wget to AWS instance metadata on %s. container: %s",
 | 
			
		||||
		l.ServerInfo.ServerName, l.ServerInfo.Container.Name)
 | 
			
		||||
}
 | 
			
		||||
@@ -382,28 +400,48 @@ func (l *base) convertToModel() models.ScanResult {
 | 
			
		||||
		Type:        ctype,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	errs := []string{}
 | 
			
		||||
	image := models.Image{
 | 
			
		||||
		Name: l.ServerInfo.Image.Name,
 | 
			
		||||
		Tag:  l.ServerInfo.Image.Tag,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	errs, warns := []string{}, []string{}
 | 
			
		||||
	for _, e := range l.errs {
 | 
			
		||||
		errs = append(errs, fmt.Sprintf("%s", e))
 | 
			
		||||
		errs = append(errs, fmt.Sprintf("%+v", e))
 | 
			
		||||
	}
 | 
			
		||||
	for _, w := range l.warns {
 | 
			
		||||
		warns = append(warns, fmt.Sprintf("%+v", w))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	scannedVia := scannedViaRemote
 | 
			
		||||
	if isLocalExec(l.ServerInfo.Port, l.ServerInfo.Host) {
 | 
			
		||||
		scannedVia = scannedViaLocal
 | 
			
		||||
	} else if l.ServerInfo.Type == config.ServerTypePseudo {
 | 
			
		||||
		scannedVia = scannedViaPseudo
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return models.ScanResult{
 | 
			
		||||
		JSONVersion:   models.JSONVersion,
 | 
			
		||||
		ServerName:    l.ServerInfo.ServerName,
 | 
			
		||||
		ScannedAt:     time.Now(),
 | 
			
		||||
		ScanMode:      l.ServerInfo.Mode.String(),
 | 
			
		||||
		Family:        l.Distro.Family,
 | 
			
		||||
		Release:       l.Distro.Release,
 | 
			
		||||
		Container:     container,
 | 
			
		||||
		Platform:      l.Platform,
 | 
			
		||||
		IPv4Addrs:     l.ServerInfo.IPv4Addrs,
 | 
			
		||||
		IPv6Addrs:     l.ServerInfo.IPv6Addrs,
 | 
			
		||||
		ScannedCves:   l.VulnInfos,
 | 
			
		||||
		RunningKernel: l.Kernel,
 | 
			
		||||
		Packages:      l.Packages,
 | 
			
		||||
		SrcPackages:   l.SrcPackages,
 | 
			
		||||
		Optional:      l.ServerInfo.Optional,
 | 
			
		||||
		Errors:        errs,
 | 
			
		||||
		JSONVersion:       models.JSONVersion,
 | 
			
		||||
		ServerName:        l.ServerInfo.ServerName,
 | 
			
		||||
		ScannedAt:         time.Now(),
 | 
			
		||||
		ScanMode:          l.ServerInfo.Mode.String(),
 | 
			
		||||
		Family:            l.Distro.Family,
 | 
			
		||||
		Release:           l.Distro.Release,
 | 
			
		||||
		Container:         container,
 | 
			
		||||
		Image:             image,
 | 
			
		||||
		Platform:          l.Platform,
 | 
			
		||||
		IPv4Addrs:         l.ServerInfo.IPv4Addrs,
 | 
			
		||||
		IPv6Addrs:         l.ServerInfo.IPv6Addrs,
 | 
			
		||||
		ScannedCves:       l.VulnInfos,
 | 
			
		||||
		ScannedVia:        scannedVia,
 | 
			
		||||
		RunningKernel:     l.Kernel,
 | 
			
		||||
		Packages:          l.Packages,
 | 
			
		||||
		SrcPackages:       l.SrcPackages,
 | 
			
		||||
		WordPressPackages: l.WordPress,
 | 
			
		||||
		LibraryScanners:   l.LibraryScanners,
 | 
			
		||||
		Optional:          l.ServerInfo.Optional,
 | 
			
		||||
		Errors:            errs,
 | 
			
		||||
		Warnings:          warns,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -427,7 +465,7 @@ func (l *base) detectInitSystem() (string, error) {
 | 
			
		||||
	f = func(cmd string) (string, error) {
 | 
			
		||||
		r := l.exec(cmd, sudo)
 | 
			
		||||
		if !r.isSuccess() {
 | 
			
		||||
			return "", fmt.Errorf("Failed to stat %s: %s", cmd, r)
 | 
			
		||||
			return "", xerrors.Errorf("Failed to stat %s: %s", cmd, r)
 | 
			
		||||
		}
 | 
			
		||||
		scanner := bufio.NewScanner(strings.NewReader(r.Stdout))
 | 
			
		||||
		scanner.Scan()
 | 
			
		||||
@@ -449,7 +487,7 @@ func (l *base) detectInitSystem() (string, error) {
 | 
			
		||||
			}
 | 
			
		||||
			return sysVinit, nil
 | 
			
		||||
		}
 | 
			
		||||
		return "", fmt.Errorf("Failed to detect a init system: %s", line)
 | 
			
		||||
		return "", xerrors.Errorf("Failed to detect a init system: %s", line)
 | 
			
		||||
	}
 | 
			
		||||
	return f("stat /proc/1/exe")
 | 
			
		||||
}
 | 
			
		||||
@@ -458,7 +496,7 @@ func (l *base) detectServiceName(pid string) (string, error) {
 | 
			
		||||
	cmd := fmt.Sprintf("systemctl status --quiet --no-pager %s", pid)
 | 
			
		||||
	r := l.exec(cmd, noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return "", fmt.Errorf("Failed to stat %s: %s", cmd, r)
 | 
			
		||||
		return "", xerrors.Errorf("Failed to stat %s: %s", cmd, r)
 | 
			
		||||
	}
 | 
			
		||||
	return l.parseSystemctlStatus(r.Stdout), nil
 | 
			
		||||
}
 | 
			
		||||
@@ -473,3 +511,184 @@ func (l *base) parseSystemctlStatus(stdout string) string {
 | 
			
		||||
	}
 | 
			
		||||
	return ss[1]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *base) scanLibraries() (err error) {
 | 
			
		||||
	// image already detected libraries
 | 
			
		||||
	if len(l.LibraryScanners) != 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// library scan for servers need lockfiles
 | 
			
		||||
	if len(l.ServerInfo.Lockfiles) == 0 && !l.ServerInfo.FindLock {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	libFilemap := extractor.FileMap{}
 | 
			
		||||
 | 
			
		||||
	detectFiles := l.ServerInfo.Lockfiles
 | 
			
		||||
 | 
			
		||||
	// auto detect lockfile
 | 
			
		||||
	if l.ServerInfo.FindLock {
 | 
			
		||||
		findopt := ""
 | 
			
		||||
		for filename := range models.LibraryMap {
 | 
			
		||||
			findopt += fmt.Sprintf("-name %q -o ", "*"+filename)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// delete last "-o "
 | 
			
		||||
		// find / -name "*package-lock.json" -o -name "*yarn.lock" ... 2>&1 | grep -v "Permission denied"
 | 
			
		||||
		cmd := fmt.Sprintf(`find / ` + findopt[:len(findopt)-3] + ` 2>&1 | grep -v "Permission denied"`)
 | 
			
		||||
		r := exec(l.ServerInfo, cmd, noSudo)
 | 
			
		||||
		if !r.isSuccess() {
 | 
			
		||||
			return xerrors.Errorf("Failed to find lock files")
 | 
			
		||||
		}
 | 
			
		||||
		detectFiles = append(detectFiles, strings.Split(r.Stdout, "\n")...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, path := range detectFiles {
 | 
			
		||||
		if path == "" {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		// skip already exist
 | 
			
		||||
		if _, ok := libFilemap[path]; ok {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		cmd := fmt.Sprintf("cat %s", path)
 | 
			
		||||
		r := exec(l.ServerInfo, cmd, noSudo)
 | 
			
		||||
		if !r.isSuccess() {
 | 
			
		||||
			return xerrors.Errorf("Failed to get target file: %s, filepath: %s", r, path)
 | 
			
		||||
		}
 | 
			
		||||
		libFilemap[path] = []byte(r.Stdout)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	results, err := analyzer.GetLibraries(libFilemap)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to get libs: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	l.LibraryScanners, err = convertLibWithScanner(results)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to scan libraries: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *base) scanWordPress() (err error) {
 | 
			
		||||
	wpOpts := []string{l.ServerInfo.WordPress.OSUser,
 | 
			
		||||
		l.ServerInfo.WordPress.DocRoot,
 | 
			
		||||
		l.ServerInfo.WordPress.CmdPath,
 | 
			
		||||
		l.ServerInfo.WordPress.WPVulnDBToken,
 | 
			
		||||
	}
 | 
			
		||||
	var isScanWp, hasEmptyOpt bool
 | 
			
		||||
	for _, opt := range wpOpts {
 | 
			
		||||
		if opt != "" {
 | 
			
		||||
			isScanWp = true
 | 
			
		||||
			break
 | 
			
		||||
		} else {
 | 
			
		||||
			hasEmptyOpt = true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if !isScanWp {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if hasEmptyOpt {
 | 
			
		||||
		return xerrors.Errorf("%s has empty WordPress opts: %s",
 | 
			
		||||
			l.getServerInfo().GetServerName(), wpOpts)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd := fmt.Sprintf("sudo -u %s -i -- %s cli version",
 | 
			
		||||
		l.ServerInfo.WordPress.OSUser,
 | 
			
		||||
		l.ServerInfo.WordPress.CmdPath)
 | 
			
		||||
	if r := exec(l.ServerInfo, cmd, noSudo); !r.isSuccess() {
 | 
			
		||||
		l.ServerInfo.WordPress.WPVulnDBToken = "secret"
 | 
			
		||||
		return xerrors.Errorf("Failed to exec `%s`. Check the OS user, command path of wp-cli, DocRoot and permission: %#v", cmd, l.ServerInfo.WordPress)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	wp, err := l.detectWordPress()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to scan wordpress: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	l.WordPress = wp
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *base) detectWordPress() (*models.WordPressPackages, error) {
 | 
			
		||||
	ver, err := l.detectWpCore()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	themes, err := l.detectWpThemes()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	plugins, err := l.detectWpPlugins()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pkgs := models.WordPressPackages{
 | 
			
		||||
		models.WpPackage{
 | 
			
		||||
			Name:    models.WPCore,
 | 
			
		||||
			Version: ver,
 | 
			
		||||
			Type:    models.WPCore,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	pkgs = append(pkgs, themes...)
 | 
			
		||||
	pkgs = append(pkgs, plugins...)
 | 
			
		||||
	return &pkgs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *base) detectWpCore() (string, error) {
 | 
			
		||||
	cmd := fmt.Sprintf("sudo -u %s -i -- %s core version --path=%s",
 | 
			
		||||
		l.ServerInfo.WordPress.OSUser,
 | 
			
		||||
		l.ServerInfo.WordPress.CmdPath,
 | 
			
		||||
		l.ServerInfo.WordPress.DocRoot)
 | 
			
		||||
 | 
			
		||||
	r := exec(l.ServerInfo, cmd, noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return "", xerrors.Errorf("Failed to get wp core version: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	return strings.TrimSpace(r.Stdout), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *base) detectWpThemes() ([]models.WpPackage, error) {
 | 
			
		||||
	cmd := fmt.Sprintf("sudo -u %s -i -- %s theme list --path=%s --format=json",
 | 
			
		||||
		l.ServerInfo.WordPress.OSUser,
 | 
			
		||||
		l.ServerInfo.WordPress.CmdPath,
 | 
			
		||||
		l.ServerInfo.WordPress.DocRoot)
 | 
			
		||||
 | 
			
		||||
	var themes []models.WpPackage
 | 
			
		||||
	r := exec(l.ServerInfo, cmd, noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to get a list of WordPress plugins: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	err := json.Unmarshal([]byte(r.Stdout), &themes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to unmarshal wp theme list: %w", cmd, err)
 | 
			
		||||
	}
 | 
			
		||||
	for i := range themes {
 | 
			
		||||
		themes[i].Type = models.WPTheme
 | 
			
		||||
	}
 | 
			
		||||
	return themes, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *base) detectWpPlugins() ([]models.WpPackage, error) {
 | 
			
		||||
	cmd := fmt.Sprintf("sudo -u %s -i -- %s plugin list --path=%s --format=json",
 | 
			
		||||
		l.ServerInfo.WordPress.OSUser,
 | 
			
		||||
		l.ServerInfo.WordPress.CmdPath,
 | 
			
		||||
		l.ServerInfo.WordPress.DocRoot)
 | 
			
		||||
 | 
			
		||||
	var plugins []models.WpPackage
 | 
			
		||||
	r := exec(l.ServerInfo, cmd, noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to wp plugin list: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	if err := json.Unmarshal([]byte(r.Stdout), &plugins); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	for i := range plugins {
 | 
			
		||||
		plugins[i].Type = models.WPPlugin
 | 
			
		||||
	}
 | 
			
		||||
	return plugins, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -47,23 +47,30 @@ func (o *centos) depsFast() []string {
 | 
			
		||||
	if o.getServerInfo().Mode.IsOffline() {
 | 
			
		||||
		return []string{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// repoquery
 | 
			
		||||
	return []string{"yum-utils"}
 | 
			
		||||
	majorVersion, _ := o.Distro.MajorVersion()
 | 
			
		||||
	if majorVersion < 8 {
 | 
			
		||||
		return []string{"yum-utils"}
 | 
			
		||||
	}
 | 
			
		||||
	return []string{"dnf-utils"}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *centos) depsFastRoot() []string {
 | 
			
		||||
	return []string{
 | 
			
		||||
		"yum-utils",
 | 
			
		||||
		"yum-plugin-ps",
 | 
			
		||||
	if o.getServerInfo().Mode.IsOffline() {
 | 
			
		||||
		return []string{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// repoquery
 | 
			
		||||
	majorVersion, _ := o.Distro.MajorVersion()
 | 
			
		||||
	if majorVersion < 8 {
 | 
			
		||||
		return []string{"yum-utils"}
 | 
			
		||||
	}
 | 
			
		||||
	return []string{"dnf-utils"}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *centos) depsDeep() []string {
 | 
			
		||||
	return []string{
 | 
			
		||||
		"yum-utils",
 | 
			
		||||
		"yum-plugin-ps",
 | 
			
		||||
		"yum-plugin-changelog",
 | 
			
		||||
	}
 | 
			
		||||
	return o.depsFastRoot()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *centos) checkIfSudoNoPasswd() error {
 | 
			
		||||
@@ -107,14 +114,6 @@ func (o rootPrivCentos) repoquery() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o rootPrivCentos) yumRepolist() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o rootPrivCentos) yumUpdateInfo() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o rootPrivCentos) yumChangelog() bool {
 | 
			
		||||
func (o rootPrivCentos) yumMakeCache() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										222
									
								
								scan/container.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								scan/container.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,222 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Corporation , Japan.
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package scan
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/knqyf263/fanal/analyzer"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	fanalos "github.com/knqyf263/fanal/analyzer/os"
 | 
			
		||||
	godeptypes "github.com/knqyf263/go-dep-parser/pkg/types"
 | 
			
		||||
 | 
			
		||||
	// Register library analyzers
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/library/bundler"
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/library/cargo"
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/library/composer"
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/library/npm"
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/library/pipenv"
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/library/poetry"
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/library/yarn"
 | 
			
		||||
 | 
			
		||||
	// Register os analyzers
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/os/alpine"
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/os/amazonlinux"
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/os/debianbase"
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/os/opensuse"
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/os/redhatbase"
 | 
			
		||||
 | 
			
		||||
	// Register package analyzers
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/pkg/apk"
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/pkg/dpkg"
 | 
			
		||||
	_ "github.com/knqyf263/fanal/analyzer/pkg/rpmcmd"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// inherit OsTypeInterface
 | 
			
		||||
type image struct {
 | 
			
		||||
	base
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newDummyOS is constructor
 | 
			
		||||
func newDummyOS(c config.ServerInfo) *image {
 | 
			
		||||
	d := &image{
 | 
			
		||||
		base: base{
 | 
			
		||||
			osPackages: osPackages{
 | 
			
		||||
				Packages:  models.Packages{},
 | 
			
		||||
				VulnInfos: models.VulnInfos{},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	d.log = util.NewCustomLogger(c)
 | 
			
		||||
	d.setServerInfo(c)
 | 
			
		||||
	return d
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func detectContainerImage(c config.ServerInfo) (itsMe bool, containerImage osTypeInterface, err error) {
 | 
			
		||||
	if err = config.IsValidImage(c.Image); err != nil {
 | 
			
		||||
		return false, nil, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	os, pkgs, libs, err := scanImage(c)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// use Alpine for setErrs
 | 
			
		||||
		return false, newDummyOS(c), err
 | 
			
		||||
	}
 | 
			
		||||
	switch os.Family {
 | 
			
		||||
	case fanalos.OpenSUSELeap, fanalos.OpenSUSETumbleweed, fanalos.OpenSUSE:
 | 
			
		||||
		return false, newDummyOS(c), xerrors.Errorf("Unsupported OS : %s", os.Family)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	libScanners, err := convertLibWithScanner(libs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, newDummyOS(c), err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p := newContainerImage(c, pkgs, libScanners)
 | 
			
		||||
	p.setDistro(os.Family, os.Name)
 | 
			
		||||
	return true, p, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func convertLibWithScanner(libs map[analyzer.FilePath][]godeptypes.Library) ([]models.LibraryScanner, error) {
 | 
			
		||||
	scanners := []models.LibraryScanner{}
 | 
			
		||||
	for path, pkgs := range libs {
 | 
			
		||||
		scanners = append(scanners, models.LibraryScanner{Path: string(path), Libs: pkgs})
 | 
			
		||||
	}
 | 
			
		||||
	return scanners, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// scanImage returns os, packages on image layers
 | 
			
		||||
func scanImage(c config.ServerInfo) (os *analyzer.OS, pkgs []analyzer.Package, libs map[analyzer.FilePath][]godeptypes.Library, err error) {
 | 
			
		||||
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
	domain := c.Image.Name + ":" + c.Image.Tag
 | 
			
		||||
	util.Log.Info("Start fetch container... ", domain)
 | 
			
		||||
 | 
			
		||||
	// Configure dockerOption
 | 
			
		||||
	dockerOption := c.Image.DockerOption
 | 
			
		||||
	if dockerOption.Timeout == 0 {
 | 
			
		||||
		dockerOption.Timeout = 60 * time.Second
 | 
			
		||||
	}
 | 
			
		||||
	files, err := analyzer.Analyze(ctx, domain, dockerOption)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, nil, xerrors.Errorf("Failed scan files %q, %w", domain, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	containerOs, err := analyzer.GetOS(files)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, nil, xerrors.Errorf("Failed scan os %q, %w", domain, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pkgs, err = analyzer.GetPackages(files)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, nil, xerrors.Errorf("Failed scan pkgs %q, %w", domain, err)
 | 
			
		||||
	}
 | 
			
		||||
	libs, err = analyzer.GetLibraries(files)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, nil, xerrors.Errorf("Failed scan libs %q, %w", domain, err)
 | 
			
		||||
	}
 | 
			
		||||
	return &containerOs, pkgs, libs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func convertFanalToVulsPkg(pkgs []analyzer.Package) (map[string]models.Package, map[string]models.SrcPackage) {
 | 
			
		||||
	modelPkgs := map[string]models.Package{}
 | 
			
		||||
	modelSrcPkgs := map[string]models.SrcPackage{}
 | 
			
		||||
	for _, pkg := range pkgs {
 | 
			
		||||
		version := pkg.Version
 | 
			
		||||
		if pkg.Epoch != 0 {
 | 
			
		||||
			version = fmt.Sprintf("%d:%s", pkg.Epoch, pkg.Version)
 | 
			
		||||
		}
 | 
			
		||||
		modelPkgs[pkg.Name] = models.Package{
 | 
			
		||||
			Name:    pkg.Name,
 | 
			
		||||
			Release: pkg.Release,
 | 
			
		||||
			Version: version,
 | 
			
		||||
			Arch:    pkg.Arch,
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// add SrcPacks
 | 
			
		||||
		if pkg.Name != pkg.SrcName {
 | 
			
		||||
			if pack, ok := modelSrcPkgs[pkg.SrcName]; ok {
 | 
			
		||||
				pack.AddBinaryName(pkg.Name)
 | 
			
		||||
				modelSrcPkgs[pkg.SrcName] = pack
 | 
			
		||||
			} else {
 | 
			
		||||
				modelSrcPkgs[pkg.SrcName] = models.SrcPackage{
 | 
			
		||||
					Name:        pkg.SrcName,
 | 
			
		||||
					Version:     pkg.SrcVersion,
 | 
			
		||||
					BinaryNames: []string{pkg.Name},
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return modelPkgs, modelSrcPkgs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newContainerImage(c config.ServerInfo, pkgs []analyzer.Package, libs []models.LibraryScanner) *image {
 | 
			
		||||
	modelPkgs, modelSrcPkgs := convertFanalToVulsPkg(pkgs)
 | 
			
		||||
	d := &image{
 | 
			
		||||
		base: base{
 | 
			
		||||
			osPackages: osPackages{
 | 
			
		||||
				Packages:    modelPkgs,
 | 
			
		||||
				SrcPackages: modelSrcPkgs,
 | 
			
		||||
				VulnInfos:   models.VulnInfos{},
 | 
			
		||||
			},
 | 
			
		||||
			LibraryScanners: libs,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	d.log = util.NewCustomLogger(c)
 | 
			
		||||
	d.setServerInfo(c)
 | 
			
		||||
	return d
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *image) checkScanMode() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *image) checkIfSudoNoPasswd() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *image) checkDeps() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *image) preCure() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *image) postScan() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *image) scanPackages() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *image) parseInstalledPackages(string) (models.Packages, models.SrcPackages, error) {
 | 
			
		||||
	return nil, nil, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *image) detectPlatform() {
 | 
			
		||||
	o.setPlatform(models.Platform{Name: "image"})
 | 
			
		||||
}
 | 
			
		||||
@@ -30,6 +30,7 @@ import (
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	version "github.com/knqyf263/go-deb-version"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// inherit OsTypeInterface
 | 
			
		||||
@@ -62,7 +63,7 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
 | 
			
		||||
			return false, deb, nil
 | 
			
		||||
		}
 | 
			
		||||
		if r.ExitStatus == 255 {
 | 
			
		||||
			return false, deb, fmt.Errorf("Unable to connect via SSH. Scan with -vvv option to print SSH debugging messages and check SSH settings. If you have never SSH to the host to be scanned, SSH to the host before scanning in order to add the HostKey. %s@%s port: %s\n%s", c.User, c.Host, c.Port, r)
 | 
			
		||||
			return false, deb, xerrors.Errorf("Unable to connect via SSH. Scan with -vvv option to print SSH debugging messages and check SSH settings. If you have never SSH to the host to be scanned, SSH to the host before scanning in order to add the HostKey. %s@%s port: %s\n%s", c.User, c.Host, c.Port, r)
 | 
			
		||||
		}
 | 
			
		||||
		util.Log.Debugf("Not Debian like Linux. %s", r)
 | 
			
		||||
		return false, deb, nil
 | 
			
		||||
@@ -160,7 +161,7 @@ func (o *debian) checkIfSudoNoPasswd() error {
 | 
			
		||||
		r := o.exec(cmd, sudo)
 | 
			
		||||
		if !r.isSuccess() {
 | 
			
		||||
			o.log.Errorf("sudo error on %s", r)
 | 
			
		||||
			return fmt.Errorf("Failed to sudo: %s", r)
 | 
			
		||||
			return xerrors.Errorf("Failed to sudo: %s", r)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -171,7 +172,7 @@ func (o *debian) checkIfSudoNoPasswd() error {
 | 
			
		||||
		r := o.exec(cmd, sudo)
 | 
			
		||||
		if !r.isSuccess() {
 | 
			
		||||
			o.log.Errorf("sudo error on %s", r)
 | 
			
		||||
			return fmt.Errorf("Failed to sudo: %s", r)
 | 
			
		||||
			return xerrors.Errorf("Failed to sudo: %s", r)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -230,7 +231,7 @@ func (o *debian) checkDeps() error {
 | 
			
		||||
			}
 | 
			
		||||
			dep.logFunc(msg)
 | 
			
		||||
			if dep.required {
 | 
			
		||||
				return fmt.Errorf(msg)
 | 
			
		||||
				return xerrors.New(msg)
 | 
			
		||||
			}
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
@@ -242,7 +243,7 @@ func (o *debian) checkDeps() error {
 | 
			
		||||
			}
 | 
			
		||||
			dep.logFunc(msg)
 | 
			
		||||
			if dep.required {
 | 
			
		||||
				return fmt.Errorf(msg)
 | 
			
		||||
				return xerrors.New(msg)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -262,7 +263,12 @@ func (o *debian) preCure() error {
 | 
			
		||||
 | 
			
		||||
func (o *debian) postScan() error {
 | 
			
		||||
	if o.getServerInfo().Mode.IsDeep() || o.getServerInfo().Mode.IsFastRoot() {
 | 
			
		||||
		return o.checkrestart()
 | 
			
		||||
		if err := o.checkrestart(); err != nil {
 | 
			
		||||
			err = xerrors.Errorf("Failed to scan need-restarting processes: %w", err)
 | 
			
		||||
			o.log.Warnf("err: %+v", err)
 | 
			
		||||
			o.warns = append(o.warns, err)
 | 
			
		||||
			// Only warning this error
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@@ -281,8 +287,9 @@ func (o *debian) scanPackages() error {
 | 
			
		||||
	}
 | 
			
		||||
	rebootRequired, err := o.rebootRequired()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		o.log.Errorf("Failed to detect the kernel reboot required: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
		o.log.Warnf("Failed to detect the kernel reboot required: %s", err)
 | 
			
		||||
		o.warns = append(o.warns, err)
 | 
			
		||||
		// Only warning this error
 | 
			
		||||
	}
 | 
			
		||||
	o.Kernel = models.Kernel{
 | 
			
		||||
		Version:        version,
 | 
			
		||||
@@ -323,7 +330,7 @@ func (o *debian) rebootRequired() (bool, error) {
 | 
			
		||||
	case 1:
 | 
			
		||||
		return false, nil
 | 
			
		||||
	default:
 | 
			
		||||
		return false, fmt.Errorf("Failed to check reboot reauired: %s", r)
 | 
			
		||||
		return false, xerrors.Errorf("Failed to check reboot reauired: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -333,7 +340,7 @@ func (o *debian) scanInstalledPackages() (models.Packages, models.Packages, mode
 | 
			
		||||
	updatable := models.Packages{}
 | 
			
		||||
	r := o.exec(dpkgQuery, noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return nil, nil, nil, fmt.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
		return nil, nil, nil, xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	installed, srcPacks, err := o.parseInstalledPackages(r.Stdout)
 | 
			
		||||
@@ -364,7 +371,7 @@ func (o *debian) scanInstalledPackages() (models.Packages, models.Packages, mode
 | 
			
		||||
	// Fill the candidate versions of upgradable packages
 | 
			
		||||
	err = o.fillCandidateVersion(updatable)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, nil, fmt.Errorf("Failed to fill candidate versions. err: %s", err)
 | 
			
		||||
		return nil, nil, nil, xerrors.Errorf("Failed to fill candidate versions. err: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	installed.MergeNewVersion(updatable)
 | 
			
		||||
 | 
			
		||||
@@ -383,7 +390,7 @@ func (o *debian) parseInstalledPackages(stdout string) (models.Packages, models.
 | 
			
		||||
		if trimmed := strings.TrimSpace(line); len(trimmed) != 0 {
 | 
			
		||||
			name, status, version, srcName, srcVersion, err := o.parseScannedPackagesLine(trimmed)
 | 
			
		||||
			if err != nil || len(status) < 2 {
 | 
			
		||||
				return nil, nil, fmt.Errorf(
 | 
			
		||||
				return nil, nil, xerrors.Errorf(
 | 
			
		||||
					"Debian: Failed to parse package line: %s", line)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
@@ -448,14 +455,14 @@ func (o *debian) parseScannedPackagesLine(line string) (name, status, version, s
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return "", "", "", "", "", fmt.Errorf("Unknown format: %s", line)
 | 
			
		||||
	return "", "", "", "", "", xerrors.Errorf("Unknown format: %s", line)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *debian) aptGetUpdate() error {
 | 
			
		||||
	o.log.Infof("apt-get update...")
 | 
			
		||||
	cmd := util.PrependProxyEnv("apt-get update")
 | 
			
		||||
	if r := o.exec(cmd, sudo); !r.isSuccess() {
 | 
			
		||||
		return fmt.Errorf("Failed to apt-get update: %s", r)
 | 
			
		||||
		return xerrors.Errorf("Failed to apt-get update: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@@ -477,7 +484,7 @@ func (o *debian) scanUnsecurePackages(updatable models.Packages) (models.VulnInf
 | 
			
		||||
	// Collect CVE information of upgradable packages
 | 
			
		||||
	vulnInfos, err := o.scanChangelogs(updatable, meta)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("Failed to scan unsecure packages. err: %s", err)
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to scan unsecure packages. err: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return vulnInfos, nil
 | 
			
		||||
@@ -487,7 +494,7 @@ func (o *debian) ensureChangelogCache(current cache.Meta) (*cache.Meta, error) {
 | 
			
		||||
	// Search from cache
 | 
			
		||||
	cached, found, err := cache.DB.GetMeta(current.Name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf(
 | 
			
		||||
		return nil, xerrors.Errorf(
 | 
			
		||||
			"Failed to get meta. Please remove cache.db and then try again. err: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -495,7 +502,7 @@ func (o *debian) ensureChangelogCache(current cache.Meta) (*cache.Meta, error) {
 | 
			
		||||
		o.log.Debugf("Not found in meta: %s", current.Name)
 | 
			
		||||
		err = cache.DB.EnsureBuckets(current)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, fmt.Errorf("Failed to ensure buckets. err: %s", err)
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to ensure buckets. err: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
		return ¤t, nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -505,7 +512,7 @@ func (o *debian) ensureChangelogCache(current cache.Meta) (*cache.Meta, error) {
 | 
			
		||||
		o.log.Debugf("Need to refesh meta: %s", current.Name)
 | 
			
		||||
		err = cache.DB.EnsureBuckets(current)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, fmt.Errorf("Failed to ensure buckets. err: %s", err)
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to ensure buckets. err: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
		return ¤t, nil
 | 
			
		||||
 | 
			
		||||
@@ -526,17 +533,17 @@ func (o *debian) fillCandidateVersion(updatables models.Packages) (err error) {
 | 
			
		||||
	cmd := fmt.Sprintf("LANGUAGE=en_US.UTF-8 apt-cache policy %s", strings.Join(names, " "))
 | 
			
		||||
	r := o.exec(cmd, noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return fmt.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
		return xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	packAptPolicy := o.splitAptCachePolicy(r.Stdout)
 | 
			
		||||
	for k, v := range packAptPolicy {
 | 
			
		||||
		ver, err := o.parseAptCachePolicy(v, k)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("Failed to parse %s", err)
 | 
			
		||||
			return xerrors.Errorf("Failed to parse %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		pack, ok := updatables[k]
 | 
			
		||||
		if !ok {
 | 
			
		||||
			return fmt.Errorf("Not found: %s", k)
 | 
			
		||||
			return xerrors.Errorf("Not found: %s", k)
 | 
			
		||||
		}
 | 
			
		||||
		pack.NewVersion = ver.Candidate
 | 
			
		||||
		pack.Repository = ver.Repo
 | 
			
		||||
@@ -551,7 +558,7 @@ func (o *debian) getUpdatablePackNames() (packNames []string, err error) {
 | 
			
		||||
	if r.isSuccess(0, 1) {
 | 
			
		||||
		return o.parseAptGetUpgrade(r.Stdout)
 | 
			
		||||
	}
 | 
			
		||||
	return packNames, fmt.Errorf(
 | 
			
		||||
	return packNames, xerrors.Errorf(
 | 
			
		||||
		"Failed to %s. status: %d, stdout: %s, stderr: %s",
 | 
			
		||||
		cmd, r.ExitStatus, r.Stdout, r.Stderr)
 | 
			
		||||
}
 | 
			
		||||
@@ -573,11 +580,11 @@ func (o *debian) parseAptGetUpgrade(stdout string) (updatableNames []string, err
 | 
			
		||||
		if len(result) == 2 {
 | 
			
		||||
			nUpdatable, err := strconv.Atoi(result[1])
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, fmt.Errorf(
 | 
			
		||||
				return nil, xerrors.Errorf(
 | 
			
		||||
					"Failed to scan upgradable packages number. line: %s", line)
 | 
			
		||||
			}
 | 
			
		||||
			if nUpdatable != len(updatableNames) {
 | 
			
		||||
				return nil, fmt.Errorf(
 | 
			
		||||
				return nil, xerrors.Errorf(
 | 
			
		||||
					"Failed to scan upgradable packages, expected: %s, detected: %d",
 | 
			
		||||
					result[1], len(updatableNames))
 | 
			
		||||
			}
 | 
			
		||||
@@ -593,7 +600,7 @@ func (o *debian) parseAptGetUpgrade(stdout string) (updatableNames []string, err
 | 
			
		||||
	}
 | 
			
		||||
	if !stopLineFound {
 | 
			
		||||
		// There are upgrades, but not found the stop line.
 | 
			
		||||
		return nil, fmt.Errorf("Failed to scan upgradable packages")
 | 
			
		||||
		return nil, xerrors.New("Failed to scan upgradable packages")
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
@@ -677,11 +684,11 @@ func (o *debian) scanChangelogs(updatablePacks models.Packages, meta *cache.Meta
 | 
			
		||||
		case err := <-errChan:
 | 
			
		||||
			errs = append(errs, err)
 | 
			
		||||
		case <-timeout:
 | 
			
		||||
			errs = append(errs, fmt.Errorf("Timeout scanPackageCveIDs"))
 | 
			
		||||
			errs = append(errs, xerrors.New("Timeout scanPackageCveIDs"))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if 0 < len(errs) {
 | 
			
		||||
		return nil, fmt.Errorf("%v", errs)
 | 
			
		||||
		return nil, xerrors.Errorf("errs: %w", errs)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var cveIDs []DetectedCveID
 | 
			
		||||
@@ -691,9 +698,9 @@ func (o *debian) scanChangelogs(updatablePacks models.Packages, meta *cache.Meta
 | 
			
		||||
	o.log.Debugf("%d Cves are found. cves: %v", len(cveIDs), cveIDs)
 | 
			
		||||
	vinfos := models.VulnInfos{}
 | 
			
		||||
	for cveID, names := range cvePackages {
 | 
			
		||||
		affected := models.PackageStatuses{}
 | 
			
		||||
		affected := models.PackageFixStatuses{}
 | 
			
		||||
		for _, n := range names {
 | 
			
		||||
			affected = append(affected, models.PackageStatus{Name: n})
 | 
			
		||||
			affected = append(affected, models.PackageFixStatus{Name: n})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		vinfos[cveID.CveID] = models.VulnInfo{
 | 
			
		||||
@@ -764,7 +771,7 @@ func (o *debian) fetchParseChangelog(pack models.Package) ([]DetectedCveID, *mod
 | 
			
		||||
		err := cache.DB.PutChangelog(
 | 
			
		||||
			o.getServerInfo().GetServerName(), pack.Name, pack.Changelog.Contents)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, nil, fmt.Errorf("Failed to put changelog into cache")
 | 
			
		||||
			return nil, nil, xerrors.New("Failed to put changelog into cache")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -838,7 +845,7 @@ var cveRe = regexp.MustCompile(`(CVE-\d{4}-\d{4,})`)
 | 
			
		||||
func (o *debian) parseChangelog(changelog, name, ver string, confidence models.Confidence) ([]DetectedCveID, *models.Package, error) {
 | 
			
		||||
	installedVer, err := version.NewVersion(ver)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, fmt.Errorf("Failed to parse installed version: %s, %s", ver, err)
 | 
			
		||||
		return nil, nil, xerrors.Errorf("Failed to parse installed version: %s, err: %w", ver, err)
 | 
			
		||||
	}
 | 
			
		||||
	buf, cveIDs := []string{}, []string{}
 | 
			
		||||
	scanner := bufio.NewScanner(strings.NewReader(changelog))
 | 
			
		||||
@@ -876,7 +883,7 @@ func (o *debian) parseChangelog(changelog, name, ver string, confidence models.C
 | 
			
		||||
			Contents: "",
 | 
			
		||||
			Method:   models.FailedToFindVersionInChangelog,
 | 
			
		||||
		}
 | 
			
		||||
		return nil, &pack, fmt.Errorf(
 | 
			
		||||
		return nil, &pack, xerrors.Errorf(
 | 
			
		||||
			"Failed to scan CVE IDs. The version is not in changelog. name: %s, version: %s",
 | 
			
		||||
			name, ver)
 | 
			
		||||
	}
 | 
			
		||||
@@ -958,7 +965,7 @@ func (o *debian) parseAptCachePolicy(stdout, name string) (packCandidateVer, err
 | 
			
		||||
		}
 | 
			
		||||
	nextline:
 | 
			
		||||
	}
 | 
			
		||||
	return ver, fmt.Errorf("Unknown Format: %s", stdout)
 | 
			
		||||
	return ver, xerrors.Errorf("Unknown Format: %s", stdout)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *debian) checkrestart() error {
 | 
			
		||||
@@ -971,7 +978,7 @@ func (o *debian) checkrestart() error {
 | 
			
		||||
	cmd := "LANGUAGE=en_US.UTF-8 checkrestart"
 | 
			
		||||
	r := o.exec(cmd, sudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return fmt.Errorf(
 | 
			
		||||
		return xerrors.Errorf(
 | 
			
		||||
			"Failed to %s. status: %d, stdout: %s, stderr: %s",
 | 
			
		||||
			cmd, r.ExitStatus, r.Stdout, r.Stderr)
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -33,6 +33,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/crypto/ssh"
 | 
			
		||||
	"golang.org/x/crypto/ssh/agent"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/cenkalti/backoff"
 | 
			
		||||
	conf "github.com/future-architect/vuls/config"
 | 
			
		||||
@@ -124,7 +125,7 @@ func parallelExec(fn func(osTypeInterface) error, timeoutSec ...int) {
 | 
			
		||||
			if len(s.getErrs()) == 0 {
 | 
			
		||||
				successes = append(successes, s)
 | 
			
		||||
			} else {
 | 
			
		||||
				util.Log.Errorf("Error: %s, err: %s",
 | 
			
		||||
				util.Log.Errorf("Error on %s, err: %+v",
 | 
			
		||||
					s.getServerInfo().GetServerName(), s.getErrs())
 | 
			
		||||
				errServers = append(errServers, s)
 | 
			
		||||
			}
 | 
			
		||||
@@ -145,10 +146,10 @@ func parallelExec(fn func(osTypeInterface) error, timeoutSec ...int) {
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if !found {
 | 
			
		||||
				msg := fmt.Sprintf("Timed out: %s",
 | 
			
		||||
				err := xerrors.Errorf("Timed out: %s",
 | 
			
		||||
					s.getServerInfo().GetServerName())
 | 
			
		||||
				util.Log.Errorf(msg)
 | 
			
		||||
				s.setErrs([]error{fmt.Errorf(msg)})
 | 
			
		||||
				util.Log.Errorf("%+v", err)
 | 
			
		||||
				s.setErrs([]error{err})
 | 
			
		||||
				errServers = append(errServers, s)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -161,8 +162,7 @@ func exec(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (resul
 | 
			
		||||
	logger := getSSHLogger(log...)
 | 
			
		||||
	logger.Debugf("Executing... %s", strings.Replace(cmd, "\n", "", -1))
 | 
			
		||||
 | 
			
		||||
	if c.Port == "local" &&
 | 
			
		||||
		(c.Host == "127.0.0.1" || c.Host == "localhost") {
 | 
			
		||||
	if isLocalExec(c.Port, c.Host) {
 | 
			
		||||
		result = localExec(c, cmd, sudo)
 | 
			
		||||
	} else if conf.Conf.SSHNative {
 | 
			
		||||
		result = sshExecNative(c, cmd, sudo)
 | 
			
		||||
@@ -174,6 +174,10 @@ func exec(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (resul
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isLocalExec(port, host string) bool {
 | 
			
		||||
	return port == "local" && (host == "127.0.0.1" || host == "localhost")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func localExec(c conf.ServerInfo, cmdstr string, sudo bool) (result execResult) {
 | 
			
		||||
	cmdstr = decorateCmd(c, cmdstr, sudo)
 | 
			
		||||
	var cmd *ex.Cmd
 | 
			
		||||
@@ -222,8 +226,8 @@ func sshExecNative(c conf.ServerInfo, cmd string, sudo bool) (result execResult)
 | 
			
		||||
 | 
			
		||||
	var session *ssh.Session
 | 
			
		||||
	if session, err = client.NewSession(); err != nil {
 | 
			
		||||
		result.Error = fmt.Errorf(
 | 
			
		||||
			"Failed to create a new session. servername: %s, err: %s",
 | 
			
		||||
		result.Error = xerrors.Errorf(
 | 
			
		||||
			"Failed to create a new session. servername: %s, err: %w",
 | 
			
		||||
			c.ServerName, err)
 | 
			
		||||
		result.ExitStatus = 999
 | 
			
		||||
		return
 | 
			
		||||
@@ -237,8 +241,8 @@ func sshExecNative(c conf.ServerInfo, cmd string, sudo bool) (result execResult)
 | 
			
		||||
		ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
 | 
			
		||||
	}
 | 
			
		||||
	if err = session.RequestPty("xterm", 400, 1000, modes); err != nil {
 | 
			
		||||
		result.Error = fmt.Errorf(
 | 
			
		||||
			"Failed to request for pseudo terminal. servername: %s, err: %s",
 | 
			
		||||
		result.Error = xerrors.Errorf(
 | 
			
		||||
			"Failed to request for pseudo terminal. servername: %s, err: %w",
 | 
			
		||||
			c.ServerName, err)
 | 
			
		||||
		result.ExitStatus = 999
 | 
			
		||||
		return
 | 
			
		||||
@@ -462,7 +466,7 @@ func addKeyAuth(auths []ssh.AuthMethod, keypath string, keypassword string) ([]s
 | 
			
		||||
	// get first pem block
 | 
			
		||||
	block, _ := pem.Decode(pemBytes)
 | 
			
		||||
	if block == nil {
 | 
			
		||||
		return auths, fmt.Errorf("no key found in %s", keypath)
 | 
			
		||||
		return auths, xerrors.Errorf("no key found in %s", keypath)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// handle plain and encrypted keyfiles
 | 
			
		||||
@@ -499,6 +503,6 @@ func parsePemBlock(block *pem.Block) (interface{}, error) {
 | 
			
		||||
	case "DSA PRIVATE KEY":
 | 
			
		||||
		return ssh.ParseDSAPrivateKey(block.Bytes)
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, fmt.Errorf("Unsupported key type %q", block.Type)
 | 
			
		||||
		return nil, xerrors.Errorf("Unsupported key type %q", block.Type)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -18,13 +18,13 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
package scan
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// inherit OsTypeInterface
 | 
			
		||||
@@ -69,7 +69,7 @@ func detectFreebsd(c config.ServerInfo) (itsMe bool, bsd osTypeInterface) {
 | 
			
		||||
 | 
			
		||||
func (o *bsd) checkScanMode() error {
 | 
			
		||||
	if o.getServerInfo().Mode.IsOffline() {
 | 
			
		||||
		return fmt.Errorf("Remove offline scan mode, FreeBSD needs internet connection")
 | 
			
		||||
		return xerrors.New("Remove offline scan mode, FreeBSD needs internet connection")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@@ -101,7 +101,7 @@ func (o *bsd) postScan() error {
 | 
			
		||||
func (o *bsd) detectIPAddr() (err error) {
 | 
			
		||||
	r := o.exec("/sbin/ifconfig", noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return fmt.Errorf("Failed to detect IP address: %v", r)
 | 
			
		||||
		return xerrors.Errorf("Failed to detect IP address: %v", r)
 | 
			
		||||
	}
 | 
			
		||||
	o.ServerInfo.IPv4Addrs, o.ServerInfo.IPv6Addrs = o.parseIfconfig(r.Stdout)
 | 
			
		||||
	return nil
 | 
			
		||||
@@ -143,12 +143,13 @@ func (o *bsd) scanPackages() error {
 | 
			
		||||
		Version: version,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rebootRequired, err := o.rebootRequired()
 | 
			
		||||
	o.Kernel.RebootRequired, err = o.rebootRequired()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		o.log.Errorf("Failed to detect the kernel reboot required: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
		err = xerrors.Errorf("Failed to detect the kernel reboot required: %w", err)
 | 
			
		||||
		o.log.Warnf("err: %+v", err)
 | 
			
		||||
		o.warns = append(o.warns, err)
 | 
			
		||||
		// Only warning this error
 | 
			
		||||
	}
 | 
			
		||||
	o.Kernel.RebootRequired = rebootRequired
 | 
			
		||||
 | 
			
		||||
	packs, err := o.scanInstalledPackages()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -173,7 +174,7 @@ func (o *bsd) parseInstalledPackages(string) (models.Packages, models.SrcPackage
 | 
			
		||||
func (o *bsd) rebootRequired() (bool, error) {
 | 
			
		||||
	r := o.exec("freebsd-version -k", noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return false, fmt.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
		return false, xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	return o.Kernel.Release != strings.TrimSpace(r.Stdout), nil
 | 
			
		||||
}
 | 
			
		||||
@@ -182,7 +183,7 @@ func (o *bsd) scanInstalledPackages() (models.Packages, error) {
 | 
			
		||||
	cmd := util.PrependProxyEnv("pkg version -v")
 | 
			
		||||
	r := o.exec(cmd, noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return nil, fmt.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	return o.parsePkgVersion(r.Stdout), nil
 | 
			
		||||
}
 | 
			
		||||
@@ -192,13 +193,13 @@ func (o *bsd) scanUnsecurePackages() (models.VulnInfos, error) {
 | 
			
		||||
	cmd := "rm -f " + vulndbPath
 | 
			
		||||
	r := o.exec(cmd, noSudo)
 | 
			
		||||
	if !r.isSuccess(0) {
 | 
			
		||||
		return nil, fmt.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd = util.PrependProxyEnv("pkg audit -F -r -f " + vulndbPath)
 | 
			
		||||
	r = o.exec(cmd, noSudo)
 | 
			
		||||
	if !r.isSuccess(0, 1) {
 | 
			
		||||
		return nil, fmt.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	if r.ExitStatus == 0 {
 | 
			
		||||
		// no vulnerabilities
 | 
			
		||||
@@ -214,7 +215,7 @@ func (o *bsd) scanUnsecurePackages() (models.VulnInfos, error) {
 | 
			
		||||
		}
 | 
			
		||||
		pack, found := o.Packages[name]
 | 
			
		||||
		if !found {
 | 
			
		||||
			return nil, fmt.Errorf("Vulnerable package: %s is not found", name)
 | 
			
		||||
			return nil, xerrors.Errorf("Vulnerable package: %s is not found", name)
 | 
			
		||||
		}
 | 
			
		||||
		packAdtRslt = append(packAdtRslt, pkgAuditResult{
 | 
			
		||||
			pack: pack,
 | 
			
		||||
@@ -247,9 +248,9 @@ func (o *bsd) scanUnsecurePackages() (models.VulnInfos, error) {
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		affected := models.PackageStatuses{}
 | 
			
		||||
		affected := models.PackageFixStatuses{}
 | 
			
		||||
		for name := range packs {
 | 
			
		||||
			affected = append(affected, models.PackageStatus{
 | 
			
		||||
			affected = append(affected, models.PackageFixStatus{
 | 
			
		||||
				Name: name,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -52,59 +52,11 @@ func (o *oracle) depsFast() []string {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *oracle) depsFastRoot() []string {
 | 
			
		||||
	if o.getServerInfo().Mode.IsOffline() {
 | 
			
		||||
		//TODO
 | 
			
		||||
		// return []string{"yum-plugin-ps"}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	majorVersion, _ := o.Distro.MajorVersion()
 | 
			
		||||
	switch majorVersion {
 | 
			
		||||
	case 5:
 | 
			
		||||
		return []string{
 | 
			
		||||
			"yum-utils",
 | 
			
		||||
			"yum-security",
 | 
			
		||||
		}
 | 
			
		||||
	case 6:
 | 
			
		||||
		return []string{
 | 
			
		||||
			"yum-utils",
 | 
			
		||||
			"yum-plugin-security",
 | 
			
		||||
			//TODO
 | 
			
		||||
			// return []string{"yum-plugin-ps"}
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		return []string{
 | 
			
		||||
			"yum-utils",
 | 
			
		||||
			//TODO
 | 
			
		||||
			// return []string{"yum-plugin-ps"}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return []string{"yum-utils"}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *oracle) depsDeep() []string {
 | 
			
		||||
	majorVersion, _ := o.Distro.MajorVersion()
 | 
			
		||||
	switch majorVersion {
 | 
			
		||||
	case 5:
 | 
			
		||||
		return []string{
 | 
			
		||||
			"yum-utils",
 | 
			
		||||
			"yum-security",
 | 
			
		||||
			"yum-changelog",
 | 
			
		||||
		}
 | 
			
		||||
	case 6:
 | 
			
		||||
		return []string{
 | 
			
		||||
			"yum-utils",
 | 
			
		||||
			"yum-plugin-security",
 | 
			
		||||
			"yum-plugin-changelog",
 | 
			
		||||
			//TODO
 | 
			
		||||
			// return []string{"yum-plugin-ps"}
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		return []string{
 | 
			
		||||
			"yum-utils",
 | 
			
		||||
			"yum-plugin-changelog",
 | 
			
		||||
			//TODO
 | 
			
		||||
			// return []string{"yum-plugin-ps"}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return o.depsFastRoot()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *oracle) checkIfSudoNoPasswd() error {
 | 
			
		||||
@@ -126,21 +78,7 @@ func (o *oracle) sudoNoPasswdCmdsFastRoot() []cmd {
 | 
			
		||||
	if o.getServerInfo().Mode.IsOffline() {
 | 
			
		||||
		return cmds
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	majorVersion, _ := o.Distro.MajorVersion()
 | 
			
		||||
	if majorVersion < 6 {
 | 
			
		||||
		return []cmd{
 | 
			
		||||
			{"yum repolist --color=never", exitStatusZero},
 | 
			
		||||
			{"yum list-security --security --color=never", exitStatusZero},
 | 
			
		||||
			{"yum info-security --color=never", exitStatusZero},
 | 
			
		||||
			{"repoquery -h", exitStatusZero},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return append(cmds,
 | 
			
		||||
		cmd{"yum repolist --color=never", exitStatusZero},
 | 
			
		||||
		cmd{"yum updateinfo list updates --security --color=never", exitStatusZero},
 | 
			
		||||
		cmd{"yum updateinfo updates --security --color=never", exitStatusZero},
 | 
			
		||||
		cmd{"repoquery -h", exitStatusZero})
 | 
			
		||||
	return append(cmds, cmd{"repoquery -h", exitStatusZero})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *oracle) sudoNoPasswdCmdsDeep() []cmd {
 | 
			
		||||
@@ -153,15 +91,6 @@ func (o rootPrivOracle) repoquery() bool {
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o rootPrivOracle) yumRepolist() bool {
 | 
			
		||||
func (o rootPrivOracle) yumMakeCache() bool {
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o rootPrivOracle) yumUpdateInfo() bool {
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// root privilege isn't needed
 | 
			
		||||
func (o rootPrivOracle) yumChangelog() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -22,11 +22,11 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	ver "github.com/knqyf263/go-rpm-version"
 | 
			
		||||
)
 | 
			
		||||
@@ -141,9 +141,7 @@ type redhatBase struct {
 | 
			
		||||
 | 
			
		||||
type rootPriv interface {
 | 
			
		||||
	repoquery() bool
 | 
			
		||||
	yumRepolist() bool
 | 
			
		||||
	yumUpdateInfo() bool
 | 
			
		||||
	yumChangelog() bool
 | 
			
		||||
	yumMakeCache() bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type cmd struct {
 | 
			
		||||
@@ -160,7 +158,7 @@ func (o *redhatBase) execCheckIfSudoNoPasswd(cmds []cmd) error {
 | 
			
		||||
		r := o.exec(util.PrependProxyEnv(cmd), sudo)
 | 
			
		||||
		if !r.isSuccess(c.expectedStatusCodes...) {
 | 
			
		||||
			o.log.Errorf("Check sudo or proxy settings: %s", r)
 | 
			
		||||
			return fmt.Errorf("Failed to sudo: %s", r)
 | 
			
		||||
			return xerrors.Errorf("Failed to sudo: %s", r)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	o.log.Infof("Sudo... Pass")
 | 
			
		||||
@@ -173,7 +171,7 @@ func (o *redhatBase) execCheckDeps(packNames []string) error {
 | 
			
		||||
		if r := o.exec(cmd, noSudo); !r.isSuccess() {
 | 
			
		||||
			msg := fmt.Sprintf("%s is not installed", name)
 | 
			
		||||
			o.log.Errorf(msg)
 | 
			
		||||
			return fmt.Errorf(msg)
 | 
			
		||||
			return xerrors.New(msg)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	o.log.Infof("Dependencies ... Pass")
 | 
			
		||||
@@ -182,7 +180,8 @@ func (o *redhatBase) execCheckDeps(packNames []string) error {
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) preCure() error {
 | 
			
		||||
	if err := o.detectIPAddr(); err != nil {
 | 
			
		||||
		o.log.Debugf("Failed to detect IP addresses: %s", err)
 | 
			
		||||
		o.log.Warnf("Failed to detect IP addresses: %s", err)
 | 
			
		||||
		o.warns = append(o.warns, err)
 | 
			
		||||
	}
 | 
			
		||||
	// Ignore this error as it just failed to detect the IP addresses
 | 
			
		||||
	return nil
 | 
			
		||||
@@ -191,12 +190,19 @@ func (o *redhatBase) preCure() error {
 | 
			
		||||
func (o *redhatBase) postScan() error {
 | 
			
		||||
	if o.isExecYumPS() {
 | 
			
		||||
		if err := o.yumPS(); err != nil {
 | 
			
		||||
			return fmt.Errorf("Failed to execute yum-ps: %s", err)
 | 
			
		||||
			err = xerrors.Errorf("Failed to execute yum-ps: %w", err)
 | 
			
		||||
			o.log.Warnf("err: %+v", err)
 | 
			
		||||
			o.warns = append(o.warns, err)
 | 
			
		||||
			// Only warning this error
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.isExecNeedsRestarting() {
 | 
			
		||||
		if err := o.needsRestarting(); err != nil {
 | 
			
		||||
			return fmt.Errorf("Failed to execute need-restarting: %s", err)
 | 
			
		||||
			err = xerrors.Errorf("Failed to execute need-restarting: %w", err)
 | 
			
		||||
			o.log.Warnf("err: %+v", err)
 | 
			
		||||
			o.warns = append(o.warns, err)
 | 
			
		||||
			// Only warning this error
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
@@ -214,43 +220,36 @@ func (o *redhatBase) scanPackages() error {
 | 
			
		||||
		o.log.Errorf("Failed to scan installed packages: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	o.Packages = installed
 | 
			
		||||
 | 
			
		||||
	rebootRequired, err := o.rebootRequired()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		o.log.Errorf("Failed to detect the kernel reboot required: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
		err = xerrors.Errorf("Failed to detect the kernel reboot required: %w", err)
 | 
			
		||||
		o.log.Warnf("err: %+v", err)
 | 
			
		||||
		o.warns = append(o.warns, err)
 | 
			
		||||
		// Only warning this error
 | 
			
		||||
	} else {
 | 
			
		||||
		o.Kernel.RebootRequired = rebootRequired
 | 
			
		||||
	}
 | 
			
		||||
	o.Kernel.RebootRequired = rebootRequired
 | 
			
		||||
 | 
			
		||||
	if o.getServerInfo().Mode.IsOffline() {
 | 
			
		||||
		switch o.Distro.Family {
 | 
			
		||||
		case config.Amazon:
 | 
			
		||||
			// nop
 | 
			
		||||
		default:
 | 
			
		||||
			o.Packages = installed
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	} else if o.Distro.Family == config.RedHat {
 | 
			
		||||
		if o.getServerInfo().Mode.IsFast() {
 | 
			
		||||
			o.Packages = installed
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	updatable, err := o.scanUpdatablePackages()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		o.log.Errorf("Failed to scan installed packages: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
		err = xerrors.Errorf("Failed to scan updatable packages: %w", err)
 | 
			
		||||
		o.log.Warnf("err: %+v", err)
 | 
			
		||||
		o.warns = append(o.warns, err)
 | 
			
		||||
		// Only warning this error
 | 
			
		||||
	} else {
 | 
			
		||||
		installed.MergeNewVersion(updatable)
 | 
			
		||||
		o.Packages = installed
 | 
			
		||||
	}
 | 
			
		||||
	installed.MergeNewVersion(updatable)
 | 
			
		||||
	o.Packages = installed
 | 
			
		||||
 | 
			
		||||
	var unsecures models.VulnInfos
 | 
			
		||||
	if unsecures, err = o.scanUnsecurePackages(updatable); err != nil {
 | 
			
		||||
		o.log.Errorf("Failed to scan vulnerable packages: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	o.VulnInfos = unsecures
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -258,7 +257,7 @@ func (o *redhatBase) rebootRequired() (bool, error) {
 | 
			
		||||
	r := o.exec("rpm -q --last kernel", noSudo)
 | 
			
		||||
	scanner := bufio.NewScanner(strings.NewReader(r.Stdout))
 | 
			
		||||
	if !r.isSuccess(0, 1) {
 | 
			
		||||
		return false, fmt.Errorf("Failed to detect the last installed kernel : %v", r)
 | 
			
		||||
		return false, xerrors.Errorf("Failed to detect the last installed kernel : %v", r)
 | 
			
		||||
	}
 | 
			
		||||
	if !r.isSuccess() || !scanner.Scan() {
 | 
			
		||||
		return false, nil
 | 
			
		||||
@@ -280,7 +279,7 @@ func (o *redhatBase) scanInstalledPackages() (models.Packages, error) {
 | 
			
		||||
 | 
			
		||||
	r := o.exec(rpmQa(o.Distro), noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return nil, fmt.Errorf("Scan packages failed: %s", r)
 | 
			
		||||
		return nil, xerrors.Errorf("Scan packages failed: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	installed, _, err := o.parseInstalledPackages(r.Stdout)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -332,7 +331,7 @@ func (o *redhatBase) parseInstalledPackagesLine(line string) (models.Package, er
 | 
			
		||||
	fields := strings.Fields(line)
 | 
			
		||||
	if len(fields) != 5 {
 | 
			
		||||
		return models.Package{},
 | 
			
		||||
			fmt.Errorf("Failed to parse package line: %s", line)
 | 
			
		||||
			xerrors.Errorf("Failed to parse package line: %s", line)
 | 
			
		||||
	}
 | 
			
		||||
	ver := ""
 | 
			
		||||
	epoch := fields[1]
 | 
			
		||||
@@ -350,15 +349,32 @@ func (o *redhatBase) parseInstalledPackagesLine(line string) (models.Package, er
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) yumMakeCache() error {
 | 
			
		||||
	cmd := `yum makecache`
 | 
			
		||||
	r := o.exec(util.PrependProxyEnv(cmd), o.sudo.yumMakeCache())
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) scanUpdatablePackages() (models.Packages, error) {
 | 
			
		||||
	cmd := `repoquery --all --pkgnarrow=updates --qf="%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{REPO}"`
 | 
			
		||||
	if err := o.yumMakeCache(); err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to `yum makecache`: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	isDnf := o.exec(util.PrependProxyEnv(`repoquery --version | grep dnf`), o.sudo.repoquery()).isSuccess()
 | 
			
		||||
	cmd := `repoquery --all --pkgnarrow=updates --qf='%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{REPO}'`
 | 
			
		||||
	if isDnf {
 | 
			
		||||
		cmd = `repoquery --upgrades --qf='%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{REPONAME}' -q`
 | 
			
		||||
	}
 | 
			
		||||
	for _, repo := range o.getServerInfo().Enablerepo {
 | 
			
		||||
		cmd += " --enablerepo=" + repo
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r := o.exec(util.PrependProxyEnv(cmd), o.sudo.repoquery())
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return nil, fmt.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Collect Updateble packages, installed, candidate version and repository.
 | 
			
		||||
@@ -393,7 +409,7 @@ func (o *redhatBase) parseUpdatablePacksLines(stdout string) (models.Packages, e
 | 
			
		||||
func (o *redhatBase) parseUpdatablePacksLine(line string) (models.Package, error) {
 | 
			
		||||
	fields := strings.Fields(line)
 | 
			
		||||
	if len(fields) < 5 {
 | 
			
		||||
		return models.Package{}, fmt.Errorf("Unknown format: %s, fields: %s", line, fields)
 | 
			
		||||
		return models.Package{}, xerrors.Errorf("Unknown format: %s, fields: %s", line, fields)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ver := ""
 | 
			
		||||
@@ -429,24 +445,6 @@ func (o *redhatBase) isExecScanUsingYum() bool {
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) isExecFillChangelogs() bool {
 | 
			
		||||
	if o.getServerInfo().Mode.IsOffline() {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	// Amazon linux has no changelos for updates
 | 
			
		||||
	return o.getServerInfo().Mode.IsDeep() &&
 | 
			
		||||
		o.Distro.Family != config.Amazon
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) isExecScanChangelogs() bool {
 | 
			
		||||
	if o.getServerInfo().Mode.IsOffline() ||
 | 
			
		||||
		o.getServerInfo().Mode.IsFast() ||
 | 
			
		||||
		o.getServerInfo().Mode.IsFastRoot() {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) isExecYumPS() bool {
 | 
			
		||||
	// RedHat has no yum-ps
 | 
			
		||||
	switch o.Distro.Family {
 | 
			
		||||
@@ -498,674 +496,25 @@ func (o *redhatBase) isExecNeedsRestarting() bool {
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) scanUnsecurePackages(updatable models.Packages) (models.VulnInfos, error) {
 | 
			
		||||
	if o.isExecFillChangelogs() {
 | 
			
		||||
		if err := o.fillChangelogs(updatable); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.isExecScanUsingYum() {
 | 
			
		||||
		return o.scanUsingYum(updatable)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Parse chnagelog because CentOS does not have security channel...
 | 
			
		||||
	if o.isExecScanChangelogs() {
 | 
			
		||||
		return o.scanChangelogs(updatable)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return models.VulnInfos{}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) fillChangelogs(updatables models.Packages) error {
 | 
			
		||||
	names := []string{}
 | 
			
		||||
	for name := range updatables {
 | 
			
		||||
		names = append(names, name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := o.fillDiffChangelogs(names); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	emptyChangelogPackNames := []string{}
 | 
			
		||||
	for _, pack := range o.Packages {
 | 
			
		||||
		if pack.NewVersion != "" && pack.Changelog.Contents == "" {
 | 
			
		||||
			emptyChangelogPackNames = append(emptyChangelogPackNames, pack.Name)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	i := 0
 | 
			
		||||
	for _, name := range emptyChangelogPackNames {
 | 
			
		||||
		i++
 | 
			
		||||
		o.log.Infof("(%d/%d) Fetched Changelogs %s", i, len(emptyChangelogPackNames), name)
 | 
			
		||||
		if err := o.fillDiffChangelogs([]string{name}); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) getAvailableChangelogs(packNames []string) (map[string]string, error) {
 | 
			
		||||
	yumopts := ""
 | 
			
		||||
	if 0 < len(o.getServerInfo().Enablerepo) {
 | 
			
		||||
		yumopts = " --enablerepo=" + strings.Join(o.getServerInfo().Enablerepo, ",")
 | 
			
		||||
	}
 | 
			
		||||
	if config.Conf.SkipBroken {
 | 
			
		||||
		yumopts += " --skip-broken"
 | 
			
		||||
	}
 | 
			
		||||
	if o.hasYumColorOption() {
 | 
			
		||||
		yumopts += " --color=never"
 | 
			
		||||
	}
 | 
			
		||||
	cmd := `yum changelog all updates %s %s | grep -A 1000000 "==================== Updated Packages ===================="`
 | 
			
		||||
	cmd = fmt.Sprintf(cmd, yumopts, strings.Join(packNames, " "))
 | 
			
		||||
 | 
			
		||||
	r := o.exec(util.PrependProxyEnv(cmd), o.sudo.yumChangelog())
 | 
			
		||||
	if !r.isSuccess(0, 1) {
 | 
			
		||||
		return nil, fmt.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return o.divideChangelogsIntoEachPackages(r.Stdout), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Divide available change logs of all updatable packages into each package's changelog
 | 
			
		||||
func (o *redhatBase) divideChangelogsIntoEachPackages(stdout string) map[string]string {
 | 
			
		||||
	changelogs := make(map[string]string)
 | 
			
		||||
	scanner := bufio.NewScanner(strings.NewReader(stdout))
 | 
			
		||||
 | 
			
		||||
	crlf, newBlock := false, true
 | 
			
		||||
	packNameVer, contents := "", []string{}
 | 
			
		||||
	for scanner.Scan() {
 | 
			
		||||
		line := scanner.Text()
 | 
			
		||||
		if strings.HasPrefix(line, "==================== Updated Packages ====================") {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if len(strings.TrimSpace(line)) != 0 && newBlock {
 | 
			
		||||
			left := strings.Fields(line)[0]
 | 
			
		||||
			// ss := strings.Split(left, ".")
 | 
			
		||||
			// packNameVer = strings.Join(ss[0:len(ss)-1], ".")
 | 
			
		||||
			packNameVer = left
 | 
			
		||||
			newBlock = false
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if len(strings.TrimSpace(line)) == 0 {
 | 
			
		||||
			if crlf {
 | 
			
		||||
				changelogs[packNameVer] = strings.Join(contents, "\n")
 | 
			
		||||
				packNameVer = ""
 | 
			
		||||
				contents = []string{}
 | 
			
		||||
				newBlock = true
 | 
			
		||||
				crlf = false
 | 
			
		||||
			} else {
 | 
			
		||||
				contents = append(contents, line)
 | 
			
		||||
				crlf = true
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			contents = append(contents, line)
 | 
			
		||||
			crlf = false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if 0 < len(contents) {
 | 
			
		||||
		changelogs[packNameVer] = strings.Join(contents, "\n")
 | 
			
		||||
	}
 | 
			
		||||
	return changelogs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) fillDiffChangelogs(packNames []string) error {
 | 
			
		||||
	changelogs, err := o.getAvailableChangelogs(packNames)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for s := range changelogs {
 | 
			
		||||
		// name, pack, found := o.Packages.FindOne(func(p models.Package) bool {
 | 
			
		||||
		name, pack, found := o.Packages.FindOne(func(p models.Package) bool {
 | 
			
		||||
			var epochNameVerRel string
 | 
			
		||||
			if index := strings.Index(p.NewVersion, ":"); 0 < index {
 | 
			
		||||
				epoch := p.NewVersion[0:index]
 | 
			
		||||
				ver := p.NewVersion[index+1 : len(p.NewVersion)]
 | 
			
		||||
				epochNameVerRel = fmt.Sprintf("%s:%s-%s", epoch, p.Name, ver)
 | 
			
		||||
			} else {
 | 
			
		||||
				epochNameVerRel = fmt.Sprintf("%s-%s", p.Name, p.NewVersion)
 | 
			
		||||
			}
 | 
			
		||||
			return strings.HasPrefix(s, epochNameVerRel)
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		if found {
 | 
			
		||||
			var detectionMethod string
 | 
			
		||||
			diff, err := o.getDiffChangelog(pack, changelogs[s])
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				detectionMethod = models.ChangelogExactMatchStr
 | 
			
		||||
			} else {
 | 
			
		||||
				o.log.Debug(err)
 | 
			
		||||
				// Try without epoch
 | 
			
		||||
				if index := strings.Index(pack.Version, ":"); 0 < index {
 | 
			
		||||
					pack.Version = pack.Version[index+1 : len(pack.Version)]
 | 
			
		||||
					o.log.Debug("Try without epoch", pack)
 | 
			
		||||
					diff, err = o.getDiffChangelog(pack, changelogs[s])
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						o.log.Debugf("Failed to find the version in changelog: %s-%s-%s",
 | 
			
		||||
							pack.Name, pack.Version, pack.Release)
 | 
			
		||||
						if len(diff) == 0 {
 | 
			
		||||
							detectionMethod = models.FailedToGetChangelog
 | 
			
		||||
						} else {
 | 
			
		||||
							detectionMethod = models.FailedToFindVersionInChangelog
 | 
			
		||||
							diff = ""
 | 
			
		||||
						}
 | 
			
		||||
					} else {
 | 
			
		||||
						o.log.Debugf("Found the version in changelog without epoch: %s-%s-%s",
 | 
			
		||||
							pack.Name, pack.Version, pack.Release)
 | 
			
		||||
						detectionMethod = models.ChangelogLenientMatchStr
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					if len(diff) == 0 {
 | 
			
		||||
						detectionMethod = models.FailedToGetChangelog
 | 
			
		||||
					} else {
 | 
			
		||||
						detectionMethod = models.FailedToFindVersionInChangelog
 | 
			
		||||
						diff = ""
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			pack = o.Packages[name]
 | 
			
		||||
			pack.Changelog = models.Changelog{
 | 
			
		||||
				Contents: diff,
 | 
			
		||||
				Method:   models.DetectionMethod(detectionMethod),
 | 
			
		||||
			}
 | 
			
		||||
			o.Packages[name] = pack
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) getDiffChangelog(pack models.Package, availableChangelog string) (string, error) {
 | 
			
		||||
	installedVer := ver.NewVersion(fmt.Sprintf("%s-%s", pack.Version, pack.Release))
 | 
			
		||||
	scanner := bufio.NewScanner(strings.NewReader(availableChangelog))
 | 
			
		||||
	diff := []string{}
 | 
			
		||||
	found := false
 | 
			
		||||
	for scanner.Scan() {
 | 
			
		||||
		line := scanner.Text()
 | 
			
		||||
		if !strings.HasPrefix(line, "* ") {
 | 
			
		||||
			diff = append(diff, line)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// openssh on RHEL
 | 
			
		||||
		//   openssh-server-6.6.1p1-35.el7_3.x86_64   rhui-rhel-7-server-rhui-rpms
 | 
			
		||||
		//   Wed Mar  1 21:00:00 2017 Jakub Jelen <jjelen@redhat.com> - 6.6.1p1-35 + 0.9.3-9
 | 
			
		||||
		ss := strings.Split(line, " + ")
 | 
			
		||||
		if 1 < len(ss) {
 | 
			
		||||
			line = ss[0]
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ss = strings.Split(line, " ")
 | 
			
		||||
		if len(ss) < 2 {
 | 
			
		||||
			diff = append(diff, line)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		v := ss[len(ss)-1]
 | 
			
		||||
		v = strings.TrimPrefix(v, "-")
 | 
			
		||||
		v = strings.TrimPrefix(v, "[")
 | 
			
		||||
		v = strings.TrimSuffix(v, "]")
 | 
			
		||||
 | 
			
		||||
		// On Amazon often end with email address. <aaa@aaa.com> Go to next line
 | 
			
		||||
		if strings.HasPrefix(v, "<") && strings.HasSuffix(v, ">") {
 | 
			
		||||
			diff = append(diff, line)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		version := ver.NewVersion(v)
 | 
			
		||||
		if installedVer.Equal(version) || installedVer.GreaterThan(version) {
 | 
			
		||||
			found = true
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		diff = append(diff, line)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(diff) == 0 || !found {
 | 
			
		||||
		return availableChangelog,
 | 
			
		||||
			fmt.Errorf("Failed to find the version in changelog: %s-%s-%s",
 | 
			
		||||
				pack.Name, pack.Version, pack.Release)
 | 
			
		||||
	}
 | 
			
		||||
	return strings.TrimSpace(strings.Join(diff, "\n")), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) scanChangelogs(updatable models.Packages) (models.VulnInfos, error) {
 | 
			
		||||
	packCveIDs := make(map[string][]string)
 | 
			
		||||
	for name := range updatable {
 | 
			
		||||
		cveIDs := []string{}
 | 
			
		||||
		pack := o.Packages[name]
 | 
			
		||||
		if pack.Changelog.Method == models.FailedToFindVersionInChangelog {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		scanner := bufio.NewScanner(strings.NewReader(pack.Changelog.Contents))
 | 
			
		||||
		for scanner.Scan() {
 | 
			
		||||
			if matches := cveRe.FindAllString(scanner.Text(), -1); 0 < len(matches) {
 | 
			
		||||
				for _, m := range matches {
 | 
			
		||||
					cveIDs = util.AppendIfMissing(cveIDs, m)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		packCveIDs[name] = cveIDs
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// transform datastructure
 | 
			
		||||
	// - From
 | 
			
		||||
	//	  "packname": []{"CVE-2017-1111", ".../
 | 
			
		||||
	//
 | 
			
		||||
	// - To
 | 
			
		||||
	//	   map {
 | 
			
		||||
	//		 "CVE-2017-1111": "packname",
 | 
			
		||||
	//	   }
 | 
			
		||||
	vinfos := models.VulnInfos{}
 | 
			
		||||
	for name, cveIDs := range packCveIDs {
 | 
			
		||||
		for _, cid := range cveIDs {
 | 
			
		||||
			if v, ok := vinfos[cid]; ok {
 | 
			
		||||
				v.AffectedPackages = append(v.AffectedPackages, models.PackageStatus{Name: name})
 | 
			
		||||
				vinfos[cid] = v
 | 
			
		||||
			} else {
 | 
			
		||||
				vinfos[cid] = models.VulnInfo{
 | 
			
		||||
					CveID:            cid,
 | 
			
		||||
					AffectedPackages: models.PackageStatuses{{Name: name}},
 | 
			
		||||
					Confidences:      models.Confidences{models.ChangelogExactMatch},
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return vinfos, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type distroAdvisoryCveIDs struct {
 | 
			
		||||
	DistroAdvisory models.DistroAdvisory
 | 
			
		||||
	CveIDs         []string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Scaning unsecure packages using yum-plugin-security.
 | 
			
		||||
// Amazon, RHEL, Oracle Linux
 | 
			
		||||
func (o *redhatBase) scanUsingYum(updatable models.Packages) (models.VulnInfos, error) {
 | 
			
		||||
	if o.Distro.Family == config.CentOS {
 | 
			
		||||
		// CentOS has no security channel.
 | 
			
		||||
		return nil, fmt.Errorf(
 | 
			
		||||
			"yum updateinfo is not suppported on CentOS")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// get advisoryID(RHSA, ALAS, ELSA) - package name,version
 | 
			
		||||
	major, err := (o.Distro.MajorVersion())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("Not implemented yet: %s, err: %s", o.Distro, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var cmd string
 | 
			
		||||
	if (o.Distro.Family == config.RedHat || o.Distro.Family == config.Oracle) && major > 5 {
 | 
			
		||||
		cmd = "yum repolist --color=never"
 | 
			
		||||
		r := o.exec(util.PrependProxyEnv(cmd), o.sudo.yumRepolist())
 | 
			
		||||
		if !r.isSuccess() {
 | 
			
		||||
			return nil, fmt.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (o.Distro.Family == config.RedHat || o.Distro.Family == config.Oracle) && major == 5 {
 | 
			
		||||
		cmd = "yum list-security --security"
 | 
			
		||||
		if o.hasYumColorOption() {
 | 
			
		||||
			cmd += " --color=never"
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		cmd = "yum updateinfo list updates --security --color=never"
 | 
			
		||||
	}
 | 
			
		||||
	r := o.exec(util.PrependProxyEnv(cmd), o.sudo.yumUpdateInfo())
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return nil, fmt.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	advIDPackNamesList, err := o.parseYumUpdateinfoListAvailable(r.Stdout)
 | 
			
		||||
 | 
			
		||||
	dict := make(map[string]models.Packages)
 | 
			
		||||
	for _, advIDPackNames := range advIDPackNamesList {
 | 
			
		||||
		packages := models.Packages{}
 | 
			
		||||
		for _, packName := range advIDPackNames.PackNames {
 | 
			
		||||
			pack, found := updatable[packName]
 | 
			
		||||
			if !found {
 | 
			
		||||
				return nil, fmt.Errorf(
 | 
			
		||||
					"Package not found. pack: %#v", packName)
 | 
			
		||||
			}
 | 
			
		||||
			packages[pack.Name] = pack
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		dict[advIDPackNames.AdvisoryID] = packages
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// get advisoryID(RHSA, ALAS, ELSA) - CVE IDs
 | 
			
		||||
	if (o.Distro.Family == config.RedHat || o.Distro.Family == config.Oracle) && major == 5 {
 | 
			
		||||
		cmd = "yum info-security"
 | 
			
		||||
		if o.hasYumColorOption() {
 | 
			
		||||
			cmd += " --color=never"
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		cmd = "yum updateinfo updates --security --color=never"
 | 
			
		||||
	}
 | 
			
		||||
	r = o.exec(util.PrependProxyEnv(cmd), o.sudo.yumUpdateInfo())
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return nil, fmt.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	advisoryCveIDsList, err := o.parseYumUpdateinfo(r.Stdout)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// All information collected.
 | 
			
		||||
	// Convert to VulnInfos.
 | 
			
		||||
	vinfos := models.VulnInfos{}
 | 
			
		||||
	for _, advIDCveIDs := range advisoryCveIDsList {
 | 
			
		||||
		for _, cveID := range advIDCveIDs.CveIDs {
 | 
			
		||||
			vinfo, found := vinfos[cveID]
 | 
			
		||||
			if found {
 | 
			
		||||
				advAppended := append(vinfo.DistroAdvisories, advIDCveIDs.DistroAdvisory)
 | 
			
		||||
				vinfo.DistroAdvisories = advAppended
 | 
			
		||||
 | 
			
		||||
				packs := dict[advIDCveIDs.DistroAdvisory.AdvisoryID]
 | 
			
		||||
				for _, pack := range packs {
 | 
			
		||||
					vinfo.AffectedPackages = append(vinfo.AffectedPackages,
 | 
			
		||||
						models.PackageStatus{Name: pack.Name})
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				packs := dict[advIDCveIDs.DistroAdvisory.AdvisoryID]
 | 
			
		||||
				affected := models.PackageStatuses{}
 | 
			
		||||
				for _, p := range packs {
 | 
			
		||||
					affected = append(affected, models.PackageStatus{Name: p.Name})
 | 
			
		||||
				}
 | 
			
		||||
				vinfo = models.VulnInfo{
 | 
			
		||||
					CveID:            cveID,
 | 
			
		||||
					DistroAdvisories: []models.DistroAdvisory{advIDCveIDs.DistroAdvisory},
 | 
			
		||||
					AffectedPackages: affected,
 | 
			
		||||
					Confidences:      models.Confidences{models.YumUpdateSecurityMatch},
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			vinfos[cveID] = vinfo
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return vinfos, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var horizontalRulePattern = regexp.MustCompile(`^=+$`)
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) parseYumUpdateinfo(stdout string) (result []distroAdvisoryCveIDs, err error) {
 | 
			
		||||
	sectionState := Outside
 | 
			
		||||
	lines := strings.Split(stdout, "\n")
 | 
			
		||||
	lines = append(lines, "=============")
 | 
			
		||||
 | 
			
		||||
	// Amazon Linux AMI Security Information
 | 
			
		||||
	advisory := models.DistroAdvisory{}
 | 
			
		||||
 | 
			
		||||
	cveIDsSetInThisSection := make(map[string]bool)
 | 
			
		||||
 | 
			
		||||
	// use this flag to Collect CVE IDs in CVEs field.
 | 
			
		||||
	inDesctiption, inCves := false, false
 | 
			
		||||
 | 
			
		||||
	for _, line := range lines {
 | 
			
		||||
		line = strings.TrimSpace(line)
 | 
			
		||||
 | 
			
		||||
		// find the new section pattern
 | 
			
		||||
		if horizontalRulePattern.MatchString(line) {
 | 
			
		||||
			// set previous section's result to return-variable
 | 
			
		||||
			if sectionState == Content {
 | 
			
		||||
				foundCveIDs := []string{}
 | 
			
		||||
				for cveID := range cveIDsSetInThisSection {
 | 
			
		||||
					foundCveIDs = append(foundCveIDs, cveID)
 | 
			
		||||
				}
 | 
			
		||||
				result = append(result, distroAdvisoryCveIDs{
 | 
			
		||||
					DistroAdvisory: advisory,
 | 
			
		||||
					CveIDs:         foundCveIDs,
 | 
			
		||||
				})
 | 
			
		||||
 | 
			
		||||
				// reset for next section.
 | 
			
		||||
				cveIDsSetInThisSection = make(map[string]bool)
 | 
			
		||||
				inDesctiption, inCves = false, false
 | 
			
		||||
				advisory = models.DistroAdvisory{}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Go to next section
 | 
			
		||||
			sectionState = o.changeSectionState(sectionState)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		switch sectionState {
 | 
			
		||||
		case Header:
 | 
			
		||||
			switch o.Distro.Family {
 | 
			
		||||
			case config.CentOS:
 | 
			
		||||
				// CentOS has no security channel.
 | 
			
		||||
				return result, fmt.Errorf(
 | 
			
		||||
					"yum updateinfo is not suppported on CentOS")
 | 
			
		||||
			case config.RedHat, config.Amazon, config.Oracle:
 | 
			
		||||
				// nop
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case Content:
 | 
			
		||||
			if found := o.isDescriptionLine(line); found {
 | 
			
		||||
				inDesctiption, inCves = true, false
 | 
			
		||||
				ss := strings.Split(line, " : ")
 | 
			
		||||
				advisory.Description += fmt.Sprintf("%s\n",
 | 
			
		||||
					strings.Join(ss[1:], " : "))
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// severity
 | 
			
		||||
			if severity, found := o.parseYumUpdateinfoToGetSeverity(line); found {
 | 
			
		||||
				advisory.Severity = severity
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// No need to parse in description except severity
 | 
			
		||||
			if inDesctiption {
 | 
			
		||||
				if ss := strings.Split(line, ": "); 1 < len(ss) {
 | 
			
		||||
					advisory.Description += fmt.Sprintf("%s\n",
 | 
			
		||||
						strings.Join(ss[1:], ": "))
 | 
			
		||||
				}
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if found := o.isCvesHeaderLine(line); found {
 | 
			
		||||
				inCves = true
 | 
			
		||||
				ss := strings.Split(line, "CVEs : ")
 | 
			
		||||
				line = strings.Join(ss[1:], " ")
 | 
			
		||||
				cveIDs := o.parseYumUpdateinfoLineToGetCveIDs(line)
 | 
			
		||||
				for _, cveID := range cveIDs {
 | 
			
		||||
					cveIDsSetInThisSection[cveID] = true
 | 
			
		||||
				}
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if inCves {
 | 
			
		||||
				cveIDs := o.parseYumUpdateinfoLineToGetCveIDs(line)
 | 
			
		||||
				for _, cveID := range cveIDs {
 | 
			
		||||
					cveIDsSetInThisSection[cveID] = true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			advisoryID, found := o.parseYumUpdateinfoToGetAdvisoryID(line)
 | 
			
		||||
			if found {
 | 
			
		||||
				advisory.AdvisoryID = advisoryID
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			issued, found := o.parseYumUpdateinfoLineToGetIssued(line)
 | 
			
		||||
			if found {
 | 
			
		||||
				advisory.Issued = issued
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			updated, found := o.parseYumUpdateinfoLineToGetUpdated(line)
 | 
			
		||||
			if found {
 | 
			
		||||
				advisory.Updated = updated
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// state
 | 
			
		||||
const (
 | 
			
		||||
	Outside = iota
 | 
			
		||||
	Header  = iota
 | 
			
		||||
	Content = iota
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) changeSectionState(state int) (newState int) {
 | 
			
		||||
	switch state {
 | 
			
		||||
	case Outside, Content:
 | 
			
		||||
		newState = Header
 | 
			
		||||
	case Header:
 | 
			
		||||
		newState = Content
 | 
			
		||||
	}
 | 
			
		||||
	return newState
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) isCvesHeaderLine(line string) bool {
 | 
			
		||||
	return strings.Contains(line, "CVEs : ")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var yumCveIDPattern = regexp.MustCompile(`(CVE-\d{4}-\d{4,})`)
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) parseYumUpdateinfoLineToGetCveIDs(line string) []string {
 | 
			
		||||
	return yumCveIDPattern.FindAllString(line, -1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var yumAdvisoryIDPattern = regexp.MustCompile(`^ *Update ID : (.*)$`)
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) parseYumUpdateinfoToGetAdvisoryID(line string) (advisoryID string, found bool) {
 | 
			
		||||
	result := yumAdvisoryIDPattern.FindStringSubmatch(line)
 | 
			
		||||
	if len(result) != 2 {
 | 
			
		||||
		return "", false
 | 
			
		||||
	}
 | 
			
		||||
	return strings.TrimSpace(result[1]), true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var yumIssuedPattern = regexp.MustCompile(`^\s*Issued : (\d{4}-\d{2}-\d{2})`)
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) parseYumUpdateinfoLineToGetIssued(line string) (date time.Time, found bool) {
 | 
			
		||||
	return o.parseYumUpdateinfoLineToGetDate(line, yumIssuedPattern)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var yumUpdatedPattern = regexp.MustCompile(`^\s*Updated : (\d{4}-\d{2}-\d{2})`)
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) parseYumUpdateinfoLineToGetUpdated(line string) (date time.Time, found bool) {
 | 
			
		||||
	return o.parseYumUpdateinfoLineToGetDate(line, yumUpdatedPattern)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) parseYumUpdateinfoLineToGetDate(line string, regexpPattern *regexp.Regexp) (date time.Time, found bool) {
 | 
			
		||||
	result := regexpPattern.FindStringSubmatch(line)
 | 
			
		||||
	if len(result) != 2 {
 | 
			
		||||
		return date, false
 | 
			
		||||
	}
 | 
			
		||||
	t, err := time.Parse("2006-01-02", result[1])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return date, false
 | 
			
		||||
	}
 | 
			
		||||
	return t, true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var yumDescriptionPattern = regexp.MustCompile(`^\s*Description : `)
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) isDescriptionLine(line string) bool {
 | 
			
		||||
	return yumDescriptionPattern.MatchString(line)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var yumSeverityPattern = regexp.MustCompile(`^ *Severity : (.*)$`)
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) parseYumUpdateinfoToGetSeverity(line string) (severity string, found bool) {
 | 
			
		||||
	result := yumSeverityPattern.FindStringSubmatch(line)
 | 
			
		||||
	if len(result) != 2 {
 | 
			
		||||
		return "", false
 | 
			
		||||
	}
 | 
			
		||||
	return strings.TrimSpace(result[1]), true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type advisoryIDPacks struct {
 | 
			
		||||
	AdvisoryID string
 | 
			
		||||
	PackNames  []string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type advisoryIDPacksList []advisoryIDPacks
 | 
			
		||||
 | 
			
		||||
func (list advisoryIDPacksList) find(advisoryID string) (advisoryIDPacks, bool) {
 | 
			
		||||
	for _, a := range list {
 | 
			
		||||
		if a.AdvisoryID == advisoryID {
 | 
			
		||||
			return a, true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return advisoryIDPacks{}, false
 | 
			
		||||
}
 | 
			
		||||
func (o *redhatBase) extractPackNameVerRel(nameVerRel string) (name, ver, rel string) {
 | 
			
		||||
	fields := strings.Split(nameVerRel, ".")
 | 
			
		||||
	archTrimed := strings.Join(fields[0:len(fields)-1], ".")
 | 
			
		||||
 | 
			
		||||
	fields = strings.Split(archTrimed, "-")
 | 
			
		||||
	rel = fields[len(fields)-1]
 | 
			
		||||
	ver = fields[len(fields)-2]
 | 
			
		||||
	name = strings.Join(fields[0:(len(fields)-2)], "-")
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// parseYumUpdateinfoListAvailable collect AdvisorID(RHSA, ALAS, ELSA), packages
 | 
			
		||||
func (o *redhatBase) parseYumUpdateinfoListAvailable(stdout string) (advisoryIDPacksList, error) {
 | 
			
		||||
	result := []advisoryIDPacks{}
 | 
			
		||||
	lines := strings.Split(stdout, "\n")
 | 
			
		||||
	for _, line := range lines {
 | 
			
		||||
 | 
			
		||||
		if !(strings.HasPrefix(line, "RHSA") ||
 | 
			
		||||
			strings.HasPrefix(line, "ALAS") ||
 | 
			
		||||
			strings.HasPrefix(line, "ELSA")) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fields := strings.Fields(line)
 | 
			
		||||
		if len(fields) != 3 {
 | 
			
		||||
			return []advisoryIDPacks{}, fmt.Errorf(
 | 
			
		||||
				"Unknown format. line: %s", line)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// extract fields
 | 
			
		||||
		advisoryID := fields[0]
 | 
			
		||||
		packVersion := fields[2]
 | 
			
		||||
		packName, _, _ := o.extractPackNameVerRel(packVersion)
 | 
			
		||||
 | 
			
		||||
		found := false
 | 
			
		||||
		for i, s := range result {
 | 
			
		||||
			if s.AdvisoryID == advisoryID {
 | 
			
		||||
				names := s.PackNames
 | 
			
		||||
				names = append(names, packName)
 | 
			
		||||
				result[i].PackNames = names
 | 
			
		||||
				found = true
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if !found {
 | 
			
		||||
			result = append(result, advisoryIDPacks{
 | 
			
		||||
				AdvisoryID: advisoryID,
 | 
			
		||||
				PackNames:  []string{packName},
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return result, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *redhatBase) yumPS() error {
 | 
			
		||||
	cmd := "LANGUAGE=en_US.UTF-8 yum info yum"
 | 
			
		||||
	r := o.exec(util.PrependProxyEnv(cmd), noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return fmt.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
		return xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	if !o.checkYumPsInstalled(r.Stdout) {
 | 
			
		||||
		switch o.Distro.Family {
 | 
			
		||||
		case config.RedHat, config.Oracle:
 | 
			
		||||
			return nil
 | 
			
		||||
		default:
 | 
			
		||||
			return fmt.Errorf("yum-plugin-ps is not installed")
 | 
			
		||||
			return xerrors.New("yum-plugin-ps is not installed")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd = "LANGUAGE=en_US.UTF-8 yum -q ps all --color=never"
 | 
			
		||||
	r = o.exec(util.PrependProxyEnv(cmd), sudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return fmt.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
		return xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	packs := o.parseYumPS(r.Stdout)
 | 
			
		||||
	for name, pack := range packs {
 | 
			
		||||
@@ -1260,7 +609,7 @@ func (o *redhatBase) needsRestarting() error {
 | 
			
		||||
	cmd := "LANGUAGE=en_US.UTF-8 needs-restarting"
 | 
			
		||||
	r := o.exec(cmd, sudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return fmt.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
		return xerrors.Errorf("Failed to SSH: %w", r)
 | 
			
		||||
	}
 | 
			
		||||
	procs := o.parseNeedsRestarting(r.Stdout)
 | 
			
		||||
	for _, proc := range procs {
 | 
			
		||||
@@ -1335,7 +684,7 @@ func (o *redhatBase) procPathToFQPN(execCommand string) (string, error) {
 | 
			
		||||
	cmd := `LANGUAGE=en_US.UTF-8 rpm -qf --queryformat "%{NAME}-%{EPOCH}:%{VERSION}-%{RELEASE}.%{ARCH}\n" ` + path
 | 
			
		||||
	r := o.exec(cmd, noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return "", fmt.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
		return "", xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	fqpn := strings.TrimSpace(r.Stdout)
 | 
			
		||||
	return strings.Replace(fqpn, "-(none):", "-", -1), nil
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										65
									
								
								scan/rhel.go
									
									
									
									
									
								
							
							
						
						
									
										65
									
								
								scan/rhel.go
									
									
									
									
									
								
							@@ -1,11 +1,10 @@
 | 
			
		||||
package scan
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// inherit OsTypeInterface
 | 
			
		||||
@@ -43,7 +42,7 @@ func (o *rhel) checkDeps() error {
 | 
			
		||||
	} else if o.getServerInfo().Mode.IsDeep() {
 | 
			
		||||
		return o.execCheckDeps(o.depsDeep())
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Errorf("Unknown scan mode")
 | 
			
		||||
	return xerrors.New("Unknown scan mode")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *rhel) depsFast() []string {
 | 
			
		||||
@@ -55,46 +54,16 @@ func (o *rhel) depsFastRoot() []string {
 | 
			
		||||
		return []string{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// repoquery
 | 
			
		||||
	majorVersion, _ := o.Distro.MajorVersion()
 | 
			
		||||
	switch majorVersion {
 | 
			
		||||
	case 5:
 | 
			
		||||
		return []string{
 | 
			
		||||
			"yum-utils",
 | 
			
		||||
			"yum-security",
 | 
			
		||||
		}
 | 
			
		||||
	case 6:
 | 
			
		||||
		return []string{
 | 
			
		||||
			"yum-utils",
 | 
			
		||||
			"yum-plugin-security",
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		return []string{
 | 
			
		||||
			"yum-utils",
 | 
			
		||||
		}
 | 
			
		||||
	if majorVersion < 8 {
 | 
			
		||||
		return []string{"yum-utils"}
 | 
			
		||||
	}
 | 
			
		||||
	return []string{"dnf-utils"}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *rhel) depsDeep() []string {
 | 
			
		||||
	majorVersion, _ := o.Distro.MajorVersion()
 | 
			
		||||
	switch majorVersion {
 | 
			
		||||
	case 5:
 | 
			
		||||
		return []string{
 | 
			
		||||
			"yum-utils",
 | 
			
		||||
			"yum-security",
 | 
			
		||||
			"yum-changelog",
 | 
			
		||||
		}
 | 
			
		||||
	case 6:
 | 
			
		||||
		return []string{
 | 
			
		||||
			"yum-utils",
 | 
			
		||||
			"yum-plugin-security",
 | 
			
		||||
			"yum-plugin-changelog",
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		return []string{
 | 
			
		||||
			"yum-utils",
 | 
			
		||||
			"yum-plugin-changelog",
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return o.depsFastRoot()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *rhel) checkIfSudoNoPasswd() error {
 | 
			
		||||
@@ -119,25 +88,17 @@ func (o *rhel) sudoNoPasswdCmdsFastRoot() []cmd {
 | 
			
		||||
	majorVersion, _ := o.Distro.MajorVersion()
 | 
			
		||||
	if majorVersion < 6 {
 | 
			
		||||
		return []cmd{
 | 
			
		||||
			// {"needs-restarting", exitStatusZero},
 | 
			
		||||
			{"yum repolist --color=never", exitStatusZero},
 | 
			
		||||
			{"yum list-security --security --color=never", exitStatusZero},
 | 
			
		||||
			{"yum info-security --color=never", exitStatusZero},
 | 
			
		||||
			{"repoquery -h", exitStatusZero},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return []cmd{
 | 
			
		||||
		{"yum repolist --color=never", exitStatusZero},
 | 
			
		||||
		{"yum updateinfo list updates --security --color=never", exitStatusZero},
 | 
			
		||||
		{"yum updateinfo updates --security --color=never ", exitStatusZero},
 | 
			
		||||
		{"repoquery -h", exitStatusZero},
 | 
			
		||||
		{"needs-restarting", exitStatusZero},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *rhel) sudoNoPasswdCmdsDeep() []cmd {
 | 
			
		||||
	return append(o.sudoNoPasswdCmdsFastRoot(),
 | 
			
		||||
		cmd{"yum changelog all updates --color=never", exitStatusZero})
 | 
			
		||||
	return o.sudoNoPasswdCmdsFastRoot()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type rootPrivRHEL struct{}
 | 
			
		||||
@@ -146,14 +107,6 @@ func (o rootPrivRHEL) repoquery() bool {
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o rootPrivRHEL) yumRepolist() bool {
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o rootPrivRHEL) yumUpdateInfo() bool {
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o rootPrivRHEL) yumChangelog() bool {
 | 
			
		||||
func (o rootPrivRHEL) yumMakeCache() bool {
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -18,9 +18,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
package scan
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
@@ -31,13 +29,20 @@ import (
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/report"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	scannedViaRemote = "remote"
 | 
			
		||||
	scannedViaLocal  = "local"
 | 
			
		||||
	scannedViaPseudo = "pseudo"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	errOSFamilyHeader      = errors.New("X-Vuls-OS-Family header is required")
 | 
			
		||||
	errOSReleaseHeader     = errors.New("X-Vuls-OS-Release header is required")
 | 
			
		||||
	errKernelVersionHeader = errors.New("X-Vuls-Kernel-Version header is required")
 | 
			
		||||
	errServerNameHeader    = errors.New("X-Vuls-Server-Name header is required")
 | 
			
		||||
	errOSFamilyHeader      = xerrors.New("X-Vuls-OS-Family header is required")
 | 
			
		||||
	errOSReleaseHeader     = xerrors.New("X-Vuls-OS-Release header is required")
 | 
			
		||||
	errKernelVersionHeader = xerrors.New("X-Vuls-Kernel-Version header is required")
 | 
			
		||||
	errServerNameHeader    = xerrors.New("X-Vuls-Server-Name header is required")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var servers, errServers []osTypeInterface
 | 
			
		||||
@@ -57,6 +62,8 @@ type osTypeInterface interface {
 | 
			
		||||
 | 
			
		||||
	preCure() error
 | 
			
		||||
	postScan() error
 | 
			
		||||
	scanWordPress() error
 | 
			
		||||
	scanLibraries() error
 | 
			
		||||
	scanPackages() error
 | 
			
		||||
	convertToModel() models.ScanResult
 | 
			
		||||
 | 
			
		||||
@@ -118,10 +125,22 @@ func detectOS(c config.ServerInfo) (osType osTypeInterface) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	itsMe, osType, fatalErr = detectContainerImage(c)
 | 
			
		||||
	if fatalErr != nil {
 | 
			
		||||
		osType.setErrs(
 | 
			
		||||
			[]error{xerrors.Errorf("Failed to detect OS: %w", fatalErr)},
 | 
			
		||||
		)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if itsMe {
 | 
			
		||||
		util.Log.Debugf("Container")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	itsMe, osType, fatalErr = detectDebianWithRetry(c)
 | 
			
		||||
	if fatalErr != nil {
 | 
			
		||||
		osType.setErrs([]error{
 | 
			
		||||
			fmt.Errorf("Failed to detect OS: %s", fatalErr)})
 | 
			
		||||
			xerrors.Errorf("Failed to detect OS: %w", fatalErr)})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -151,7 +170,7 @@ func detectOS(c config.ServerInfo) (osType osTypeInterface) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//TODO darwin https://github.com/mizzy/specinfra/blob/master/lib/specinfra/helper/detect_os/darwin.rb
 | 
			
		||||
	osType.setErrs([]error{fmt.Errorf("Unknown OS Type")})
 | 
			
		||||
	osType.setErrs([]error{xerrors.New("Unknown OS Type")})
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -176,20 +195,56 @@ func PrintSSHableServerNames() bool {
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InitServers detect the kind of OS distribution of target servers
 | 
			
		||||
func InitServers(timeoutSec int) error {
 | 
			
		||||
	servers, errServers = detectServerOSes(timeoutSec)
 | 
			
		||||
	if len(servers) == 0 {
 | 
			
		||||
		return fmt.Errorf("No scannable servers")
 | 
			
		||||
func needScans() (needBaseServer, scanContainer, scanImage bool) {
 | 
			
		||||
	scanContainer = true
 | 
			
		||||
	scanImage = true
 | 
			
		||||
	if !config.Conf.ContainersOnly && !config.Conf.ImagesOnly {
 | 
			
		||||
		needBaseServer = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	actives, inactives := detectContainerOSes(timeoutSec)
 | 
			
		||||
	if config.Conf.ContainersOnly {
 | 
			
		||||
		servers = actives
 | 
			
		||||
		errServers = inactives
 | 
			
		||||
	} else {
 | 
			
		||||
	if config.Conf.ImagesOnly && !config.Conf.ContainersOnly {
 | 
			
		||||
		scanContainer = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if config.Conf.ContainersOnly && !config.Conf.ImagesOnly {
 | 
			
		||||
		scanImage = false
 | 
			
		||||
	}
 | 
			
		||||
	return needBaseServer, scanContainer, scanImage
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InitServers detect the kind of OS distribution of target servers
 | 
			
		||||
func InitServers(timeoutSec int) error {
 | 
			
		||||
	needBaseServers, scanContainer, scanImage := needScans()
 | 
			
		||||
 | 
			
		||||
	// use global servers, errServers when scan containers and images
 | 
			
		||||
	servers, errServers = detectServerOSes(timeoutSec)
 | 
			
		||||
	if len(servers) == 0 {
 | 
			
		||||
		return xerrors.New("No scannable base servers")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// scan additional servers
 | 
			
		||||
	var actives, inactives []osTypeInterface
 | 
			
		||||
	if scanImage {
 | 
			
		||||
		oks, errs := detectImageOSes(timeoutSec)
 | 
			
		||||
		actives = append(actives, oks...)
 | 
			
		||||
		inactives = append(inactives, errs...)
 | 
			
		||||
	}
 | 
			
		||||
	if scanContainer {
 | 
			
		||||
		oks, errs := detectContainerOSes(timeoutSec)
 | 
			
		||||
		actives = append(actives, oks...)
 | 
			
		||||
		inactives = append(inactives, errs...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if needBaseServers {
 | 
			
		||||
		servers = append(servers, actives...)
 | 
			
		||||
		errServers = append(errServers, inactives...)
 | 
			
		||||
	} else {
 | 
			
		||||
		servers = actives
 | 
			
		||||
		errServers = inactives
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(servers) == 0 {
 | 
			
		||||
		return xerrors.New("No scannable servers")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@@ -215,7 +270,7 @@ func detectServerOSes(timeoutSec int) (servers, errServers []osTypeInterface) {
 | 
			
		||||
		case res := <-osTypeChan:
 | 
			
		||||
			if 0 < len(res.getErrs()) {
 | 
			
		||||
				errServers = append(errServers, res)
 | 
			
		||||
				util.Log.Errorf("(%d/%d) Failed: %s, err: %s",
 | 
			
		||||
				util.Log.Errorf("(%d/%d) Failed: %s, err: %+v",
 | 
			
		||||
					i+1, len(config.Conf.Servers),
 | 
			
		||||
					res.getServerInfo().ServerName,
 | 
			
		||||
					res.getErrs())
 | 
			
		||||
@@ -241,7 +296,7 @@ func detectServerOSes(timeoutSec int) (servers, errServers []osTypeInterface) {
 | 
			
		||||
					u := &unknown{}
 | 
			
		||||
					u.setServerInfo(sInfo)
 | 
			
		||||
					u.setErrs([]error{
 | 
			
		||||
						fmt.Errorf("Timed out"),
 | 
			
		||||
						xerrors.New("Timed out"),
 | 
			
		||||
					})
 | 
			
		||||
					errServers = append(errServers, u)
 | 
			
		||||
					util.Log.Errorf("(%d/%d) Timed out: %s",
 | 
			
		||||
@@ -279,7 +334,7 @@ func detectContainerOSes(timeoutSec int) (actives, inactives []osTypeInterface)
 | 
			
		||||
				sinfo := osi.getServerInfo()
 | 
			
		||||
				if 0 < len(osi.getErrs()) {
 | 
			
		||||
					inactives = append(inactives, osi)
 | 
			
		||||
					util.Log.Errorf("Failed: %s err: %s", sinfo.ServerName, osi.getErrs())
 | 
			
		||||
					util.Log.Errorf("Failed: %s err: %+v", sinfo.ServerName, osi.getErrs())
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				actives = append(actives, osi)
 | 
			
		||||
@@ -301,11 +356,10 @@ func detectContainerOSes(timeoutSec int) (actives, inactives []osTypeInterface)
 | 
			
		||||
					u := &unknown{}
 | 
			
		||||
					u.setServerInfo(sInfo)
 | 
			
		||||
					u.setErrs([]error{
 | 
			
		||||
						fmt.Errorf("Timed out"),
 | 
			
		||||
						xerrors.New("Timed out"),
 | 
			
		||||
					})
 | 
			
		||||
					inactives = append(inactives)
 | 
			
		||||
					util.Log.Errorf("Timed out: %s", servername)
 | 
			
		||||
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -321,8 +375,8 @@ func detectContainerOSesOnServer(containerHost osTypeInterface) (oses []osTypeIn
 | 
			
		||||
 | 
			
		||||
	running, err := containerHost.runningContainers()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		containerHost.setErrs([]error{fmt.Errorf(
 | 
			
		||||
			"Failed to get running containers on %s. err: %s",
 | 
			
		||||
		containerHost.setErrs([]error{xerrors.Errorf(
 | 
			
		||||
			"Failed to get running containers on %s. err: %w",
 | 
			
		||||
			containerHost.getServerInfo().ServerName, err)})
 | 
			
		||||
		return append(oses, containerHost)
 | 
			
		||||
	}
 | 
			
		||||
@@ -353,8 +407,8 @@ func detectContainerOSesOnServer(containerHost osTypeInterface) (oses []osTypeIn
 | 
			
		||||
 | 
			
		||||
	exitedContainers, err := containerHost.exitedContainers()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		containerHost.setErrs([]error{fmt.Errorf(
 | 
			
		||||
			"Failed to get exited containers on %s. err: %s",
 | 
			
		||||
		containerHost.setErrs([]error{xerrors.Errorf(
 | 
			
		||||
			"Failed to get exited containers on %s. err: %w",
 | 
			
		||||
			containerHost.getServerInfo().ServerName, err)})
 | 
			
		||||
		return append(oses, containerHost)
 | 
			
		||||
	}
 | 
			
		||||
@@ -388,7 +442,7 @@ func detectContainerOSesOnServer(containerHost osTypeInterface) (oses []osTypeIn
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if 0 < len(exited) || 0 < len(unknown) {
 | 
			
		||||
		containerHost.setErrs([]error{fmt.Errorf(
 | 
			
		||||
		containerHost.setErrs([]error{xerrors.Errorf(
 | 
			
		||||
			"Some containers on %s are exited or unknown. exited: %s, unknown: %s",
 | 
			
		||||
			containerHost.getServerInfo().ServerName, exited, unknown)})
 | 
			
		||||
		return append(oses, containerHost)
 | 
			
		||||
@@ -396,11 +450,86 @@ func detectContainerOSesOnServer(containerHost osTypeInterface) (oses []osTypeIn
 | 
			
		||||
	return oses
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func detectImageOSes(timeoutSec int) (actives, inactives []osTypeInterface) {
 | 
			
		||||
	util.Log.Info("Detecting OS of static containers... ")
 | 
			
		||||
	osTypesChan := make(chan []osTypeInterface, len(servers))
 | 
			
		||||
	defer close(osTypesChan)
 | 
			
		||||
	for _, s := range servers {
 | 
			
		||||
		go func(s osTypeInterface) {
 | 
			
		||||
			defer func() {
 | 
			
		||||
				if p := recover(); p != nil {
 | 
			
		||||
					util.Log.Debugf("Panic: %s on %s",
 | 
			
		||||
						p, s.getServerInfo().GetServerName())
 | 
			
		||||
				}
 | 
			
		||||
			}()
 | 
			
		||||
			osTypesChan <- detectImageOSesOnServer(s)
 | 
			
		||||
		}(s)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	timeout := time.After(time.Duration(timeoutSec) * time.Second)
 | 
			
		||||
	for i := 0; i < len(servers); i++ {
 | 
			
		||||
		select {
 | 
			
		||||
		case res := <-osTypesChan:
 | 
			
		||||
			for _, osi := range res {
 | 
			
		||||
				sinfo := osi.getServerInfo()
 | 
			
		||||
				if 0 < len(osi.getErrs()) {
 | 
			
		||||
					inactives = append(inactives, osi)
 | 
			
		||||
					util.Log.Errorf("Failed: %s err: %+v", sinfo.ServerName, osi.getErrs())
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				actives = append(actives, osi)
 | 
			
		||||
				util.Log.Infof("Detected: %s@%s: %s",
 | 
			
		||||
					sinfo.Image.Name, sinfo.ServerName, osi.getDistro())
 | 
			
		||||
			}
 | 
			
		||||
		case <-timeout:
 | 
			
		||||
			msg := "Timed out while detecting static containers"
 | 
			
		||||
			util.Log.Error(msg)
 | 
			
		||||
			for servername, sInfo := range config.Conf.Servers {
 | 
			
		||||
				found := false
 | 
			
		||||
				for _, o := range append(actives, inactives...) {
 | 
			
		||||
					if servername == o.getServerInfo().ServerName {
 | 
			
		||||
						found = true
 | 
			
		||||
						break
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				if !found {
 | 
			
		||||
					u := &unknown{}
 | 
			
		||||
					u.setServerInfo(sInfo)
 | 
			
		||||
					u.setErrs([]error{
 | 
			
		||||
						xerrors.New("Timed out"),
 | 
			
		||||
					})
 | 
			
		||||
					inactives = append(inactives)
 | 
			
		||||
					util.Log.Errorf("Timed out: %s", servername)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func detectImageOSesOnServer(containerHost osTypeInterface) (oses []osTypeInterface) {
 | 
			
		||||
	containerHostInfo := containerHost.getServerInfo()
 | 
			
		||||
	if len(containerHostInfo.Images) == 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for idx, containerConf := range containerHostInfo.Images {
 | 
			
		||||
		copied := containerHostInfo
 | 
			
		||||
		// change servername for original
 | 
			
		||||
		copied.ServerName = fmt.Sprintf("%s:%s@%s", idx, containerConf.Tag, containerHostInfo.ServerName)
 | 
			
		||||
		copied.Image = containerConf
 | 
			
		||||
		copied.Type = ""
 | 
			
		||||
		os := detectOS(copied)
 | 
			
		||||
		oses = append(oses, os)
 | 
			
		||||
	}
 | 
			
		||||
	return oses
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckScanModes checks scan mode
 | 
			
		||||
func CheckScanModes() error {
 | 
			
		||||
	for _, s := range servers {
 | 
			
		||||
		if err := s.checkScanMode(); err != nil {
 | 
			
		||||
			return fmt.Errorf("servers.%s.scanMode err: %s",
 | 
			
		||||
			return xerrors.Errorf("servers.%s.scanMode err: %w",
 | 
			
		||||
				s.getServerInfo().GetServerName(), err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -458,7 +587,7 @@ func detectPlatforms(timeoutSec int) {
 | 
			
		||||
// Scan scan
 | 
			
		||||
func Scan(timeoutSec int) error {
 | 
			
		||||
	if len(servers) == 0 {
 | 
			
		||||
		return fmt.Errorf("No server defined. Check the configuration")
 | 
			
		||||
		return xerrors.New("No server defined. Check the configuration")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := setupChangelogCache(); err != nil {
 | 
			
		||||
@@ -536,7 +665,7 @@ func ViaHTTP(header http.Header, body string) (models.ScanResult, error) {
 | 
			
		||||
			redhatBase: redhatBase{base: base},
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		return models.ScanResult{}, fmt.Errorf("Server mode for %s is not implemented yet", family)
 | 
			
		||||
		return models.ScanResult{}, xerrors.Errorf("Server mode for %s is not implemented yet", family)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	installedPackages, srcPackages, err := osType.parseInstalledPackages(body)
 | 
			
		||||
@@ -592,13 +721,19 @@ func scanVulns(jsonDir string, scannedAt time.Time, timeoutSec int) error {
 | 
			
		||||
		if err = o.scanPackages(); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if err = o.scanWordPress(); err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to scan WordPress: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		if err = o.scanLibraries(); err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to scan Library: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		return o.postScan()
 | 
			
		||||
	}, timeoutSec)
 | 
			
		||||
 | 
			
		||||
	hostname, _ := os.Hostname()
 | 
			
		||||
	ipv4s, ipv6s, err := ip()
 | 
			
		||||
	ipv4s, ipv6s, err := util.IP()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		util.Log.Errorf("Failed to fetch scannedIPs: %s", err)
 | 
			
		||||
		util.Log.Errorf("Failed to fetch scannedIPs. err: %+v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, s := range append(servers, errServers...) {
 | 
			
		||||
@@ -611,6 +746,11 @@ func scanVulns(jsonDir string, scannedAt time.Time, timeoutSec int) error {
 | 
			
		||||
		r.ScannedIPv6Addrs = ipv6s
 | 
			
		||||
		r.Config.Scan = config.Conf
 | 
			
		||||
		results = append(results, r)
 | 
			
		||||
 | 
			
		||||
		if 0 < len(r.Warnings) {
 | 
			
		||||
			util.Log.Warnf("Some warnings occurred during scanning on %s. Please fix the warnings to get a useful information. Execute configtest subcommand before scanning to know the cause of the warnings. warnings: %v",
 | 
			
		||||
				r.ServerName, r.Warnings)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	config.Conf.FormatJSON = true
 | 
			
		||||
@@ -619,44 +759,23 @@ func scanVulns(jsonDir string, scannedAt time.Time, timeoutSec int) error {
 | 
			
		||||
	}
 | 
			
		||||
	for _, w := range ws {
 | 
			
		||||
		if err := w.Write(results...); err != nil {
 | 
			
		||||
			return fmt.Errorf("Failed to write summary report: %s", err)
 | 
			
		||||
			return xerrors.Errorf("Failed to write summary report: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	report.StdoutWriter{}.WriteScanSummary(results...)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ip returns scanner network ip addresses
 | 
			
		||||
func ip() (ipv4Addrs []string, ipv6Addrs []string, err error) {
 | 
			
		||||
	ifaces, err := net.Interfaces()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
	for _, i := range ifaces {
 | 
			
		||||
		addrs, _ := i.Addrs()
 | 
			
		||||
		for _, addr := range addrs {
 | 
			
		||||
			var ip net.IP
 | 
			
		||||
			switch v := addr.(type) {
 | 
			
		||||
			case *net.IPNet:
 | 
			
		||||
				ip = v.IP
 | 
			
		||||
			case *net.IPAddr:
 | 
			
		||||
				ip = v.IP
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// only global unicast address
 | 
			
		||||
			if !ip.IsGlobalUnicast() {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if ok := ip.To4(); ok != nil {
 | 
			
		||||
				ipv4Addrs = append(ipv4Addrs, ip.String())
 | 
			
		||||
			} else {
 | 
			
		||||
				ipv6Addrs = append(ipv6Addrs, ip.String())
 | 
			
		||||
			}
 | 
			
		||||
	errServerNames := []string{}
 | 
			
		||||
	for _, r := range results {
 | 
			
		||||
		if 0 < len(r.Errors) {
 | 
			
		||||
			errServerNames = append(errServerNames, r.ServerName)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return ipv4Addrs, ipv6Addrs, nil
 | 
			
		||||
	if 0 < len(errServerNames) {
 | 
			
		||||
		return fmt.Errorf("An error occurred on %s", errServerNames)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EnsureResultDir ensures the directory for scan results
 | 
			
		||||
@@ -670,20 +789,20 @@ func EnsureResultDir(scannedAt time.Time) (currentDir string, err error) {
 | 
			
		||||
	}
 | 
			
		||||
	jsonDir := filepath.Join(resultsDir, jsonDirName)
 | 
			
		||||
	if err := os.MkdirAll(jsonDir, 0700); err != nil {
 | 
			
		||||
		return "", fmt.Errorf("Failed to create dir: %s", err)
 | 
			
		||||
		return "", xerrors.Errorf("Failed to create dir: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	symlinkPath := filepath.Join(resultsDir, "current")
 | 
			
		||||
	if _, err := os.Lstat(symlinkPath); err == nil {
 | 
			
		||||
		if err := os.Remove(symlinkPath); err != nil {
 | 
			
		||||
			return "", fmt.Errorf(
 | 
			
		||||
				"Failed to remove symlink. path: %s, err: %s", symlinkPath, err)
 | 
			
		||||
			return "", xerrors.Errorf(
 | 
			
		||||
				"Failed to remove symlink. path: %s, err: %w", symlinkPath, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := os.Symlink(jsonDir, symlinkPath); err != nil {
 | 
			
		||||
		return "", fmt.Errorf(
 | 
			
		||||
			"Failed to create symlink: path: %s, err: %s", symlinkPath, err)
 | 
			
		||||
		return "", xerrors.Errorf(
 | 
			
		||||
			"Failed to create symlink: path: %s, err: %w", symlinkPath, err)
 | 
			
		||||
	}
 | 
			
		||||
	return jsonDir, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										36
									
								
								scan/suse.go
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								scan/suse.go
									
									
									
									
									
								
							@@ -9,6 +9,7 @@ import (
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// inherit OsTypeInterface
 | 
			
		||||
@@ -120,12 +121,14 @@ func (o *suse) scanPackages() error {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rebootRequired, err := o.rebootRequired()
 | 
			
		||||
	o.Kernel.RebootRequired, err = o.rebootRequired()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		o.log.Errorf("Failed to detect the kernel reboot required: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
		err = xerrors.Errorf("Failed to detect the kernel reboot required: %w", err)
 | 
			
		||||
		o.log.Warnf("err: %+v", err)
 | 
			
		||||
		o.warns = append(o.warns, err)
 | 
			
		||||
		// Only warning this error
 | 
			
		||||
	}
 | 
			
		||||
	o.Kernel.RebootRequired = rebootRequired
 | 
			
		||||
 | 
			
		||||
	if o.getServerInfo().Mode.IsOffline() {
 | 
			
		||||
		o.Packages = installed
 | 
			
		||||
		return nil
 | 
			
		||||
@@ -133,12 +136,15 @@ func (o *suse) scanPackages() error {
 | 
			
		||||
 | 
			
		||||
	updatable, err := o.scanUpdatablePackages()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		o.log.Errorf("Failed to scan updatable packages: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
		err = xerrors.Errorf("Failed to scan updatable packages: %w", err)
 | 
			
		||||
		o.log.Warnf("err: %+v", err)
 | 
			
		||||
		o.warns = append(o.warns, err)
 | 
			
		||||
		// Only warning this error
 | 
			
		||||
	} else {
 | 
			
		||||
		installed.MergeNewVersion(updatable)
 | 
			
		||||
	}
 | 
			
		||||
	installed.MergeNewVersion(updatable)
 | 
			
		||||
	o.Packages = installed
 | 
			
		||||
 | 
			
		||||
	o.Packages = installed
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -160,7 +166,7 @@ func (o *suse) scanUpdatablePackages() (models.Packages, error) {
 | 
			
		||||
	}
 | 
			
		||||
	r := o.exec(cmd, noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return nil, fmt.Errorf("Failed to scan updatable packages: %v", r)
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to scan updatable packages: %v", r)
 | 
			
		||||
	}
 | 
			
		||||
	return o.parseZypperLULines(r.Stdout)
 | 
			
		||||
}
 | 
			
		||||
@@ -184,16 +190,16 @@ func (o *suse) parseZypperLULines(stdout string) (models.Packages, error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *suse) parseZypperLUOneLine(line string) (*models.Package, error) {
 | 
			
		||||
	fs := strings.Fields(line)
 | 
			
		||||
	if len(fs) != 11 {
 | 
			
		||||
		return nil, fmt.Errorf("zypper -q lu Unknown format: %s", line)
 | 
			
		||||
	ss := strings.Split(line, "|")
 | 
			
		||||
	if len(ss) != 6 {
 | 
			
		||||
		return nil, xerrors.Errorf("zypper -q lu Unknown format: %s", line)
 | 
			
		||||
	}
 | 
			
		||||
	available := strings.Split(fs[8], "-")
 | 
			
		||||
	available := strings.Split(strings.TrimSpace(ss[4]), "-")
 | 
			
		||||
	return &models.Package{
 | 
			
		||||
		Name:       fs[4],
 | 
			
		||||
		Name:       strings.TrimSpace(ss[2]),
 | 
			
		||||
		NewVersion: available[0],
 | 
			
		||||
		NewRelease: available[1],
 | 
			
		||||
		Arch:       fs[10],
 | 
			
		||||
		Arch:       strings.TrimSpace(ss[5]),
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,8 @@ func TestScanUpdatablePackages(t *testing.T) {
 | 
			
		||||
	stdout := `S | Repository                                  | Name                          | Current Version             | Available Version           | Arch
 | 
			
		||||
--+---------------------------------------------+-------------------------------+-----------------------------+-----------------------------+-------
 | 
			
		||||
v | SLES12-SP2-Updates                          | SUSEConnect                   | 0.3.0-19.8.1                | 0.3.1-19.11.2               | x86_64
 | 
			
		||||
v | SLES12-SP2-Updates                          | SuSEfirewall2                 | 3.6.312-2.3.1               | 3.6.312-2.10.1              | noarch`
 | 
			
		||||
v | SLES12-SP2-Updates                          | SuSEfirewall2                 | 3.6.312-2.3.1               | 3.6.312-2.10.1              | noarch
 | 
			
		||||
v | Clone of SLES11-SP3-Updates for x86_64 | ConsoleKit | 0.2.10-64.65.1 | 0.2.10-64.69.1 | x86_64`
 | 
			
		||||
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in  string
 | 
			
		||||
@@ -51,6 +52,12 @@ v | SLES12-SP2-Updates                          | SuSEfirewall2
 | 
			
		||||
					NewRelease: "2.10.1",
 | 
			
		||||
					Arch:       "noarch",
 | 
			
		||||
				},
 | 
			
		||||
				models.Package{
 | 
			
		||||
					Name:       "ConsoleKit",
 | 
			
		||||
					NewVersion: "0.2.10",
 | 
			
		||||
					NewRelease: "64.69.1",
 | 
			
		||||
					Arch:       "x86_64",
 | 
			
		||||
				},
 | 
			
		||||
			),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -84,10 +84,11 @@ func (h VulsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		scannedAt := result.ScannedAt
 | 
			
		||||
		if scannedAt.IsZero() {
 | 
			
		||||
			scannedAt = time.Now().Truncate(1 * time.Hour)
 | 
			
		||||
			result.ScannedAt = scannedAt
 | 
			
		||||
		}
 | 
			
		||||
		dir, err := scan.EnsureResultDir(scannedAt)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Errorf("Failed to ensure the result directory: %s", err)
 | 
			
		||||
			util.Log.Errorf("Failed to ensure the result directory: %+v", err)
 | 
			
		||||
			http.Error(w, err.Error(), http.StatusServiceUnavailable)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
@@ -99,7 +100,7 @@ func (h VulsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
 | 
			
		||||
	for _, w := range reports {
 | 
			
		||||
		if err := w.Write(result); err != nil {
 | 
			
		||||
			util.Log.Errorf("Failed to report: %s", err)
 | 
			
		||||
			util.Log.Errorf("Failed to report. err: %+v", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								util/util.go
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								util/util.go
									
									
									
									
									
								
							@@ -19,6 +19,7 @@ package util
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
@@ -93,6 +94,38 @@ func URLPathParamJoin(baseURL string, paths []string, params map[string]string)
 | 
			
		||||
	return u.String(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IP returns scanner network ip addresses
 | 
			
		||||
func IP() (ipv4Addrs []string, ipv6Addrs []string, err error) {
 | 
			
		||||
	ifaces, err := net.Interfaces()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
	for _, i := range ifaces {
 | 
			
		||||
		addrs, _ := i.Addrs()
 | 
			
		||||
		for _, addr := range addrs {
 | 
			
		||||
			var ip net.IP
 | 
			
		||||
			switch v := addr.(type) {
 | 
			
		||||
			case *net.IPNet:
 | 
			
		||||
				ip = v.IP
 | 
			
		||||
			case *net.IPAddr:
 | 
			
		||||
				ip = v.IP
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// only global unicast address
 | 
			
		||||
			if !ip.IsGlobalUnicast() {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if ok := ip.To4(); ok != nil {
 | 
			
		||||
				ipv4Addrs = append(ipv4Addrs, ip.String())
 | 
			
		||||
			} else {
 | 
			
		||||
				ipv6Addrs = append(ipv6Addrs, ip.String())
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return ipv4Addrs, ipv6Addrs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ProxyEnv returns shell environment variables to set proxy
 | 
			
		||||
func ProxyEnv() string {
 | 
			
		||||
	httpProxyEnv := ""
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										279
									
								
								wordpress/wordpress.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										279
									
								
								wordpress/wordpress.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,279 @@
 | 
			
		||||
/* Vuls - Vulnerability Scanner
 | 
			
		||||
Copyright (C) 2016  Future Corporation , Japan.
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
This program is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package wordpress
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	version "github.com/hashicorp/go-version"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
//WpCveInfos is for wpvulndb's json
 | 
			
		||||
type WpCveInfos struct {
 | 
			
		||||
	ReleaseDate  string `json:"release_date"`
 | 
			
		||||
	ChangelogURL string `json:"changelog_url"`
 | 
			
		||||
	// Status        string `json:"status"`
 | 
			
		||||
	LatestVersion string `json:"latest_version"`
 | 
			
		||||
	LastUpdated   string `json:"last_updated"`
 | 
			
		||||
	// Popular         bool        `json:"popular"`
 | 
			
		||||
	Vulnerabilities []WpCveInfo `json:"vulnerabilities"`
 | 
			
		||||
	Error           string      `json:"error"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//WpCveInfo is for wpvulndb's json
 | 
			
		||||
type WpCveInfo struct {
 | 
			
		||||
	ID        int    `json:"id"`
 | 
			
		||||
	Title     string `json:"title"`
 | 
			
		||||
	CreatedAt string `json:"created_at"`
 | 
			
		||||
	UpdatedAt string `json:"updated_at"`
 | 
			
		||||
	// PublishedDate string     `json:"published_date"`
 | 
			
		||||
	VulnType   string     `json:"vuln_type"`
 | 
			
		||||
	References References `json:"references"`
 | 
			
		||||
	FixedIn    string     `json:"fixed_in"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//References is for wpvulndb's json
 | 
			
		||||
type References struct {
 | 
			
		||||
	URL     []string `json:"url"`
 | 
			
		||||
	Cve     []string `json:"cve"`
 | 
			
		||||
	Secunia []string `json:"secunia"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillWordPress access to wpvulndb and fetch scurity alerts and then set to the given ScanResult.
 | 
			
		||||
// https://wpvulndb.com/
 | 
			
		||||
func FillWordPress(r *models.ScanResult, token string) (int, error) {
 | 
			
		||||
	// Core
 | 
			
		||||
	ver := strings.Replace(r.WordPressPackages.CoreVersion(), ".", "", -1)
 | 
			
		||||
	if ver == "" {
 | 
			
		||||
		return 0, xerrors.New("Failed to get WordPress core version")
 | 
			
		||||
	}
 | 
			
		||||
	url := fmt.Sprintf("https://wpvulndb.com/api/v3/wordpresses/%s", ver)
 | 
			
		||||
	body, err := httpRequest(url, token)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	if body == "" {
 | 
			
		||||
		util.Log.Warnf("A result of REST access is empty: %s", url)
 | 
			
		||||
	}
 | 
			
		||||
	wpVinfos, err := convertToVinfos(models.WPCore, body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//TODO add a flag ignore inactive plugin or themes such as -wp-ignore-inactive flag to cmd line option or config.toml
 | 
			
		||||
 | 
			
		||||
	// Themes
 | 
			
		||||
	for _, p := range r.WordPressPackages.Themes() {
 | 
			
		||||
		url := fmt.Sprintf("https://wpvulndb.com/api/v3/themes/%s", p.Name)
 | 
			
		||||
		body, err := httpRequest(url, token)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		if body == "" {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		templateVinfos, err := convertToVinfos(p.Name, body)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, v := range templateVinfos {
 | 
			
		||||
			for _, fixstat := range v.WpPackageFixStats {
 | 
			
		||||
				pkg, ok := r.WordPressPackages.Find(fixstat.Name)
 | 
			
		||||
				if !ok {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				ok, err := match(pkg.Version, fixstat.FixedIn)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return 0, xerrors.Errorf("Not a semantic versioning: %w", err)
 | 
			
		||||
				}
 | 
			
		||||
				if ok {
 | 
			
		||||
					wpVinfos = append(wpVinfos, v)
 | 
			
		||||
					util.Log.Infof("[match] %s installed: %s, fixedIn: %s", pkg.Name, pkg.Version, fixstat.FixedIn)
 | 
			
		||||
				} else {
 | 
			
		||||
					util.Log.Debugf("[miss] %s installed: %s, fixedIn: %s", pkg.Name, pkg.Version, fixstat.FixedIn)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Plugins
 | 
			
		||||
	for _, p := range r.WordPressPackages.Plugins() {
 | 
			
		||||
		url := fmt.Sprintf("https://wpvulndb.com/api/v3/plugins/%s", p.Name)
 | 
			
		||||
		body, err := httpRequest(url, token)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		if body == "" {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pluginVinfos, err := convertToVinfos(p.Name, body)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, v := range pluginVinfos {
 | 
			
		||||
			for _, fixstat := range v.WpPackageFixStats {
 | 
			
		||||
				pkg, ok := r.WordPressPackages.Find(fixstat.Name)
 | 
			
		||||
				if !ok {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				ok, err := match(pkg.Version, fixstat.FixedIn)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return 0, xerrors.Errorf("Not a semantic versioning: %w", err)
 | 
			
		||||
				}
 | 
			
		||||
				if ok {
 | 
			
		||||
					wpVinfos = append(wpVinfos, v)
 | 
			
		||||
					//TODO Debugf
 | 
			
		||||
					util.Log.Infof("[match] %s installed: %s, fixedIn: %s", pkg.Name, pkg.Version, fixstat.FixedIn)
 | 
			
		||||
				} else {
 | 
			
		||||
					//TODO Debugf
 | 
			
		||||
					util.Log.Infof("[miss] %s installed: %s, fixedIn: %s", pkg.Name, pkg.Version, fixstat.FixedIn)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, wpVinfo := range wpVinfos {
 | 
			
		||||
		if vinfo, ok := r.ScannedCves[wpVinfo.CveID]; ok {
 | 
			
		||||
			vinfo.CveContents[models.WPVulnDB] = wpVinfo.CveContents[models.WPVulnDB]
 | 
			
		||||
			vinfo.VulnType = wpVinfo.VulnType
 | 
			
		||||
			vinfo.Confidences = append(vinfo.Confidences, wpVinfo.Confidences...)
 | 
			
		||||
			vinfo.WpPackageFixStats = append(vinfo.WpPackageFixStats, wpVinfo.WpPackageFixStats...)
 | 
			
		||||
			r.ScannedCves[wpVinfo.CveID] = vinfo
 | 
			
		||||
		} else {
 | 
			
		||||
			r.ScannedCves[wpVinfo.CveID] = wpVinfo
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return len(wpVinfos), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func match(installedVer, fixedIn string) (bool, error) {
 | 
			
		||||
	v1, err := version.NewVersion(installedVer)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
	v2, err := version.NewVersion(fixedIn)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
	return v1.LessThan(v2), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func convertToVinfos(pkgName, body string) (vinfos []models.VulnInfo, err error) {
 | 
			
		||||
	if body == "" {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	// "pkgName" : CVE Detailed data
 | 
			
		||||
	pkgnameCves := map[string]WpCveInfos{}
 | 
			
		||||
	if err = json.Unmarshal([]byte(body), &pkgnameCves); err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to unmarshal %s. err: %w", body, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, v := range pkgnameCves {
 | 
			
		||||
		vs := extractToVulnInfos(pkgName, v.Vulnerabilities)
 | 
			
		||||
		vinfos = append(vinfos, vs...)
 | 
			
		||||
	}
 | 
			
		||||
	return vinfos, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func extractToVulnInfos(pkgName string, cves []WpCveInfo) (vinfos []models.VulnInfo) {
 | 
			
		||||
	for _, vulnerability := range cves {
 | 
			
		||||
		var cveIDs []string
 | 
			
		||||
 | 
			
		||||
		if len(vulnerability.References.Cve) == 0 {
 | 
			
		||||
			cveIDs = append(cveIDs, fmt.Sprintf("WPVDBID-%d", vulnerability.ID))
 | 
			
		||||
		}
 | 
			
		||||
		for _, cveNumber := range vulnerability.References.Cve {
 | 
			
		||||
			cveIDs = append(cveIDs, "CVE-"+cveNumber)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var refs []models.Reference
 | 
			
		||||
		for _, url := range vulnerability.References.URL {
 | 
			
		||||
			refs = append(refs, models.Reference{
 | 
			
		||||
				Link: url,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, cveID := range cveIDs {
 | 
			
		||||
			vinfos = append(vinfos, models.VulnInfo{
 | 
			
		||||
				CveID: cveID,
 | 
			
		||||
				CveContents: models.NewCveContents(
 | 
			
		||||
					models.CveContent{
 | 
			
		||||
						Type:       models.WPVulnDB,
 | 
			
		||||
						CveID:      cveID,
 | 
			
		||||
						Title:      vulnerability.Title,
 | 
			
		||||
						References: refs,
 | 
			
		||||
					},
 | 
			
		||||
				),
 | 
			
		||||
				VulnType: vulnerability.VulnType,
 | 
			
		||||
				Confidences: []models.Confidence{
 | 
			
		||||
					models.WPVulnDBMatch,
 | 
			
		||||
				},
 | 
			
		||||
				WpPackageFixStats: []models.WpPackageFixStatus{{
 | 
			
		||||
					Name:    pkgName,
 | 
			
		||||
					FixedIn: vulnerability.FixedIn,
 | 
			
		||||
				}},
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func httpRequest(url, token string) (string, error) {
 | 
			
		||||
	retry := 1
 | 
			
		||||
	util.Log.Debugf("%s", url)
 | 
			
		||||
	req, err := http.NewRequest("GET", url, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	req.Header.Set("Authorization", fmt.Sprintf("Token token=%s", token))
 | 
			
		||||
loop:
 | 
			
		||||
	resp, err := new(http.Client).Do(req)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	body, err := ioutil.ReadAll(resp.Body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
	if resp.StatusCode == 200 {
 | 
			
		||||
		return string(body), nil
 | 
			
		||||
	} else if resp.StatusCode == 404 {
 | 
			
		||||
		// This package is not in WPVulnDB
 | 
			
		||||
		return "", nil
 | 
			
		||||
	} else if resp.StatusCode == 429 && retry <= 3 {
 | 
			
		||||
		// 429 Too Many Requests
 | 
			
		||||
		util.Log.Debugf("sleep %d min(s): %s", retry, resp.Status)
 | 
			
		||||
		time.Sleep(time.Duration(retry) * time.Minute)
 | 
			
		||||
		retry++
 | 
			
		||||
		goto loop
 | 
			
		||||
	}
 | 
			
		||||
	return "", err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								wordpress/wordpress_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								wordpress/wordpress_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
package wordpress
 | 
			
		||||
		Reference in New Issue
	
	Block a user