Compare commits
	
		
			59 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					83d1f80959 | ||
| 
						 | 
					a33cff8f13 | ||
| 
						 | 
					8679759f60 | ||
| 
						 | 
					53deaee3d7 | ||
| 
						 | 
					5a14a58fe4 | ||
| 
						 | 
					fb1fbf8f95 | ||
| 
						 | 
					cfbf779f9b | ||
| 
						 | 
					d576b6c6c1 | ||
| 
						 | 
					514eb71482 | ||
| 
						 | 
					43ed904db1 | ||
| 
						 | 
					0a440ca629 | ||
| 
						 | 
					eff1dbf95b | ||
| 
						 | 
					9a32a94806 | ||
| 
						 | 
					2534098509 | ||
| 
						 | 
					9497365758 | ||
| 
						 | 
					101c44c9c0 | ||
| 
						 | 
					ffd745c004 | ||
| 
						 | 
					5fea4eaef8 | ||
| 
						 | 
					1f610043cf | ||
| 
						 | 
					3f8de02683 | ||
| 
						 | 
					d02535d053 | ||
| 
						 | 
					75fceff5f7 | ||
| 
						 | 
					ebd3834a35 | ||
| 
						 | 
					93059b74c3 | ||
| 
						 | 
					2fc3462d35 | ||
| 
						 | 
					f78dab50cb | ||
| 
						 | 
					edb324c3d9 | ||
| 
						 | 
					83bcca6e66 | ||
| 
						 | 
					a124518d78 | ||
| 
						 | 
					94bf630e29 | ||
| 
						 | 
					31bb33fd90 | ||
| 
						 | 
					4b680b9960 | ||
| 
						 | 
					8a8ab8cb18 | ||
| 
						 | 
					8146f5fd1b | ||
| 
						 | 
					425c585e47 | ||
| 
						 | 
					4f1578b2d6 | ||
| 
						 | 
					7969b343b0 | ||
| 
						 | 
					58cf1f4c8e | ||
| 
						 | 
					a5b87af862 | ||
| 
						 | 
					a0e592b934 | ||
| 
						 | 
					7eccc538bb | ||
| 
						 | 
					59daa8570a | ||
| 
						 | 
					3f52d318bc | ||
| 
						 | 
					11a7a0c934 | ||
| 
						 | 
					89f49b0e29 | ||
| 
						 | 
					72457cbf8e | ||
| 
						 | 
					c11ba27509 | ||
| 
						 | 
					8a611f9ba6 | ||
| 
						 | 
					4a73875e4d | ||
| 
						 | 
					d9d5e612ff | ||
| 
						 | 
					4d8599e4fc | ||
| 
						 | 
					59c7061d29 | ||
| 
						 | 
					996557c667 | ||
| 
						 | 
					519fb19a77 | ||
| 
						 | 
					36456cb151 | ||
| 
						 | 
					4ae87cc36c | ||
| 
						 | 
					b37df89fb1 | ||
| 
						 | 
					d18e7a751d | ||
| 
						 | 
					8d5ea98e50 | 
							
								
								
									
										5
									
								
								.github/workflows/golangci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -13,10 +13,11 @@ jobs:
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v2
 | 
			
		||||
      - name: golangci-lint
 | 
			
		||||
        uses: golangci/golangci-lint-action@v1
 | 
			
		||||
        uses: golangci/golangci-lint-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
 | 
			
		||||
          version: v1.26
 | 
			
		||||
          version: v1.32
 | 
			
		||||
          args: --timeout=10m
 | 
			
		||||
          
 | 
			
		||||
          # Optional: working directory, useful for monorepos
 | 
			
		||||
          # working-directory: somedir
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/goreleaser.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -19,7 +19,7 @@ jobs:
 | 
			
		||||
        name: Set up Go
 | 
			
		||||
        uses: actions/setup-go@v2
 | 
			
		||||
        with:
 | 
			
		||||
          go-version: 1.14
 | 
			
		||||
          go-version: 1.15
 | 
			
		||||
      -
 | 
			
		||||
        name: Run GoReleaser
 | 
			
		||||
        uses: goreleaser/goreleaser-action@v2
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -11,7 +11,7 @@ jobs:
 | 
			
		||||
    - name: Set up Go 1.x
 | 
			
		||||
      uses: actions/setup-go@v2
 | 
			
		||||
      with:
 | 
			
		||||
        go-version: 1.14.x
 | 
			
		||||
        go-version: 1.15.x
 | 
			
		||||
      id: go
 | 
			
		||||
 | 
			
		||||
    - name: Check out code into the Go module directory
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/tidy.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -19,4 +19,4 @@ jobs:
 | 
			
		||||
          github_token: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
          git_user_name: kotakanbe
 | 
			
		||||
          git_user_email: kotakanbe@gmail.com
 | 
			
		||||
          go_version: 1.14.x
 | 
			
		||||
          go_version: 1.15.6
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -1,4 +1,3 @@
 | 
			
		||||
vuls
 | 
			
		||||
.vscode
 | 
			
		||||
*.txt
 | 
			
		||||
*.json
 | 
			
		||||
@@ -15,4 +14,4 @@ results/
 | 
			
		||||
!setup/docker/*
 | 
			
		||||
.DS_Store
 | 
			
		||||
dist/
 | 
			
		||||
.idea
 | 
			
		||||
.idea
 | 
			
		||||
 
 | 
			
		||||
@@ -6,17 +6,104 @@ release:
 | 
			
		||||
    owner: future-architect
 | 
			
		||||
    name: vuls
 | 
			
		||||
builds:
 | 
			
		||||
- goos:
 | 
			
		||||
- id: vuls
 | 
			
		||||
  goos:
 | 
			
		||||
  - linux
 | 
			
		||||
  goarch:
 | 
			
		||||
  - amd64
 | 
			
		||||
  main: .
 | 
			
		||||
  main: ./cmd/vuls/main.go
 | 
			
		||||
  flags:
 | 
			
		||||
      - -a
 | 
			
		||||
  ldflags: -s -w -X main.version={{.Version}} -X main.revision={{.Commit}} 
 | 
			
		||||
  - -a
 | 
			
		||||
  ldflags: 
 | 
			
		||||
  - -s -w -X github.com/future-architect/vuls/config.Version={{.Version}} -X github.com/future-architect/vuls/config.Revision={{.Commit}}-{{ .CommitDate }}
 | 
			
		||||
  binary: vuls
 | 
			
		||||
 | 
			
		||||
- id: vuls-scanner
 | 
			
		||||
  env:
 | 
			
		||||
  - CGO_ENABLED=0
 | 
			
		||||
  goos:
 | 
			
		||||
  - linux
 | 
			
		||||
  goarch:
 | 
			
		||||
  - 386
 | 
			
		||||
  - amd64
 | 
			
		||||
  - arm
 | 
			
		||||
  - arm64
 | 
			
		||||
  main: ./cmd/scanner/main.go
 | 
			
		||||
  flags:
 | 
			
		||||
  - -a
 | 
			
		||||
  - -tags=scanner
 | 
			
		||||
  ldflags: 
 | 
			
		||||
  - -s -w -X github.com/future-architect/vuls/config.Version={{.Version}} -X github.com/future-architect/vuls/config.Revision={{.Commit}}-{{ .CommitDate }}
 | 
			
		||||
  binary: vuls-scanner
 | 
			
		||||
 | 
			
		||||
- id: trivy-to-vuls
 | 
			
		||||
  env:
 | 
			
		||||
  - CGO_ENABLED=0
 | 
			
		||||
  goos:
 | 
			
		||||
  - linux
 | 
			
		||||
  goarch:
 | 
			
		||||
  - 386
 | 
			
		||||
  - amd64
 | 
			
		||||
  - arm
 | 
			
		||||
  - arm64
 | 
			
		||||
  main: ./contrib/trivy/cmd/main.go
 | 
			
		||||
  binary: trivy-to-vuls
 | 
			
		||||
 | 
			
		||||
- id: future-vuls
 | 
			
		||||
  env:
 | 
			
		||||
  - CGO_ENABLED=0
 | 
			
		||||
  goos:
 | 
			
		||||
  - linux
 | 
			
		||||
  goarch:
 | 
			
		||||
  - 386
 | 
			
		||||
  - amd64
 | 
			
		||||
  - arm
 | 
			
		||||
  - arm64
 | 
			
		||||
  flags:
 | 
			
		||||
  - -a
 | 
			
		||||
  - -tags=scanner
 | 
			
		||||
  main: ./contrib/future-vuls/cmd/main.go
 | 
			
		||||
  binary: future-vuls
 | 
			
		||||
 | 
			
		||||
archives:
 | 
			
		||||
- name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
 | 
			
		||||
 | 
			
		||||
- id: vuls
 | 
			
		||||
  name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
 | 
			
		||||
  builds:
 | 
			
		||||
  - vuls
 | 
			
		||||
  format: tar.gz
 | 
			
		||||
  files:
 | 
			
		||||
  - LICENSE
 | 
			
		||||
  - NOTICE
 | 
			
		||||
  - README*
 | 
			
		||||
  - CHANGELOG.md
 | 
			
		||||
 | 
			
		||||
- id: vuls-scanner
 | 
			
		||||
  name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
 | 
			
		||||
  builds:
 | 
			
		||||
  - vuls-scanner
 | 
			
		||||
  format: tar.gz
 | 
			
		||||
  files:
 | 
			
		||||
  - LICENSE
 | 
			
		||||
  - NOTICE
 | 
			
		||||
  - README*
 | 
			
		||||
  - CHANGELOG.md
 | 
			
		||||
 | 
			
		||||
- id: trivy-to-vuls
 | 
			
		||||
  name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
 | 
			
		||||
  builds:
 | 
			
		||||
  - trivy-to-vuls
 | 
			
		||||
  format: tar.gz
 | 
			
		||||
  files:
 | 
			
		||||
  - LICENSE
 | 
			
		||||
  - NOTICE
 | 
			
		||||
  - README*
 | 
			
		||||
  - CHANGELOG.md
 | 
			
		||||
 | 
			
		||||
- id: future-vuls
 | 
			
		||||
  name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
 | 
			
		||||
  builds:
 | 
			
		||||
  - future-vuls
 | 
			
		||||
  format: tar.gz
 | 
			
		||||
  files:
 | 
			
		||||
  - LICENSE
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ COPY . $GOPATH/src/$REPOSITORY
 | 
			
		||||
RUN cd $GOPATH/src/$REPOSITORY && make install
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FROM alpine:3.7
 | 
			
		||||
FROM alpine:3.11
 | 
			
		||||
 | 
			
		||||
MAINTAINER hikachan sadayuki-matsuno
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										26
									
								
								GNUmakefile
									
									
									
									
									
								
							
							
						
						@@ -20,19 +20,26 @@ BUILDTIME := $(shell date "+%Y%m%d_%H%M%S")
 | 
			
		||||
LDFLAGS := -X 'github.com/future-architect/vuls/config.Version=$(VERSION)' \
 | 
			
		||||
    -X 'github.com/future-architect/vuls/config.Revision=build-$(BUILDTIME)_$(REVISION)'
 | 
			
		||||
GO := GO111MODULE=on go
 | 
			
		||||
CGO_UNABLED := CGO_ENABLED=0 go
 | 
			
		||||
GO_OFF := GO111MODULE=off go
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
all: build
 | 
			
		||||
 | 
			
		||||
build: main.go pretest fmt
 | 
			
		||||
	$(GO) build -a -ldflags "$(LDFLAGS)" -o vuls $<
 | 
			
		||||
build: ./cmd/vuls/main.go pretest fmt
 | 
			
		||||
	$(GO) build -a -ldflags "$(LDFLAGS)" -o vuls ./cmd/vuls
 | 
			
		||||
 | 
			
		||||
b: 	main.go pretest fmt
 | 
			
		||||
	$(GO) build -ldflags "$(LDFLAGS)" -o vuls $<
 | 
			
		||||
b: ./cmd/vuls/main.go 
 | 
			
		||||
	$(GO) build -a -ldflags "$(LDFLAGS)" -o vuls ./cmd/vuls
 | 
			
		||||
 | 
			
		||||
install: main.go pretest
 | 
			
		||||
	$(GO) install -ldflags "$(LDFLAGS)"
 | 
			
		||||
install: ./cmd/vuls/main.go pretest fmt
 | 
			
		||||
	$(GO) install -ldflags "$(LDFLAGS)" ./cmd/vuls
 | 
			
		||||
 | 
			
		||||
build-scanner: ./cmd/scanner/main.go pretest fmt
 | 
			
		||||
	$(CGO_UNABLED) build -tags=scanner -a -ldflags "$(LDFLAGS)" -o vuls ./cmd/scanner
 | 
			
		||||
 | 
			
		||||
install-scanner: ./cmd/scanner/main.go pretest fmt
 | 
			
		||||
	$(CGO_UNABLED) install -tags=scanner -ldflags "$(LDFLAGS)" ./cmd/scanner
 | 
			
		||||
 | 
			
		||||
lint:
 | 
			
		||||
	$(GO_OFF) get -u golang.org/x/lint/golint
 | 
			
		||||
@@ -66,3 +73,10 @@ cov:
 | 
			
		||||
clean:
 | 
			
		||||
	echo $(PKGS) | xargs go clean || exit;
 | 
			
		||||
 | 
			
		||||
# trivy-to-vuls
 | 
			
		||||
build-trivy-to-vuls: pretest fmt
 | 
			
		||||
	$(GO) build -o trivy-to-vuls contrib/trivy/cmd/*.go
 | 
			
		||||
 | 
			
		||||
# future-vuls
 | 
			
		||||
build-future-vuls: pretest fmt
 | 
			
		||||
	$(GO) build -o future-vuls contrib/future-vuls/cmd/*.go
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										92
									
								
								README.md
									
									
									
									
									
								
							
							
						
						@@ -9,7 +9,7 @@
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
Vulnerability scanner for Linux/FreeBSD, agentless, written in golang.
 | 
			
		||||
Vulnerability scanner for Linux/FreeBSD, agent-less, written in Go.
 | 
			
		||||
We have a slack team. [Join slack team](http://goo.gl/forms/xm5KFo35tu)
 | 
			
		||||
Twitter: [@vuls_en](https://twitter.com/vuls_en)
 | 
			
		||||
 | 
			
		||||
@@ -23,20 +23,6 @@ Twitter: [@vuls_en](https://twitter.com/vuls_en)
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
## 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.
 | 
			
		||||
@@ -66,36 +52,47 @@ Vuls is a tool created to solve the problems listed above. It has the following
 | 
			
		||||
 | 
			
		||||
- Alpine, Amazon Linux, CentOS, Debian, Oracle Linux, Raspbian, RHEL, SUSE Enterprise Linux, and Ubuntu
 | 
			
		||||
- FreeBSD
 | 
			
		||||
- Cloud, on-premise, Docker Container and Docker Image
 | 
			
		||||
- Cloud, on-premise, Running Docker Container
 | 
			
		||||
 | 
			
		||||
### High-quality scan
 | 
			
		||||
 | 
			
		||||
Vuls uses multiple vulnerability databases
 | 
			
		||||
- Vulnerability Database
 | 
			
		||||
  - [NVD](https://nvd.nist.gov/)
 | 
			
		||||
  - [JVN(Japanese)](http://jvndb.jvn.jp/apis/myjvn/)
 | 
			
		||||
 | 
			
		||||
- [NVD](https://nvd.nist.gov/)
 | 
			
		||||
- [JVN(Japanese)](http://jvndb.jvn.jp/apis/myjvn/)
 | 
			
		||||
- OVAL
 | 
			
		||||
  - [Red Hat](https://www.redhat.com/security/data/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/)
 | 
			
		||||
  - [SUSE](http://ftp.suse.com/pub/projects/security/oval/)
 | 
			
		||||
  - [Oracle Linux](https://linux.oracle.com/security/oval/)
 | 
			
		||||
 | 
			
		||||
- [Alpine-secdb](https://git.alpinelinux.org/cgit/alpine-secdb/)
 | 
			
		||||
- [Debian Security Bug Tracker](https://security-tracker.debian.org/tracker/)
 | 
			
		||||
- [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
 | 
			
		||||
- Security Advisory
 | 
			
		||||
  - [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
 | 
			
		||||
  - Changelog
 | 
			
		||||
 | 
			
		||||
- PoC, Exploit
 | 
			
		||||
  - [Exploit Database](https://www.exploit-db.com/)
 | 
			
		||||
  - [Metasploit-Framework modules](https://www.rapid7.com/db/?q=&type=metasploit)
 | 
			
		||||
 | 
			
		||||
- CERT
 | 
			
		||||
  - [US-CERT](https://www.us-cert.gov/ncas/alerts)
 | 
			
		||||
  - [JPCERT](http://www.jpcert.or.jp/at/2019.html)
 | 
			
		||||
 | 
			
		||||
- Libraries
 | 
			
		||||
  - [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)
 | 
			
		||||
 | 
			
		||||
- WordPress
 | 
			
		||||
  - [WPVulnDB](https://wpvulndb.com/api)
 | 
			
		||||
 | 
			
		||||
### Scan mode
 | 
			
		||||
 | 
			
		||||
@@ -134,19 +131,6 @@ Vuls uses multiple vulnerability databases
 | 
			
		||||
- 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.
 | 
			
		||||
 | 
			
		||||
### **Static** Analysis
 | 
			
		||||
 | 
			
		||||
**Image scan function is no longer supported from Vuls v0.9.5. Use Trivy directry**
 | 
			
		||||
 | 
			
		||||
~~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
 | 
			
		||||
@@ -184,7 +168,7 @@ Vuls has some options to detect the vulnerabilities
 | 
			
		||||
 | 
			
		||||
## 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/)
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
@@ -195,12 +179,6 @@ kotakanbe ([@kotakanbe](https://twitter.com/kotakanbe)) created vuls and [these
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
## 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)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								cache/bolt.go
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -141,7 +141,7 @@ func (b Bolt) PrettyPrint(meta Meta) error {
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChangelog get the changelgo of specified packName from the Bucket
 | 
			
		||||
// GetChangelog get the changelog of specified packName from the Bucket
 | 
			
		||||
func (b Bolt) GetChangelog(servername, packName string) (changelog string, err error) {
 | 
			
		||||
	err = b.db.View(func(tx *bolt.Tx) error {
 | 
			
		||||
		bkt := tx.Bucket([]byte(servername))
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										36
									
								
								cmd/scanner/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,36 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"context"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	commands "github.com/future-architect/vuls/subcmds"
 | 
			
		||||
	"github.com/google/subcommands"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	subcommands.Register(subcommands.HelpCommand(), "")
 | 
			
		||||
	subcommands.Register(subcommands.FlagsCommand(), "")
 | 
			
		||||
	subcommands.Register(subcommands.CommandsCommand(), "")
 | 
			
		||||
	subcommands.Register(&commands.DiscoverCmd{}, "discover")
 | 
			
		||||
	subcommands.Register(&commands.ScanCmd{}, "scan")
 | 
			
		||||
	subcommands.Register(&commands.HistoryCmd{}, "history")
 | 
			
		||||
	subcommands.Register(&commands.ConfigtestCmd{}, "configtest")
 | 
			
		||||
	subcommands.Register(&commands.SaaSCmd{}, "saas")
 | 
			
		||||
 | 
			
		||||
	var v = flag.Bool("v", false, "Show version")
 | 
			
		||||
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
 | 
			
		||||
	if *v {
 | 
			
		||||
		fmt.Printf("vuls %s %s\n", config.Version, config.Revision)
 | 
			
		||||
		os.Exit(int(subcommands.ExitSuccess))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
	os.Exit(int(subcommands.Execute(ctx)))
 | 
			
		||||
}
 | 
			
		||||
@@ -7,8 +7,8 @@ import (
 | 
			
		||||
 | 
			
		||||
	"context"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/commands"
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	commands "github.com/future-architect/vuls/subcmds"
 | 
			
		||||
	"github.com/google/subcommands"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										311
									
								
								config/config.go
									
									
									
									
									
								
							
							
						
						@@ -16,7 +16,7 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Version of Vuls
 | 
			
		||||
var Version = "0.9.6"
 | 
			
		||||
var Version = "`make build` or `make install` will show the version"
 | 
			
		||||
 | 
			
		||||
// Revision of Git
 | 
			
		||||
var Revision string
 | 
			
		||||
@@ -75,7 +75,7 @@ const (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// ServerTypePseudo is used for ServerInfo.Type
 | 
			
		||||
	// ServerTypePseudo is used for ServerInfo.Type, r.Family
 | 
			
		||||
	ServerTypePseudo = "pseudo"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -114,10 +114,11 @@ type Config struct {
 | 
			
		||||
	UUID       bool `json:"uuid,omitempty"`
 | 
			
		||||
	DetectIPS  bool `json:"detectIps,omitempty"`
 | 
			
		||||
 | 
			
		||||
	CveDict  GoCveDictConf `json:"cveDict,omitempty"`
 | 
			
		||||
	OvalDict GovalDictConf `json:"ovalDict,omitempty"`
 | 
			
		||||
	Gost     GostConf      `json:"gost,omitempty"`
 | 
			
		||||
	Exploit  ExploitConf   `json:"exploit,omitempty"`
 | 
			
		||||
	CveDict    GoCveDictConf  `json:"cveDict,omitempty"`
 | 
			
		||||
	OvalDict   GovalDictConf  `json:"ovalDict,omitempty"`
 | 
			
		||||
	Gost       GostConf       `json:"gost,omitempty"`
 | 
			
		||||
	Exploit    ExploitConf    `json:"exploit,omitempty"`
 | 
			
		||||
	Metasploit MetasploitConf `json:"metasploit,omitempty"`
 | 
			
		||||
 | 
			
		||||
	Slack    SlackConf    `json:"-"`
 | 
			
		||||
	EMail    SMTPConf     `json:"-"`
 | 
			
		||||
@@ -125,16 +126,12 @@ type Config struct {
 | 
			
		||||
	Syslog   SyslogConf   `json:"-"`
 | 
			
		||||
	AWS      AWS          `json:"-"`
 | 
			
		||||
	Azure    Azure        `json:"-"`
 | 
			
		||||
	Stride   StrideConf   `json:"-"`
 | 
			
		||||
	HipChat  HipChatConf  `json:"-"`
 | 
			
		||||
	ChatWork ChatWorkConf `json:"-"`
 | 
			
		||||
	Telegram TelegramConf `json:"-"`
 | 
			
		||||
	Saas     SaasConf     `json:"-"`
 | 
			
		||||
 | 
			
		||||
	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"`
 | 
			
		||||
@@ -150,8 +147,10 @@ type Config struct {
 | 
			
		||||
	FormatOneLineText bool `json:"formatOneLineText,omitempty"`
 | 
			
		||||
	FormatList        bool `json:"formatList,omitempty"`
 | 
			
		||||
	FormatFullText    bool `json:"formatFullText,omitempty"`
 | 
			
		||||
	FormatCsvList     bool `json:"formatCsvList,omitempty"`
 | 
			
		||||
	GZIP              bool `json:"gzip,omitempty"`
 | 
			
		||||
	Diff              bool `json:"diff,omitempty"`
 | 
			
		||||
	WpIgnoreInactive  bool `json:"wpIgnoreInactive,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ValidateOnConfigtest validates
 | 
			
		||||
@@ -244,6 +243,10 @@ func (c Config) ValidateOnReportDB() bool {
 | 
			
		||||
		errs = append(errs, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := validateDB("msfdb", c.Metasploit.Type, c.Metasploit.SQLite3Path, c.Metasploit.URL); err != nil {
 | 
			
		||||
		errs = append(errs, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, err := range errs {
 | 
			
		||||
		log.Error(err)
 | 
			
		||||
	}
 | 
			
		||||
@@ -275,18 +278,10 @@ func (c Config) ValidateOnReport() bool {
 | 
			
		||||
		errs = append(errs, slackerrs...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if hipchaterrs := c.HipChat.Validate(); 0 < len(hipchaterrs) {
 | 
			
		||||
		errs = append(errs, hipchaterrs...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if chatworkerrs := c.ChatWork.Validate(); 0 < len(chatworkerrs) {
 | 
			
		||||
		errs = append(errs, chatworkerrs...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if strideerrs := c.Stride.Validate(); 0 < len(strideerrs) {
 | 
			
		||||
		errs = append(errs, strideerrs...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if telegramerrs := c.Telegram.Validate(); 0 < len(telegramerrs) {
 | 
			
		||||
		errs = append(errs, telegramerrs...)
 | 
			
		||||
	}
 | 
			
		||||
@@ -434,33 +429,6 @@ func (c *SMTPConf) Validate() (errs []error) {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StrideConf is stride config
 | 
			
		||||
type StrideConf struct {
 | 
			
		||||
	HookURL   string `json:"-"`
 | 
			
		||||
	AuthToken string `json:"-"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Validate validates configuration
 | 
			
		||||
func (c *StrideConf) Validate() (errs []error) {
 | 
			
		||||
	if !Conf.ToStride {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(c.HookURL) == 0 {
 | 
			
		||||
		errs = append(errs, xerrors.New("stride.HookURL must not be empty"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(c.AuthToken) == 0 {
 | 
			
		||||
		errs = append(errs, xerrors.New("stride.AuthToken must not be empty"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := valid.ValidateStruct(c)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		errs = append(errs, err)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SlackConf is slack config
 | 
			
		||||
type SlackConf struct {
 | 
			
		||||
	HookURL     string   `valid:"url" json:"-" toml:"hookURL,omitempty"`
 | 
			
		||||
@@ -504,32 +472,6 @@ func (c *SlackConf) Validate() (errs []error) {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HipChatConf is HipChat config
 | 
			
		||||
type HipChatConf struct {
 | 
			
		||||
	AuthToken string `json:"-"`
 | 
			
		||||
	Room      string `json:"-"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Validate validates configuration
 | 
			
		||||
func (c *HipChatConf) Validate() (errs []error) {
 | 
			
		||||
	if !Conf.ToHipChat {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if len(c.Room) == 0 {
 | 
			
		||||
		errs = append(errs, xerrors.New("hipcaht.room must not be empty"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(c.AuthToken) == 0 {
 | 
			
		||||
		errs = append(errs, xerrors.New("hipcaht.AuthToken must not be empty"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := valid.ValidateStruct(c)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		errs = append(errs, err)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ChatWorkConf is ChatWork config
 | 
			
		||||
type ChatWorkConf struct {
 | 
			
		||||
	APIToken string `json:"-"`
 | 
			
		||||
@@ -542,11 +484,11 @@ func (c *ChatWorkConf) Validate() (errs []error) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if len(c.Room) == 0 {
 | 
			
		||||
		errs = append(errs, xerrors.New("chatworkcaht.room must not be empty"))
 | 
			
		||||
		errs = append(errs, xerrors.New("chatWorkConf.room must not be empty"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(c.APIToken) == 0 {
 | 
			
		||||
		errs = append(errs, xerrors.New("chatworkcaht.ApiToken must not be empty"))
 | 
			
		||||
		errs = append(errs, xerrors.New("chatWorkConf.ApiToken must not be empty"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := valid.ValidateStruct(c)
 | 
			
		||||
@@ -582,9 +524,9 @@ func (c *TelegramConf) Validate() (errs []error) {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SaasConf is stride config
 | 
			
		||||
// SaasConf is FutureVuls config
 | 
			
		||||
type SaasConf struct {
 | 
			
		||||
	GroupID int    `json:"-"`
 | 
			
		||||
	GroupID int64  `json:"-"`
 | 
			
		||||
	Token   string `json:"-"`
 | 
			
		||||
	URL     string `json:"-"`
 | 
			
		||||
}
 | 
			
		||||
@@ -753,16 +695,15 @@ func (c *HTTPConf) Validate() (errs []error) {
 | 
			
		||||
 | 
			
		||||
const httpKey = "VULS_HTTP_URL"
 | 
			
		||||
 | 
			
		||||
// Overwrite set options with the following priority.
 | 
			
		||||
// 1. Command line option
 | 
			
		||||
// 2. Environment variable
 | 
			
		||||
// 3. config.toml
 | 
			
		||||
func (c *HTTPConf) Overwrite(cmdOpt HTTPConf) {
 | 
			
		||||
// Init set options with the following priority.
 | 
			
		||||
// 1. Environment variable
 | 
			
		||||
// 2. config.toml
 | 
			
		||||
func (c *HTTPConf) Init(toml HTTPConf) {
 | 
			
		||||
	if os.Getenv(httpKey) != "" {
 | 
			
		||||
		c.URL = os.Getenv(httpKey)
 | 
			
		||||
	}
 | 
			
		||||
	if cmdOpt.URL != "" {
 | 
			
		||||
		c.URL = cmdOpt.URL
 | 
			
		||||
	if toml.URL != "" {
 | 
			
		||||
		c.URL = toml.URL
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -792,11 +733,10 @@ const cveDBType = "CVEDB_TYPE"
 | 
			
		||||
const cveDBURL = "CVEDB_URL"
 | 
			
		||||
const cveDBPATH = "CVEDB_SQLITE3_PATH"
 | 
			
		||||
 | 
			
		||||
// Overwrite set options with the following priority.
 | 
			
		||||
// 1. Command line option
 | 
			
		||||
// 2. Environment variable
 | 
			
		||||
// 3. config.toml
 | 
			
		||||
func (cnf *GoCveDictConf) Overwrite(cmdOpt GoCveDictConf) {
 | 
			
		||||
// Init set options with the following priority.
 | 
			
		||||
// 1. Environment variable
 | 
			
		||||
// 2. config.toml
 | 
			
		||||
func (cnf *GoCveDictConf) Init() {
 | 
			
		||||
	if os.Getenv(cveDBType) != "" {
 | 
			
		||||
		cnf.Type = os.Getenv(cveDBType)
 | 
			
		||||
	}
 | 
			
		||||
@@ -806,16 +746,6 @@ func (cnf *GoCveDictConf) Overwrite(cmdOpt GoCveDictConf) {
 | 
			
		||||
	if os.Getenv(cveDBPATH) != "" {
 | 
			
		||||
		cnf.SQLite3Path = os.Getenv(cveDBPATH)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if cmdOpt.Type != "" {
 | 
			
		||||
		cnf.Type = cmdOpt.Type
 | 
			
		||||
	}
 | 
			
		||||
	if cmdOpt.URL != "" {
 | 
			
		||||
		cnf.URL = cmdOpt.URL
 | 
			
		||||
	}
 | 
			
		||||
	if cmdOpt.SQLite3Path != "" {
 | 
			
		||||
		cnf.SQLite3Path = cmdOpt.SQLite3Path
 | 
			
		||||
	}
 | 
			
		||||
	cnf.setDefault()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -851,11 +781,10 @@ const govalType = "OVALDB_TYPE"
 | 
			
		||||
const govalURL = "OVALDB_URL"
 | 
			
		||||
const govalPATH = "OVALDB_SQLITE3_PATH"
 | 
			
		||||
 | 
			
		||||
// Overwrite set options with the following priority.
 | 
			
		||||
// 1. Command line option
 | 
			
		||||
// 2. Environment variable
 | 
			
		||||
// 3. config.toml
 | 
			
		||||
func (cnf *GovalDictConf) Overwrite(cmdOpt GovalDictConf) {
 | 
			
		||||
// Init set options with the following priority.
 | 
			
		||||
// 1. Environment variable
 | 
			
		||||
// 2. config.toml
 | 
			
		||||
func (cnf *GovalDictConf) Init() {
 | 
			
		||||
	if os.Getenv(govalType) != "" {
 | 
			
		||||
		cnf.Type = os.Getenv(govalType)
 | 
			
		||||
	}
 | 
			
		||||
@@ -865,16 +794,6 @@ func (cnf *GovalDictConf) Overwrite(cmdOpt GovalDictConf) {
 | 
			
		||||
	if os.Getenv(govalPATH) != "" {
 | 
			
		||||
		cnf.SQLite3Path = os.Getenv(govalPATH)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if cmdOpt.Type != "" {
 | 
			
		||||
		cnf.Type = cmdOpt.Type
 | 
			
		||||
	}
 | 
			
		||||
	if cmdOpt.URL != "" {
 | 
			
		||||
		cnf.URL = cmdOpt.URL
 | 
			
		||||
	}
 | 
			
		||||
	if cmdOpt.SQLite3Path != "" {
 | 
			
		||||
		cnf.SQLite3Path = cmdOpt.SQLite3Path
 | 
			
		||||
	}
 | 
			
		||||
	cnf.setDefault()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -909,11 +828,10 @@ const gostDBType = "GOSTDB_TYPE"
 | 
			
		||||
const gostDBURL = "GOSTDB_URL"
 | 
			
		||||
const gostDBPATH = "GOSTDB_SQLITE3_PATH"
 | 
			
		||||
 | 
			
		||||
// Overwrite set options with the following priority.
 | 
			
		||||
// 1. Command line option
 | 
			
		||||
// 2. Environment variable
 | 
			
		||||
// 3. config.toml
 | 
			
		||||
func (cnf *GostConf) Overwrite(cmdOpt GostConf) {
 | 
			
		||||
// Init set options with the following priority.
 | 
			
		||||
// 1. Environment variable
 | 
			
		||||
// 2. config.toml
 | 
			
		||||
func (cnf *GostConf) Init() {
 | 
			
		||||
	if os.Getenv(gostDBType) != "" {
 | 
			
		||||
		cnf.Type = os.Getenv(gostDBType)
 | 
			
		||||
	}
 | 
			
		||||
@@ -923,16 +841,6 @@ func (cnf *GostConf) Overwrite(cmdOpt GostConf) {
 | 
			
		||||
	if os.Getenv(gostDBPATH) != "" {
 | 
			
		||||
		cnf.SQLite3Path = os.Getenv(gostDBPATH)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if cmdOpt.Type != "" {
 | 
			
		||||
		cnf.Type = cmdOpt.Type
 | 
			
		||||
	}
 | 
			
		||||
	if cmdOpt.URL != "" {
 | 
			
		||||
		cnf.URL = cmdOpt.URL
 | 
			
		||||
	}
 | 
			
		||||
	if cmdOpt.SQLite3Path != "" {
 | 
			
		||||
		cnf.SQLite3Path = cmdOpt.SQLite3Path
 | 
			
		||||
	}
 | 
			
		||||
	cnf.setDefault()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -967,11 +875,10 @@ const exploitDBType = "EXPLOITDB_TYPE"
 | 
			
		||||
const exploitDBURL = "EXPLOITDB_URL"
 | 
			
		||||
const exploitDBPATH = "EXPLOITDB_SQLITE3_PATH"
 | 
			
		||||
 | 
			
		||||
// Overwrite set options with the following priority.
 | 
			
		||||
// 1. Command line option
 | 
			
		||||
// 2. Environment variable
 | 
			
		||||
// 3. config.toml
 | 
			
		||||
func (cnf *ExploitConf) Overwrite(cmdOpt ExploitConf) {
 | 
			
		||||
// Init set options with the following priority.
 | 
			
		||||
// 1. Environment variable
 | 
			
		||||
// 2. config.toml
 | 
			
		||||
func (cnf *ExploitConf) Init() {
 | 
			
		||||
	if os.Getenv(exploitDBType) != "" {
 | 
			
		||||
		cnf.Type = os.Getenv(exploitDBType)
 | 
			
		||||
	}
 | 
			
		||||
@@ -981,16 +888,6 @@ func (cnf *ExploitConf) Overwrite(cmdOpt ExploitConf) {
 | 
			
		||||
	if os.Getenv(exploitDBPATH) != "" {
 | 
			
		||||
		cnf.SQLite3Path = os.Getenv(exploitDBPATH)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if cmdOpt.Type != "" {
 | 
			
		||||
		cnf.Type = cmdOpt.Type
 | 
			
		||||
	}
 | 
			
		||||
	if cmdOpt.URL != "" {
 | 
			
		||||
		cnf.URL = cmdOpt.URL
 | 
			
		||||
	}
 | 
			
		||||
	if cmdOpt.SQLite3Path != "" {
 | 
			
		||||
		cnf.SQLite3Path = cmdOpt.SQLite3Path
 | 
			
		||||
	}
 | 
			
		||||
	cnf.setDefault()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -999,6 +896,53 @@ func (cnf *ExploitConf) IsFetchViaHTTP() bool {
 | 
			
		||||
	return Conf.Exploit.Type == "http"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MetasploitConf is metasploit config
 | 
			
		||||
type MetasploitConf struct {
 | 
			
		||||
	// DB type for metasploit dictionary (sqlite3, mysql, postgres or redis)
 | 
			
		||||
	Type string
 | 
			
		||||
 | 
			
		||||
	// http://metasploit-dictionary.com:1324 or DB connection string
 | 
			
		||||
	URL string `json:"-"`
 | 
			
		||||
 | 
			
		||||
	// /path/to/metasploit.sqlite3
 | 
			
		||||
	SQLite3Path string `json:"-"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cnf *MetasploitConf) setDefault() {
 | 
			
		||||
	if cnf.Type == "" {
 | 
			
		||||
		cnf.Type = "sqlite3"
 | 
			
		||||
	}
 | 
			
		||||
	if cnf.URL == "" && cnf.SQLite3Path == "" {
 | 
			
		||||
		wd, _ := os.Getwd()
 | 
			
		||||
		cnf.SQLite3Path = filepath.Join(wd, "go-msfdb.sqlite3")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const metasploitDBType = "METASPLOITDB_TYPE"
 | 
			
		||||
const metasploitDBURL = "METASPLOITDB_URL"
 | 
			
		||||
const metasploitDBPATH = "METASPLOITDB_SQLITE3_PATH"
 | 
			
		||||
 | 
			
		||||
// Init set options with the following priority.
 | 
			
		||||
// 1. Environment variable
 | 
			
		||||
// 2. config.toml
 | 
			
		||||
func (cnf *MetasploitConf) Init() {
 | 
			
		||||
	if os.Getenv(metasploitDBType) != "" {
 | 
			
		||||
		cnf.Type = os.Getenv(metasploitDBType)
 | 
			
		||||
	}
 | 
			
		||||
	if os.Getenv(metasploitDBURL) != "" {
 | 
			
		||||
		cnf.URL = os.Getenv(metasploitDBURL)
 | 
			
		||||
	}
 | 
			
		||||
	if os.Getenv(metasploitDBPATH) != "" {
 | 
			
		||||
		cnf.SQLite3Path = os.Getenv(metasploitDBPATH)
 | 
			
		||||
	}
 | 
			
		||||
	cnf.setDefault()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsFetchViaHTTP returns wether fetch via http
 | 
			
		||||
func (cnf *MetasploitConf) IsFetchViaHTTP() bool {
 | 
			
		||||
	return Conf.Metasploit.Type == "http"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AWS is aws config
 | 
			
		||||
type AWS struct {
 | 
			
		||||
	// AWS profile to use
 | 
			
		||||
@@ -1031,42 +975,42 @@ type Azure struct {
 | 
			
		||||
 | 
			
		||||
// ServerInfo has SSH Info, additional CPE packages to scan.
 | 
			
		||||
type ServerInfo struct {
 | 
			
		||||
	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,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
 | 
			
		||||
	UUIDs                  map[string]string           `toml:"uuids,omitempty" json:"uuids,omitempty"`
 | 
			
		||||
	Memo                   string                      `toml:"memo,omitempty" json:"memo,omitempty"`
 | 
			
		||||
	Enablerepo             []string                    `toml:"enablerepo,omitempty" json:"enablerepo,omitempty"` // For CentOS, RHEL, Amazon
 | 
			
		||||
	Optional               map[string]interface{}      `toml:"optional,omitempty" json:"optional,omitempty"`     // Optional key-value set that will be outputted to JSON
 | 
			
		||||
	Lockfiles              []string                    `toml:"lockfiles,omitempty" json:"lockfiles,omitempty"`   // ie) path/to/package-lock.json
 | 
			
		||||
	FindLock               bool                        `toml:"findLock,omitempty" json:"findLock,omitempty"`
 | 
			
		||||
	Type                   string                      `toml:"type,omitempty" json:"type,omitempty"` // "pseudo" or ""
 | 
			
		||||
	ServerName         string                      `toml:"-" json:"serverName,omitempty"`
 | 
			
		||||
	User               string                      `toml:"user,omitempty" json:"user,omitempty"`
 | 
			
		||||
	Host               string                      `toml:"host,omitempty" json:"host,omitempty"`
 | 
			
		||||
	JumpServer         []string                    `toml:"jumpServer,omitempty" json:"jumpServer,omitempty"`
 | 
			
		||||
	Port               string                      `toml:"port,omitempty" json:"port,omitempty"`
 | 
			
		||||
	SSHConfigPath      string                      `toml:"sshConfigPath,omitempty" json:"sshConfigPath,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"`
 | 
			
		||||
	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
 | 
			
		||||
	UUIDs              map[string]string           `toml:"uuids,omitempty" json:"uuids,omitempty"`
 | 
			
		||||
	Memo               string                      `toml:"memo,omitempty" json:"memo,omitempty"`
 | 
			
		||||
	Enablerepo         []string                    `toml:"enablerepo,omitempty" json:"enablerepo,omitempty"` // For CentOS, RHEL, Amazon
 | 
			
		||||
	Optional           map[string]interface{}      `toml:"optional,omitempty" json:"optional,omitempty"`     // Optional key-value set that will be outputted to JSON
 | 
			
		||||
	Lockfiles          []string                    `toml:"lockfiles,omitempty" json:"lockfiles,omitempty"`   // ie) path/to/package-lock.json
 | 
			
		||||
	FindLock           bool                        `toml:"findLock,omitempty" json:"findLock,omitempty"`
 | 
			
		||||
	Type               string                      `toml:"type,omitempty" json:"type,omitempty"` // "pseudo" or ""
 | 
			
		||||
	WordPress          WordPressConf               `toml:"wordpress,omitempty" json:"wordpress,omitempty"`
 | 
			
		||||
	IgnoredJSONKeys    []string                    `toml:"ignoredJSONKeys,omitempty" json:"ignoredJSONKeys,omitempty"`
 | 
			
		||||
 | 
			
		||||
	WordPress WordPressConf `toml:"wordpress,omitempty" json:"wordpress,omitempty"`
 | 
			
		||||
 | 
			
		||||
	// used internal
 | 
			
		||||
	IPv4Addrs      []string       `toml:"-" json:"ipv4Addrs,omitempty"`
 | 
			
		||||
	IPv6Addrs      []string       `toml:"-" json:"ipv6Addrs,omitempty"`
 | 
			
		||||
	IPSIdentifiers map[IPS]string `toml:"-" json:"ipsIdentifiers,omitempty"`
 | 
			
		||||
 | 
			
		||||
	LogMsgAnsiColor string    `toml:"-" json:"-"` // DebugLog Color
 | 
			
		||||
	Container       Container `toml:"-" json:"-"`
 | 
			
		||||
	Distro          Distro    `toml:"-" json:"-"`
 | 
			
		||||
	Mode            ScanMode  `toml:"-" json:"-"`
 | 
			
		||||
	// internal use
 | 
			
		||||
	IPv4Addrs       []string       `toml:"-" json:"ipv4Addrs,omitempty"`
 | 
			
		||||
	IPv6Addrs       []string       `toml:"-" json:"ipv6Addrs,omitempty"`
 | 
			
		||||
	IPSIdentifiers  map[IPS]string `toml:"-" json:"ipsIdentifiers,omitempty"`
 | 
			
		||||
	LogMsgAnsiColor string         `toml:"-" json:"-"` // DebugLog Color
 | 
			
		||||
	Container       Container      `toml:"-" json:"-"`
 | 
			
		||||
	Distro          Distro         `toml:"-" json:"-"`
 | 
			
		||||
	Mode            ScanMode       `toml:"-" json:"-"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ContainerSetting is used for loading container setting in config.toml
 | 
			
		||||
@@ -1165,7 +1109,7 @@ const (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GetServerName returns ServerName if this serverInfo is about host.
 | 
			
		||||
// If this serverInfo is abount a container, returns containerID@ServerName
 | 
			
		||||
// If this serverInfo is about a container, returns containerID@ServerName
 | 
			
		||||
func (s ServerInfo) GetServerName() string {
 | 
			
		||||
	if len(s.Container.ContainerID) == 0 {
 | 
			
		||||
		return s.ServerName
 | 
			
		||||
@@ -1184,21 +1128,18 @@ func (l Distro) String() string {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MajorVersion returns Major version
 | 
			
		||||
func (l Distro) MajorVersion() (ver int, err error) {
 | 
			
		||||
func (l Distro) MajorVersion() (int, error) {
 | 
			
		||||
	if l.Family == Amazon {
 | 
			
		||||
		ss := strings.Fields(l.Release)
 | 
			
		||||
		if len(ss) == 1 {
 | 
			
		||||
			return 1, nil
 | 
			
		||||
		}
 | 
			
		||||
		ver, err = strconv.Atoi(ss[0])
 | 
			
		||||
		return
 | 
			
		||||
		return strconv.Atoi(ss[0])
 | 
			
		||||
	}
 | 
			
		||||
	if 0 < len(l.Release) {
 | 
			
		||||
		ver, err = strconv.Atoi(strings.Split(l.Release, ".")[0])
 | 
			
		||||
	} else {
 | 
			
		||||
		err = xerrors.New("Release is empty")
 | 
			
		||||
		return strconv.Atoi(strings.Split(l.Release, ".")[0])
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
	return 0, xerrors.New("Release is empty")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsContainer returns whether this ServerInfo is about container
 | 
			
		||||
 
 | 
			
		||||
@@ -63,7 +63,7 @@ func TestSyslogConfValidate(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestMajorVersion(t *testing.T) {
 | 
			
		||||
func TestDistro_MajorVersion(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in  Distro
 | 
			
		||||
		out int
 | 
			
		||||
 
 | 
			
		||||
@@ -21,8 +21,6 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
 | 
			
		||||
	}
 | 
			
		||||
	Conf.EMail = conf.EMail
 | 
			
		||||
	Conf.Slack = conf.Slack
 | 
			
		||||
	Conf.Stride = conf.Stride
 | 
			
		||||
	Conf.HipChat = conf.HipChat
 | 
			
		||||
	Conf.ChatWork = conf.ChatWork
 | 
			
		||||
	Conf.Telegram = conf.Telegram
 | 
			
		||||
	Conf.Saas = conf.Saas
 | 
			
		||||
@@ -35,6 +33,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
 | 
			
		||||
	Conf.OvalDict = conf.OvalDict
 | 
			
		||||
	Conf.Gost = conf.Gost
 | 
			
		||||
	Conf.Exploit = conf.Exploit
 | 
			
		||||
	Conf.Metasploit = conf.Metasploit
 | 
			
		||||
 | 
			
		||||
	d := conf.Default
 | 
			
		||||
	Conf.Default = d
 | 
			
		||||
@@ -44,7 +43,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
 | 
			
		||||
		d.KeyPassword = keyPass
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	i := 0
 | 
			
		||||
	index := 0
 | 
			
		||||
	for serverName, v := range conf.Servers {
 | 
			
		||||
		if 0 < len(v.KeyPassword) {
 | 
			
		||||
			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)
 | 
			
		||||
@@ -57,6 +56,11 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
 | 
			
		||||
				return xerrors.Errorf("%s is invalid. host is empty", serverName)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			s.JumpServer = v.JumpServer
 | 
			
		||||
			if len(s.JumpServer) == 0 {
 | 
			
		||||
				s.JumpServer = d.JumpServer
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			switch {
 | 
			
		||||
			case v.Port != "":
 | 
			
		||||
				s.Port = v.Port
 | 
			
		||||
@@ -77,6 +81,11 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			s.SSHConfigPath = v.SSHConfigPath
 | 
			
		||||
			if len(s.SSHConfigPath) == 0 {
 | 
			
		||||
				s.SSHConfigPath = d.SSHConfigPath
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			s.KeyPath = v.KeyPath
 | 
			
		||||
			if len(s.KeyPath) == 0 {
 | 
			
		||||
				s.KeyPath = d.KeyPath
 | 
			
		||||
@@ -105,7 +114,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
 | 
			
		||||
			case "offline":
 | 
			
		||||
				s.Mode.Set(Offline)
 | 
			
		||||
			default:
 | 
			
		||||
				return xerrors.Errorf("scanMode: %s of %s is invalie. Specify -fast, -fast-root, -deep or offline", m, serverName)
 | 
			
		||||
				return xerrors.Errorf("scanMode: %s of %s is invalid. Specify -fast, -fast-root, -deep or offline", m, serverName)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if err := s.Mode.validate(); err != nil {
 | 
			
		||||
@@ -153,10 +162,6 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
 | 
			
		||||
			s.Containers[contName] = cont
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(v.DependencyCheckXMLPath) != 0 || len(d.DependencyCheckXMLPath) != 0 {
 | 
			
		||||
			return xerrors.Errorf("[DEPRECATED] dependencyCheckXMLPath IS DEPRECATED. USE owaspDCXMLPath INSTEAD: %s", serverName)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.OwaspDCXMLPath = v.OwaspDCXMLPath
 | 
			
		||||
		if len(s.OwaspDCXMLPath) == 0 {
 | 
			
		||||
			s.OwaspDCXMLPath = d.OwaspDCXMLPath
 | 
			
		||||
@@ -197,14 +202,14 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
 | 
			
		||||
		for _, reg := range s.IgnorePkgsRegexp {
 | 
			
		||||
			_, err := regexp.Compile(reg)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return xerrors.Errorf("Faild to parse %s in %s. err: %w", reg, serverName, err)
 | 
			
		||||
				return xerrors.Errorf("Failed 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 xerrors.Errorf("Faild to parse %s in %s@%s. err: %w",
 | 
			
		||||
					return xerrors.Errorf("Failed to parse %s in %s@%s. err: %w",
 | 
			
		||||
						reg, contName, serverName, err)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
@@ -257,12 +262,23 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
 | 
			
		||||
		s.WordPress.OSUser = v.WordPress.OSUser
 | 
			
		||||
		s.WordPress.IgnoreInactive = v.WordPress.IgnoreInactive
 | 
			
		||||
 | 
			
		||||
		s.LogMsgAnsiColor = Colors[i%len(Colors)]
 | 
			
		||||
		i++
 | 
			
		||||
		s.IgnoredJSONKeys = v.IgnoredJSONKeys
 | 
			
		||||
		if len(s.IgnoredJSONKeys) == 0 {
 | 
			
		||||
			s.IgnoredJSONKeys = d.IgnoredJSONKeys
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.LogMsgAnsiColor = Colors[index%len(Colors)]
 | 
			
		||||
		index++
 | 
			
		||||
 | 
			
		||||
		servers[serverName] = s
 | 
			
		||||
	}
 | 
			
		||||
	Conf.Servers = servers
 | 
			
		||||
 | 
			
		||||
	Conf.CveDict.Init()
 | 
			
		||||
	Conf.OvalDict.Init()
 | 
			
		||||
	Conf.Gost.Init()
 | 
			
		||||
	Conf.Exploit.Init()
 | 
			
		||||
	Conf.Metasploit.Init()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -280,5 +296,5 @@ func toCpeURI(cpename string) (string, error) {
 | 
			
		||||
		}
 | 
			
		||||
		return naming.BindToURI(wfn), nil
 | 
			
		||||
	}
 | 
			
		||||
	return "", xerrors.Errorf("Unknow CPE format: %s", cpename)
 | 
			
		||||
	return "", xerrors.Errorf("Unknown CPE format: %s", cpename)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										38
									
								
								contrib/future-vuls/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,38 @@
 | 
			
		||||
# future-vuls
 | 
			
		||||
 | 
			
		||||
## Main Features
 | 
			
		||||
 | 
			
		||||
- upload vuls results json to future-vuls
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
git clone https://github.com/future-architect/vuls.git
 | 
			
		||||
make build-future-vuls
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Command Reference
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
Upload to FutureVuls
 | 
			
		||||
 | 
			
		||||
Usage:
 | 
			
		||||
  future-vuls upload [flags]
 | 
			
		||||
 | 
			
		||||
Flags:
 | 
			
		||||
      --config string   config file (default is $HOME/.cobra.yaml)
 | 
			
		||||
  -g, --group-id int    future vuls group id, ENV: VULS_GROUP_ID
 | 
			
		||||
  -h, --help            help for upload
 | 
			
		||||
  -s, --stdin           input from stdin. ENV: VULS_STDIN
 | 
			
		||||
  -t, --token string    future vuls token
 | 
			
		||||
      --url string      future vuls upload url
 | 
			
		||||
      --uuid string     server uuid. ENV: VULS_SERVER_UUID
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Usage
 | 
			
		||||
 | 
			
		||||
- update results json
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 cat results.json | future-vuls upload --stdin --token xxxx --url https://xxxx --group-id 1 --uuid xxxx
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										98
									
								
								contrib/future-vuls/cmd/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,98 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/saas"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	configFile string
 | 
			
		||||
	stdIn      bool
 | 
			
		||||
	jsonDir    string
 | 
			
		||||
	serverUUID string
 | 
			
		||||
	groupID    int64
 | 
			
		||||
	token      string
 | 
			
		||||
	url        string
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	var err error
 | 
			
		||||
	var cmdFvulsUploader = &cobra.Command{
 | 
			
		||||
		Use:   "upload",
 | 
			
		||||
		Short: "Upload to FutureVuls",
 | 
			
		||||
		Long:  `Upload to FutureVuls`,
 | 
			
		||||
		Run: func(cmd *cobra.Command, args []string) {
 | 
			
		||||
			if len(serverUUID) == 0 {
 | 
			
		||||
				serverUUID = os.Getenv("VULS_SERVER_UUID")
 | 
			
		||||
			}
 | 
			
		||||
			if groupID == 0 {
 | 
			
		||||
				envGroupID := os.Getenv("VULS_GROUP_ID")
 | 
			
		||||
				if groupID, err = strconv.ParseInt(envGroupID, 10, 64); err != nil {
 | 
			
		||||
					fmt.Printf("Invalid GroupID: %s\n", envGroupID)
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if len(url) == 0 {
 | 
			
		||||
				url = os.Getenv("VULS_URL")
 | 
			
		||||
			}
 | 
			
		||||
			if len(token) == 0 {
 | 
			
		||||
				token = os.Getenv("VULS_TOKEN")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var scanResultJSON []byte
 | 
			
		||||
			if stdIn {
 | 
			
		||||
				reader := bufio.NewReader(os.Stdin)
 | 
			
		||||
				buf := new(bytes.Buffer)
 | 
			
		||||
				if _, err = buf.ReadFrom(reader); err != nil {
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
				scanResultJSON = buf.Bytes()
 | 
			
		||||
			} else {
 | 
			
		||||
				fmt.Println("use --stdin option")
 | 
			
		||||
				os.Exit(1)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var scanResult models.ScanResult
 | 
			
		||||
			if err = json.Unmarshal(scanResultJSON, &scanResult); err != nil {
 | 
			
		||||
				fmt.Println("Failed to parse json", err)
 | 
			
		||||
				os.Exit(1)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			scanResult.ServerUUID = serverUUID
 | 
			
		||||
 | 
			
		||||
			config.Conf.Saas.GroupID = groupID
 | 
			
		||||
			config.Conf.Saas.Token = token
 | 
			
		||||
			config.Conf.Saas.URL = url
 | 
			
		||||
			if err = (saas.Writer{}).Write(scanResult); err != nil {
 | 
			
		||||
				fmt.Println(err)
 | 
			
		||||
				os.Exit(1)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			return
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	cmdFvulsUploader.PersistentFlags().StringVar(&serverUUID, "uuid", "", "server uuid. ENV: VULS_SERVER_UUID")
 | 
			
		||||
	cmdFvulsUploader.PersistentFlags().StringVar(&configFile, "config", "", "config file (default is $HOME/.cobra.yaml)")
 | 
			
		||||
	cmdFvulsUploader.PersistentFlags().BoolVarP(&stdIn, "stdin", "s", false, "input from stdin. ENV: VULS_STDIN")
 | 
			
		||||
	// TODO Read JSON file from directory
 | 
			
		||||
	//	cmdFvulsUploader.Flags().StringVarP(&jsonDir, "results-dir", "d", "./", "vuls scan results json dir")
 | 
			
		||||
	cmdFvulsUploader.PersistentFlags().Int64VarP(&groupID, "group-id", "g", 0, "future vuls group id, ENV: VULS_GROUP_ID")
 | 
			
		||||
	cmdFvulsUploader.PersistentFlags().StringVarP(&token, "token", "t", "", "future vuls token")
 | 
			
		||||
	cmdFvulsUploader.PersistentFlags().StringVar(&url, "url", "", "future vuls upload url")
 | 
			
		||||
 | 
			
		||||
	var rootCmd = &cobra.Command{Use: "future-vuls"}
 | 
			
		||||
	rootCmd.AddCommand(cmdFvulsUploader)
 | 
			
		||||
	if err = rootCmd.Execute(); err != nil {
 | 
			
		||||
		fmt.Println("Failed to execute command", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										35
									
								
								contrib/trivy/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,35 @@
 | 
			
		||||
# trivy-to-vuls
 | 
			
		||||
 | 
			
		||||
## Main Features
 | 
			
		||||
 | 
			
		||||
- convert trivy's results json to vuls's report json
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
git clone https://github.com/future-architect/vuls.git
 | 
			
		||||
make build-trivy-to-vuls
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Command Reference
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
Parse trivy json to vuls results
 | 
			
		||||
 | 
			
		||||
Usage:
 | 
			
		||||
  trivy-to-vuls parse [flags]
 | 
			
		||||
 | 
			
		||||
Flags:
 | 
			
		||||
  -h, --help                          help for parse
 | 
			
		||||
  -s, --stdin                         input from stdin
 | 
			
		||||
  -d, --trivy-json-dir string         trivy json dir (default "./")
 | 
			
		||||
  -f, --trivy-json-file-name string   trivy json file name (default "results.json")
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Usage
 | 
			
		||||
 | 
			
		||||
- use trivy output
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 trivy -q image -f=json python:3.4-alpine | trivy-to-vuls parse --stdin
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										78
									
								
								contrib/trivy/cmd/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,78 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/contrib/trivy/parser"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	serverUUID   string
 | 
			
		||||
	stdIn        bool
 | 
			
		||||
	jsonDir      string
 | 
			
		||||
	jsonFileName string
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	var err error
 | 
			
		||||
	var cmdTrivyToVuls = &cobra.Command{
 | 
			
		||||
		Use:   "parse",
 | 
			
		||||
		Short: "Parse trivy json to vuls results",
 | 
			
		||||
		Long:  `Parse trivy json to vuls results`,
 | 
			
		||||
		Run: func(cmd *cobra.Command, args []string) {
 | 
			
		||||
			jsonFilePath := filepath.Join(jsonDir, jsonFileName)
 | 
			
		||||
			var trivyJSON []byte
 | 
			
		||||
			if stdIn {
 | 
			
		||||
				reader := bufio.NewReader(os.Stdin)
 | 
			
		||||
				buf := new(bytes.Buffer)
 | 
			
		||||
				if _, err = buf.ReadFrom(reader); err != nil {
 | 
			
		||||
					os.Exit(1)
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
				trivyJSON = buf.Bytes()
 | 
			
		||||
			} else {
 | 
			
		||||
				if trivyJSON, err = ioutil.ReadFile(jsonFilePath); err != nil {
 | 
			
		||||
					fmt.Println("Failed to read file", err)
 | 
			
		||||
					os.Exit(1)
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			scanResult := &models.ScanResult{
 | 
			
		||||
				JSONVersion: models.JSONVersion,
 | 
			
		||||
				ScannedCves: models.VulnInfos{},
 | 
			
		||||
			}
 | 
			
		||||
			if scanResult, err = parser.Parse(trivyJSON, scanResult); err != nil {
 | 
			
		||||
				fmt.Println("Failed to execute command", err)
 | 
			
		||||
				os.Exit(1)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			var resultJSON []byte
 | 
			
		||||
			if resultJSON, err = json.MarshalIndent(scanResult, "", "   "); err != nil {
 | 
			
		||||
				fmt.Println("Failed to create json", err)
 | 
			
		||||
				os.Exit(1)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			fmt.Println(string(resultJSON))
 | 
			
		||||
			return
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	cmdTrivyToVuls.Flags().BoolVarP(&stdIn, "stdin", "s", false, "input from stdin")
 | 
			
		||||
	cmdTrivyToVuls.Flags().StringVarP(&jsonDir, "trivy-json-dir", "d", "./", "trivy json dir")
 | 
			
		||||
	cmdTrivyToVuls.Flags().StringVarP(&jsonFileName, "trivy-json-file-name", "f", "results.json", "trivy json file name")
 | 
			
		||||
 | 
			
		||||
	var rootCmd = &cobra.Command{Use: "trivy-to-vuls"}
 | 
			
		||||
	rootCmd.AddCommand(cmdTrivyToVuls)
 | 
			
		||||
	if err = rootCmd.Execute(); err != nil {
 | 
			
		||||
		fmt.Println("Failed to execute command", err)
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										164
									
								
								contrib/trivy/parser/parser.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,164 @@
 | 
			
		||||
package parser
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/aquasecurity/fanal/analyzer/os"
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/report"
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/types"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Parse :
 | 
			
		||||
func Parse(vulnJSON []byte, scanResult *models.ScanResult) (result *models.ScanResult, err error) {
 | 
			
		||||
	var trivyResults report.Results
 | 
			
		||||
	if err = json.Unmarshal(vulnJSON, &trivyResults); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pkgs := models.Packages{}
 | 
			
		||||
	vulnInfos := models.VulnInfos{}
 | 
			
		||||
	uniqueLibraryScannerPaths := map[string]models.LibraryScanner{}
 | 
			
		||||
	for _, trivyResult := range trivyResults {
 | 
			
		||||
		for _, vuln := range trivyResult.Vulnerabilities {
 | 
			
		||||
			if _, ok := vulnInfos[vuln.VulnerabilityID]; !ok {
 | 
			
		||||
				vulnInfos[vuln.VulnerabilityID] = models.VulnInfo{
 | 
			
		||||
					CveID: vuln.VulnerabilityID,
 | 
			
		||||
					Confidences: models.Confidences{
 | 
			
		||||
						{
 | 
			
		||||
							Score:           100,
 | 
			
		||||
							DetectionMethod: models.TrivyMatchStr,
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
					CveContents:      models.CveContents{},
 | 
			
		||||
					LibraryFixedIns:  models.LibraryFixedIns{},
 | 
			
		||||
					// VulnType : "",
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			vulnInfo := vulnInfos[vuln.VulnerabilityID]
 | 
			
		||||
			var notFixedYet bool
 | 
			
		||||
			fixState := ""
 | 
			
		||||
			if len(vuln.FixedVersion) == 0 {
 | 
			
		||||
				notFixedYet = true
 | 
			
		||||
				fixState = "Affected"
 | 
			
		||||
			}
 | 
			
		||||
			var references models.References
 | 
			
		||||
			for _, reference := range vuln.References {
 | 
			
		||||
				references = append(references, models.Reference{
 | 
			
		||||
					Source: "trivy",
 | 
			
		||||
					Link:   reference,
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			sort.Slice(references, func(i, j int) bool {
 | 
			
		||||
				return references[i].Link < references[j].Link
 | 
			
		||||
			})
 | 
			
		||||
 | 
			
		||||
			vulnInfo.CveContents = models.CveContents{
 | 
			
		||||
				models.Trivy: models.CveContent{
 | 
			
		||||
					Cvss3Severity: vuln.Severity,
 | 
			
		||||
					References:    references,
 | 
			
		||||
					Title:         vuln.Title,
 | 
			
		||||
					Summary:       vuln.Description,
 | 
			
		||||
				},
 | 
			
		||||
			}
 | 
			
		||||
			// do only if image type is Vuln
 | 
			
		||||
			if IsTrivySupportedOS(trivyResult.Type) {
 | 
			
		||||
				pkgs[vuln.PkgName] = models.Package{
 | 
			
		||||
					Name:    vuln.PkgName,
 | 
			
		||||
					Version: vuln.InstalledVersion,
 | 
			
		||||
				}
 | 
			
		||||
				vulnInfo.AffectedPackages = append(vulnInfo.AffectedPackages, models.PackageFixStatus{
 | 
			
		||||
					Name:        vuln.PkgName,
 | 
			
		||||
					NotFixedYet: notFixedYet,
 | 
			
		||||
					FixState:    fixState,
 | 
			
		||||
					FixedIn:     vuln.FixedVersion,
 | 
			
		||||
				})
 | 
			
		||||
 | 
			
		||||
				// overwrite every time if os package
 | 
			
		||||
				scanResult.Family = trivyResult.Type
 | 
			
		||||
				scanResult.ServerName = trivyResult.Target
 | 
			
		||||
				scanResult.Optional = map[string]interface{}{
 | 
			
		||||
					"trivy-target": trivyResult.Target,
 | 
			
		||||
				}
 | 
			
		||||
				scanResult.ScannedAt = time.Now()
 | 
			
		||||
				scanResult.ScannedBy = "trivy"
 | 
			
		||||
				scanResult.ScannedVia = "trivy"
 | 
			
		||||
			} else {
 | 
			
		||||
				// LibraryScanの結果
 | 
			
		||||
				vulnInfo.LibraryFixedIns = append(vulnInfo.LibraryFixedIns, models.LibraryFixedIn{
 | 
			
		||||
					Key:     trivyResult.Type,
 | 
			
		||||
					Name:    vuln.PkgName,
 | 
			
		||||
					Path:    trivyResult.Target,
 | 
			
		||||
					FixedIn: vuln.FixedVersion,
 | 
			
		||||
				})
 | 
			
		||||
				libScanner := uniqueLibraryScannerPaths[trivyResult.Target]
 | 
			
		||||
				libScanner.Libs = append(libScanner.Libs, types.Library{
 | 
			
		||||
					Name:    vuln.PkgName,
 | 
			
		||||
					Version: vuln.InstalledVersion,
 | 
			
		||||
				})
 | 
			
		||||
				uniqueLibraryScannerPaths[trivyResult.Target] = libScanner
 | 
			
		||||
			}
 | 
			
		||||
			vulnInfos[vuln.VulnerabilityID] = vulnInfo
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// flatten and unique libraries
 | 
			
		||||
	libraryScanners := make([]models.LibraryScanner, 0, len(uniqueLibraryScannerPaths))
 | 
			
		||||
	for path, v := range uniqueLibraryScannerPaths {
 | 
			
		||||
		uniqueLibrary := map[string]types.Library{}
 | 
			
		||||
		for _, lib := range v.Libs {
 | 
			
		||||
			uniqueLibrary[lib.Name+lib.Version] = lib
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var libraries []types.Library
 | 
			
		||||
		for _, library := range uniqueLibrary {
 | 
			
		||||
			libraries = append(libraries, library)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		sort.Slice(libraries, func(i, j int) bool {
 | 
			
		||||
			return libraries[i].Name < libraries[j].Name
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		libscanner := models.LibraryScanner{
 | 
			
		||||
			Path: path,
 | 
			
		||||
			Libs: libraries,
 | 
			
		||||
		}
 | 
			
		||||
		libraryScanners = append(libraryScanners, libscanner)
 | 
			
		||||
	}
 | 
			
		||||
	sort.Slice(libraryScanners, func(i, j int) bool {
 | 
			
		||||
		return libraryScanners[i].Path < libraryScanners[j].Path
 | 
			
		||||
	})
 | 
			
		||||
	scanResult.ScannedCves = vulnInfos
 | 
			
		||||
	scanResult.Packages = pkgs
 | 
			
		||||
	scanResult.LibraryScanners = libraryScanners
 | 
			
		||||
	return scanResult, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsTrivySupportedOS :
 | 
			
		||||
func IsTrivySupportedOS(family string) bool {
 | 
			
		||||
	supportedFamilies := []string{
 | 
			
		||||
		os.RedHat,
 | 
			
		||||
		os.Debian,
 | 
			
		||||
		os.Ubuntu,
 | 
			
		||||
		os.CentOS,
 | 
			
		||||
		os.Fedora,
 | 
			
		||||
		os.Amazon,
 | 
			
		||||
		os.Oracle,
 | 
			
		||||
		os.Windows,
 | 
			
		||||
		os.OpenSUSE,
 | 
			
		||||
		os.OpenSUSELeap,
 | 
			
		||||
		os.OpenSUSETumbleweed,
 | 
			
		||||
		os.SLES,
 | 
			
		||||
		os.Photon,
 | 
			
		||||
		os.Alpine,
 | 
			
		||||
	}
 | 
			
		||||
	for _, supportedFamily := range supportedFamilies {
 | 
			
		||||
		if family == supportedFamily {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5482
									
								
								contrib/trivy/parser/parser_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -1,3 +1,5 @@
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package exploit
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ import (
 | 
			
		||||
	"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.
 | 
			
		||||
// FillGitHubSecurityAlerts access to owner/repo on GitHub and fetch security 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(
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										75
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						@@ -1,6 +1,6 @@
 | 
			
		||||
module github.com/future-architect/vuls
 | 
			
		||||
 | 
			
		||||
go 1.13
 | 
			
		||||
go 1.15
 | 
			
		||||
 | 
			
		||||
replace (
 | 
			
		||||
	gopkg.in/mattn/go-colorable.v0 => github.com/mattn/go-colorable v0.1.0
 | 
			
		||||
@@ -8,46 +8,73 @@ replace (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/Azure/azure-sdk-for-go v42.0.0+incompatible
 | 
			
		||||
	github.com/Azure/azure-sdk-for-go v49.1.0+incompatible
 | 
			
		||||
	github.com/Azure/go-autorest/autorest v0.11.15 // indirect
 | 
			
		||||
	github.com/Azure/go-autorest/autorest/adal v0.9.10 // indirect
 | 
			
		||||
	github.com/BurntSushi/toml v0.3.1
 | 
			
		||||
	github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91
 | 
			
		||||
	github.com/aquasecurity/fanal v0.0.0-20200427221647-c3528846e21c
 | 
			
		||||
	github.com/aquasecurity/go-dep-parser v0.0.0-20200123140603-4dc0125084da // indirect
 | 
			
		||||
	github.com/aquasecurity/trivy v0.6.0
 | 
			
		||||
	github.com/aquasecurity/trivy-db v0.0.0-20200427221211-19fb3b7a88b5
 | 
			
		||||
	github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a
 | 
			
		||||
	github.com/aws/aws-sdk-go v1.30.16
 | 
			
		||||
	github.com/aquasecurity/fanal v0.0.0-20201218050947-981a0510f9cb
 | 
			
		||||
	github.com/aquasecurity/trivy v0.14.0
 | 
			
		||||
	github.com/aquasecurity/trivy-db v0.0.0-20201220084758-2d91316c83fa
 | 
			
		||||
	github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef
 | 
			
		||||
	github.com/aws/aws-sdk-go v1.36.12
 | 
			
		||||
	github.com/boltdb/bolt v1.3.1
 | 
			
		||||
	github.com/briandowns/spinner v1.12.0 // indirect
 | 
			
		||||
	github.com/caarlos0/env/v6 v6.4.0 // indirect
 | 
			
		||||
	github.com/cenkalti/backoff v2.2.1+incompatible
 | 
			
		||||
	github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b
 | 
			
		||||
	github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21
 | 
			
		||||
	github.com/emersion/go-smtp v0.14.0
 | 
			
		||||
	github.com/goccy/go-yaml v1.8.4 // indirect
 | 
			
		||||
	github.com/golang/protobuf v1.4.3 // indirect
 | 
			
		||||
	github.com/google/subcommands v1.2.0
 | 
			
		||||
	github.com/google/wire v0.4.0 // indirect
 | 
			
		||||
	github.com/gosuri/uitable v0.0.4
 | 
			
		||||
	github.com/grokify/html-strip-tags-go v0.0.0-20200923094847-079d207a09f1 // indirect
 | 
			
		||||
	github.com/hashicorp/go-uuid v1.0.2
 | 
			
		||||
	github.com/hashicorp/go-version v1.2.0
 | 
			
		||||
	github.com/hashicorp/go-version v1.2.1
 | 
			
		||||
	github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c
 | 
			
		||||
	github.com/jesseduffield/gocui v0.3.0
 | 
			
		||||
	github.com/k0kubun/pp v3.0.1+incompatible
 | 
			
		||||
	github.com/knqyf263/go-apk-version v0.0.0-20200507080916-9f84b1e3c54c
 | 
			
		||||
	github.com/knqyf263/go-cpe v0.0.0-20180327054844-659663f6eca2
 | 
			
		||||
	github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f
 | 
			
		||||
	github.com/knqyf263/go-cpe v0.0.0-20201213041631-54f6ab28673f
 | 
			
		||||
	github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d
 | 
			
		||||
	github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936
 | 
			
		||||
	github.com/knqyf263/go-version v1.1.1
 | 
			
		||||
	github.com/knqyf263/gost v0.1.3
 | 
			
		||||
	github.com/kotakanbe/go-cve-dictionary v0.4.2
 | 
			
		||||
	github.com/knqyf263/gost v0.1.7
 | 
			
		||||
	github.com/kotakanbe/go-cve-dictionary v0.5.6
 | 
			
		||||
	github.com/kotakanbe/go-pingscanner v0.1.0
 | 
			
		||||
	github.com/kotakanbe/goval-dictionary v0.2.5
 | 
			
		||||
	github.com/kotakanbe/goval-dictionary v0.2.16
 | 
			
		||||
	github.com/kotakanbe/logrus-prefixed-formatter v0.0.0-20180123152602-928f7356cb96
 | 
			
		||||
	github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
 | 
			
		||||
	github.com/magiconair/properties v1.8.4 // indirect
 | 
			
		||||
	github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
 | 
			
		||||
	github.com/mitchellh/go-homedir v1.1.0
 | 
			
		||||
	github.com/mozqnet/go-exploitdb v0.0.0-20190911093644-f647f17ea8ca
 | 
			
		||||
	github.com/mitchellh/mapstructure v1.4.0 // indirect
 | 
			
		||||
	github.com/mozqnet/go-exploitdb v0.1.2
 | 
			
		||||
	github.com/nlopes/slack v0.6.0
 | 
			
		||||
	github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1 // indirect
 | 
			
		||||
	github.com/nsf/termbox-go v0.0.0-20201124104050-ed494de23a00 // indirect
 | 
			
		||||
	github.com/olekukonko/tablewriter v0.0.4
 | 
			
		||||
	github.com/parnurzeal/gorequest v0.2.16
 | 
			
		||||
	github.com/pelletier/go-toml v1.8.1 // indirect
 | 
			
		||||
	github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
 | 
			
		||||
	github.com/sirupsen/logrus v1.5.0
 | 
			
		||||
	github.com/spf13/afero v1.2.2
 | 
			
		||||
	golang.org/x/crypto v0.0.0-20200427165652-729f1e841bcc
 | 
			
		||||
	golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
 | 
			
		||||
	golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
 | 
			
		||||
	k8s.io/utils v0.0.0-20191114184206-e782cd3c129f
 | 
			
		||||
	github.com/sirupsen/logrus v1.7.0
 | 
			
		||||
	github.com/spf13/afero v1.5.1
 | 
			
		||||
	github.com/spf13/cast v1.3.1 // indirect
 | 
			
		||||
	github.com/spf13/cobra v1.1.1
 | 
			
		||||
	github.com/spf13/jwalterweatherman v1.1.0 // indirect
 | 
			
		||||
	github.com/spf13/viper v1.7.1 // indirect
 | 
			
		||||
	github.com/takuzoo3868/go-msfdb v0.1.3
 | 
			
		||||
	go.uber.org/multierr v1.6.0 // indirect
 | 
			
		||||
	go.uber.org/zap v1.16.0 // indirect
 | 
			
		||||
	golang.org/x/crypto v0.0.0-20201217014255-9d1352758620
 | 
			
		||||
	golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect
 | 
			
		||||
	golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5
 | 
			
		||||
	golang.org/x/sys v0.0.0-20201218084310-7d0127a74742 // indirect
 | 
			
		||||
	golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
 | 
			
		||||
	golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58 // indirect
 | 
			
		||||
	golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
 | 
			
		||||
	google.golang.org/appengine v1.6.7 // indirect
 | 
			
		||||
	gopkg.in/ini.v1 v1.62.0 // indirect
 | 
			
		||||
	gopkg.in/yaml.v2 v2.4.0 // indirect
 | 
			
		||||
	honnef.co/go/tools v0.1.0 // indirect
 | 
			
		||||
	k8s.io/utils v0.0.0-20201110183641-67b214c5f920
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
@@ -21,8 +23,23 @@ type packCves struct {
 | 
			
		||||
	cves      []models.CveContent
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (deb Debian) supported(major string) bool {
 | 
			
		||||
	_, ok := map[string]string{
 | 
			
		||||
		"8":  "jessie",
 | 
			
		||||
		"9":  "stretch",
 | 
			
		||||
		"10": "buster",
 | 
			
		||||
	}[major]
 | 
			
		||||
	return ok
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DetectUnfixed fills cve information that has in Gost
 | 
			
		||||
func (deb Debian) DetectUnfixed(driver db.DB, r *models.ScanResult, _ bool) (nCVEs int, err error) {
 | 
			
		||||
	if !deb.supported(major(r.Release)) {
 | 
			
		||||
		// only logging
 | 
			
		||||
		util.Log.Warnf("Debian %s is not supported yet", r.Release)
 | 
			
		||||
		return 0, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	linuxImage := "linux-image-" + r.RunningKernel.Release
 | 
			
		||||
	// Add linux and set the version of running kernel to search OVAL.
 | 
			
		||||
	if r.Container.ContainerID == "" {
 | 
			
		||||
@@ -37,9 +54,17 @@ func (deb Debian) DetectUnfixed(driver db.DB, r *models.ScanResult, _ bool) (nCV
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Debian Security Tracker does not support Package for Raspbian, so skip it.
 | 
			
		||||
	var scanResult models.ScanResult
 | 
			
		||||
	if r.Family != config.Raspbian {
 | 
			
		||||
		scanResult = *r
 | 
			
		||||
	} else {
 | 
			
		||||
		scanResult = r.RemoveRaspbianPackFromResult()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	packCvesList := []packCves{}
 | 
			
		||||
	if config.Conf.Gost.IsFetchViaHTTP() {
 | 
			
		||||
		url, _ := util.URLPathJoin(config.Conf.Gost.URL, "debian", major(r.Release), "pkgs")
 | 
			
		||||
		url, _ := util.URLPathJoin(config.Conf.Gost.URL, "debian", major(scanResult.Release), "pkgs")
 | 
			
		||||
		responses, err := getAllUnfixedCvesViaHTTP(r, url)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
@@ -64,8 +89,8 @@ func (deb Debian) DetectUnfixed(driver db.DB, r *models.ScanResult, _ bool) (nCV
 | 
			
		||||
		if driver == nil {
 | 
			
		||||
			return 0, nil
 | 
			
		||||
		}
 | 
			
		||||
		for _, pack := range r.Packages {
 | 
			
		||||
			cveDebs := driver.GetUnfixedCvesDebian(major(r.Release), pack.Name)
 | 
			
		||||
		for _, pack := range scanResult.Packages {
 | 
			
		||||
			cveDebs := driver.GetUnfixedCvesDebian(major(scanResult.Release), pack.Name)
 | 
			
		||||
			cves := []models.CveContent{}
 | 
			
		||||
			for _, cveDeb := range cveDebs {
 | 
			
		||||
				cves = append(cves, *deb.ConvertToModel(&cveDeb))
 | 
			
		||||
@@ -78,8 +103,8 @@ func (deb Debian) DetectUnfixed(driver db.DB, r *models.ScanResult, _ bool) (nCV
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// SrcPack
 | 
			
		||||
		for _, pack := range r.SrcPackages {
 | 
			
		||||
			cveDebs := driver.GetUnfixedCvesDebian(major(r.Release), pack.Name)
 | 
			
		||||
		for _, pack := range scanResult.SrcPackages {
 | 
			
		||||
			cveDebs := driver.GetUnfixedCvesDebian(major(scanResult.Release), pack.Name)
 | 
			
		||||
			cves := []models.CveContent{}
 | 
			
		||||
			for _, cveDeb := range cveDebs {
 | 
			
		||||
				cves = append(cves, *deb.ConvertToModel(&cveDeb))
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										61
									
								
								gost/debian_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,61 @@
 | 
			
		||||
package gost
 | 
			
		||||
 | 
			
		||||
import "testing"
 | 
			
		||||
 | 
			
		||||
func TestDebian_Supported(t *testing.T) {
 | 
			
		||||
	type fields struct {
 | 
			
		||||
		Base Base
 | 
			
		||||
	}
 | 
			
		||||
	type args struct {
 | 
			
		||||
		major string
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name string
 | 
			
		||||
		args args
 | 
			
		||||
		want bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "8 is supported",
 | 
			
		||||
			args: args{
 | 
			
		||||
				major: "8",
 | 
			
		||||
			},
 | 
			
		||||
			want: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "9 is supported",
 | 
			
		||||
			args: args{
 | 
			
		||||
				major: "9",
 | 
			
		||||
			},
 | 
			
		||||
			want: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "10 is supported",
 | 
			
		||||
			args: args{
 | 
			
		||||
				major: "10",
 | 
			
		||||
			},
 | 
			
		||||
			want: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "11 is not supported yet",
 | 
			
		||||
			args: args{
 | 
			
		||||
				major: "11",
 | 
			
		||||
			},
 | 
			
		||||
			want: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "empty string is not supported yet",
 | 
			
		||||
			args: args{
 | 
			
		||||
				major: "",
 | 
			
		||||
			},
 | 
			
		||||
			want: false,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			deb := Debian{}
 | 
			
		||||
			if got := deb.supported(tt.args.major); got != tt.want {
 | 
			
		||||
				t.Errorf("Debian.Supported() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
@@ -23,7 +25,7 @@ func NewClient(family string) Client {
 | 
			
		||||
	switch family {
 | 
			
		||||
	case cnf.RedHat, cnf.CentOS:
 | 
			
		||||
		return RedHat{}
 | 
			
		||||
	case cnf.Debian:
 | 
			
		||||
	case cnf.Debian, cnf.Raspbian:
 | 
			
		||||
		return Debian{}
 | 
			
		||||
	case cnf.Windows:
 | 
			
		||||
		return Microsoft{}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
@@ -26,19 +28,20 @@ func (ms Microsoft) DetectUnfixed(driver db.DB, r *models.ScanResult, _ bool) (n
 | 
			
		||||
		if _, ok := r.ScannedCves[cveID]; !ok {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		cveCont := ms.ConvertToModel(&msCve)
 | 
			
		||||
		cveCont, mitigations := ms.ConvertToModel(&msCve)
 | 
			
		||||
		v, _ := r.ScannedCves[cveID]
 | 
			
		||||
		if v.CveContents == nil {
 | 
			
		||||
			v.CveContents = models.CveContents{}
 | 
			
		||||
		}
 | 
			
		||||
		v.CveContents[models.Microsoft] = *cveCont
 | 
			
		||||
		v.Mitigations = append(v.Mitigations, mitigations...)
 | 
			
		||||
		r.ScannedCves[cveID] = v
 | 
			
		||||
	}
 | 
			
		||||
	return len(cveIDs), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConvertToModel converts gost model to vuls model
 | 
			
		||||
func (ms Microsoft) ConvertToModel(cve *gostmodels.MicrosoftCVE) *models.CveContent {
 | 
			
		||||
func (ms Microsoft) ConvertToModel(cve *gostmodels.MicrosoftCVE) (*models.CveContent, []models.Mitigation) {
 | 
			
		||||
	v3score := 0.0
 | 
			
		||||
	var v3Vector string
 | 
			
		||||
	for _, scoreSet := range cve.ScoreSets {
 | 
			
		||||
@@ -80,6 +83,18 @@ func (ms Microsoft) ConvertToModel(cve *gostmodels.MicrosoftCVE) *models.CveCont
 | 
			
		||||
		option["kbids"] = strings.Join(kbids, ",")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vendorURL := "https://msrc.microsoft.com/update-guide/vulnerability/" + cve.CveID
 | 
			
		||||
	mitigations := []models.Mitigation{}
 | 
			
		||||
	if cve.Mitigation != "" {
 | 
			
		||||
		mitigations = []models.Mitigation{
 | 
			
		||||
			{
 | 
			
		||||
				CveContentType: models.Microsoft,
 | 
			
		||||
				Mitigation:     cve.Mitigation,
 | 
			
		||||
				URL:            vendorURL,
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &models.CveContent{
 | 
			
		||||
		Type:          models.Microsoft,
 | 
			
		||||
		CveID:         cve.CveID,
 | 
			
		||||
@@ -90,10 +105,9 @@ func (ms Microsoft) ConvertToModel(cve *gostmodels.MicrosoftCVE) *models.CveCont
 | 
			
		||||
		Cvss3Severity: v3Severity,
 | 
			
		||||
		References:    refs,
 | 
			
		||||
		CweIDs:        cwe,
 | 
			
		||||
		Mitigation:    cve.Mitigation,
 | 
			
		||||
		Published:     cve.PublishDate,
 | 
			
		||||
		LastModified:  cve.LastUpdateDate,
 | 
			
		||||
		SourceLink:    "https://portal.msrc.microsoft.com/ja-jp/security-guidance/advisory/" + cve.CveID,
 | 
			
		||||
		SourceLink:    vendorURL,
 | 
			
		||||
		Optional:      option,
 | 
			
		||||
	}
 | 
			
		||||
	}, mitigations
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/knqyf263/gost/db"
 | 
			
		||||
)
 | 
			
		||||
@@ -16,7 +16,3 @@ type Pseudo struct {
 | 
			
		||||
func (pse Pseudo) DetectUnfixed(driver db.DB, r *models.ScanResult, _ bool) (int, error) {
 | 
			
		||||
	return 0, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func major(osVer string) (majorVersion string) {
 | 
			
		||||
	return strings.Split(osVer, ".")[0]
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
@@ -46,7 +48,7 @@ func (red RedHat) fillFixed(driver db.DB, r *models.ScanResult) error {
 | 
			
		||||
			if redCve.ID == 0 {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			cveCont := red.ConvertToModel(&redCve)
 | 
			
		||||
			cveCont, mitigations := red.ConvertToModel(&redCve)
 | 
			
		||||
			v, ok := r.ScannedCves[res.request.cveID]
 | 
			
		||||
			if ok {
 | 
			
		||||
				if v.CveContents == nil {
 | 
			
		||||
@@ -61,6 +63,7 @@ func (red RedHat) fillFixed(driver db.DB, r *models.ScanResult) error {
 | 
			
		||||
					Confidences: models.Confidences{models.RedHatAPIMatch},
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			v.Mitigations = append(v.Mitigations, mitigations...)
 | 
			
		||||
			r.ScannedCves[res.request.cveID] = v
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
@@ -71,7 +74,7 @@ func (red RedHat) fillFixed(driver db.DB, r *models.ScanResult) error {
 | 
			
		||||
			if len(redCve.Name) == 0 {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			cveCont := red.ConvertToModel(&redCve)
 | 
			
		||||
			cveCont, mitigations := red.ConvertToModel(&redCve)
 | 
			
		||||
			v, ok := r.ScannedCves[cveID]
 | 
			
		||||
			if ok {
 | 
			
		||||
				if v.CveContents == nil {
 | 
			
		||||
@@ -86,6 +89,7 @@ func (red RedHat) fillFixed(driver db.DB, r *models.ScanResult) error {
 | 
			
		||||
					Confidences: models.Confidences{models.RedHatAPIMatch},
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			v.Mitigations = append(v.Mitigations, mitigations...)
 | 
			
		||||
			r.ScannedCves[cveID] = v
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -109,7 +113,7 @@ func (red RedHat) fillUnfixed(driver db.DB, r *models.ScanResult, ignoreWillNotF
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, cve := range cves {
 | 
			
		||||
				cveCont := red.ConvertToModel(&cve)
 | 
			
		||||
				cveCont, mitigations := red.ConvertToModel(&cve)
 | 
			
		||||
				v, ok := r.ScannedCves[cve.Name]
 | 
			
		||||
				if ok {
 | 
			
		||||
					if v.CveContents == nil {
 | 
			
		||||
@@ -125,6 +129,7 @@ func (red RedHat) fillUnfixed(driver db.DB, r *models.ScanResult, ignoreWillNotF
 | 
			
		||||
					}
 | 
			
		||||
					nCVEs++
 | 
			
		||||
				}
 | 
			
		||||
				v.Mitigations = append(v.Mitigations, mitigations...)
 | 
			
		||||
				pkgStats := red.mergePackageStates(v,
 | 
			
		||||
					cve.PackageState, r.Packages, r.Release)
 | 
			
		||||
				if 0 < len(pkgStats) {
 | 
			
		||||
@@ -141,7 +146,7 @@ func (red RedHat) fillUnfixed(driver db.DB, r *models.ScanResult, ignoreWillNotF
 | 
			
		||||
			// CVE-ID: RedhatCVE
 | 
			
		||||
			cves := driver.GetUnfixedCvesRedhat(major(r.Release), pack.Name, ignoreWillNotFix)
 | 
			
		||||
			for _, cve := range cves {
 | 
			
		||||
				cveCont := red.ConvertToModel(&cve)
 | 
			
		||||
				cveCont, mitigations := red.ConvertToModel(&cve)
 | 
			
		||||
				v, ok := r.ScannedCves[cve.Name]
 | 
			
		||||
				if ok {
 | 
			
		||||
					if v.CveContents == nil {
 | 
			
		||||
@@ -157,7 +162,7 @@ func (red RedHat) fillUnfixed(driver db.DB, r *models.ScanResult, ignoreWillNotF
 | 
			
		||||
					}
 | 
			
		||||
					nCVEs++
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				v.Mitigations = append(v.Mitigations, mitigations...)
 | 
			
		||||
				pkgStats := red.mergePackageStates(v,
 | 
			
		||||
					cve.PackageState, r.Packages, r.Release)
 | 
			
		||||
				if 0 < len(pkgStats) {
 | 
			
		||||
@@ -218,7 +223,7 @@ func (red RedHat) parseCwe(str string) (cwes []string) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConvertToModel converts gost model to vuls model
 | 
			
		||||
func (red RedHat) ConvertToModel(cve *gostmodels.RedhatCVE) *models.CveContent {
 | 
			
		||||
func (red RedHat) ConvertToModel(cve *gostmodels.RedhatCVE) (*models.CveContent, []models.Mitigation) {
 | 
			
		||||
	cwes := red.parseCwe(cve.Cwe)
 | 
			
		||||
 | 
			
		||||
	details := []string{}
 | 
			
		||||
@@ -249,6 +254,18 @@ func (red RedHat) ConvertToModel(cve *gostmodels.RedhatCVE) *models.CveContent {
 | 
			
		||||
		refs = append(refs, models.Reference{Link: r.Reference})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vendorURL := "https://access.redhat.com/security/cve/" + cve.Name
 | 
			
		||||
	mitigations := []models.Mitigation{}
 | 
			
		||||
	if cve.Mitigation != "" {
 | 
			
		||||
		mitigations = []models.Mitigation{
 | 
			
		||||
			{
 | 
			
		||||
				CveContentType: models.RedHatAPI,
 | 
			
		||||
				Mitigation:     cve.Mitigation,
 | 
			
		||||
				URL:            vendorURL,
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &models.CveContent{
 | 
			
		||||
		Type:          models.RedHatAPI,
 | 
			
		||||
		CveID:         cve.Name,
 | 
			
		||||
@@ -262,8 +279,7 @@ func (red RedHat) ConvertToModel(cve *gostmodels.RedhatCVE) *models.CveContent {
 | 
			
		||||
		Cvss3Severity: v3severity,
 | 
			
		||||
		References:    refs,
 | 
			
		||||
		CweIDs:        cwes,
 | 
			
		||||
		Mitigation:    cve.Mitigation,
 | 
			
		||||
		Published:     cve.PublicDate,
 | 
			
		||||
		SourceLink:    "https://access.redhat.com/security/cve/" + cve.Name,
 | 
			
		||||
	}
 | 
			
		||||
		SourceLink:    vendorURL,
 | 
			
		||||
	}, mitigations
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ package gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cenkalti/backoff"
 | 
			
		||||
@@ -181,3 +182,7 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er
 | 
			
		||||
		json:    body,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func major(osVer string) (majorVersion string) {
 | 
			
		||||
	return strings.Split(osVer, ".")[0]
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 247 KiB  | 
| 
		 Before Width: | Height: | Size: 97 KiB  | 
| 
		 Before Width: | Height: | Size: 91 KiB  | 
@@ -1,414 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
 | 
			
		||||
  <!--Created by yEd 3.17-->
 | 
			
		||||
  <key attr.name="Description" attr.type="string" for="graph" id="d0"/>
 | 
			
		||||
  <key for="port" id="d1" yfiles.type="portgraphics"/>
 | 
			
		||||
  <key for="port" id="d2" yfiles.type="portgeometry"/>
 | 
			
		||||
  <key for="port" id="d3" yfiles.type="portuserdata"/>
 | 
			
		||||
  <key attr.name="url" attr.type="string" for="node" id="d4"/>
 | 
			
		||||
  <key attr.name="description" attr.type="string" for="node" id="d5"/>
 | 
			
		||||
  <key for="node" id="d6" yfiles.type="nodegraphics"/>
 | 
			
		||||
  <key for="graphml" id="d7" yfiles.type="resources"/>
 | 
			
		||||
  <key attr.name="url" attr.type="string" for="edge" id="d8"/>
 | 
			
		||||
  <key attr.name="description" attr.type="string" for="edge" id="d9"/>
 | 
			
		||||
  <key for="edge" id="d10" yfiles.type="edgegraphics"/>
 | 
			
		||||
  <graph edgedefault="directed" id="G">
 | 
			
		||||
    <data key="d0"/>
 | 
			
		||||
    <node id="n0">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="309.6849206349206" y="0.0"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="83.482421875" x="92.2587890625" y="18.93359375">Detect the OS<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="0.1619001116071429" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n1">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.decision">
 | 
			
		||||
          <y:Geometry height="40.0" width="80.0" x="403.6849206349206" y="206.44247787610618"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="38.0" y="18.0">
 | 
			
		||||
            <y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n2">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="90.44247787610618" width="268.0" x="309.6849206349206" y="86.0"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="right" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="88.796875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="170.763671875" x="48.61816406250006" y="0.8228014380530908">Get installed packages
 | 
			
		||||
Alpine: apk
 | 
			
		||||
Debian/Ubuntu: dpkg-query
 | 
			
		||||
Amazon/RHEL/CentOS: rpm
 | 
			
		||||
SUSE: zypper
 | 
			
		||||
FreeBSD: pkg<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="2.220446049250313E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n3">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="630.0546766682629"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="152.634765625" x="57.6826171875" y="18.93359375">Write results to JSON files<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="0.1619001116071429" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n4">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="287.8409153761062"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="232.744140625" x="17.6279296875" y="4.80078125">Get CVE IDs by using package manager
 | 
			
		||||
Amazon: yum plugin security
 | 
			
		||||
FreeBSD: pkg audit<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n5">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="750.4705298628534"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="42.595703125" x="112.7021484375" y="18.93359375">Report<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n6" yfiles.foldertype="group">
 | 
			
		||||
      <data key="d4"/>
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:ProxyAutoBoundsNode>
 | 
			
		||||
          <y:Realizers active="0">
 | 
			
		||||
            <y:GroupNode>
 | 
			
		||||
              <y:Geometry height="116.89483989807195" width="333.6788874841973" x="234.29467728596296" y="709.1901021013174"/>
 | 
			
		||||
              <y:Fill color="#F5F5F5" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="dashed" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="333.6788874841973" x="0.0" y="0.0">Vulnerability Database</y:NodeLabel>
 | 
			
		||||
              <y:Shape type="roundrectangle"/>
 | 
			
		||||
              <y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
 | 
			
		||||
              <y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
 | 
			
		||||
              <y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
 | 
			
		||||
            </y:GroupNode>
 | 
			
		||||
            <y:GroupNode>
 | 
			
		||||
              <y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
 | 
			
		||||
              <y:Fill color="#F5F5F5" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="dashed" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 1</y:NodeLabel>
 | 
			
		||||
              <y:Shape type="roundrectangle"/>
 | 
			
		||||
              <y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
 | 
			
		||||
              <y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
 | 
			
		||||
              <y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
 | 
			
		||||
            </y:GroupNode>
 | 
			
		||||
          </y:Realizers>
 | 
			
		||||
        </y:ProxyAutoBoundsNode>
 | 
			
		||||
      </data>
 | 
			
		||||
      <graph edgedefault="directed" id="n6:">
 | 
			
		||||
        <node id="n6::n0">
 | 
			
		||||
          <data key="d6">
 | 
			
		||||
            <y:GenericNode configuration="com.yworks.flowchart.dataBase">
 | 
			
		||||
              <y:Geometry height="65.22882427307195" width="136.83944374209864" x="416.1341210280616" y="745.8561177263174"/>
 | 
			
		||||
              <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="117.970703125" x="9.434370308549205" y="23.548005886535975">CVE DB (NVD / JVN)<y:LabelModel>
 | 
			
		||||
                  <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
                </y:LabelModel>
 | 
			
		||||
                <y:ModelParameter>
 | 
			
		||||
                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-8.326672684688674E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
                </y:ModelParameter>
 | 
			
		||||
              </y:NodeLabel>
 | 
			
		||||
            </y:GenericNode>
 | 
			
		||||
          </data>
 | 
			
		||||
        </node>
 | 
			
		||||
        <node id="n6::n1">
 | 
			
		||||
          <data key="d6">
 | 
			
		||||
            <y:GenericNode configuration="com.yworks.flowchart.dataBase">
 | 
			
		||||
              <y:Geometry height="65.22882427307195" width="136.83944374209864" x="249.29467728596296" y="745.8561177263174"/>
 | 
			
		||||
              <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="55.533203125" x="40.653120308549205" y="23.548005886535975">OVAL DB<y:LabelModel>
 | 
			
		||||
                  <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
                </y:LabelModel>
 | 
			
		||||
                <y:ModelParameter>
 | 
			
		||||
                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-8.326672684688674E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
                </y:ModelParameter>
 | 
			
		||||
              </y:NodeLabel>
 | 
			
		||||
            </y:GenericNode>
 | 
			
		||||
          </data>
 | 
			
		||||
        </node>
 | 
			
		||||
      </graph>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n7">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="27.144753476611868" y="287.8409153761062"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="260.83984375" x="3.580078125" y="11.8671875">Check upgradable packages
 | 
			
		||||
Debian/Ubuntu: apt-get upgrade --dry-run<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n8">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.loopLimit">
 | 
			
		||||
          <y:Geometry height="51.10998735777497" width="137.19216182048035" x="92.54867256637169" y="376.28592169721867"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="131.751953125" x="2.7201043477401754" y="9.422181178887513">foreach 
 | 
			
		||||
upgradable  packages<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="5.551115123125783E-16" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n9">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="27.144753476611868" y="459.8409153761062"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="213.619140625" x="27.1904296875" y="11.8671875">Parse changelog and get  CVE IDs
 | 
			
		||||
Debian/Ubuntu: aptitude changelog<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n10">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.loopLimitEnd">
 | 
			
		||||
          <y:Geometry height="50.0" width="137.0" x="92.64475347661187" y="545.8409153761062"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="55.24609375" x="40.876953125" y="15.93359375">end loop<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <edge id="e0" source="n2" target="n1">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="45.22123893805309" tx="0.0" ty="-20.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="none"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e1" source="n1" target="n4">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="40.0" sy="0.0" tx="0.0" ty="-28.0">
 | 
			
		||||
            <y:Point x="743.3698412698412" y="226.44247787610618"/>
 | 
			
		||||
          </y:Path>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:EdgeLabel alignment="right" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="51.806640625" x="183.35883739927397" y="2.000003510871693">Amazon
 | 
			
		||||
FreeBSD<y:LabelModel>
 | 
			
		||||
              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="1.9999999999998863" distanceToCenter="false" position="right" ratio="0.7796030035582084" segment="0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
 | 
			
		||||
          </y:EdgeLabel>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e2" source="n0" target="n2">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="28.0" tx="0.0" ty="-45.22123893805309"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e3" source="n5" target="n6">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="10.8330078125"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="none"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e4" source="n1" target="n3">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="-123.36984126984123" ty="0.0">
 | 
			
		||||
            <y:Point x="443.6849206349206" y="658.0546766682629"/>
 | 
			
		||||
          </y:Path>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:EdgeLabel alignment="right" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="102.9296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="77.078125" x="-97.68364242524859" y="5.005267793098369">Alpine Linux
 | 
			
		||||
CentOS
 | 
			
		||||
RHEL
 | 
			
		||||
Ubuntu
 | 
			
		||||
Debian
 | 
			
		||||
Oracle Linux
 | 
			
		||||
Suse<y:LabelModel>
 | 
			
		||||
              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="59.14459455430983" distanceToCenter="true" position="right" ratio="0.0" segment="0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
 | 
			
		||||
          </y:EdgeLabel>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e5" source="n4" target="n3">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e6" source="n7" target="n8">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="28.0" tx="0.0" ty="-25.554993678887485"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e7" source="n8" target="n9">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="25.554993678887485" tx="0.0" ty="-28.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e8" source="n9" target="n10">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="28.0" tx="0.0" ty="-25.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e9" source="n3" target="n5">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e10" source="n1" target="n7">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
 | 
			
		||||
            <y:Point x="161.14475347661187" y="226.44247787610618"/>
 | 
			
		||||
          </y:Path>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="56.98046875" x="-196.80057112212188" y="20.933597260871807">Raspbian<y:LabelModel>
 | 
			
		||||
              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="left" ratio="0.6447921222409765" segment="0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
 | 
			
		||||
          </y:EdgeLabel>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e11" source="n10" target="n3">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="-125.78842258255952" ty="0.0">
 | 
			
		||||
            <y:Point x="161.14475347661187" y="658.0546766682629"/>
 | 
			
		||||
          </y:Path>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
  </graph>
 | 
			
		||||
  <data key="d7">
 | 
			
		||||
    <y:Resources/>
 | 
			
		||||
  </data>
 | 
			
		||||
</graphml>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 78 KiB  | 
@@ -1,515 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
 | 
			
		||||
  <!--Created by yEd 3.17-->
 | 
			
		||||
  <key attr.name="Description" attr.type="string" for="graph" id="d0"/>
 | 
			
		||||
  <key for="port" id="d1" yfiles.type="portgraphics"/>
 | 
			
		||||
  <key for="port" id="d2" yfiles.type="portgeometry"/>
 | 
			
		||||
  <key for="port" id="d3" yfiles.type="portuserdata"/>
 | 
			
		||||
  <key attr.name="url" attr.type="string" for="node" id="d4"/>
 | 
			
		||||
  <key attr.name="description" attr.type="string" for="node" id="d5"/>
 | 
			
		||||
  <key for="node" id="d6" yfiles.type="nodegraphics"/>
 | 
			
		||||
  <key for="graphml" id="d7" yfiles.type="resources"/>
 | 
			
		||||
  <key attr.name="url" attr.type="string" for="edge" id="d8"/>
 | 
			
		||||
  <key attr.name="description" attr.type="string" for="edge" id="d9"/>
 | 
			
		||||
  <key for="edge" id="d10" yfiles.type="edgegraphics"/>
 | 
			
		||||
  <graph edgedefault="directed" id="G">
 | 
			
		||||
    <data key="d0"/>
 | 
			
		||||
    <node id="n0">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="309.6849206349206" y="0.0"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="83.482421875" x="92.2587890625" y="18.93359375">Detect the OS<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="0.1619001116071429" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n1">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.decision">
 | 
			
		||||
          <y:Geometry height="40.0" width="80.0" x="403.6849206349206" y="206.44247787610618"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="38.0" y="18.0">
 | 
			
		||||
            <y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n2">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="90.44247787610618" width="268.0" x="309.6849206349206" y="86.0"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="right" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="88.796875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="170.763671875" x="48.61816406250006" y="0.8228014380530908">Get installed packages
 | 
			
		||||
Alpine Linux: apk
 | 
			
		||||
Debian/Ubuntu: dpkg-query
 | 
			
		||||
Amazon/RHEL/CentOS: rpm
 | 
			
		||||
FreeBSD: pkg
 | 
			
		||||
SUSE: zypper<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="2.220446049250313E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n3">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="10.0" y="287.8409153761062"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="260.83984375" x="3.580078125" y="11.8671875">Check upgradable packages
 | 
			
		||||
Debian/Ubuntu: apt-get upgrade --dry-run<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n4">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.loopLimit">
 | 
			
		||||
          <y:Geometry height="51.10998735777497" width="137.19216182048035" x="75.40391908975982" y="376.28592169721867"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="131.751953125" x="2.7201043477401754" y="9.422181178887513">foreach 
 | 
			
		||||
upgradable  packages<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="5.551115123125783E-16" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n5">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="10.0" y="459.8409153761062"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="213.619140625" x="27.1904296875" y="11.8671875">Parse changelog and get  CVE IDs
 | 
			
		||||
Debian/Ubuntu: aptitude changelog<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n6">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.loopLimitEnd">
 | 
			
		||||
          <y:Geometry height="50.0" width="137.0" x="75.5" y="545.8409153761062"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="55.24609375" x="40.876953125" y="15.93359375">end loop<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n7">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="625.8409153761062"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="152.634765625" x="57.6826171875" y="18.93359375">Write results to JSON files<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="0.1619001116071429" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n8">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="287.8409153761062"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="232.744140625" x="17.6279296875" y="4.80078125">Get CVE IDs by using package manager
 | 
			
		||||
Amazon/RHEL: yum plugin security
 | 
			
		||||
FreeBSD: pkg audit<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n9">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="716.4553275126422"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="42.595703125" x="112.7021484375" y="18.93359375">Report<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n10">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="309.6849206349206" y="371.39590905499364"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="293.06640625" x="-12.533203124999943" y="11.8671875">Get all changelogs of updatable packages at once
 | 
			
		||||
yum changelog<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="2.220446049250313E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n11">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="309.68492063492056" y="459.8409153761062"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="205.52734375" x="31.236328125000057" y="18.93359375">Parse changelogs and get CVE IDs <y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="2.220446049250313E-16" nodeRatioY="0.1619001116071429" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n12">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:GenericNode configuration="com.yworks.flowchart.process">
 | 
			
		||||
          <y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="373.8409153761062"/>
 | 
			
		||||
          <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="293.06640625" x="-12.533203124999886" y="11.8671875">Get all changelogs of updatable packages at once
 | 
			
		||||
Amazon / RHEL: yum changelog<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="2.220446049250313E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
        </y:GenericNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n13" yfiles.foldertype="group">
 | 
			
		||||
      <data key="d4"/>
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:ProxyAutoBoundsNode>
 | 
			
		||||
          <y:Realizers active="0">
 | 
			
		||||
            <y:GroupNode>
 | 
			
		||||
              <y:Geometry height="116.89483989807195" width="333.6788874841973" x="229.74083438685204" y="675.1748997511062"/>
 | 
			
		||||
              <y:Fill color="#F5F5F5" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="dashed" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="333.6788874841973" x="0.0" y="0.0">Vulnerability Database</y:NodeLabel>
 | 
			
		||||
              <y:Shape type="roundrectangle"/>
 | 
			
		||||
              <y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
 | 
			
		||||
              <y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
 | 
			
		||||
              <y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
 | 
			
		||||
            </y:GroupNode>
 | 
			
		||||
            <y:GroupNode>
 | 
			
		||||
              <y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
 | 
			
		||||
              <y:Fill color="#F5F5F5" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="dashed" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 1</y:NodeLabel>
 | 
			
		||||
              <y:Shape type="roundrectangle"/>
 | 
			
		||||
              <y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
 | 
			
		||||
              <y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
 | 
			
		||||
              <y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
 | 
			
		||||
            </y:GroupNode>
 | 
			
		||||
          </y:Realizers>
 | 
			
		||||
        </y:ProxyAutoBoundsNode>
 | 
			
		||||
      </data>
 | 
			
		||||
      <graph edgedefault="directed" id="n13:">
 | 
			
		||||
        <node id="n13::n0">
 | 
			
		||||
          <data key="d6">
 | 
			
		||||
            <y:GenericNode configuration="com.yworks.flowchart.dataBase">
 | 
			
		||||
              <y:Geometry height="65.22882427307195" width="136.83944374209864" x="411.5802781289507" y="711.8409153761062"/>
 | 
			
		||||
              <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="117.970703125" x="9.434370308549205" y="23.548005886535975">CVE DB (NVD / JVN)<y:LabelModel>
 | 
			
		||||
                  <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
                </y:LabelModel>
 | 
			
		||||
                <y:ModelParameter>
 | 
			
		||||
                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-8.326672684688674E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
                </y:ModelParameter>
 | 
			
		||||
              </y:NodeLabel>
 | 
			
		||||
            </y:GenericNode>
 | 
			
		||||
          </data>
 | 
			
		||||
        </node>
 | 
			
		||||
        <node id="n13::n1">
 | 
			
		||||
          <data key="d6">
 | 
			
		||||
            <y:GenericNode configuration="com.yworks.flowchart.dataBase">
 | 
			
		||||
              <y:Geometry height="65.22882427307195" width="136.83944374209864" x="244.74083438685204" y="711.8409153761062"/>
 | 
			
		||||
              <y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="55.533203125" x="40.653120308549205" y="23.548005886535975">OVAL DB<y:LabelModel>
 | 
			
		||||
                  <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
                </y:LabelModel>
 | 
			
		||||
                <y:ModelParameter>
 | 
			
		||||
                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-8.326672684688674E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
                </y:ModelParameter>
 | 
			
		||||
              </y:NodeLabel>
 | 
			
		||||
            </y:GenericNode>
 | 
			
		||||
          </data>
 | 
			
		||||
        </node>
 | 
			
		||||
      </graph>
 | 
			
		||||
    </node>
 | 
			
		||||
    <edge id="e0" source="n2" target="n1">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="45.22123893805309" tx="0.0" ty="-20.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="none"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e1" source="n1" target="n3">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="-40.0" sy="0.0" tx="0.0" ty="-28.0">
 | 
			
		||||
            <y:Point x="144.0" y="226.44247787610618"/>
 | 
			
		||||
          </y:Path>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:EdgeLabel alignment="right" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="56.98046875" x="-257.65322875976574" y="2.0000035108718635">Debian
 | 
			
		||||
Ubuntu
 | 
			
		||||
Raspbian<y:LabelModel>
 | 
			
		||||
              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="1.9999999999998863" distanceToCenter="false" position="left" ratio="0.8652035780364729" segment="0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
 | 
			
		||||
          </y:EdgeLabel>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e2" source="n3" target="n4">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="28.0" tx="0.0" ty="-25.554993678887485"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e3" source="n4" target="n5">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="25.554993678887485" tx="0.0" ty="-28.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e4" source="n5" target="n6">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="28.0" tx="0.0" ty="-25.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e5" source="n6" target="n7">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="68.5" sy="0.0" tx="0.0" ty="-28.0">
 | 
			
		||||
            <y:Point x="743.3698412698412" y="570.8409153761062"/>
 | 
			
		||||
          </y:Path>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e6" source="n1" target="n8">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="40.0" sy="0.0" tx="0.0" ty="-28.0">
 | 
			
		||||
            <y:Point x="743.3698412698412" y="226.44247787610618"/>
 | 
			
		||||
          </y:Path>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:EdgeLabel alignment="right" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="51.806640625" x="200.87829463898197" y="4.000003510871693">Amazon
 | 
			
		||||
RHEL
 | 
			
		||||
FreeBSD<y:LabelModel>
 | 
			
		||||
              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="6.999999999999886" distanceToCenter="false" position="right" ratio="0.8192728556300707" segment="-1"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
 | 
			
		||||
          </y:EdgeLabel>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e7" source="n0" target="n2">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="28.0" tx="0.0" ty="-45.22123893805309"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e8" source="n7" target="n9">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="28.0" tx="0.0" ty="-28.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e9" source="n1" target="n10">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="20.0" tx="0.0" ty="-28.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="46.708984375" x="-53.35447755843876" y="5.000003510871807">CentOS<y:LabelModel>
 | 
			
		||||
              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.0" segment="0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
 | 
			
		||||
          </y:EdgeLabel>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e10" source="n10" target="n11">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="28.0" tx="0.0" ty="-28.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e11" source="n11" target="n7">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="-24.34091537610618">
 | 
			
		||||
            <y:Point x="743.3698412698412" y="487.8409153761062"/>
 | 
			
		||||
          </y:Path>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e12" source="n8" target="n12">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e13" source="n12" target="n7">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e14" source="n9" target="n13">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="10.8330078125"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="none"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e15" source="n1" target="n7">
 | 
			
		||||
      <data key="d9"/>
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
 | 
			
		||||
            <y:Point x="999.0" y="226.44247787610618"/>
 | 
			
		||||
            <y:Point x="999.0" y="570.8409153761062"/>
 | 
			
		||||
            <y:Point x="743.3698412698412" y="570.8409153761062"/>
 | 
			
		||||
          </y:Path>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="none"/>
 | 
			
		||||
          <y:EdgeLabel alignment="right" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="76.8203125" x="422.923942251054" y="13.867191010871807">Alpine Linux
 | 
			
		||||
SUSE<y:LabelModel>
 | 
			
		||||
              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.8856709076027529" segment="0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
 | 
			
		||||
          </y:EdgeLabel>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
  </graph>
 | 
			
		||||
  <data key="d7">
 | 
			
		||||
    <y:Resources/>
 | 
			
		||||
  </data>
 | 
			
		||||
</graphml>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 90 KiB  | 
| 
		 Before Width: | Height: | Size: 179 KiB  | 
@@ -1,265 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
 | 
			
		||||
  <!--Created by yEd 3.14.2-->
 | 
			
		||||
  <key attr.name="Description" attr.type="string" for="graph" id="d0"/>
 | 
			
		||||
  <key for="port" id="d1" yfiles.type="portgraphics"/>
 | 
			
		||||
  <key for="port" id="d2" yfiles.type="portgeometry"/>
 | 
			
		||||
  <key for="port" id="d3" yfiles.type="portuserdata"/>
 | 
			
		||||
  <key attr.name="url" attr.type="string" for="node" id="d4"/>
 | 
			
		||||
  <key attr.name="description" attr.type="string" for="node" id="d5"/>
 | 
			
		||||
  <key for="node" id="d6" yfiles.type="nodegraphics"/>
 | 
			
		||||
  <key for="graphml" id="d7" yfiles.type="resources"/>
 | 
			
		||||
  <key attr.name="url" attr.type="string" for="edge" id="d8"/>
 | 
			
		||||
  <key attr.name="description" attr.type="string" for="edge" id="d9"/>
 | 
			
		||||
  <key for="edge" id="d10" yfiles.type="edgegraphics"/>
 | 
			
		||||
  <graph edgedefault="directed" id="G">
 | 
			
		||||
    <data key="d0"/>
 | 
			
		||||
    <node id="n0">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:ShapeNode>
 | 
			
		||||
          <y:Geometry height="50.0" width="73.25" x="478.6165008544913" y="1358.206868489578"/>
 | 
			
		||||
          <y:Fill color="#C0C0C0" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="28.87890625" x="22.185546875" y="15.93359375">Vuls<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
          <y:Shape type="roundrectangle"/>
 | 
			
		||||
        </y:ShapeNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n1">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:ShapeNode>
 | 
			
		||||
          <y:Geometry height="50.0" width="73.25" x="711.9623756408686" y="1043.7241210937468"/>
 | 
			
		||||
          <y:Fill color="#C0C0C0" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="38.623046875" x="17.3134765625" y="15.93359375">Nginx<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
          <y:Shape type="roundrectangle"/>
 | 
			
		||||
        </y:ShapeNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n2">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:ShapeNode>
 | 
			
		||||
          <y:Geometry height="50.0" width="73.25" x="711.9623756408686" y="1287.206868489578"/>
 | 
			
		||||
          <y:Fill color="#C0C0C0" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="42.7890625" x="15.23046875" y="15.93359375">MySQL<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
          <y:Shape type="roundrectangle"/>
 | 
			
		||||
        </y:ShapeNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n3" yfiles.foldertype="group">
 | 
			
		||||
      <data key="d4"/>
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:ProxyAutoBoundsNode>
 | 
			
		||||
          <y:Realizers active="0">
 | 
			
		||||
            <y:GroupNode>
 | 
			
		||||
              <y:Geometry height="101.666015625" width="291.7208747863772" x="602.72693824768" y="1146.2994791666624"/>
 | 
			
		||||
              <y:Fill color="#F5F5F5" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="dashed" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="291.7208747863772" x="0.0" y="0.0">Web/App</y:NodeLabel>
 | 
			
		||||
              <y:Shape type="roundrectangle"/>
 | 
			
		||||
              <y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
 | 
			
		||||
              <y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
 | 
			
		||||
              <y:BorderInsets bottom="0" bottomF="0.0" left="23" leftF="23.0" right="0" rightF="0.0" top="0" topF="0.0"/>
 | 
			
		||||
            </y:GroupNode>
 | 
			
		||||
            <y:GroupNode>
 | 
			
		||||
              <y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
 | 
			
		||||
              <y:Fill color="#F5F5F5" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="dashed" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 5</y:NodeLabel>
 | 
			
		||||
              <y:Shape type="roundrectangle"/>
 | 
			
		||||
              <y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
 | 
			
		||||
              <y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
 | 
			
		||||
              <y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
 | 
			
		||||
            </y:GroupNode>
 | 
			
		||||
          </y:Realizers>
 | 
			
		||||
        </y:ProxyAutoBoundsNode>
 | 
			
		||||
      </data>
 | 
			
		||||
      <graph edgedefault="directed" id="n3:">
 | 
			
		||||
        <node id="n3::n0">
 | 
			
		||||
          <data key="d6">
 | 
			
		||||
            <y:ShapeNode>
 | 
			
		||||
              <y:Geometry height="50.0" width="73.25" x="640.72693824768" y="1182.9654947916624"/>
 | 
			
		||||
              <y:Fill color="#C0C0C0" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
 | 
			
		||||
                  <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
                </y:LabelModel>
 | 
			
		||||
                <y:ModelParameter>
 | 
			
		||||
                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
                </y:ModelParameter>
 | 
			
		||||
              </y:NodeLabel>
 | 
			
		||||
              <y:Shape type="roundrectangle"/>
 | 
			
		||||
            </y:ShapeNode>
 | 
			
		||||
          </data>
 | 
			
		||||
        </node>
 | 
			
		||||
        <node id="n3::n1">
 | 
			
		||||
          <data key="d6">
 | 
			
		||||
            <y:ShapeNode>
 | 
			
		||||
              <y:Geometry height="50.0" width="73.25" x="723.4623756408686" y="1182.9654947916624"/>
 | 
			
		||||
              <y:Fill color="#C0C0C0" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
 | 
			
		||||
                  <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
                </y:LabelModel>
 | 
			
		||||
                <y:ModelParameter>
 | 
			
		||||
                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
                </y:ModelParameter>
 | 
			
		||||
              </y:NodeLabel>
 | 
			
		||||
              <y:Shape type="roundrectangle"/>
 | 
			
		||||
            </y:ShapeNode>
 | 
			
		||||
          </data>
 | 
			
		||||
        </node>
 | 
			
		||||
        <node id="n3::n2">
 | 
			
		||||
          <data key="d6">
 | 
			
		||||
            <y:ShapeNode>
 | 
			
		||||
              <y:Geometry height="50.0" width="73.25" x="806.1978130340572" y="1182.9654947916624"/>
 | 
			
		||||
              <y:Fill color="#C0C0C0" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
 | 
			
		||||
                  <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
                </y:LabelModel>
 | 
			
		||||
                <y:ModelParameter>
 | 
			
		||||
                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
                </y:ModelParameter>
 | 
			
		||||
              </y:NodeLabel>
 | 
			
		||||
              <y:Shape type="roundrectangle"/>
 | 
			
		||||
            </y:ShapeNode>
 | 
			
		||||
          </data>
 | 
			
		||||
        </node>
 | 
			
		||||
      </graph>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n4">
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:ShapeNode>
 | 
			
		||||
          <y:Geometry height="50.0" width="73.25" x="821.1978130340572" y="1287.206868489578"/>
 | 
			
		||||
          <y:Fill color="#C0C0C0" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="35.412109375" x="18.9189453125" y="15.93359375">Redis<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
          <y:Shape type="roundrectangle"/>
 | 
			
		||||
        </y:ShapeNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <edge id="e0" source="n3" target="n1">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="none"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e1" source="n3" target="n2">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="none"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e2" source="n0" target="n3::n0">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="dashed" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e3" source="n0" target="n3::n1">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="dashed" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e4" source="n0" target="n3::n2">
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="dashed" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e5" source="n3" target="n4">
 | 
			
		||||
      <data key="d9"/>
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="none"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e6" source="n0" target="n4">
 | 
			
		||||
      <data key="d9"/>
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="dashed" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e7" source="n0" target="n1">
 | 
			
		||||
      <data key="d9"/>
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="dashed" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e8" source="n0" target="n2">
 | 
			
		||||
      <data key="d9"/>
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="dashed" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
  </graph>
 | 
			
		||||
  <data key="d7">
 | 
			
		||||
    <y:Resources/>
 | 
			
		||||
  </data>
 | 
			
		||||
</graphml>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 14 KiB  | 
@@ -1,194 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
 | 
			
		||||
  <!--Created by yEd 3.14.2-->
 | 
			
		||||
  <key attr.name="Description" attr.type="string" for="graph" id="d0"/>
 | 
			
		||||
  <key for="port" id="d1" yfiles.type="portgraphics"/>
 | 
			
		||||
  <key for="port" id="d2" yfiles.type="portgeometry"/>
 | 
			
		||||
  <key for="port" id="d3" yfiles.type="portuserdata"/>
 | 
			
		||||
  <key attr.name="url" attr.type="string" for="node" id="d4"/>
 | 
			
		||||
  <key attr.name="description" attr.type="string" for="node" id="d5"/>
 | 
			
		||||
  <key for="node" id="d6" yfiles.type="nodegraphics"/>
 | 
			
		||||
  <key for="graphml" id="d7" yfiles.type="resources"/>
 | 
			
		||||
  <key attr.name="url" attr.type="string" for="edge" id="d8"/>
 | 
			
		||||
  <key attr.name="description" attr.type="string" for="edge" id="d9"/>
 | 
			
		||||
  <key for="edge" id="d10" yfiles.type="edgegraphics"/>
 | 
			
		||||
  <graph edgedefault="directed" id="G">
 | 
			
		||||
    <data key="d0"/>
 | 
			
		||||
    <node id="n0">
 | 
			
		||||
      <data key="d5"/>
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:ShapeNode>
 | 
			
		||||
          <y:Geometry height="50.0" width="73.25" x="508.30825042724564" y="1132.4827473958312"/>
 | 
			
		||||
          <y:Fill color="#C0C0C0" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="28.87890625" x="22.185546875" y="15.93359375">Vuls<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
          <y:Shape type="roundrectangle"/>
 | 
			
		||||
        </y:ShapeNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n1">
 | 
			
		||||
      <data key="d5"/>
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:ShapeNode>
 | 
			
		||||
          <y:Geometry height="50.0" width="73.25" x="749.6541252136229" y="993.2413736979156"/>
 | 
			
		||||
          <y:Fill color="#C0C0C0" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="23.8046875" x="24.72265625" y="15.93359375">ELB<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
          <y:Shape type="roundrectangle"/>
 | 
			
		||||
        </y:ShapeNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n2">
 | 
			
		||||
      <data key="d5"/>
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:ShapeNode>
 | 
			
		||||
          <y:Geometry height="50.0" width="73.25" x="749.6541252136229" y="1236.7241210937468"/>
 | 
			
		||||
          <y:Fill color="#C0C0C0" transparent="false"/>
 | 
			
		||||
          <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="27.0390625" x="23.10546875" y="15.93359375">RDS<y:LabelModel>
 | 
			
		||||
              <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
            </y:LabelModel>
 | 
			
		||||
            <y:ModelParameter>
 | 
			
		||||
              <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
            </y:ModelParameter>
 | 
			
		||||
          </y:NodeLabel>
 | 
			
		||||
          <y:Shape type="roundrectangle"/>
 | 
			
		||||
        </y:ShapeNode>
 | 
			
		||||
      </data>
 | 
			
		||||
    </node>
 | 
			
		||||
    <node id="n3" yfiles.foldertype="group">
 | 
			
		||||
      <data key="d4"/>
 | 
			
		||||
      <data key="d5"/>
 | 
			
		||||
      <data key="d6">
 | 
			
		||||
        <y:ProxyAutoBoundsNode>
 | 
			
		||||
          <y:Realizers active="0">
 | 
			
		||||
            <y:GroupNode>
 | 
			
		||||
              <y:Geometry height="101.666015625" width="291.7208747863772" x="640.4186878204343" y="1095.8167317708312"/>
 | 
			
		||||
              <y:Fill color="#F5F5F5" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="dashed" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="291.7208747863772" x="0.0" y="0.0">Web/App</y:NodeLabel>
 | 
			
		||||
              <y:Shape type="roundrectangle"/>
 | 
			
		||||
              <y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
 | 
			
		||||
              <y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
 | 
			
		||||
              <y:BorderInsets bottom="0" bottomF="0.0" left="23" leftF="23.0" right="0" rightF="0.0" top="0" topF="0.0"/>
 | 
			
		||||
            </y:GroupNode>
 | 
			
		||||
            <y:GroupNode>
 | 
			
		||||
              <y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
 | 
			
		||||
              <y:Fill color="#F5F5F5" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="dashed" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 5</y:NodeLabel>
 | 
			
		||||
              <y:Shape type="roundrectangle"/>
 | 
			
		||||
              <y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
 | 
			
		||||
              <y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
 | 
			
		||||
              <y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
 | 
			
		||||
            </y:GroupNode>
 | 
			
		||||
          </y:Realizers>
 | 
			
		||||
        </y:ProxyAutoBoundsNode>
 | 
			
		||||
      </data>
 | 
			
		||||
      <graph edgedefault="directed" id="n3:">
 | 
			
		||||
        <node id="n3::n0">
 | 
			
		||||
          <data key="d5"/>
 | 
			
		||||
          <data key="d6">
 | 
			
		||||
            <y:ShapeNode>
 | 
			
		||||
              <y:Geometry height="50.0" width="73.25" x="678.4186878204343" y="1132.4827473958312"/>
 | 
			
		||||
              <y:Fill color="#C0C0C0" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
 | 
			
		||||
                  <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
                </y:LabelModel>
 | 
			
		||||
                <y:ModelParameter>
 | 
			
		||||
                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
                </y:ModelParameter>
 | 
			
		||||
              </y:NodeLabel>
 | 
			
		||||
              <y:Shape type="roundrectangle"/>
 | 
			
		||||
            </y:ShapeNode>
 | 
			
		||||
          </data>
 | 
			
		||||
        </node>
 | 
			
		||||
        <node id="n3::n1">
 | 
			
		||||
          <data key="d5"/>
 | 
			
		||||
          <data key="d6">
 | 
			
		||||
            <y:ShapeNode>
 | 
			
		||||
              <y:Geometry height="50.0" width="73.25" x="761.1541252136229" y="1132.4827473958312"/>
 | 
			
		||||
              <y:Fill color="#C0C0C0" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
 | 
			
		||||
                  <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
                </y:LabelModel>
 | 
			
		||||
                <y:ModelParameter>
 | 
			
		||||
                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
                </y:ModelParameter>
 | 
			
		||||
              </y:NodeLabel>
 | 
			
		||||
              <y:Shape type="roundrectangle"/>
 | 
			
		||||
            </y:ShapeNode>
 | 
			
		||||
          </data>
 | 
			
		||||
        </node>
 | 
			
		||||
        <node id="n3::n2">
 | 
			
		||||
          <data key="d5"/>
 | 
			
		||||
          <data key="d6">
 | 
			
		||||
            <y:ShapeNode>
 | 
			
		||||
              <y:Geometry height="50.0" width="73.25" x="843.8895626068115" y="1132.4827473958312"/>
 | 
			
		||||
              <y:Fill color="#C0C0C0" transparent="false"/>
 | 
			
		||||
              <y:BorderStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
              <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
 | 
			
		||||
                  <y:SmartNodeLabelModel distance="4.0"/>
 | 
			
		||||
                </y:LabelModel>
 | 
			
		||||
                <y:ModelParameter>
 | 
			
		||||
                  <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
 | 
			
		||||
                </y:ModelParameter>
 | 
			
		||||
              </y:NodeLabel>
 | 
			
		||||
              <y:Shape type="roundrectangle"/>
 | 
			
		||||
            </y:ShapeNode>
 | 
			
		||||
          </data>
 | 
			
		||||
        </node>
 | 
			
		||||
      </graph>
 | 
			
		||||
    </node>
 | 
			
		||||
    <edge id="e0" source="n3" target="n1">
 | 
			
		||||
      <data key="d9"/>
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="none"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e1" source="n3" target="n2">
 | 
			
		||||
      <data key="d9"/>
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="none"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
    <edge id="e2" source="n0" target="n3::n0">
 | 
			
		||||
      <data key="d9"/>
 | 
			
		||||
      <data key="d10">
 | 
			
		||||
        <y:PolyLineEdge>
 | 
			
		||||
          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
 | 
			
		||||
          <y:LineStyle color="#000000" type="line" width="1.0"/>
 | 
			
		||||
          <y:Arrows source="none" target="standard"/>
 | 
			
		||||
          <y:BendStyle smoothed="false"/>
 | 
			
		||||
        </y:PolyLineEdge>
 | 
			
		||||
      </data>
 | 
			
		||||
    </edge>
 | 
			
		||||
  </graph>
 | 
			
		||||
  <data key="d7">
 | 
			
		||||
    <y:Resources/>
 | 
			
		||||
  </data>
 | 
			
		||||
</graphml>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 5.6 KiB  | 
@@ -17,8 +17,9 @@ import (
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// FillLibrary fills LibraryScanner informations
 | 
			
		||||
func FillLibrary(r *models.ScanResult) (totalCnt int, err error) {
 | 
			
		||||
// DetectLibsCves fills LibraryScanner information
 | 
			
		||||
func DetectLibsCves(r *models.ScanResult) (err error) {
 | 
			
		||||
	totalCnt := 0
 | 
			
		||||
	if len(r.LibraryScanners) == 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
@@ -26,32 +27,40 @@ func FillLibrary(r *models.ScanResult) (totalCnt int, err error) {
 | 
			
		||||
	// initialize trivy's logger and db
 | 
			
		||||
	err = log.InitLogger(false, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	util.Log.Info("Updating library db...")
 | 
			
		||||
	if err := downloadDB(config.Version, config.Conf.TrivyCacheDBDir, config.Conf.NoProgress, false, false); err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := db2.Init(config.Conf.TrivyCacheDBDir); err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer db2.Close()
 | 
			
		||||
 | 
			
		||||
	for _, lib := range r.LibraryScanners {
 | 
			
		||||
		vinfos, err := lib.Scan()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		for _, vinfo := range vinfos {
 | 
			
		||||
			vinfo.Confidences.AppendIfMissing(models.TrivyMatch)
 | 
			
		||||
			r.ScannedCves[vinfo.CveID] = vinfo
 | 
			
		||||
			if v, ok := r.ScannedCves[vinfo.CveID]; !ok {
 | 
			
		||||
				r.ScannedCves[vinfo.CveID] = vinfo
 | 
			
		||||
			} else {
 | 
			
		||||
				v.LibraryFixedIns = append(v.LibraryFixedIns, vinfo.LibraryFixedIns...)
 | 
			
		||||
				r.ScannedCves[vinfo.CveID] = v
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		totalCnt += len(vinfos)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return totalCnt, nil
 | 
			
		||||
	util.Log.Infof("%s: %d CVEs are detected with Library",
 | 
			
		||||
		r.FormatServerName(), totalCnt)
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func downloadDB(appVersion, cacheDir string, quiet, light, skipUpdate bool) error {
 | 
			
		||||
 
 | 
			
		||||
@@ -42,15 +42,23 @@ func (v CveContents) Except(exceptCtypes ...CveContentType) (values CveContents)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SourceLinks returns link of source
 | 
			
		||||
func (v CveContents) SourceLinks(lang, myFamily, cveID string) (values []CveContentStr) {
 | 
			
		||||
	if lang == "ja" {
 | 
			
		||||
		if cont, found := v[Jvn]; found && 0 < len(cont.SourceLink) {
 | 
			
		||||
			values = append(values, CveContentStr{Jvn, cont.SourceLink})
 | 
			
		||||
// PrimarySrcURLs returns link of source
 | 
			
		||||
func (v CveContents) PrimarySrcURLs(lang, myFamily, cveID string) (values []CveContentStr) {
 | 
			
		||||
	if cveID == "" {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if cont, found := v[Nvd]; found {
 | 
			
		||||
		for _, r := range cont.References {
 | 
			
		||||
			for _, t := range r.Tags {
 | 
			
		||||
				if t == "Vendor Advisory" {
 | 
			
		||||
					values = append(values, CveContentStr{Nvd, r.Link})
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	order := CveContentTypes{Nvd, NvdXML, NewCveContentType(myFamily)}
 | 
			
		||||
	order := CveContentTypes{Nvd, NewCveContentType(myFamily)}
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if cont, found := v[ctype]; found {
 | 
			
		||||
			if cont.SourceLink == "" {
 | 
			
		||||
@@ -60,6 +68,12 @@ func (v CveContents) SourceLinks(lang, myFamily, cveID string) (values []CveCont
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if lang == "ja" {
 | 
			
		||||
		if cont, found := v[Jvn]; found && 0 < len(cont.SourceLink) {
 | 
			
		||||
			values = append(values, CveContentStr{Jvn, cont.SourceLink})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(values) == 0 {
 | 
			
		||||
		return []CveContentStr{{
 | 
			
		||||
			Type:  Nvd,
 | 
			
		||||
@@ -69,6 +83,22 @@ func (v CveContents) SourceLinks(lang, myFamily, cveID string) (values []CveCont
 | 
			
		||||
	return values
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PatchURLs returns link of patch
 | 
			
		||||
func (v CveContents) PatchURLs() (urls []string) {
 | 
			
		||||
	cont, found := v[Nvd]
 | 
			
		||||
	if !found {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	for _, r := range cont.References {
 | 
			
		||||
		for _, t := range r.Tags {
 | 
			
		||||
			if t == "Patch" {
 | 
			
		||||
				urls = append(urls, r.Link)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
// Severities returns Severities
 | 
			
		||||
func (v CveContents) Severities(myFamily string) (values []CveContentStr) {
 | 
			
		||||
@@ -184,7 +214,6 @@ type CveContent struct {
 | 
			
		||||
	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"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -199,8 +228,6 @@ type CveContentType string
 | 
			
		||||
// NewCveContentType create CveContentType
 | 
			
		||||
func NewCveContentType(name string) CveContentType {
 | 
			
		||||
	switch name {
 | 
			
		||||
	case "nvdxml":
 | 
			
		||||
		return NvdXML
 | 
			
		||||
	case "nvd":
 | 
			
		||||
		return Nvd
 | 
			
		||||
	case "jvn":
 | 
			
		||||
@@ -225,26 +252,13 @@ func NewCveContentType(name string) CveContentType {
 | 
			
		||||
		return Amazon
 | 
			
		||||
	case "trivy":
 | 
			
		||||
		return Trivy
 | 
			
		||||
	// 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
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// NvdXML is NvdXML
 | 
			
		||||
	NvdXML CveContentType = "nvdxml"
 | 
			
		||||
 | 
			
		||||
	// Nvd is Nvd
 | 
			
		||||
	// Nvd is Nvd JSON
 | 
			
		||||
	Nvd CveContentType = "nvd"
 | 
			
		||||
 | 
			
		||||
	// Jvn is Jvn
 | 
			
		||||
@@ -256,7 +270,7 @@ const (
 | 
			
		||||
	// RedHatAPI is RedHat
 | 
			
		||||
	RedHatAPI CveContentType = "redhat_api"
 | 
			
		||||
 | 
			
		||||
	// DebianSecurityTracker is Debian Secury tracker
 | 
			
		||||
	// DebianSecurityTracker is Debian Security tracker
 | 
			
		||||
	DebianSecurityTracker CveContentType = "debian_security_tracker"
 | 
			
		||||
 | 
			
		||||
	// Debian is Debian
 | 
			
		||||
@@ -283,21 +297,6 @@ const (
 | 
			
		||||
	// Trivy is Trivy
 | 
			
		||||
	Trivy CveContentType = "trivy"
 | 
			
		||||
 | 
			
		||||
	// 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"
 | 
			
		||||
)
 | 
			
		||||
@@ -308,7 +307,6 @@ type CveContentTypes []CveContentType
 | 
			
		||||
// AllCveContetTypes has all of CveContentTypes
 | 
			
		||||
var AllCveContetTypes = CveContentTypes{
 | 
			
		||||
	Nvd,
 | 
			
		||||
	NvdXML,
 | 
			
		||||
	Jvn,
 | 
			
		||||
	RedHat,
 | 
			
		||||
	RedHatAPI,
 | 
			
		||||
@@ -319,11 +317,6 @@ var AllCveContetTypes = CveContentTypes{
 | 
			
		||||
	DebianSecurityTracker,
 | 
			
		||||
	WPVulnDB,
 | 
			
		||||
	Trivy,
 | 
			
		||||
	// NodeSec,
 | 
			
		||||
	// PythonSec,
 | 
			
		||||
	// PhpSec,
 | 
			
		||||
	// RubySec,
 | 
			
		||||
	// RustSec,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Except returns CveContentTypes except for given args
 | 
			
		||||
@@ -354,7 +347,8 @@ type References []Reference
 | 
			
		||||
 | 
			
		||||
// Reference has a related link of the CVE
 | 
			
		||||
type Reference struct {
 | 
			
		||||
	Source string `json:"source"`
 | 
			
		||||
	Link   string `json:"link"`
 | 
			
		||||
	RefID  string `json:"refID"`
 | 
			
		||||
	Link   string   `json:"link,omitempty"`
 | 
			
		||||
	Source string   `json:"source,omitempty"`
 | 
			
		||||
	RefID  string   `json:"refID,omitempty"`
 | 
			
		||||
	Tags   []string `json:"tags,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -52,25 +52,43 @@ func TestSourceLinks(t *testing.T) {
 | 
			
		||||
						Type:       RedHat,
 | 
			
		||||
						SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
 | 
			
		||||
					},
 | 
			
		||||
					NvdXML: {
 | 
			
		||||
						Type:       NvdXML,
 | 
			
		||||
					Nvd: {
 | 
			
		||||
						Type: Nvd,
 | 
			
		||||
						References: []Reference{
 | 
			
		||||
							{
 | 
			
		||||
								Link:   "https://lists.apache.org/thread.html/765be3606d865de513f6df9288842c3cf58b09a987c617a535f2b99d@%3Cusers.tapestry.apache.org%3E",
 | 
			
		||||
								Source: "",
 | 
			
		||||
								RefID:  "",
 | 
			
		||||
								Tags:   []string{"Vendor Advisory"},
 | 
			
		||||
							},
 | 
			
		||||
							{
 | 
			
		||||
								Link:   "http://yahoo.com",
 | 
			
		||||
								Source: "",
 | 
			
		||||
								RefID:  "",
 | 
			
		||||
								Tags:   []string{"Vendor"},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: []CveContentStr{
 | 
			
		||||
				{
 | 
			
		||||
					Type:  Jvn,
 | 
			
		||||
					Value: "https://jvn.jp/vu/JVNVU93610402/",
 | 
			
		||||
					Type:  Nvd,
 | 
			
		||||
					Value: "https://lists.apache.org/thread.html/765be3606d865de513f6df9288842c3cf58b09a987c617a535f2b99d@%3Cusers.tapestry.apache.org%3E",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Type:  NvdXML,
 | 
			
		||||
					Type:  Nvd,
 | 
			
		||||
					Value: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Type:  RedHat,
 | 
			
		||||
					Value: "https://access.redhat.com/security/cve/CVE-2017-6074",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Type:  Jvn,
 | 
			
		||||
					Value: "https://jvn.jp/vu/JVNVU93610402/",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		// lang: en
 | 
			
		||||
@@ -87,17 +105,9 @@ func TestSourceLinks(t *testing.T) {
 | 
			
		||||
						Type:       RedHat,
 | 
			
		||||
						SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
 | 
			
		||||
					},
 | 
			
		||||
					NvdXML: {
 | 
			
		||||
						Type:       NvdXML,
 | 
			
		||||
						SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: []CveContentStr{
 | 
			
		||||
				{
 | 
			
		||||
					Type:  NvdXML,
 | 
			
		||||
					Value: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Type:  RedHat,
 | 
			
		||||
					Value: "https://access.redhat.com/security/cve/CVE-2017-6074",
 | 
			
		||||
@@ -120,71 +130,9 @@ func TestSourceLinks(t *testing.T) {
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for i, tt := range tests {
 | 
			
		||||
		actual := tt.in.cont.SourceLinks(tt.in.lang, "redhat", tt.in.cveID)
 | 
			
		||||
		actual := tt.in.cont.PrimarySrcURLs(tt.in.lang, "redhat", tt.in.cveID)
 | 
			
		||||
		if !reflect.DeepEqual(tt.out, actual) {
 | 
			
		||||
			t.Errorf("\n[%d] expected: %v\n  actual: %v\n", i, tt.out, actual)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestVendorLink(t *testing.T) {
 | 
			
		||||
	type in struct {
 | 
			
		||||
		family string
 | 
			
		||||
		vinfo  VulnInfo
 | 
			
		||||
	}
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in  in
 | 
			
		||||
		out map[string]string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				family: "redhat",
 | 
			
		||||
				vinfo: VulnInfo{
 | 
			
		||||
					CveID: "CVE-2017-6074",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						Jvn: {
 | 
			
		||||
							Type:       Jvn,
 | 
			
		||||
							SourceLink: "https://jvn.jp/vu/JVNVU93610402/",
 | 
			
		||||
						},
 | 
			
		||||
						RedHat: {
 | 
			
		||||
							Type:       RedHat,
 | 
			
		||||
							SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
 | 
			
		||||
						},
 | 
			
		||||
						NvdXML: {
 | 
			
		||||
							Type:       NvdXML,
 | 
			
		||||
							SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: map[string]string{
 | 
			
		||||
				"RHEL-CVE": "https://access.redhat.com/security/cve/CVE-2017-6074",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				family: "ubuntu",
 | 
			
		||||
				vinfo: VulnInfo{
 | 
			
		||||
					CveID: "CVE-2017-6074",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						RedHat: {
 | 
			
		||||
							Type:       Ubuntu,
 | 
			
		||||
							SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: map[string]string{
 | 
			
		||||
				"Ubuntu-CVE": "http://people.ubuntu.com/~ubuntu-security/cve/CVE-2017-6074",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		actual := tt.in.vinfo.VendorLinks(tt.in.family)
 | 
			
		||||
		for k := range tt.out {
 | 
			
		||||
			if tt.out[k] != actual[k] {
 | 
			
		||||
				t.Errorf("\nexpected: %s\n  actual: %s\n", tt.out[k], actual[k])
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,20 +10,18 @@ import (
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/types"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	// "github.com/aquasecurity/go-dep-parser/pkg/types"
 | 
			
		||||
	"github.com/knqyf263/go-version"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// LibraryScanners is an array of LibraryScanner
 | 
			
		||||
type LibraryScanners []LibraryScanner
 | 
			
		||||
 | 
			
		||||
// Find : find by name
 | 
			
		||||
func (lss LibraryScanners) Find(name string) map[string]types.Library {
 | 
			
		||||
func (lss LibraryScanners) Find(path, name string) map[string]types.Library {
 | 
			
		||||
	filtered := map[string]types.Library{}
 | 
			
		||||
	for _, ls := range lss {
 | 
			
		||||
		for _, lib := range ls.Libs {
 | 
			
		||||
			if lib.Name == name {
 | 
			
		||||
			if ls.Path == path && lib.Name == name {
 | 
			
		||||
				filtered[ls.Path] = lib
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
@@ -40,20 +38,13 @@ type LibraryScanner struct {
 | 
			
		||||
 | 
			
		||||
// Scan : scan target library
 | 
			
		||||
func (s LibraryScanner) Scan() ([]VulnInfo, error) {
 | 
			
		||||
	scanner := library.DriverFactory{}.NewDriver(filepath.Base(string(s.Path)))
 | 
			
		||||
	if scanner == nil {
 | 
			
		||||
		return nil, xerrors.New("unknown file type")
 | 
			
		||||
	scanner, err := library.DriverFactory{}.NewDriver(filepath.Base(string(s.Path)))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to new a library driver: %w", 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)
 | 
			
		||||
		tvulns, err := scanner.Detect(pkg.Name, pkg.Version)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("failed to detect %s vulnerabilities: %w", scanner.Type(), err)
 | 
			
		||||
		}
 | 
			
		||||
@@ -72,7 +63,7 @@ func (s LibraryScanner) convertFanalToVuln(tvulns []types.DetectedVulnerability)
 | 
			
		||||
	for _, tvuln := range tvulns {
 | 
			
		||||
		vinfo, err := s.getVulnDetail(tvuln)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Debugf("failed to getVulnDetail. err: %s, tvun: %#v", err, tvuln)
 | 
			
		||||
			util.Log.Debugf("failed to getVulnDetail. err: %s, tvuln: %#v", err, tvuln)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		vulns = append(vulns, vinfo)
 | 
			
		||||
@@ -94,6 +85,7 @@ func (s LibraryScanner) getVulnDetail(tvuln types.DetectedVulnerability) (vinfo
 | 
			
		||||
				Key:     s.GetLibraryKey(),
 | 
			
		||||
				Name:    tvuln.PkgName,
 | 
			
		||||
				FixedIn: tvuln.FixedVersion,
 | 
			
		||||
				Path:    s.Path,
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -141,4 +133,5 @@ type LibraryFixedIn struct {
 | 
			
		||||
	Key     string `json:"key,omitempty"`
 | 
			
		||||
	Name    string `json:"name,omitempty"`
 | 
			
		||||
	FixedIn string `json:"fixedIn,omitempty"`
 | 
			
		||||
	Path    string `json:"path,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ import (
 | 
			
		||||
 | 
			
		||||
func TestLibraryScanners_Find(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		path string
 | 
			
		||||
		name string
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
@@ -30,7 +31,7 @@ func TestLibraryScanners_Find(t *testing.T) {
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			args: args{"libA"},
 | 
			
		||||
			args: args{"/pathA", "libA"},
 | 
			
		||||
			want: map[string]types.Library{
 | 
			
		||||
				"/pathA": {
 | 
			
		||||
					Name:    "libA",
 | 
			
		||||
@@ -60,16 +61,12 @@ func TestLibraryScanners_Find(t *testing.T) {
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			args: args{"libA"},
 | 
			
		||||
			args: args{"/pathA", "libA"},
 | 
			
		||||
			want: map[string]types.Library{
 | 
			
		||||
				"/pathA": {
 | 
			
		||||
					Name:    "libA",
 | 
			
		||||
					Version: "1.0.0",
 | 
			
		||||
				},
 | 
			
		||||
				"/pathB": {
 | 
			
		||||
					Name:    "libA",
 | 
			
		||||
					Version: "1.0.5",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
@@ -85,13 +82,13 @@ func TestLibraryScanners_Find(t *testing.T) {
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			args: args{"libB"},
 | 
			
		||||
			args: args{"/pathA", "libB"},
 | 
			
		||||
			want: map[string]types.Library{},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			if got := tt.lss.Find(tt.args.name); !reflect.DeepEqual(got, tt.want) {
 | 
			
		||||
			if got := tt.lss.Find(tt.args.path, tt.args.name); !reflect.DeepEqual(got, tt.want) {
 | 
			
		||||
				t.Errorf("LibraryScanners.Find() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@ package models
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
@@ -173,9 +174,44 @@ type Changelog struct {
 | 
			
		||||
 | 
			
		||||
// AffectedProcess keep a processes information affected by software update
 | 
			
		||||
type AffectedProcess struct {
 | 
			
		||||
	PID         string   `json:"pid,omitempty"`
 | 
			
		||||
	Name        string   `json:"name,omitempty"`
 | 
			
		||||
	ListenPorts []string `json:"listenPorts,omitempty"`
 | 
			
		||||
	PID             string     `json:"pid,omitempty"`
 | 
			
		||||
	Name            string     `json:"name,omitempty"`
 | 
			
		||||
	ListenPorts     []string   `json:"listenPorts,omitempty"`
 | 
			
		||||
	ListenPortStats []PortStat `json:"listenPortStats,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PortStat has the result of parsing the port information to the address and port.
 | 
			
		||||
type PortStat struct {
 | 
			
		||||
	BindAddress     string   `json:"bindAddress"`
 | 
			
		||||
	Port            string   `json:"port"`
 | 
			
		||||
	PortReachableTo []string `json:"portReachableTo"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewPortStat create a PortStat from ipPort str
 | 
			
		||||
func NewPortStat(ipPort string) (*PortStat, error) {
 | 
			
		||||
	if ipPort == "" {
 | 
			
		||||
		return &PortStat{}, nil
 | 
			
		||||
	}
 | 
			
		||||
	sep := strings.LastIndex(ipPort, ":")
 | 
			
		||||
	if sep == -1 {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to parse IP:Port: %s", ipPort)
 | 
			
		||||
	}
 | 
			
		||||
	return &PortStat{
 | 
			
		||||
		BindAddress: ipPort[:sep],
 | 
			
		||||
		Port:        ipPort[sep+1:],
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HasReachablePort checks if Package.AffectedProcs has PortReachableTo
 | 
			
		||||
func (p Package) HasReachablePort() bool {
 | 
			
		||||
	for _, ap := range p.AffectedProcs {
 | 
			
		||||
		for _, lp := range ap.ListenPortStats {
 | 
			
		||||
			if len(lp.PortReachableTo) > 0 {
 | 
			
		||||
				return true
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NeedRestartProcess keep a processes information affected by software update
 | 
			
		||||
@@ -227,3 +263,28 @@ func (s SrcPackages) FindByBinName(name string) (*SrcPackage, bool) {
 | 
			
		||||
	}
 | 
			
		||||
	return nil, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// raspiPackNamePattern is a regular expression pattern to detect the Raspberry Pi specific package from the package name.
 | 
			
		||||
// e.g. libraspberrypi-dev, rpi-eeprom, python3-rpi.gpio, pi-bluetooth
 | 
			
		||||
var raspiPackNamePattern = regexp.MustCompile(`(.*raspberry.*|^rpi.*|.*-rpi.*|^pi-.*)`)
 | 
			
		||||
 | 
			
		||||
// raspiPackNamePattern is a regular expression pattern to detect the Raspberry Pi specific package from the version.
 | 
			
		||||
// e.g. ffmpeg 7:4.1.4-1+rpt7~deb10u1, vlc 3.0.10-0+deb10u1+rpt2
 | 
			
		||||
var raspiPackVersionPattern = regexp.MustCompile(`.+\+rp(t|i)\d+`)
 | 
			
		||||
 | 
			
		||||
// raspiPackNameList is a package name array of Raspberry Pi specific packages that are difficult to detect with regular expressions.
 | 
			
		||||
var raspiPackNameList = []string{"piclone", "pipanel", "pishutdown", "piwiz", "pixflat-icons"}
 | 
			
		||||
 | 
			
		||||
// IsRaspbianPackage judges whether it is a package related to Raspberry Pi from the package name and version
 | 
			
		||||
func IsRaspbianPackage(name, version string) bool {
 | 
			
		||||
	if raspiPackNamePattern.MatchString(name) || raspiPackVersionPattern.MatchString(version) {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	for _, n := range raspiPackNameList {
 | 
			
		||||
		if n == name {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -297,3 +297,134 @@ func TestPackage_FormatVersionFromTo(t *testing.T) {
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_IsRaspbianPackage(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		name string
 | 
			
		||||
		ver  string
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name   string
 | 
			
		||||
		in     []args
 | 
			
		||||
		expect []bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "nameRegExp",
 | 
			
		||||
			in: []args{
 | 
			
		||||
				{
 | 
			
		||||
					name: "libraspberrypi-dev",
 | 
			
		||||
					ver:  "1.20200811-1",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					name: "rpi-eeprom",
 | 
			
		||||
					ver:  "7.10-1",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					name: "python3-rpi.gpio",
 | 
			
		||||
					ver:  "0.7.0-0.1~bpo10+1",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					name: "arping",
 | 
			
		||||
					ver:  "2.19-6",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					name: "pi-bluetooth",
 | 
			
		||||
					ver:  "0.1.14",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expect: []bool{true, true, true, false, true, false},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "verRegExp",
 | 
			
		||||
			in: []args{
 | 
			
		||||
				{
 | 
			
		||||
					name: "ffmpeg",
 | 
			
		||||
					ver:  "7:4.1.6-1~deb10u1+rpt1",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					name: "gcc",
 | 
			
		||||
					ver:  "4:8.3.0-1+rpi2",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expect: []bool{true, true},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "nameList",
 | 
			
		||||
			in: []args{
 | 
			
		||||
				{
 | 
			
		||||
					name: "piclone",
 | 
			
		||||
					ver:  "0.16",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expect: []bool{true},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "debianPackage",
 | 
			
		||||
			in: []args{
 | 
			
		||||
				{
 | 
			
		||||
					name: "apt",
 | 
			
		||||
					ver:  "1.8.2.1",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expect: []bool{false},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			for i, p := range tt.in {
 | 
			
		||||
				ret := IsRaspbianPackage(p.name, p.ver)
 | 
			
		||||
				if !reflect.DeepEqual(ret, tt.expect[i]) {
 | 
			
		||||
					t.Errorf("[%s->%s] expected: %t, actual: %t, in: %#v", tt.name, tt.in[i].name, tt.expect[i], ret, tt.in[i])
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_parseListenPorts(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name   string
 | 
			
		||||
		args   string
 | 
			
		||||
		expect PortStat
 | 
			
		||||
	}{{
 | 
			
		||||
		name: "empty",
 | 
			
		||||
		args: "",
 | 
			
		||||
		expect: PortStat{
 | 
			
		||||
			BindAddress: "",
 | 
			
		||||
			Port:        "",
 | 
			
		||||
		},
 | 
			
		||||
	}, {
 | 
			
		||||
		name: "normal",
 | 
			
		||||
		args: "127.0.0.1:22",
 | 
			
		||||
		expect: PortStat{
 | 
			
		||||
			BindAddress: "127.0.0.1",
 | 
			
		||||
			Port:        "22",
 | 
			
		||||
		},
 | 
			
		||||
	}, {
 | 
			
		||||
		name: "asterisk",
 | 
			
		||||
		args: "*:22",
 | 
			
		||||
		expect: PortStat{
 | 
			
		||||
			BindAddress: "*",
 | 
			
		||||
			Port:        "22",
 | 
			
		||||
		},
 | 
			
		||||
	}, {
 | 
			
		||||
		name: "ipv6_loopback",
 | 
			
		||||
		args: "[::1]:22",
 | 
			
		||||
		expect: PortStat{
 | 
			
		||||
			BindAddress: "[::1]",
 | 
			
		||||
			Port:        "22",
 | 
			
		||||
		},
 | 
			
		||||
	}}
 | 
			
		||||
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			listenPort, err := NewPortStat(tt.args)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				t.Errorf("unexpected error occurred: %s", err)
 | 
			
		||||
			} else if !reflect.DeepEqual(*listenPort, tt.expect) {
 | 
			
		||||
				t.Errorf("base.parseListenPorts() = %v, want %v", *listenPort, tt.expect)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@ package models
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
@@ -216,7 +217,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. err: %+v", pkgRegexp, err)
 | 
			
		||||
			util.Log.Errorf("Failed to parse %s. err: %+v", pkgRegexp, err)
 | 
			
		||||
			continue
 | 
			
		||||
		} else {
 | 
			
		||||
			regexps = append(regexps, re)
 | 
			
		||||
@@ -339,20 +340,21 @@ func (r ScanResult) FormatServerName() (name string) {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FormatTextReportHeadedr returns header of text report
 | 
			
		||||
func (r ScanResult) FormatTextReportHeadedr() string {
 | 
			
		||||
// FormatTextReportHeader returns header of text report
 | 
			
		||||
func (r ScanResult) FormatTextReportHeader() string {
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	for i := 0; i < len(r.ServerInfo()); i++ {
 | 
			
		||||
		buf.WriteString("=")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return fmt.Sprintf("%s\n%s\n%s, %s, %s, %s, %s\n",
 | 
			
		||||
	return fmt.Sprintf("%s\n%s\n%s, %s, %s, %s, %s, %s\n",
 | 
			
		||||
		r.ServerInfo(),
 | 
			
		||||
		buf.String(),
 | 
			
		||||
		r.ScannedCves.FormatCveSummary(),
 | 
			
		||||
		r.ScannedCves.FormatFixedStatus(r.Packages),
 | 
			
		||||
		r.FormatUpdatablePacksSummary(),
 | 
			
		||||
		r.FormatExploitCveSummary(),
 | 
			
		||||
		r.FormatMetasploitCveSummary(),
 | 
			
		||||
		r.FormatAlertSummary(),
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
@@ -388,7 +390,18 @@ func (r ScanResult) FormatExploitCveSummary() string {
 | 
			
		||||
	return fmt.Sprintf("%d exploits", nExploitCve)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FormatAlertSummary returns a summary of XCERT alerts
 | 
			
		||||
// FormatMetasploitCveSummary returns a summary of exploit cve
 | 
			
		||||
func (r ScanResult) FormatMetasploitCveSummary() string {
 | 
			
		||||
	nMetasploitCve := 0
 | 
			
		||||
	for _, vuln := range r.ScannedCves {
 | 
			
		||||
		if 0 < len(vuln.Metasploits) {
 | 
			
		||||
			nMetasploitCve++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%d modules", nMetasploitCve)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FormatAlertSummary returns a summary of CERT alerts
 | 
			
		||||
func (r ScanResult) FormatAlertSummary() string {
 | 
			
		||||
	jaCnt := 0
 | 
			
		||||
	enCnt := 0
 | 
			
		||||
@@ -404,6 +417,10 @@ func (r ScanResult) FormatAlertSummary() string {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r ScanResult) isDisplayUpdatableNum() bool {
 | 
			
		||||
	if r.Family == config.FreeBSD {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var mode config.ScanMode
 | 
			
		||||
	s, _ := config.Conf.Servers[r.ServerName]
 | 
			
		||||
	mode = s.Mode
 | 
			
		||||
@@ -460,3 +477,51 @@ type Platform struct {
 | 
			
		||||
	Name       string `json:"name"` // aws or azure or gcp or other...
 | 
			
		||||
	InstanceID string `json:"instanceID"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RemoveRaspbianPackFromResult is for Raspberry Pi and removes the Raspberry Pi dedicated package from ScanResult.
 | 
			
		||||
func (r ScanResult) RemoveRaspbianPackFromResult() ScanResult {
 | 
			
		||||
	if r.Family != config.Raspbian {
 | 
			
		||||
		return r
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result := r
 | 
			
		||||
	packs := make(Packages)
 | 
			
		||||
	for _, pack := range r.Packages {
 | 
			
		||||
		if !IsRaspbianPackage(pack.Name, pack.Version) {
 | 
			
		||||
			packs[pack.Name] = pack
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	srcPacks := make(SrcPackages)
 | 
			
		||||
	for _, pack := range r.SrcPackages {
 | 
			
		||||
		if !IsRaspbianPackage(pack.Name, pack.Version) {
 | 
			
		||||
			srcPacks[pack.Name] = pack
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result.Packages = packs
 | 
			
		||||
	result.SrcPackages = srcPacks
 | 
			
		||||
 | 
			
		||||
	return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ClearFields clears a given fields of ScanResult
 | 
			
		||||
func (r ScanResult) ClearFields(targetTagNames []string) ScanResult {
 | 
			
		||||
	if len(targetTagNames) == 0 {
 | 
			
		||||
		return r
 | 
			
		||||
	}
 | 
			
		||||
	target := map[string]bool{}
 | 
			
		||||
	for _, n := range targetTagNames {
 | 
			
		||||
		target[strings.ToLower(n)] = true
 | 
			
		||||
	}
 | 
			
		||||
	t := reflect.ValueOf(r).Type()
 | 
			
		||||
	for i := 0; i < t.NumField(); i++ {
 | 
			
		||||
		f := t.Field(i)
 | 
			
		||||
		jsonValue := strings.Split(f.Tag.Get("json"), ",")[0]
 | 
			
		||||
		if ok := target[strings.ToLower(jsonValue)]; ok {
 | 
			
		||||
			vv := reflect.New(f.Type).Elem().Interface()
 | 
			
		||||
			reflect.ValueOf(&r).Elem().FieldByName(f.Name).Set(reflect.ValueOf(vv))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ func TestFilterByCvssOver(t *testing.T) {
 | 
			
		||||
							CveID: "CVE-2017-0001",
 | 
			
		||||
							CveContents: NewCveContents(
 | 
			
		||||
								CveContent{
 | 
			
		||||
									Type:         NvdXML,
 | 
			
		||||
									Type:         Nvd,
 | 
			
		||||
									CveID:        "CVE-2017-0001",
 | 
			
		||||
									Cvss2Score:   7.1,
 | 
			
		||||
									LastModified: time.Time{},
 | 
			
		||||
@@ -38,7 +38,7 @@ func TestFilterByCvssOver(t *testing.T) {
 | 
			
		||||
							CveID: "CVE-2017-0002",
 | 
			
		||||
							CveContents: NewCveContents(
 | 
			
		||||
								CveContent{
 | 
			
		||||
									Type:         NvdXML,
 | 
			
		||||
									Type:         Nvd,
 | 
			
		||||
									CveID:        "CVE-2017-0002",
 | 
			
		||||
									Cvss2Score:   6.9,
 | 
			
		||||
									LastModified: time.Time{},
 | 
			
		||||
@@ -49,7 +49,7 @@ func TestFilterByCvssOver(t *testing.T) {
 | 
			
		||||
							CveID: "CVE-2017-0003",
 | 
			
		||||
							CveContents: NewCveContents(
 | 
			
		||||
								CveContent{
 | 
			
		||||
									Type:         NvdXML,
 | 
			
		||||
									Type:         Nvd,
 | 
			
		||||
									CveID:        "CVE-2017-0003",
 | 
			
		||||
									Cvss2Score:   6.9,
 | 
			
		||||
									LastModified: time.Time{},
 | 
			
		||||
@@ -71,7 +71,7 @@ func TestFilterByCvssOver(t *testing.T) {
 | 
			
		||||
						CveID: "CVE-2017-0001",
 | 
			
		||||
						CveContents: NewCveContents(
 | 
			
		||||
							CveContent{
 | 
			
		||||
								Type:         NvdXML,
 | 
			
		||||
								Type:         Nvd,
 | 
			
		||||
								CveID:        "CVE-2017-0001",
 | 
			
		||||
								Cvss2Score:   7.1,
 | 
			
		||||
								LastModified: time.Time{},
 | 
			
		||||
@@ -82,7 +82,7 @@ func TestFilterByCvssOver(t *testing.T) {
 | 
			
		||||
						CveID: "CVE-2017-0003",
 | 
			
		||||
						CveContents: NewCveContents(
 | 
			
		||||
							CveContent{
 | 
			
		||||
								Type:         NvdXML,
 | 
			
		||||
								Type:         Nvd,
 | 
			
		||||
								CveID:        "CVE-2017-0003",
 | 
			
		||||
								Cvss2Score:   6.9,
 | 
			
		||||
								LastModified: time.Time{},
 | 
			
		||||
@@ -688,7 +688,7 @@ func TestIsDisplayUpdatableNum(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			mode:     []byte{config.Fast},
 | 
			
		||||
			family:   config.FreeBSD,
 | 
			
		||||
			expected: true,
 | 
			
		||||
			expected: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			mode:     []byte{config.Fast},
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
@@ -47,9 +49,9 @@ func ConvertJvnToModel(cveID string, jvn *cvedict.Jvn) *CveContent {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConvertNvdJSONToModel convert NVD to CveContent
 | 
			
		||||
func ConvertNvdJSONToModel(cveID string, nvd *cvedict.NvdJSON) *CveContent {
 | 
			
		||||
func ConvertNvdJSONToModel(cveID string, nvd *cvedict.NvdJSON) (*CveContent, []Exploit, []Mitigation) {
 | 
			
		||||
	if nvd == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
		return nil, nil, nil
 | 
			
		||||
	}
 | 
			
		||||
	// var cpes = []Cpe{}
 | 
			
		||||
	// for _, c := range nvd.Cpes {
 | 
			
		||||
@@ -59,12 +61,29 @@ func ConvertNvdJSONToModel(cveID string, nvd *cvedict.NvdJSON) *CveContent {
 | 
			
		||||
	// 	})
 | 
			
		||||
	// }
 | 
			
		||||
 | 
			
		||||
	var refs = []Reference{}
 | 
			
		||||
	refs := []Reference{}
 | 
			
		||||
	exploits := []Exploit{}
 | 
			
		||||
	mitigations := []Mitigation{}
 | 
			
		||||
	for _, r := range nvd.References {
 | 
			
		||||
		refs = append(refs, Reference{
 | 
			
		||||
			Link:   r.Link,
 | 
			
		||||
			Source: r.Source,
 | 
			
		||||
			Tags:   strings.Split(r.Tags, ","),
 | 
			
		||||
		})
 | 
			
		||||
		if strings.Contains(r.Tags, "Exploit") {
 | 
			
		||||
			exploits = append(exploits, Exploit{
 | 
			
		||||
				//TODO Add const to here
 | 
			
		||||
				// https://github.com/vulsio/go-exploitdb/blob/master/models/exploit.go#L13-L18
 | 
			
		||||
				ExploitType: "nvd",
 | 
			
		||||
				URL:         r.Link,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
		if strings.Contains(r.Tags, "Mitigation") {
 | 
			
		||||
			mitigations = append(mitigations, Mitigation{
 | 
			
		||||
				CveContentType: Nvd,
 | 
			
		||||
				URL:            r.Link,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cweIDs := []string{}
 | 
			
		||||
@@ -93,5 +112,5 @@ func ConvertNvdJSONToModel(cveID string, nvd *cvedict.NvdJSON) *CveContent {
 | 
			
		||||
		References:   refs,
 | 
			
		||||
		Published:    nvd.PublishedDate,
 | 
			
		||||
		LastModified: nvd.LastModifiedDate,
 | 
			
		||||
	}
 | 
			
		||||
	}, exploits, mitigations
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -134,7 +134,7 @@ func (ps PackageFixStatuses) Sort() {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PackageFixStatus has name and other status abount the package
 | 
			
		||||
// PackageFixStatus has name and other status about the package
 | 
			
		||||
type PackageFixStatus struct {
 | 
			
		||||
	Name        string `json:"name,omitempty"`
 | 
			
		||||
	NotFixedYet bool   `json:"notFixedYet,omitempty"`
 | 
			
		||||
@@ -147,9 +147,11 @@ type VulnInfo struct {
 | 
			
		||||
	CveID                string               `json:"cveID,omitempty"`
 | 
			
		||||
	Confidences          Confidences          `json:"confidences,omitempty"`
 | 
			
		||||
	AffectedPackages     PackageFixStatuses   `json:"affectedPackages,omitempty"`
 | 
			
		||||
	DistroAdvisories     DistroAdvisories     `json:"distroAdvisories,omitempty"` // for Aamazon, RHEL, FreeBSD
 | 
			
		||||
	DistroAdvisories     DistroAdvisories     `json:"distroAdvisories,omitempty"` // for Amazon, RHEL, FreeBSD
 | 
			
		||||
	CveContents          CveContents          `json:"cveContents,omitempty"`
 | 
			
		||||
	Exploits             []Exploit            `json:"exploits,omitempty"`
 | 
			
		||||
	Metasploits          []Metasploit         `json:"metasploits,omitempty"`
 | 
			
		||||
	Mitigations          []Mitigation         `json:"mitigations,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"`
 | 
			
		||||
@@ -159,7 +161,7 @@ type VulnInfo struct {
 | 
			
		||||
	VulnType string `json:"vulnType,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Alert has XCERT alert information
 | 
			
		||||
// Alert has CERT alert information
 | 
			
		||||
type Alert struct {
 | 
			
		||||
	URL   string `json:"url,omitempty"`
 | 
			
		||||
	Title string `json:"title,omitempty"`
 | 
			
		||||
@@ -232,7 +234,7 @@ func (g WpPackages) Add(pkg WpPackage) WpPackages {
 | 
			
		||||
	return append(g, pkg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Titles returns tilte (TUI)
 | 
			
		||||
// Titles returns title (TUI)
 | 
			
		||||
func (v VulnInfo) Titles(lang, myFamily string) (values []CveContentStr) {
 | 
			
		||||
	if lang == "ja" {
 | 
			
		||||
		if cont, found := v.CveContents[Jvn]; found && 0 < len(cont.Title) {
 | 
			
		||||
@@ -245,7 +247,7 @@ func (v VulnInfo) Titles(lang, myFamily string) (values []CveContentStr) {
 | 
			
		||||
		values = append(values, CveContentStr{RedHatAPI, cont.Title})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	order := CveContentTypes{Trivy, Nvd, NvdXML, NewCveContentType(myFamily)}
 | 
			
		||||
	order := CveContentTypes{Trivy, Nvd, NewCveContentType(myFamily)}
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(append(order, Jvn)...)...)
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		// Only JVN has meaningful title. so return first 100 char of summary
 | 
			
		||||
@@ -285,7 +287,7 @@ func (v VulnInfo) Summaries(lang, myFamily string) (values []CveContentStr) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	order := CveContentTypes{Trivy, NewCveContentType(myFamily), Nvd, NvdXML}
 | 
			
		||||
	order := CveContentTypes{Trivy, NewCveContentType(myFamily), Nvd}
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(append(order, Jvn)...)...)
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if cont, found := v.CveContents[ctype]; found && 0 < len(cont.Summary) {
 | 
			
		||||
@@ -321,30 +323,9 @@ func (v VulnInfo) Summaries(lang, myFamily string) (values []CveContentStr) {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Mitigations returns mitigations
 | 
			
		||||
func (v VulnInfo) Mitigations(myFamily string) (values []CveContentStr) {
 | 
			
		||||
	order := CveContentTypes{RedHatAPI}
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if cont, found := v.CveContents[ctype]; found && 0 < len(cont.Mitigation) {
 | 
			
		||||
			values = append(values, CveContentStr{
 | 
			
		||||
				Type:  ctype,
 | 
			
		||||
				Value: cont.Mitigation,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(values) == 0 {
 | 
			
		||||
		return []CveContentStr{{
 | 
			
		||||
			Type:  Unknown,
 | 
			
		||||
			Value: "-",
 | 
			
		||||
		}}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Cvss2Scores returns CVSS V2 Scores
 | 
			
		||||
func (v VulnInfo) Cvss2Scores(myFamily string) (values []CveContentCvss) {
 | 
			
		||||
	order := []CveContentType{Nvd, NvdXML, RedHatAPI, RedHat, Jvn}
 | 
			
		||||
	order := []CveContentType{Nvd, RedHatAPI, RedHat, Jvn}
 | 
			
		||||
	if myFamily != config.RedHat && myFamily != config.CentOS {
 | 
			
		||||
		order = append(order, NewCveContentType(myFamily))
 | 
			
		||||
	}
 | 
			
		||||
@@ -482,7 +463,7 @@ func (v VulnInfo) MaxCvssScore() CveContentCvss {
 | 
			
		||||
 | 
			
		||||
// MaxCvss2Score returns Max CVSS V2 Score
 | 
			
		||||
func (v VulnInfo) MaxCvss2Score() CveContentCvss {
 | 
			
		||||
	order := []CveContentType{Nvd, NvdXML, RedHat, RedHatAPI, Jvn}
 | 
			
		||||
	order := []CveContentType{Nvd, RedHat, RedHatAPI, Jvn}
 | 
			
		||||
	max := 0.0
 | 
			
		||||
	value := CveContentCvss{
 | 
			
		||||
		Type:  Unknown,
 | 
			
		||||
@@ -508,7 +489,7 @@ func (v VulnInfo) MaxCvss2Score() CveContentCvss {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If CVSS score isn't on NVD, RedHat and JVN, use OVAL and advisory Severity.
 | 
			
		||||
	// Convert severity to cvss srore roughly, then returns max severity.
 | 
			
		||||
	// Convert severity to cvss score roughly, then returns max severity.
 | 
			
		||||
	// Only Ubuntu, RedHat and Oracle have severity data in OVAL.
 | 
			
		||||
	order = []CveContentType{Ubuntu, RedHat, Oracle}
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
@@ -614,10 +595,10 @@ type CveContentCvss struct {
 | 
			
		||||
type CvssType string
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// CVSS2 means CVSS vesion2
 | 
			
		||||
	// CVSS2 means CVSS version2
 | 
			
		||||
	CVSS2 CvssType = "2"
 | 
			
		||||
 | 
			
		||||
	// CVSS3 means CVSS vesion3
 | 
			
		||||
	// CVSS3 means CVSS version3
 | 
			
		||||
	CVSS3 CvssType = "3"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -679,70 +660,6 @@ func (v VulnInfo) FormatMaxCvssScore() string {
 | 
			
		||||
		max.Type)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Cvss2CalcURL returns CVSS v2 caluclator's URL
 | 
			
		||||
func (v VulnInfo) Cvss2CalcURL() string {
 | 
			
		||||
	return "https://nvd.nist.gov/vuln-metrics/cvss/v2-calculator?name=" + v.CveID
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Cvss3CalcURL returns CVSS v3 caluclator's URL
 | 
			
		||||
func (v VulnInfo) Cvss3CalcURL() string {
 | 
			
		||||
	return "https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator?name=" + v.CveID
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
		for _, advisory := range v.DistroAdvisories {
 | 
			
		||||
			aidURL := strings.Replace(advisory.AdvisoryID, ":", "-", -1)
 | 
			
		||||
			links[advisory.AdvisoryID] = fmt.Sprintf("https://rhn.redhat.com/errata/%s.html", aidURL)
 | 
			
		||||
		}
 | 
			
		||||
		return links
 | 
			
		||||
	case config.Oracle:
 | 
			
		||||
		links["Oracle-CVE"] = fmt.Sprintf("https://linux.oracle.com/cve/%s.html", v.CveID)
 | 
			
		||||
		for _, advisory := range v.DistroAdvisories {
 | 
			
		||||
			links[advisory.AdvisoryID] =
 | 
			
		||||
				fmt.Sprintf("https://linux.oracle.com/errata/%s.html", advisory.AdvisoryID)
 | 
			
		||||
		}
 | 
			
		||||
		return links
 | 
			
		||||
	case config.Amazon:
 | 
			
		||||
		links["RHEL-CVE"] = "https://access.redhat.com/security/cve/" + v.CveID
 | 
			
		||||
		for _, advisory := range v.DistroAdvisories {
 | 
			
		||||
			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:
 | 
			
		||||
		links["Ubuntu-CVE"] = "http://people.ubuntu.com/~ubuntu-security/cve/" + v.CveID
 | 
			
		||||
		return links
 | 
			
		||||
	case config.Debian:
 | 
			
		||||
		links["Debian-CVE"] = "https://security-tracker.debian.org/tracker/" + v.CveID
 | 
			
		||||
	case config.SUSEEnterpriseServer:
 | 
			
		||||
		links["SUSE-CVE"] = "https://www.suse.com/security/cve/" + v.CveID
 | 
			
		||||
	case config.FreeBSD:
 | 
			
		||||
		for _, advisory := range v.DistroAdvisories {
 | 
			
		||||
			links["FreeBSD-VuXML"] = fmt.Sprintf("https://vuxml.freebsd.org/freebsd/%s.html", advisory.AdvisoryID)
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
		return links
 | 
			
		||||
	}
 | 
			
		||||
	return links
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DistroAdvisories is a list of DistroAdvisory
 | 
			
		||||
type DistroAdvisories []DistroAdvisory
 | 
			
		||||
 | 
			
		||||
@@ -791,7 +708,22 @@ type Exploit struct {
 | 
			
		||||
	BinaryURL    *string                   `json:"binaryURL,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AlertDict has target cve's JPCERT and USCERT alert data
 | 
			
		||||
// Metasploit :
 | 
			
		||||
type Metasploit struct {
 | 
			
		||||
	Name        string   `json:"name"`
 | 
			
		||||
	Title       string   `json:"title"`
 | 
			
		||||
	Description string   `json:"description,omitempty"`
 | 
			
		||||
	URLs        []string `json:",omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Mitigation has a link and content
 | 
			
		||||
type Mitigation struct {
 | 
			
		||||
	CveContentType CveContentType `json:"cveContentType,omitempty"`
 | 
			
		||||
	Mitigation     string         `json:"mitigation,omitempty"`
 | 
			
		||||
	URL            string         `json:"url,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AlertDict has target cve JPCERT and USCERT alert data
 | 
			
		||||
type AlertDict struct {
 | 
			
		||||
	Ja []Alert `json:"ja"`
 | 
			
		||||
	En []Alert `json:"en"`
 | 
			
		||||
@@ -812,7 +744,7 @@ func (a AlertDict) FormatSource() string {
 | 
			
		||||
// Confidences is a list of Confidence
 | 
			
		||||
type Confidences []Confidence
 | 
			
		||||
 | 
			
		||||
// AppendIfMissing appends confidence to the list if missiong
 | 
			
		||||
// AppendIfMissing appends confidence to the list if missing
 | 
			
		||||
func (cs *Confidences) AppendIfMissing(confidence Confidence) {
 | 
			
		||||
	for _, c := range *cs {
 | 
			
		||||
		if c.DetectionMethod == confidence.DetectionMethod {
 | 
			
		||||
@@ -830,7 +762,7 @@ func (cs Confidences) SortByConfident() Confidences {
 | 
			
		||||
	return cs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Confidence is a ranking how confident the CVE-ID was deteted correctly
 | 
			
		||||
// Confidence is a ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
// Score: 0 - 100
 | 
			
		||||
type Confidence struct {
 | 
			
		||||
	Score           int             `json:"score"`
 | 
			
		||||
@@ -889,36 +821,36 @@ const (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// CpeNameMatch is a ranking how confident the CVE-ID was deteted correctly
 | 
			
		||||
	// CpeNameMatch is a ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	CpeNameMatch = Confidence{100, CpeNameMatchStr, 1}
 | 
			
		||||
 | 
			
		||||
	// YumUpdateSecurityMatch is a ranking how confident the CVE-ID was deteted correctly
 | 
			
		||||
	// YumUpdateSecurityMatch is a ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	YumUpdateSecurityMatch = Confidence{100, YumUpdateSecurityMatchStr, 2}
 | 
			
		||||
 | 
			
		||||
	// PkgAuditMatch is a ranking how confident the CVE-ID was deteted correctly
 | 
			
		||||
	// PkgAuditMatch is a ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	PkgAuditMatch = Confidence{100, PkgAuditMatchStr, 2}
 | 
			
		||||
 | 
			
		||||
	// OvalMatch is a ranking how confident the CVE-ID was deteted correctly
 | 
			
		||||
	// OvalMatch is a ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	OvalMatch = Confidence{100, OvalMatchStr, 0}
 | 
			
		||||
 | 
			
		||||
	// RedHatAPIMatch ranking how confident the CVE-ID was deteted correctly
 | 
			
		||||
	// RedHatAPIMatch ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	RedHatAPIMatch = Confidence{100, RedHatAPIStr, 0}
 | 
			
		||||
 | 
			
		||||
	// DebianSecurityTrackerMatch ranking how confident the CVE-ID was deteted correctly
 | 
			
		||||
	// DebianSecurityTrackerMatch ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	DebianSecurityTrackerMatch = Confidence{100, DebianSecurityTrackerMatchStr, 0}
 | 
			
		||||
 | 
			
		||||
	// TrivyMatch ranking how confident the CVE-ID was deteted correctly
 | 
			
		||||
	// TrivyMatch ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	TrivyMatch = Confidence{100, TrivyMatchStr, 0}
 | 
			
		||||
 | 
			
		||||
	// ChangelogExactMatch is a ranking how confident the CVE-ID was deteted correctly
 | 
			
		||||
	// ChangelogExactMatch is a ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	ChangelogExactMatch = Confidence{95, ChangelogExactMatchStr, 3}
 | 
			
		||||
 | 
			
		||||
	// ChangelogLenientMatch is a ranking how confident the CVE-ID was deteted correctly
 | 
			
		||||
	// ChangelogLenientMatch is a ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	ChangelogLenientMatch = Confidence{50, ChangelogLenientMatchStr, 4}
 | 
			
		||||
 | 
			
		||||
	// GitHubMatch is a ranking how confident the CVE-ID was deteted correctly
 | 
			
		||||
	// GitHubMatch is a ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	GitHubMatch = Confidence{97, GitHubMatchStr, 2}
 | 
			
		||||
 | 
			
		||||
	// WPVulnDBMatch is a ranking how confident the CVE-ID was deteted correctly
 | 
			
		||||
	// WPVulnDBMatch is a ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	WPVulnDBMatch = Confidence{100, WPVulnDBMatchStr, 0}
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -28,10 +28,10 @@ func TestTitles(t *testing.T) {
 | 
			
		||||
							Type:    RedHat,
 | 
			
		||||
							Summary: "Summary RedHat",
 | 
			
		||||
						},
 | 
			
		||||
						NvdXML: {
 | 
			
		||||
							Type:    NvdXML,
 | 
			
		||||
						Nvd: {
 | 
			
		||||
							Type:    Nvd,
 | 
			
		||||
							Summary: "Summary NVD",
 | 
			
		||||
							// Severity is NIOT included in NVD
 | 
			
		||||
							// Severity is NOT included in NVD
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
@@ -42,7 +42,7 @@ func TestTitles(t *testing.T) {
 | 
			
		||||
					Value: "Title1",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Type:  NvdXML,
 | 
			
		||||
					Type:  Nvd,
 | 
			
		||||
					Value: "Summary NVD",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
@@ -65,17 +65,17 @@ func TestTitles(t *testing.T) {
 | 
			
		||||
							Type:    RedHat,
 | 
			
		||||
							Summary: "Summary RedHat",
 | 
			
		||||
						},
 | 
			
		||||
						NvdXML: {
 | 
			
		||||
							Type:    NvdXML,
 | 
			
		||||
						Nvd: {
 | 
			
		||||
							Type:    Nvd,
 | 
			
		||||
							Summary: "Summary NVD",
 | 
			
		||||
							// Severity is NIOT included in NVD
 | 
			
		||||
							// Severity is NOT included in NVD
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: []CveContentStr{
 | 
			
		||||
				{
 | 
			
		||||
					Type:  NvdXML,
 | 
			
		||||
					Type:  Nvd,
 | 
			
		||||
					Value: "Summary NVD",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
@@ -130,10 +130,10 @@ func TestSummaries(t *testing.T) {
 | 
			
		||||
							Type:    RedHat,
 | 
			
		||||
							Summary: "Summary RedHat",
 | 
			
		||||
						},
 | 
			
		||||
						NvdXML: {
 | 
			
		||||
							Type:    NvdXML,
 | 
			
		||||
						Nvd: {
 | 
			
		||||
							Type:    Nvd,
 | 
			
		||||
							Summary: "Summary NVD",
 | 
			
		||||
							// Severity is NIOT included in NVD
 | 
			
		||||
							// Severity is NOT included in NVD
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
@@ -148,7 +148,7 @@ func TestSummaries(t *testing.T) {
 | 
			
		||||
					Value: "Summary RedHat",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Type:  NvdXML,
 | 
			
		||||
					Type:  Nvd,
 | 
			
		||||
					Value: "Summary NVD",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
@@ -168,10 +168,10 @@ func TestSummaries(t *testing.T) {
 | 
			
		||||
							Type:    RedHat,
 | 
			
		||||
							Summary: "Summary RedHat",
 | 
			
		||||
						},
 | 
			
		||||
						NvdXML: {
 | 
			
		||||
							Type:    NvdXML,
 | 
			
		||||
						Nvd: {
 | 
			
		||||
							Type:    Nvd,
 | 
			
		||||
							Summary: "Summary NVD",
 | 
			
		||||
							// Severity is NIOT included in NVD
 | 
			
		||||
							// Severity is NOT included in NVD
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
@@ -182,7 +182,7 @@ func TestSummaries(t *testing.T) {
 | 
			
		||||
					Value: "Summary RedHat",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Type:  NvdXML,
 | 
			
		||||
					Type:  Nvd,
 | 
			
		||||
					Value: "Summary NVD",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
@@ -219,8 +219,8 @@ func TestCountGroupBySeverity(t *testing.T) {
 | 
			
		||||
				"CVE-2017-0002": {
 | 
			
		||||
					CveID: "CVE-2017-0002",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						NvdXML: {
 | 
			
		||||
							Type:       NvdXML,
 | 
			
		||||
						Nvd: {
 | 
			
		||||
							Type:       Nvd,
 | 
			
		||||
							Cvss2Score: 6.0,
 | 
			
		||||
						},
 | 
			
		||||
						RedHat: {
 | 
			
		||||
@@ -232,8 +232,8 @@ func TestCountGroupBySeverity(t *testing.T) {
 | 
			
		||||
				"CVE-2017-0003": {
 | 
			
		||||
					CveID: "CVE-2017-0003",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						NvdXML: {
 | 
			
		||||
							Type:       NvdXML,
 | 
			
		||||
						Nvd: {
 | 
			
		||||
							Type:       Nvd,
 | 
			
		||||
							Cvss2Score: 2.0,
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
@@ -241,8 +241,8 @@ func TestCountGroupBySeverity(t *testing.T) {
 | 
			
		||||
				"CVE-2017-0004": {
 | 
			
		||||
					CveID: "CVE-2017-0004",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						NvdXML: {
 | 
			
		||||
							Type:       NvdXML,
 | 
			
		||||
						Nvd: {
 | 
			
		||||
							Type:       Nvd,
 | 
			
		||||
							Cvss2Score: 5.0,
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
@@ -280,8 +280,8 @@ func TestToSortedSlice(t *testing.T) {
 | 
			
		||||
				"CVE-2017-0002": {
 | 
			
		||||
					CveID: "CVE-2017-0002",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						NvdXML: {
 | 
			
		||||
							Type:       NvdXML,
 | 
			
		||||
						Nvd: {
 | 
			
		||||
							Type:       Nvd,
 | 
			
		||||
							Cvss2Score: 6.0,
 | 
			
		||||
						},
 | 
			
		||||
						RedHat: {
 | 
			
		||||
@@ -293,8 +293,8 @@ func TestToSortedSlice(t *testing.T) {
 | 
			
		||||
				"CVE-2017-0001": {
 | 
			
		||||
					CveID: "CVE-2017-0001",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						NvdXML: {
 | 
			
		||||
							Type:       NvdXML,
 | 
			
		||||
						Nvd: {
 | 
			
		||||
							Type:       Nvd,
 | 
			
		||||
							Cvss2Score: 7.0,
 | 
			
		||||
						},
 | 
			
		||||
						RedHat: {
 | 
			
		||||
@@ -308,8 +308,8 @@ func TestToSortedSlice(t *testing.T) {
 | 
			
		||||
				{
 | 
			
		||||
					CveID: "CVE-2017-0001",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						NvdXML: {
 | 
			
		||||
							Type:       NvdXML,
 | 
			
		||||
						Nvd: {
 | 
			
		||||
							Type:       Nvd,
 | 
			
		||||
							Cvss2Score: 7.0,
 | 
			
		||||
						},
 | 
			
		||||
						RedHat: {
 | 
			
		||||
@@ -321,8 +321,8 @@ func TestToSortedSlice(t *testing.T) {
 | 
			
		||||
				{
 | 
			
		||||
					CveID: "CVE-2017-0002",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						NvdXML: {
 | 
			
		||||
							Type:       NvdXML,
 | 
			
		||||
						Nvd: {
 | 
			
		||||
							Type:       Nvd,
 | 
			
		||||
							Cvss2Score: 6.0,
 | 
			
		||||
						},
 | 
			
		||||
						RedHat: {
 | 
			
		||||
@@ -339,8 +339,8 @@ func TestToSortedSlice(t *testing.T) {
 | 
			
		||||
				"CVE-2017-0002": {
 | 
			
		||||
					CveID: "CVE-2017-0002",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						NvdXML: {
 | 
			
		||||
							Type:       NvdXML,
 | 
			
		||||
						Nvd: {
 | 
			
		||||
							Type:       Nvd,
 | 
			
		||||
							Cvss2Score: 6.0,
 | 
			
		||||
						},
 | 
			
		||||
						RedHat: {
 | 
			
		||||
@@ -372,8 +372,8 @@ func TestToSortedSlice(t *testing.T) {
 | 
			
		||||
				{
 | 
			
		||||
					CveID: "CVE-2017-0002",
 | 
			
		||||
					CveContents: CveContents{
 | 
			
		||||
						NvdXML: {
 | 
			
		||||
							Type:       NvdXML,
 | 
			
		||||
						Nvd: {
 | 
			
		||||
							Type:       Nvd,
 | 
			
		||||
							Cvss2Score: 6.0,
 | 
			
		||||
						},
 | 
			
		||||
						RedHat: {
 | 
			
		||||
@@ -456,8 +456,8 @@ func TestCvss2Scores(t *testing.T) {
 | 
			
		||||
						Cvss2Score:    8.0,
 | 
			
		||||
						Cvss2Vector:   "AV:N/AC:L/Au:N/C:N/I:N/A:P",
 | 
			
		||||
					},
 | 
			
		||||
					NvdXML: {
 | 
			
		||||
						Type:          NvdXML,
 | 
			
		||||
					Nvd: {
 | 
			
		||||
						Type:          Nvd,
 | 
			
		||||
						Cvss2Score:    8.1,
 | 
			
		||||
						Cvss2Vector:   "AV:N/AC:L/Au:N/C:N/I:N/A:P",
 | 
			
		||||
						Cvss2Severity: "HIGH",
 | 
			
		||||
@@ -466,7 +466,7 @@ func TestCvss2Scores(t *testing.T) {
 | 
			
		||||
			},
 | 
			
		||||
			out: []CveContentCvss{
 | 
			
		||||
				{
 | 
			
		||||
					Type: NvdXML,
 | 
			
		||||
					Type: Nvd,
 | 
			
		||||
					Value: Cvss{
 | 
			
		||||
						Type:     CVSS2,
 | 
			
		||||
						Score:    8.1,
 | 
			
		||||
@@ -528,11 +528,11 @@ func TestMaxCvss2Scores(t *testing.T) {
 | 
			
		||||
						Cvss2Score:    8.0,
 | 
			
		||||
						Cvss2Vector:   "AV:N/AC:L/Au:N/C:N/I:N/A:P",
 | 
			
		||||
					},
 | 
			
		||||
					NvdXML: {
 | 
			
		||||
						Type:        NvdXML,
 | 
			
		||||
					Nvd: {
 | 
			
		||||
						Type:        Nvd,
 | 
			
		||||
						Cvss2Score:  8.1,
 | 
			
		||||
						Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
 | 
			
		||||
						// Severity is NIOT included in NVD
 | 
			
		||||
						// Severity is NOT included in NVD
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
@@ -602,8 +602,8 @@ func TestCvss3Scores(t *testing.T) {
 | 
			
		||||
						Cvss3Score:    8.0,
 | 
			
		||||
						Cvss3Vector:   "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
 | 
			
		||||
					},
 | 
			
		||||
					NvdXML: {
 | 
			
		||||
						Type:          NvdXML,
 | 
			
		||||
					Nvd: {
 | 
			
		||||
						Type:          Nvd,
 | 
			
		||||
						Cvss2Score:    8.1,
 | 
			
		||||
						Cvss2Vector:   "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
 | 
			
		||||
						Cvss2Severity: "HIGH",
 | 
			
		||||
@@ -611,6 +611,13 @@ func TestCvss3Scores(t *testing.T) {
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: []CveContentCvss{
 | 
			
		||||
				{
 | 
			
		||||
					Type: Nvd,
 | 
			
		||||
					Value: Cvss{
 | 
			
		||||
						Type:  CVSS3,
 | 
			
		||||
						Score: 0.0,
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Type: RedHat,
 | 
			
		||||
					Value: Cvss{
 | 
			
		||||
@@ -692,8 +699,8 @@ func TestMaxCvssScores(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			in: VulnInfo{
 | 
			
		||||
				CveContents: CveContents{
 | 
			
		||||
					NvdXML: {
 | 
			
		||||
						Type:       NvdXML,
 | 
			
		||||
					Nvd: {
 | 
			
		||||
						Type:       Nvd,
 | 
			
		||||
						Cvss3Score: 7.0,
 | 
			
		||||
					},
 | 
			
		||||
					RedHat: {
 | 
			
		||||
@@ -755,15 +762,15 @@ func TestMaxCvssScores(t *testing.T) {
 | 
			
		||||
						Type:          Ubuntu,
 | 
			
		||||
						Cvss2Severity: "MEDIUM",
 | 
			
		||||
					},
 | 
			
		||||
					NvdXML: {
 | 
			
		||||
						Type:          NvdXML,
 | 
			
		||||
					Nvd: {
 | 
			
		||||
						Type:          Nvd,
 | 
			
		||||
						Cvss2Score:    7.0,
 | 
			
		||||
						Cvss2Severity: "HIGH",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: CveContentCvss{
 | 
			
		||||
				Type: NvdXML,
 | 
			
		||||
				Type: Nvd,
 | 
			
		||||
				Value: Cvss{
 | 
			
		||||
					Type:     CVSS2,
 | 
			
		||||
					Score:    7.0,
 | 
			
		||||
@@ -798,8 +805,8 @@ func TestMaxCvssScores(t *testing.T) {
 | 
			
		||||
						Type:          Ubuntu,
 | 
			
		||||
						Cvss2Severity: "MEDIUM",
 | 
			
		||||
					},
 | 
			
		||||
					NvdXML: {
 | 
			
		||||
						Type:          NvdXML,
 | 
			
		||||
					Nvd: {
 | 
			
		||||
						Type:          Nvd,
 | 
			
		||||
						Cvss2Score:    4.0,
 | 
			
		||||
						Cvss2Severity: "MEDIUM",
 | 
			
		||||
					},
 | 
			
		||||
@@ -811,7 +818,7 @@ func TestMaxCvssScores(t *testing.T) {
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: CveContentCvss{
 | 
			
		||||
				Type: NvdXML,
 | 
			
		||||
				Type: Nvd,
 | 
			
		||||
				Value: Cvss{
 | 
			
		||||
					Type:     CVSS2,
 | 
			
		||||
					Score:    4,
 | 
			
		||||
@@ -857,10 +864,10 @@ func TestFormatMaxCvssScore(t *testing.T) {
 | 
			
		||||
						Cvss2Severity: "HIGH",
 | 
			
		||||
						Cvss3Score:    8.0,
 | 
			
		||||
					},
 | 
			
		||||
					NvdXML: {
 | 
			
		||||
						Type:       NvdXML,
 | 
			
		||||
					Nvd: {
 | 
			
		||||
						Type:       Nvd,
 | 
			
		||||
						Cvss2Score: 8.1,
 | 
			
		||||
						// Severity is NIOT included in NVD
 | 
			
		||||
						// Severity is NOT included in NVD
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
@@ -881,8 +888,8 @@ func TestFormatMaxCvssScore(t *testing.T) {
 | 
			
		||||
						Cvss3Severity: "HIGH",
 | 
			
		||||
						Cvss3Score:    9.9,
 | 
			
		||||
					},
 | 
			
		||||
					NvdXML: {
 | 
			
		||||
						Type:       NvdXML,
 | 
			
		||||
					Nvd: {
 | 
			
		||||
						Type:       Nvd,
 | 
			
		||||
						Cvss2Score: 8.1,
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
@@ -922,7 +929,7 @@ func TestSortPackageStatues(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestStorePackageStatueses(t *testing.T) {
 | 
			
		||||
func TestStorePackageStatuses(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		pkgstats PackageFixStatuses
 | 
			
		||||
		in       PackageFixStatus
 | 
			
		||||
@@ -985,7 +992,7 @@ func TestAppendIfMissing(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestSortByConfiden(t *testing.T) {
 | 
			
		||||
func TestSortByConfident(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in  Confidences
 | 
			
		||||
		out Confidences
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								msf/empty.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1 @@
 | 
			
		||||
package msf
 | 
			
		||||
							
								
								
									
										75
									
								
								msf/msf.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,75 @@
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package msf
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	cnf "github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"github.com/takuzoo3868/go-msfdb/db"
 | 
			
		||||
	metasploitmodels "github.com/takuzoo3868/go-msfdb/models"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// FillWithMetasploit fills metasploit module information that has in module
 | 
			
		||||
func FillWithMetasploit(driver db.DB, r *models.ScanResult) (nMetasploitCve int, err error) {
 | 
			
		||||
	if driver == nil {
 | 
			
		||||
		return 0, nil
 | 
			
		||||
	}
 | 
			
		||||
	for cveID, vuln := range r.ScannedCves {
 | 
			
		||||
		if cveID == "" {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		ms := driver.GetModuleByCveID(cveID)
 | 
			
		||||
		if len(ms) == 0 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		modules := ConvertToModels(ms)
 | 
			
		||||
		vuln.Metasploits = modules
 | 
			
		||||
		r.ScannedCves[cveID] = vuln
 | 
			
		||||
		nMetasploitCve++
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nMetasploitCve, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConvertToModels converts gost model to vuls model
 | 
			
		||||
func ConvertToModels(ms []*metasploitmodels.Metasploit) (modules []models.Metasploit) {
 | 
			
		||||
	for _, m := range ms {
 | 
			
		||||
		var links []string
 | 
			
		||||
		if 0 < len(m.References) {
 | 
			
		||||
			for _, u := range m.References {
 | 
			
		||||
				links = append(links, u.Link)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		module := models.Metasploit{
 | 
			
		||||
			Name:        m.Name,
 | 
			
		||||
			Title:       m.Title,
 | 
			
		||||
			Description: m.Description,
 | 
			
		||||
			URLs:        links,
 | 
			
		||||
		}
 | 
			
		||||
		modules = append(modules, module)
 | 
			
		||||
	}
 | 
			
		||||
	return modules
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckHTTPHealth do health check
 | 
			
		||||
func CheckHTTPHealth() error {
 | 
			
		||||
	if !cnf.Conf.Metasploit.IsFetchViaHTTP() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	url := fmt.Sprintf("%s/health", cnf.Conf.Metasploit.URL)
 | 
			
		||||
	var errs []error
 | 
			
		||||
	var resp *http.Response
 | 
			
		||||
	resp, _, errs = gorequest.New().Get(url).End()
 | 
			
		||||
	//  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 xerrors.Errorf("Failed to connect to metasploit server. url: %s, errs: %w", url, errs)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package oval
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package oval
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
@@ -38,7 +40,13 @@ func (o DebianBase) update(r *models.ScanResult, defPacks defPacks) {
 | 
			
		||||
				defPacks.def.Debian.CveID)
 | 
			
		||||
			cveContents = models.CveContents{}
 | 
			
		||||
		}
 | 
			
		||||
		vinfo.Confidences.AppendIfMissing(models.OvalMatch)
 | 
			
		||||
		if r.Family != config.Raspbian {
 | 
			
		||||
			vinfo.Confidences.AppendIfMissing(models.OvalMatch)
 | 
			
		||||
		} else {
 | 
			
		||||
			if len(vinfo.Confidences) == 0 {
 | 
			
		||||
				vinfo.Confidences.AppendIfMissing(models.OvalMatch)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		cveContents[ctype] = ovalContent
 | 
			
		||||
		vinfo.CveContents = cveContents
 | 
			
		||||
	}
 | 
			
		||||
@@ -53,7 +61,7 @@ func (o DebianBase) update(r *models.ScanResult, defPacks defPacks) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Update package status of source packages.
 | 
			
		||||
	// In the case of Debian based Linux, sometimes source package name is difined as affected package in OVAL.
 | 
			
		||||
	// In the case of Debian based Linux, sometimes source package name is defined as affected package in OVAL.
 | 
			
		||||
	// To display binary package name showed in apt-get, need to convert source name to binary name.
 | 
			
		||||
	for binName := range defPacks.binpkgFixstat {
 | 
			
		||||
		if srcPack, ok := r.SrcPackages.FindByBinName(binName); ok {
 | 
			
		||||
@@ -132,12 +140,28 @@ func (o Debian) FillWithOval(driver db.DB, r *models.ScanResult) (nCVEs int, err
 | 
			
		||||
 | 
			
		||||
	var relatedDefs ovalResult
 | 
			
		||||
	if config.Conf.OvalDict.IsFetchViaHTTP() {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameViaHTTP(r); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		if r.Family != config.Raspbian {
 | 
			
		||||
			if relatedDefs, err = getDefsByPackNameViaHTTP(r); err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			// OVAL does not support Package for Raspbian, so skip it.
 | 
			
		||||
			result := r.RemoveRaspbianPackFromResult()
 | 
			
		||||
			if relatedDefs, err = getDefsByPackNameViaHTTP(&result); err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		if r.Family != config.Raspbian {
 | 
			
		||||
			if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			// OVAL does not support Package for Raspbian, so skip it.
 | 
			
		||||
			result := r.RemoveRaspbianPackFromResult()
 | 
			
		||||
			if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, &result); err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -337,7 +361,7 @@ func (o Ubuntu) fillWithOval(driver db.DB, r *models.ScanResult, kernelNamesInOv
 | 
			
		||||
		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",
 | 
			
		||||
			util.Log.Warnf("Unable to detect vulns of running kernel because the version of the running kernel is unknown. server: %s",
 | 
			
		||||
				r.ServerName)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -365,13 +389,13 @@ func (o Ubuntu) fillWithOval(driver db.DB, r *models.ScanResult, kernelNamesInOv
 | 
			
		||||
		}
 | 
			
		||||
		for srcPackName, srcPack := range r.SrcPackages {
 | 
			
		||||
			copiedSourcePkgs[srcPackName] = srcPack
 | 
			
		||||
			targetBianryNames := []string{}
 | 
			
		||||
			targetBinaryNames := []string{}
 | 
			
		||||
			for _, n := range srcPack.BinaryNames {
 | 
			
		||||
				if n == kernelPkgInOVAL || !strings.HasPrefix(n, "linux-") {
 | 
			
		||||
					targetBianryNames = append(targetBianryNames, n)
 | 
			
		||||
					targetBinaryNames = append(targetBinaryNames, n)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			srcPack.BinaryNames = targetBianryNames
 | 
			
		||||
			srcPack.BinaryNames = targetBinaryNames
 | 
			
		||||
			r.SrcPackages[srcPackName] = srcPack
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package oval
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								oval/empty.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1 @@
 | 
			
		||||
package oval
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package oval
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
@@ -65,7 +67,7 @@ func (b Base) CheckIfOvalFetched(driver db.DB, osFamily, release string) (fetche
 | 
			
		||||
	}
 | 
			
		||||
	count := 0
 | 
			
		||||
	if err := json.Unmarshal([]byte(body), &count); err != nil {
 | 
			
		||||
		return false, xerrors.Errorf("Failed to Unmarshall. body: %s, err: %w", body, err)
 | 
			
		||||
		return false, xerrors.Errorf("Failed to Unmarshal. body: %s, err: %w", body, err)
 | 
			
		||||
	}
 | 
			
		||||
	return 0 < count, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -83,7 +85,7 @@ func (b Base) CheckIfOvalFresh(driver db.DB, osFamily, release string) (ok bool,
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := json.Unmarshal([]byte(body), &lastModified); err != nil {
 | 
			
		||||
			return false, xerrors.Errorf("Failed to Unmarshall. body: %s, err: %w", body, err)
 | 
			
		||||
			return false, xerrors.Errorf("Failed to Unmarshal. body: %s, err: %w", body, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package oval
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
@@ -100,7 +102,7 @@ func (o RedHatBase) update(r *models.ScanResult, defPacks defPacks) (nCVEs int)
 | 
			
		||||
			cveContents := vinfo.CveContents
 | 
			
		||||
			if v, ok := vinfo.CveContents[ctype]; ok {
 | 
			
		||||
				if v.LastModified.After(ovalContent.LastModified) {
 | 
			
		||||
					util.Log.Debugf("%s, OvalID: %d ignroed: ",
 | 
			
		||||
					util.Log.Debugf("%s, OvalID: %d ignored: ",
 | 
			
		||||
						cve.CveID, defPacks.def.ID)
 | 
			
		||||
				} else {
 | 
			
		||||
					util.Log.Debugf("%s OVAL will be overwritten", cve.CveID)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package oval
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package oval
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								oval/util.go
									
									
									
									
									
								
							
							
						
						@@ -1,3 +1,5 @@
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package oval
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
@@ -212,7 +214,7 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er
 | 
			
		||||
 | 
			
		||||
	defs := []ovalmodels.Definition{}
 | 
			
		||||
	if err := json.Unmarshal([]byte(body), &defs); err != nil {
 | 
			
		||||
		errChan <- xerrors.Errorf("Failed to Unmarshall. body: %s, err: %w", body, err)
 | 
			
		||||
		errChan <- xerrors.Errorf("Failed to Unmarshal. body: %s, err: %w", body, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	resChan <- response{
 | 
			
		||||
@@ -276,6 +278,9 @@ func getDefsByPackNameFromOvalDB(driver db.DB, r *models.ScanResult) (relatedDef
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func major(version string) string {
 | 
			
		||||
	if version == "" {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	ss := strings.SplitN(version, ":", 2)
 | 
			
		||||
	ver := ""
 | 
			
		||||
	if len(ss) == 1 {
 | 
			
		||||
@@ -327,13 +332,14 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family string, ru
 | 
			
		||||
				config.Amazon,
 | 
			
		||||
				config.SUSEEnterpriseServer,
 | 
			
		||||
				config.Debian,
 | 
			
		||||
				config.Ubuntu:
 | 
			
		||||
				config.Ubuntu,
 | 
			
		||||
				config.Raspbian:
 | 
			
		||||
				// Use fixed state in OVAL for these distros.
 | 
			
		||||
				return true, false, ovalPack.Version
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// But CentOS can't judge whether fixed or unfixed.
 | 
			
		||||
			// Because fixed state in RHEL's OVAL is different.
 | 
			
		||||
			// Because fixed state in RHEL OVAL is different.
 | 
			
		||||
			// So, it have to be judged version comparison.
 | 
			
		||||
 | 
			
		||||
			// `offline` or `fast` scan mode can't get a updatable version.
 | 
			
		||||
@@ -362,7 +368,8 @@ var esVerPattern = regexp.MustCompile(`\.el(\d+)(?:_\d+)?`)
 | 
			
		||||
func lessThan(family, newVer string, packInOVAL ovalmodels.Package) (bool, error) {
 | 
			
		||||
	switch family {
 | 
			
		||||
	case config.Debian,
 | 
			
		||||
		config.Ubuntu:
 | 
			
		||||
		config.Ubuntu,
 | 
			
		||||
		config.Raspbian:
 | 
			
		||||
		vera, err := debver.NewVersion(newVer)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, err
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package oval
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
@@ -16,7 +18,7 @@ func TestUpsert(t *testing.T) {
 | 
			
		||||
		def      ovalmodels.Definition
 | 
			
		||||
		packName string
 | 
			
		||||
		fixStat  fixStat
 | 
			
		||||
		upserted bool
 | 
			
		||||
		upsert   bool
 | 
			
		||||
		out      ovalResult
 | 
			
		||||
	}{
 | 
			
		||||
		//insert
 | 
			
		||||
@@ -30,7 +32,7 @@ func TestUpsert(t *testing.T) {
 | 
			
		||||
				notFixedYet: true,
 | 
			
		||||
				fixedIn:     "1.0.0",
 | 
			
		||||
			},
 | 
			
		||||
			upserted: false,
 | 
			
		||||
			upsert: false,
 | 
			
		||||
			out: ovalResult{
 | 
			
		||||
				[]defPacks{
 | 
			
		||||
					{
 | 
			
		||||
@@ -83,7 +85,7 @@ func TestUpsert(t *testing.T) {
 | 
			
		||||
				notFixedYet: false,
 | 
			
		||||
				fixedIn:     "3.0.0",
 | 
			
		||||
			},
 | 
			
		||||
			upserted: true,
 | 
			
		||||
			upsert: true,
 | 
			
		||||
			out: ovalResult{
 | 
			
		||||
				[]defPacks{
 | 
			
		||||
					{
 | 
			
		||||
@@ -117,9 +119,9 @@ func TestUpsert(t *testing.T) {
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for i, tt := range tests {
 | 
			
		||||
		upserted := tt.res.upsert(tt.def, tt.packName, tt.fixStat)
 | 
			
		||||
		if tt.upserted != upserted {
 | 
			
		||||
			t.Errorf("[%d]\nexpected: %t\n  actual: %t\n", i, tt.upserted, upserted)
 | 
			
		||||
		upsert := tt.res.upsert(tt.def, tt.packName, tt.fixStat)
 | 
			
		||||
		if tt.upsert != upsert {
 | 
			
		||||
			t.Errorf("[%d]\nexpected: %t\n  actual: %t\n", i, tt.upsert, upsert)
 | 
			
		||||
		}
 | 
			
		||||
		if !reflect.DeepEqual(tt.out, tt.res) {
 | 
			
		||||
			t.Errorf("[%d]\nexpected: %v\n  actual: %v\n", i, tt.out, tt.res)
 | 
			
		||||
@@ -1089,11 +1091,15 @@ func TestIsOvalDefAffected(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestMajor(t *testing.T) {
 | 
			
		||||
func Test_major(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in       string
 | 
			
		||||
		expected string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			in:       "",
 | 
			
		||||
			expected: "",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in:       "4.1",
 | 
			
		||||
			expected: "4",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package report
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package report
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
@@ -9,24 +11,27 @@ import (
 | 
			
		||||
	cvedb "github.com/kotakanbe/go-cve-dictionary/db"
 | 
			
		||||
	ovaldb "github.com/kotakanbe/goval-dictionary/db"
 | 
			
		||||
	exploitdb "github.com/mozqnet/go-exploitdb/db"
 | 
			
		||||
	metasploitdb "github.com/takuzoo3868/go-msfdb/db"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DBClient is a dictionarie's db client for reporting
 | 
			
		||||
// DBClient is DB client for reporting
 | 
			
		||||
type DBClient struct {
 | 
			
		||||
	CveDB     cvedb.DB
 | 
			
		||||
	OvalDB    ovaldb.DB
 | 
			
		||||
	GostDB    gostdb.DB
 | 
			
		||||
	ExploitDB exploitdb.DB
 | 
			
		||||
	CveDB        cvedb.DB
 | 
			
		||||
	OvalDB       ovaldb.DB
 | 
			
		||||
	GostDB       gostdb.DB
 | 
			
		||||
	ExploitDB    exploitdb.DB
 | 
			
		||||
	MetasploitDB metasploitdb.DB
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DBClientConf has a configuration of Vulnerability DBs
 | 
			
		||||
type DBClientConf struct {
 | 
			
		||||
	CveDictCnf  config.GoCveDictConf
 | 
			
		||||
	OvalDictCnf config.GovalDictConf
 | 
			
		||||
	GostCnf     config.GostConf
 | 
			
		||||
	ExploitCnf  config.ExploitConf
 | 
			
		||||
	DebugSQL    bool
 | 
			
		||||
	CveDictCnf    config.GoCveDictConf
 | 
			
		||||
	OvalDictCnf   config.GovalDictConf
 | 
			
		||||
	GostCnf       config.GostConf
 | 
			
		||||
	ExploitCnf    config.ExploitConf
 | 
			
		||||
	MetasploitCnf config.MetasploitConf
 | 
			
		||||
	DebugSQL      bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewDBClient returns db clients
 | 
			
		||||
@@ -66,11 +71,21 @@ func NewDBClient(cnf DBClientConf) (dbclient *DBClient, locked bool, err error)
 | 
			
		||||
			cnf.ExploitCnf.SQLite3Path, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	metasploitdb, locked, err := NewMetasploitDB(cnf)
 | 
			
		||||
	if locked {
 | 
			
		||||
		return nil, true, xerrors.Errorf("metasploitDB is locked: %s",
 | 
			
		||||
			cnf.MetasploitCnf.SQLite3Path)
 | 
			
		||||
	} else if err != nil {
 | 
			
		||||
		util.Log.Warnf("Unable to use metasploitDB: %s, err: %s",
 | 
			
		||||
			cnf.MetasploitCnf.SQLite3Path, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &DBClient{
 | 
			
		||||
		CveDB:     cveDriver,
 | 
			
		||||
		OvalDB:    ovaldb,
 | 
			
		||||
		GostDB:    gostdb,
 | 
			
		||||
		ExploitDB: exploitdb,
 | 
			
		||||
		CveDB:        cveDriver,
 | 
			
		||||
		OvalDB:       ovaldb,
 | 
			
		||||
		GostDB:       gostdb,
 | 
			
		||||
		ExploitDB:    exploitdb,
 | 
			
		||||
		MetasploitDB: metasploitdb,
 | 
			
		||||
	}, false, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -177,6 +192,32 @@ func NewExploitDB(cnf DBClientConf) (driver exploitdb.DB, locked bool, err error
 | 
			
		||||
	return driver, false, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewMetasploitDB returns db client for Metasploit
 | 
			
		||||
func NewMetasploitDB(cnf DBClientConf) (driver metasploitdb.DB, locked bool, err error) {
 | 
			
		||||
	if config.Conf.Metasploit.IsFetchViaHTTP() {
 | 
			
		||||
		return nil, false, nil
 | 
			
		||||
	}
 | 
			
		||||
	path := cnf.MetasploitCnf.URL
 | 
			
		||||
	if cnf.MetasploitCnf.Type == "sqlite3" {
 | 
			
		||||
		path = cnf.MetasploitCnf.SQLite3Path
 | 
			
		||||
 | 
			
		||||
		if _, err := os.Stat(path); os.IsNotExist(err) {
 | 
			
		||||
			util.Log.Warnf("--msfdb-path=%s file not found. Fetch go-msfdb before reporting if you want to display metasploit modules of detected CVE-IDs. For details, see `https://github.com/takuzoo3868/go-msfdb`", path)
 | 
			
		||||
			return nil, false, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	util.Log.Debugf("Open metasploit db (%s): %s", cnf.MetasploitCnf.Type, path)
 | 
			
		||||
	if driver, locked, err = metasploitdb.NewDB(cnf.MetasploitCnf.Type, path, cnf.DebugSQL, false); err != nil {
 | 
			
		||||
		if locked {
 | 
			
		||||
			util.Log.Errorf("metasploitDB is locked. err: %+v", err)
 | 
			
		||||
			return nil, true, err
 | 
			
		||||
		}
 | 
			
		||||
		return nil, false, err
 | 
			
		||||
	}
 | 
			
		||||
	return driver, false, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CloseDB close dbs
 | 
			
		||||
func (d DBClient) CloseDB() {
 | 
			
		||||
	if d.CveDB != nil {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										112
									
								
								report/email.go
									
									
									
									
									
								
							
							
						
						@@ -1,13 +1,15 @@
 | 
			
		||||
package report
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/mail"
 | 
			
		||||
	"net/smtp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	sasl "github.com/emersion/go-sasl"
 | 
			
		||||
	smtp "github.com/emersion/go-smtp"
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
@@ -20,7 +22,6 @@ func (w EMailWriter) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
	conf := config.Conf
 | 
			
		||||
	var message string
 | 
			
		||||
	sender := NewEMailSender()
 | 
			
		||||
 | 
			
		||||
	m := map[string]int{}
 | 
			
		||||
	for _, r := range rs {
 | 
			
		||||
		if conf.FormatOneEMail {
 | 
			
		||||
@@ -84,7 +85,75 @@ type EMailSender interface {
 | 
			
		||||
 | 
			
		||||
type emailSender struct {
 | 
			
		||||
	conf config.SMTPConf
 | 
			
		||||
	send func(string, smtp.Auth, string, []string, []byte) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *emailSender) sendMail(smtpServerAddr, message string) (err error) {
 | 
			
		||||
	var c *smtp.Client
 | 
			
		||||
	var auth sasl.Client
 | 
			
		||||
	emailConf := e.conf
 | 
			
		||||
	//TLS Config
 | 
			
		||||
	tlsConfig := &tls.Config{
 | 
			
		||||
		ServerName: emailConf.SMTPAddr,
 | 
			
		||||
	}
 | 
			
		||||
	switch emailConf.SMTPPort {
 | 
			
		||||
	case "465":
 | 
			
		||||
		//New TLS connection
 | 
			
		||||
		c, err = smtp.DialTLS(smtpServerAddr, tlsConfig)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to create TLS connection to SMTP server: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		c, err = smtp.Dial(smtpServerAddr)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to create connection to SMTP server: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	defer c.Close()
 | 
			
		||||
 | 
			
		||||
	if err = c.Hello("localhost"); err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to send Hello command: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ok, _ := c.Extension("STARTTLS"); ok {
 | 
			
		||||
		if err := c.StartTLS(tlsConfig); err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to STARTTLS: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ok, param := c.Extension("AUTH"); ok {
 | 
			
		||||
		authList := strings.Split(param, " ")
 | 
			
		||||
		auth = e.newSaslClient(authList)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = c.Auth(auth); err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to authenticate: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	if err = c.Mail(emailConf.From, nil); err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to send Mail command: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	for _, to := range emailConf.To {
 | 
			
		||||
		if err = c.Rcpt(to); err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to send Rcpt command: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w, err := c.Data()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to send Data command: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	_, err = w.Write([]byte(message))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to write EMail message: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	err = w.Close()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to close Writer: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	err = c.Quit()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to close connection: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *emailSender) Send(subject, body string) (err error) {
 | 
			
		||||
@@ -113,30 +182,13 @@ func (e *emailSender) Send(subject, body string) (err error) {
 | 
			
		||||
	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),
 | 
			
		||||
		)
 | 
			
		||||
		err = e.sendMail(smtpServer, message)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to send emails: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	err = e.send(
 | 
			
		||||
		smtpServer,
 | 
			
		||||
		nil,
 | 
			
		||||
		emailConf.From,
 | 
			
		||||
		mailAddresses,
 | 
			
		||||
		[]byte(message),
 | 
			
		||||
	)
 | 
			
		||||
	err = e.sendMail(smtpServer, message)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to send emails: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
@@ -145,5 +197,19 @@ func (e *emailSender) Send(subject, body string) (err error) {
 | 
			
		||||
 | 
			
		||||
// NewEMailSender creates emailSender
 | 
			
		||||
func NewEMailSender() EMailSender {
 | 
			
		||||
	return &emailSender{config.Conf.EMail, smtp.SendMail}
 | 
			
		||||
	return &emailSender{config.Conf.EMail}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *emailSender) newSaslClient(authList []string) sasl.Client {
 | 
			
		||||
	for _, v := range authList {
 | 
			
		||||
		switch v {
 | 
			
		||||
		case "PLAIN":
 | 
			
		||||
			auth := sasl.NewPlainClient("", e.conf.User, e.conf.Password)
 | 
			
		||||
			return auth
 | 
			
		||||
		case "LOGIN":
 | 
			
		||||
			auth := sasl.NewLoginClient(e.conf.User, e.conf.Password)
 | 
			
		||||
			return auth
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,115 +0,0 @@
 | 
			
		||||
package report
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/smtp"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type emailRecorder struct {
 | 
			
		||||
	addr string
 | 
			
		||||
	auth smtp.Auth
 | 
			
		||||
	from string
 | 
			
		||||
	to   []string
 | 
			
		||||
	body string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type mailTest struct {
 | 
			
		||||
	in  config.SMTPConf
 | 
			
		||||
	out emailRecorder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var mailTests = []mailTest{
 | 
			
		||||
	{
 | 
			
		||||
		config.SMTPConf{
 | 
			
		||||
			SMTPAddr: "127.0.0.1",
 | 
			
		||||
			SMTPPort: "25",
 | 
			
		||||
 | 
			
		||||
			From: "from@address.com",
 | 
			
		||||
			To:   []string{"to@address.com"},
 | 
			
		||||
			Cc:   []string{"cc@address.com"},
 | 
			
		||||
		},
 | 
			
		||||
		emailRecorder{
 | 
			
		||||
			addr: "127.0.0.1:25",
 | 
			
		||||
			auth: smtp.PlainAuth("", "", "", "127.0.0.1"),
 | 
			
		||||
			from: "from@address.com",
 | 
			
		||||
			to:   []string{"to@address.com", "cc@address.com"},
 | 
			
		||||
			body: "body",
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		config.SMTPConf{
 | 
			
		||||
			SMTPAddr: "127.0.0.1",
 | 
			
		||||
			SMTPPort: "25",
 | 
			
		||||
 | 
			
		||||
			User:     "vuls",
 | 
			
		||||
			Password: "password",
 | 
			
		||||
 | 
			
		||||
			From: "from@address.com",
 | 
			
		||||
			To:   []string{"to1@address.com", "to2@address.com"},
 | 
			
		||||
			Cc:   []string{"cc1@address.com", "cc2@address.com"},
 | 
			
		||||
		},
 | 
			
		||||
		emailRecorder{
 | 
			
		||||
			addr: "127.0.0.1:25",
 | 
			
		||||
			auth: smtp.PlainAuth(
 | 
			
		||||
				"",
 | 
			
		||||
				"vuls",
 | 
			
		||||
				"password",
 | 
			
		||||
				"127.0.0.1",
 | 
			
		||||
			),
 | 
			
		||||
			from: "from@address.com",
 | 
			
		||||
			to: []string{"to1@address.com", "to2@address.com",
 | 
			
		||||
				"cc1@address.com", "cc2@address.com"},
 | 
			
		||||
			body: "body",
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestSend(t *testing.T) {
 | 
			
		||||
	for i, test := range mailTests {
 | 
			
		||||
		f, r := mockSend(nil)
 | 
			
		||||
		sender := &emailSender{conf: test.in, send: f}
 | 
			
		||||
 | 
			
		||||
		subject := "subject"
 | 
			
		||||
		body := "body"
 | 
			
		||||
		if err := sender.Send(subject, body); err != nil {
 | 
			
		||||
			t.Errorf("unexpected error: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if r.addr != test.out.addr {
 | 
			
		||||
			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) && r.auth != nil {
 | 
			
		||||
			t.Errorf("#%d: wrong 'auth' field.\r\nexpected: %v\n got: %v", i, test.out.auth, r.auth)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if r.from != test.out.from {
 | 
			
		||||
			t.Errorf("#%d: wrong 'from' field.\r\nexpected: %v\n got: %v", i, test.out.from, r.from)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !reflect.DeepEqual(r.to, test.out.to) {
 | 
			
		||||
			t.Errorf("#%d: wrong 'to' field.\r\nexpected: %v\n got: %v", i, test.out.to, r.to)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if r.body != test.out.body {
 | 
			
		||||
			t.Errorf("#%d: wrong 'body' field.\r\nexpected: %v\n got: %v", i, test.out.body, r.body)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func mockSend(errToReturn error) (func(string, smtp.Auth, string, []string, []byte) error, *emailRecorder) {
 | 
			
		||||
	r := new(emailRecorder)
 | 
			
		||||
	return func(addr string, a smtp.Auth, from string, to []string, msg []byte) error {
 | 
			
		||||
		// Split into header and body
 | 
			
		||||
		messages := strings.Split(string(msg), "\r\n\r\n")
 | 
			
		||||
		body := messages[1]
 | 
			
		||||
		*r = emailRecorder{addr, a, from, to, body}
 | 
			
		||||
		return errToReturn
 | 
			
		||||
	}, r
 | 
			
		||||
}
 | 
			
		||||
@@ -1,74 +0,0 @@
 | 
			
		||||
package report
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// HipChatWriter send report to HipChat
 | 
			
		||||
type HipChatWriter struct{}
 | 
			
		||||
 | 
			
		||||
func (w HipChatWriter) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
	conf := config.Conf.HipChat
 | 
			
		||||
 | 
			
		||||
	for _, r := range rs {
 | 
			
		||||
		serverInfo := fmt.Sprintf("%s", r.ServerInfo())
 | 
			
		||||
		if err = postMessage(conf.Room, conf.AuthToken, serverInfo); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, vinfo := range r.ScannedCves {
 | 
			
		||||
			maxCvss := vinfo.MaxCvssScore()
 | 
			
		||||
			severity := strings.ToUpper(maxCvss.Value.Severity)
 | 
			
		||||
			if severity == "" {
 | 
			
		||||
				severity = "?"
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			message := fmt.Sprintf(`<a href="https://nvd.nist.gov/vuln/detail\%s"> %s </a> <br/>%s (%s)<br/>%s`,
 | 
			
		||||
				vinfo.CveID,
 | 
			
		||||
				vinfo.CveID,
 | 
			
		||||
				strconv.FormatFloat(maxCvss.Value.Score, 'f', 1, 64),
 | 
			
		||||
				severity,
 | 
			
		||||
				vinfo.Summaries(config.Conf.Lang, r.Family)[0].Value,
 | 
			
		||||
			)
 | 
			
		||||
 | 
			
		||||
			if err = postMessage(conf.Room, conf.AuthToken, message); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func postMessage(room, token, message string) error {
 | 
			
		||||
	uri := fmt.Sprintf("https://api.hipchat.com/v2/room/%s/notification?auth_token=%s", room, token)
 | 
			
		||||
 | 
			
		||||
	payload := url.Values{
 | 
			
		||||
		"color":          {"purple"},
 | 
			
		||||
		"message_format": {"html"},
 | 
			
		||||
		"message":        {message},
 | 
			
		||||
	}
 | 
			
		||||
	reqs, err := http.NewRequest("POST", uri, strings.NewReader(payload.Encode()))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	reqs.Header.Add("Content-Type", "application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
	client := &http.Client{}
 | 
			
		||||
 | 
			
		||||
	resp, err := client.Do(reqs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@@ -37,7 +37,7 @@ type HTTPResponseWriter struct {
 | 
			
		||||
func (w HTTPResponseWriter) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
	res, err := json.Marshal(rs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to marshal scah results: %w", err)
 | 
			
		||||
		return xerrors.Errorf("Failed to marshal scan results: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	w.Writer.Header().Set("Content-Type", "application/json")
 | 
			
		||||
	_, err = w.Writer.Write(res)
 | 
			
		||||
 
 | 
			
		||||
@@ -41,14 +41,8 @@ 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 xerrors.Errorf("Failed to Marshal to JSON: %w", err)
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				if b, err = json.Marshal(r); err != nil {
 | 
			
		||||
					return xerrors.Errorf("Failed to Marshal to JSON: %w", err)
 | 
			
		||||
				}
 | 
			
		||||
			if b, err = json.MarshalIndent(r, "", "    "); err != nil {
 | 
			
		||||
				return xerrors.Errorf("Failed to Marshal to JSON: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			if err := writeFile(p, b, 0600); err != nil {
 | 
			
		||||
				return xerrors.Errorf("Failed to write JSON. path: %s, err: %w", p, err)
 | 
			
		||||
@@ -102,6 +96,17 @@ func (w LocalFileWriter) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
				return xerrors.Errorf("Failed to write XML. path: %s, err: %w", p, err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if c.Conf.FormatCsvList {
 | 
			
		||||
			p := path + "_short.csv"
 | 
			
		||||
			if c.Conf.Diff {
 | 
			
		||||
				p = path + "_short_diff.csv"
 | 
			
		||||
			}
 | 
			
		||||
			if err := formatCsvList(r, p); err != nil {
 | 
			
		||||
				return xerrors.Errorf("Failed to write CSV: %s, %w", p, err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										664
									
								
								report/report.go
									
									
									
									
									
								
							
							
						
						@@ -1,19 +1,15 @@
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package report
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/libmanager"
 | 
			
		||||
	gostdb "github.com/knqyf263/gost/db"
 | 
			
		||||
 | 
			
		||||
	"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"
 | 
			
		||||
@@ -22,117 +18,128 @@ import (
 | 
			
		||||
	"github.com/future-architect/vuls/github"
 | 
			
		||||
	"github.com/future-architect/vuls/gost"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/msf"
 | 
			
		||||
	"github.com/future-architect/vuls/oval"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/future-architect/vuls/wordpress"
 | 
			
		||||
	"github.com/hashicorp/go-uuid"
 | 
			
		||||
	gostdb "github.com/knqyf263/gost/db"
 | 
			
		||||
	cvedb "github.com/kotakanbe/go-cve-dictionary/db"
 | 
			
		||||
	cvemodels "github.com/kotakanbe/go-cve-dictionary/models"
 | 
			
		||||
	ovaldb "github.com/kotakanbe/goval-dictionary/db"
 | 
			
		||||
	exploitdb "github.com/mozqnet/go-exploitdb/db"
 | 
			
		||||
	metasploitdb "github.com/takuzoo3868/go-msfdb/db"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	vulsOpenTag  = "<vulsreport>"
 | 
			
		||||
	vulsCloseTag = "</vulsreport>"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// FillCveInfos fills CVE Detailed Information
 | 
			
		||||
func FillCveInfos(dbclient DBClient, rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
 | 
			
		||||
	var filledResults []models.ScanResult
 | 
			
		||||
 | 
			
		||||
	// Use the same reportedAt for all rs
 | 
			
		||||
	reportedAt := time.Now()
 | 
			
		||||
	hostname, _ := os.Hostname()
 | 
			
		||||
	for _, r := range rs {
 | 
			
		||||
		if c.Conf.RefreshCve || needToRefreshCve(r) {
 | 
			
		||||
			if ovalSupported(&r) {
 | 
			
		||||
				r.ScannedCves = models.VulnInfos{}
 | 
			
		||||
			}
 | 
			
		||||
			cpeURIs := []string{}
 | 
			
		||||
	for i, r := range rs {
 | 
			
		||||
		if !c.Conf.RefreshCve && !needToRefreshCve(r) {
 | 
			
		||||
			util.Log.Info("No need to refresh")
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
			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, 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
 | 
			
		||||
						owaspDCXMLPath := con.OwaspDCXMLPath
 | 
			
		||||
						if owaspDCXMLPath != "" {
 | 
			
		||||
							cpes, err := parser.Parse(owaspDCXMLPath)
 | 
			
		||||
							if err != nil {
 | 
			
		||||
								return nil, xerrors.Errorf("Failed to read OWASP Dependency Check XML on %s, `%s`, err: %w",
 | 
			
		||||
									r.ServerInfo(), owaspDCXMLPath, err)
 | 
			
		||||
							}
 | 
			
		||||
							cpeURIs = append(cpeURIs, cpes...)
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
		if !reuseScannedCves(&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, xerrors.Errorf("Failed to read OWASP Dependency Check XML on %s, `%s`, err: %w",
 | 
			
		||||
						r.ServerName, owaspDCXMLPath, err)
 | 
			
		||||
				}
 | 
			
		||||
				cpeURIs = append(cpeURIs, cpes...)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Integrations
 | 
			
		||||
			githubInts := GithubSecurityAlerts(c.Conf.Servers[r.ServerName].GitHubRepos)
 | 
			
		||||
 | 
			
		||||
			wpOpt := WordPressOption{c.Conf.Servers[r.ServerName].WordPress.WPVulnDBToken}
 | 
			
		||||
 | 
			
		||||
			if err := FillCveInfo(dbclient,
 | 
			
		||||
				&r,
 | 
			
		||||
				cpeURIs,
 | 
			
		||||
				true,
 | 
			
		||||
				githubInts,
 | 
			
		||||
				wpOpt); err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			r.Lang = c.Conf.Lang
 | 
			
		||||
			r.ReportedAt = reportedAt
 | 
			
		||||
			r.ReportedVersion = c.Version
 | 
			
		||||
			r.ReportedRevision = c.Revision
 | 
			
		||||
			r.ReportedBy = hostname
 | 
			
		||||
			r.Config.Report = c.Conf
 | 
			
		||||
			r.Config.Report.Servers = map[string]c.ServerInfo{
 | 
			
		||||
				r.ServerName: c.Conf.Servers[r.ServerName],
 | 
			
		||||
			}
 | 
			
		||||
			if err := overwriteJSONFile(dir, r); err != nil {
 | 
			
		||||
				return nil, xerrors.Errorf("Failed to write JSON: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			filledResults = append(filledResults, r)
 | 
			
		||||
		} else {
 | 
			
		||||
			util.Log.Debugf("No need to refresh")
 | 
			
		||||
			filledResults = append(filledResults, r)
 | 
			
		||||
			// runningContainer
 | 
			
		||||
			if s, ok := c.Conf.Servers[r.ServerName]; ok {
 | 
			
		||||
				if con, ok := s.Containers[r.Container.Name]; ok {
 | 
			
		||||
					cpeURIs = con.Cpes
 | 
			
		||||
					owaspDCXMLPath := con.OwaspDCXMLPath
 | 
			
		||||
					if owaspDCXMLPath != "" {
 | 
			
		||||
						cpes, err := parser.Parse(owaspDCXMLPath)
 | 
			
		||||
						if err != nil {
 | 
			
		||||
							return nil, xerrors.Errorf("Failed to read OWASP Dependency Check XML on %s, `%s`, err: %w",
 | 
			
		||||
								r.ServerInfo(), owaspDCXMLPath, err)
 | 
			
		||||
						}
 | 
			
		||||
						cpeURIs = append(cpeURIs, cpes...)
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := libmanager.DetectLibsCves(&r); err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to fill with Library dependency: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := DetectPkgCves(dbclient, &r); err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to detect Pkg CVE: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := DetectCpeURIsCves(dbclient.CveDB, &r, cpeURIs); err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to detect CVE of `%s`: %w", cpeURIs, err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := DetectGitHubCves(&r); err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to detect GitHub Cves: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := DetectWordPressCves(&r); err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to detect WordPress Cves: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := FillCveInfo(dbclient, &r); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		r.ReportedBy, _ = os.Hostname()
 | 
			
		||||
		r.Lang = c.Conf.Lang
 | 
			
		||||
		r.ReportedAt = reportedAt
 | 
			
		||||
		r.ReportedVersion = c.Version
 | 
			
		||||
		r.ReportedRevision = c.Revision
 | 
			
		||||
		r.Config.Report = c.Conf
 | 
			
		||||
		r.Config.Report.Servers = map[string]c.ServerInfo{
 | 
			
		||||
			r.ServerName: c.Conf.Servers[r.ServerName],
 | 
			
		||||
		}
 | 
			
		||||
		rs[i] = r
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Overwrite the json file every time to clear the fields specified in config.IgnoredJSONKeys
 | 
			
		||||
	for _, r := range rs {
 | 
			
		||||
		if s, ok := c.Conf.Servers[r.ServerName]; ok {
 | 
			
		||||
			r = r.ClearFields(s.IgnoredJSONKeys)
 | 
			
		||||
		}
 | 
			
		||||
		if err := overwriteJSONFile(dir, r); err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to write JSON: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.Diff {
 | 
			
		||||
		prevs, err := loadPrevious(filledResults)
 | 
			
		||||
		prevs, err := loadPrevious(rs)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		diff, err := diff(filledResults, prevs)
 | 
			
		||||
		diff, err := diff(rs, prevs)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		filledResults = []models.ScanResult{}
 | 
			
		||||
		for _, r := range diff {
 | 
			
		||||
			if err := fillCveDetail(dbclient.CveDB, &r); err != nil {
 | 
			
		||||
		for i, r := range diff {
 | 
			
		||||
			if err := fillCvesWithNvdJvn(dbclient.CveDB, &r); err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			filledResults = append(filledResults, r)
 | 
			
		||||
			rs[i] = r
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	filtered := []models.ScanResult{}
 | 
			
		||||
	for _, r := range filledResults {
 | 
			
		||||
	for i, r := range rs {
 | 
			
		||||
		r = r.FilterByCvssOver(c.Conf.CvssScoreOver)
 | 
			
		||||
		r = r.FilterIgnoreCves()
 | 
			
		||||
		r = r.FilterUnfixed()
 | 
			
		||||
@@ -141,28 +148,31 @@ func FillCveInfos(dbclient DBClient, rs []models.ScanResult, dir string) ([]mode
 | 
			
		||||
		if c.Conf.IgnoreUnscoredCves {
 | 
			
		||||
			r.ScannedCves = r.ScannedCves.FindScoredVulns()
 | 
			
		||||
		}
 | 
			
		||||
		filtered = append(filtered, r)
 | 
			
		||||
		rs[i] = r
 | 
			
		||||
	}
 | 
			
		||||
	return filtered, nil
 | 
			
		||||
	return rs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillCveInfo fill scanResult with cve info.
 | 
			
		||||
func FillCveInfo(dbclient DBClient, r *models.ScanResult, cpeURIs []string, ignoreWillNotFix bool, integrations ...Integration) error {
 | 
			
		||||
	util.Log.Debugf("need to refresh")
 | 
			
		||||
// DetectPkgCves detects OS pkg cves
 | 
			
		||||
func DetectPkgCves(dbclient DBClient, r *models.ScanResult) error {
 | 
			
		||||
	// Pkg Scan
 | 
			
		||||
	if r.Release != "" {
 | 
			
		||||
		// OVAL
 | 
			
		||||
		if err := detectPkgsCvesWithOval(dbclient.OvalDB, r); err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to detect CVE with OVAL: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	nCVEs, err := libmanager.FillLibrary(r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to fill with Library dependency: %w", err)
 | 
			
		||||
		// gost
 | 
			
		||||
		if err := detectPkgsCvesWithGost(dbclient.GostDB, r); err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to detect CVE with gost: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	} else if reuseScannedCves(r) {
 | 
			
		||||
		util.Log.Infof("r.Release is empty. Use CVEs as it as.")
 | 
			
		||||
	} else if r.Family == config.ServerTypePseudo {
 | 
			
		||||
		util.Log.Infof("pseudo type. Skip OVAL and gost detection")
 | 
			
		||||
	} else {
 | 
			
		||||
		return xerrors.Errorf("Failed to fill CVEs. r.Release is empty")
 | 
			
		||||
	}
 | 
			
		||||
	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)
 | 
			
		||||
 | 
			
		||||
	for i, v := range r.ScannedCves {
 | 
			
		||||
		for j, p := range v.AffectedPackages {
 | 
			
		||||
@@ -173,46 +183,105 @@ func FillCveInfo(dbclient DBClient, r *models.ScanResult, cpeURIs []string, igno
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	nCVEs, err = fillVulnByCpeURIs(dbclient.CveDB, r, cpeURIs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		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)
 | 
			
		||||
	// To keep backward compatibility
 | 
			
		||||
	// Newer versions use ListenPortStats,
 | 
			
		||||
	// but older versions of Vuls are set to ListenPorts.
 | 
			
		||||
	// Set ListenPorts to ListenPortStats to allow newer Vuls to report old results.
 | 
			
		||||
	for i, pkg := range r.Packages {
 | 
			
		||||
		for j, proc := range pkg.AffectedProcs {
 | 
			
		||||
			for _, ipPort := range proc.ListenPorts {
 | 
			
		||||
				ps, err := models.NewPortStat(ipPort)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					util.Log.Warnf("Failed to parse ip:port: %s, err:%+v", ipPort, err)
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				r.Packages[i].AffectedProcs[j].ListenPortStats = append(
 | 
			
		||||
					r.Packages[i].AffectedProcs[j].ListenPortStats, *ps)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	util.Log.Infof("%s: %d CVEs are detected with GitHub Security Alerts", r.FormatServerName(), ints.GithubAlertsCveCounts)
 | 
			
		||||
 | 
			
		||||
	nCVEs, err = FillWithGost(dbclient.GostDB, r, ignoreWillNotFix)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DetectGitHubCves fetches CVEs from GitHub Security Alerts
 | 
			
		||||
func DetectGitHubCves(r *models.ScanResult) error {
 | 
			
		||||
	repos := c.Conf.Servers[r.ServerName].GitHubRepos
 | 
			
		||||
	if len(repos) == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	githubInts := GithubSecurityAlerts(repos)
 | 
			
		||||
 | 
			
		||||
	ints := &integrationResults{}
 | 
			
		||||
	for _, o := range []Integration{githubInts} {
 | 
			
		||||
		if err := o.apply(r, ints); err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to detect CVE with integration: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	util.Log.Infof("%s: %d CVEs are detected with GitHub Security Alerts",
 | 
			
		||||
		r.FormatServerName(), ints.GithubAlertsCveCounts)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DetectWordPressCves detects CVEs of WordPress
 | 
			
		||||
func DetectWordPressCves(r *models.ScanResult) error {
 | 
			
		||||
	token := c.Conf.Servers[r.ServerName].WordPress.WPVulnDBToken
 | 
			
		||||
	if token == "" {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	wpVulnCaches := map[string]string{}
 | 
			
		||||
	wpOpt := WordPressOption{
 | 
			
		||||
		token,
 | 
			
		||||
		&wpVulnCaches,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ints := &integrationResults{}
 | 
			
		||||
	for _, o := range []Integration{wpOpt} {
 | 
			
		||||
		if err := o.apply(r, ints); err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to detect CVE with integration: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	util.Log.Infof("%s: %d CVEs are detected with wpscan API",
 | 
			
		||||
		r.FormatServerName(), ints.WordPressCveCounts)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillCveInfo fill scanResult with cve info.
 | 
			
		||||
func FillCveInfo(dbclient DBClient, r *models.ScanResult) error {
 | 
			
		||||
	util.Log.Infof("Fill CVE detailed with gost")
 | 
			
		||||
	if err := gost.NewClient(r.Family).FillCVEsWithRedHat(dbclient.GostDB, r); err != nil {
 | 
			
		||||
		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 {
 | 
			
		||||
	util.Log.Infof("Fill CVE detailed with CVE-DB")
 | 
			
		||||
	if err := fillCvesWithNvdJvn(dbclient.CveDB, r); err != nil {
 | 
			
		||||
		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)
 | 
			
		||||
	util.Log.Infof("Fill exploit with Exploit-DB")
 | 
			
		||||
	nExploitCve, err := fillWithExploitDB(dbclient.ExploitDB, r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to fill with exploit: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	util.Log.Infof("%s: %d exploits are detected",
 | 
			
		||||
		r.FormatServerName(), nExploitCve)
 | 
			
		||||
 | 
			
		||||
	util.Log.Infof("Fill metasploit module with Metasploit-DB")
 | 
			
		||||
	nMetasploitCve, err := fillWithMetasploit(dbclient.MetasploitDB, r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to fill with metasploit: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	util.Log.Infof("%s: %d modules are detected",
 | 
			
		||||
		r.FormatServerName(), nMetasploitCve)
 | 
			
		||||
 | 
			
		||||
	util.Log.Infof("Fill CWE with NVD")
 | 
			
		||||
	fillCweDict(r)
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// fillCveDetail fetches NVD, JVN from CVE Database
 | 
			
		||||
func fillCveDetail(driver cvedb.DB, r *models.ScanResult) error {
 | 
			
		||||
// fillCvesWithNvdJvn fills CVE detail with NVD, JVN
 | 
			
		||||
func fillCvesWithNvdJvn(driver cvedb.DB, r *models.ScanResult) error {
 | 
			
		||||
	cveIDs := []string{}
 | 
			
		||||
	for _, v := range r.ScannedCves {
 | 
			
		||||
		cveIDs = append(cveIDs, v.CveID)
 | 
			
		||||
@@ -223,7 +292,7 @@ func fillCveDetail(driver cvedb.DB, r *models.ScanResult) error {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	for _, d := range ds {
 | 
			
		||||
		nvd := models.ConvertNvdJSONToModel(d.CveID, d.NvdJSON)
 | 
			
		||||
		nvd, exploits, mitigations := models.ConvertNvdJSONToModel(d.CveID, d.NvdJSON)
 | 
			
		||||
		jvn := models.ConvertJvnToModel(d.CveID, d.Jvn)
 | 
			
		||||
 | 
			
		||||
		alerts := fillCertAlerts(&d)
 | 
			
		||||
@@ -238,6 +307,8 @@ func fillCveDetail(driver cvedb.DB, r *models.ScanResult) error {
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				vinfo.AlertDict = alerts
 | 
			
		||||
				vinfo.Exploits = append(vinfo.Exploits, exploits...)
 | 
			
		||||
				vinfo.Mitigations = append(vinfo.Mitigations, mitigations...)
 | 
			
		||||
				r.ScannedCves[cveID] = vinfo
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
@@ -268,13 +339,13 @@ func fillCertAlerts(cvedetail *cvemodels.CveDetail) (dict models.AlertDict) {
 | 
			
		||||
	return dict
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillWithOval fetches OVAL database
 | 
			
		||||
func FillWithOval(driver ovaldb.DB, r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
// detectPkgsCvesWithOval fetches OVAL database
 | 
			
		||||
func detectPkgsCvesWithOval(driver ovaldb.DB, r *models.ScanResult) error {
 | 
			
		||||
	var ovalClient oval.Client
 | 
			
		||||
	var ovalFamily string
 | 
			
		||||
 | 
			
		||||
	switch r.Family {
 | 
			
		||||
	case c.Debian:
 | 
			
		||||
	case c.Debian, c.Raspbian:
 | 
			
		||||
		ovalClient = oval.NewDebian()
 | 
			
		||||
		ovalFamily = c.Debian
 | 
			
		||||
	case c.Ubuntu:
 | 
			
		||||
@@ -300,73 +371,81 @@ func FillWithOval(driver ovaldb.DB, r *models.ScanResult) (nCVEs int, err error)
 | 
			
		||||
	case c.Amazon:
 | 
			
		||||
		ovalClient = oval.NewAmazon()
 | 
			
		||||
		ovalFamily = c.Amazon
 | 
			
		||||
	case c.Raspbian, c.FreeBSD, c.Windows:
 | 
			
		||||
		return 0, nil
 | 
			
		||||
	case c.FreeBSD, c.Windows:
 | 
			
		||||
		return nil
 | 
			
		||||
	case c.ServerTypePseudo:
 | 
			
		||||
		return 0, nil
 | 
			
		||||
		return nil
 | 
			
		||||
	default:
 | 
			
		||||
		if r.Family == "" {
 | 
			
		||||
			return 0, xerrors.New("Probably an error occurred during scanning. Check the error message")
 | 
			
		||||
			return xerrors.New("Probably an error occurred during scanning. Check the error message")
 | 
			
		||||
		}
 | 
			
		||||
		return 0, xerrors.Errorf("OVAL for %s is not implemented yet", r.Family)
 | 
			
		||||
		return xerrors.Errorf("OVAL for %s is not implemented yet", r.Family)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !c.Conf.OvalDict.IsFetchViaHTTP() {
 | 
			
		||||
		if driver == nil {
 | 
			
		||||
			return 0, xerrors.Errorf("You have to fetch OVAL data for %s before reporting. For details, see `https://github.com/kotakanbe/goval-dictionary#usage`", r.Family)
 | 
			
		||||
			return xerrors.Errorf("You have to fetch OVAL data for %s before reporting. For details, see `https://github.com/kotakanbe/goval-dictionary#usage`", r.Family)
 | 
			
		||||
		}
 | 
			
		||||
		if err = driver.NewOvalDB(ovalFamily); err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to New Oval DB. err: %w", err)
 | 
			
		||||
		if err := driver.NewOvalDB(ovalFamily); err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to New Oval DB. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	util.Log.Debugf("Check whether oval fetched: %s %s", ovalFamily, r.Release)
 | 
			
		||||
	ok, err := ovalClient.CheckIfOvalFetched(driver, ovalFamily, r.Release)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if !ok {
 | 
			
		||||
		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)
 | 
			
		||||
		return xerrors.Errorf("OVAL entries of %s %s are not found. Fetch OVAL before reporting. For details, see `https://github.com/kotakanbe/goval-dictionary#usage`", ovalFamily, r.Release)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err = ovalClient.CheckIfOvalFresh(driver, ovalFamily, r.Release)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ovalClient.FillWithOval(driver, r)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillWithGost fills CVEs with gost dataabase
 | 
			
		||||
// https://github.com/knqyf263/gost
 | 
			
		||||
func FillWithGost(driver gostdb.DB, r *models.ScanResult, ignoreWillNotFix bool) (nCVEs int, err error) {
 | 
			
		||||
	gostClient := gost.NewClient(r.Family)
 | 
			
		||||
	// TODO chekc if fetched
 | 
			
		||||
	// TODO chekc if fresh enough
 | 
			
		||||
	if nCVEs, err = gostClient.DetectUnfixed(driver, r, ignoreWillNotFix); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	nCVEs, err := ovalClient.FillWithOval(driver, r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nCVEs, gostClient.FillCVEsWithRedHat(driver, r)
 | 
			
		||||
 | 
			
		||||
	util.Log.Infof("%s: %d CVEs are detected with OVAL", r.FormatServerName(), nCVEs)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillWithExploit fills Exploits with exploit dataabase
 | 
			
		||||
func detectPkgsCvesWithGost(driver gostdb.DB, r *models.ScanResult) error {
 | 
			
		||||
	nCVEs, err := gost.NewClient(r.Family).DetectUnfixed(driver, r, true)
 | 
			
		||||
 | 
			
		||||
	util.Log.Infof("%s: %d unfixed CVEs are detected with gost",
 | 
			
		||||
		r.FormatServerName(), nCVEs)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// fillWithExploitDB fills Exploits with exploit dataabase
 | 
			
		||||
// https://github.com/mozqnet/go-exploitdb
 | 
			
		||||
func FillWithExploit(driver exploitdb.DB, r *models.ScanResult) (nExploitCve int, err error) {
 | 
			
		||||
	// TODO chekc if fetched
 | 
			
		||||
	// TODO chekc if fresh enough
 | 
			
		||||
func fillWithExploitDB(driver exploitdb.DB, r *models.ScanResult) (nExploitCve int, err error) {
 | 
			
		||||
	return exploit.FillWithExploit(driver, r)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fillVulnByCpeURIs(driver cvedb.DB, r *models.ScanResult, cpeURIs []string) (nCVEs int, err error) {
 | 
			
		||||
// fillWithMetasploit fills metasploit modules with metasploit database
 | 
			
		||||
// https://github.com/takuzoo3868/go-msfdb
 | 
			
		||||
func fillWithMetasploit(driver metasploitdb.DB, r *models.ScanResult) (nMetasploitCve int, err error) {
 | 
			
		||||
	return msf.FillWithMetasploit(driver, r)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DetectCpeURIsCves detects CVEs of given CPE-URIs
 | 
			
		||||
func DetectCpeURIsCves(driver cvedb.DB, r *models.ScanResult, cpeURIs []string) error {
 | 
			
		||||
	nCVEs := 0
 | 
			
		||||
	if len(cpeURIs) != 0 && driver == nil && !config.Conf.CveDict.IsFetchViaHTTP() {
 | 
			
		||||
		return 0, xerrors.Errorf("cpeURIs %s specified, but cve-dictionary DB not found. Fetch cve-dictionary beofre reporting. For details, see `https://github.com/kotakanbe/go-cve-dictionary#deploy-go-cve-dictionary`",
 | 
			
		||||
		return xerrors.Errorf("cpeURIs %s specified, but cve-dictionary DB not found. Fetch cve-dictionary before reporting. For details, see `https://github.com/kotakanbe/go-cve-dictionary#deploy-go-cve-dictionary`",
 | 
			
		||||
			cpeURIs)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, name := range cpeURIs {
 | 
			
		||||
		details, err := CveClient.FetchCveDetailsByCpeName(driver, name)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		for _, detail := range details {
 | 
			
		||||
			if val, ok := r.ScannedCves[detail.CveID]; ok {
 | 
			
		||||
@@ -386,7 +465,8 @@ func fillVulnByCpeURIs(driver cvedb.DB, r *models.ScanResult, cpeURIs []string)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nCVEs, nil
 | 
			
		||||
	util.Log.Infof("%s: %d CVEs are detected with CPE", r.FormatServerName(), nCVEs)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type integrationResults struct {
 | 
			
		||||
@@ -429,14 +509,15 @@ func (g GithubSecurityAlertOption) apply(r *models.ScanResult, ints *integration
 | 
			
		||||
 | 
			
		||||
// WordPressOption :
 | 
			
		||||
type WordPressOption struct {
 | 
			
		||||
	token string
 | 
			
		||||
	token        string
 | 
			
		||||
	wpVulnCaches *map[string]string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g WordPressOption) apply(r *models.ScanResult, ints *integrationResults) (err error) {
 | 
			
		||||
	if g.token == "" {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	n, err := wordpress.FillWordPress(r, g.token)
 | 
			
		||||
	n, err := wordpress.FillWordPress(r, g.token, g.wpVulnCaches)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to fetch from WPVulnDB. Check the WPVulnDBToken in config.toml. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
@@ -498,266 +579,3 @@ func fillCweDict(r *models.ScanResult) {
 | 
			
		||||
	r.CweDict = dict
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const reUUID = "[\\da-f]{8}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{12}"
 | 
			
		||||
 | 
			
		||||
// 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.
 | 
			
		||||
func getOrCreateServerUUID(r models.ScanResult, server c.ServerInfo) (serverUUID string, err error) {
 | 
			
		||||
	if id, ok := server.UUIDs[r.ServerName]; !ok {
 | 
			
		||||
		if serverUUID, err = uuid.GenerateUUID(); err != nil {
 | 
			
		||||
			return "", xerrors.Errorf("Failed to generate UUID: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		matched, err := regexp.MatchString(reUUID, id)
 | 
			
		||||
		if !matched || err != nil {
 | 
			
		||||
			if serverUUID, err = uuid.GenerateUUID(); err != nil {
 | 
			
		||||
				return "", xerrors.Errorf("Failed to generate UUID: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return serverUUID, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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) (err error) {
 | 
			
		||||
	// Sort Host->Container
 | 
			
		||||
	sort.Slice(results, func(i, j int) bool {
 | 
			
		||||
		if results[i].ServerName == results[j].ServerName {
 | 
			
		||||
			return results[i].Container.ContainerID < results[j].Container.ContainerID
 | 
			
		||||
		}
 | 
			
		||||
		return results[i].ServerName < results[j].ServerName
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	re := regexp.MustCompile(reUUID)
 | 
			
		||||
	for i, r := range results {
 | 
			
		||||
		server := c.Conf.Servers[r.ServerName]
 | 
			
		||||
		if server.UUIDs == nil {
 | 
			
		||||
			server.UUIDs = map[string]string{}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		name := ""
 | 
			
		||||
		if r.IsContainer() {
 | 
			
		||||
			name = fmt.Sprintf("%s@%s", r.Container.Name, r.ServerName)
 | 
			
		||||
			serverUUID, err := getOrCreateServerUUID(r, server)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			if serverUUID != "" {
 | 
			
		||||
				server.UUIDs[r.ServerName] = serverUUID
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			name = r.ServerName
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if id, ok := server.UUIDs[name]; ok {
 | 
			
		||||
			ok := re.MatchString(id)
 | 
			
		||||
			if !ok || err != nil {
 | 
			
		||||
				util.Log.Warnf("UUID is invalid. Re-generate UUID %s: %s", id, err)
 | 
			
		||||
			} else {
 | 
			
		||||
				if r.IsContainer() {
 | 
			
		||||
					results[i].Container.UUID = id
 | 
			
		||||
					results[i].ServerUUID = server.UUIDs[r.ServerName]
 | 
			
		||||
				} else {
 | 
			
		||||
					results[i].ServerUUID = id
 | 
			
		||||
				}
 | 
			
		||||
				// continue if the UUID has already assigned and valid
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Generate a new UUID and set to config and scan result
 | 
			
		||||
		serverUUID, err := uuid.GenerateUUID()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		server.UUIDs[name] = serverUUID
 | 
			
		||||
		server = cleanForTOMLEncoding(server, c.Conf.Default)
 | 
			
		||||
		c.Conf.Servers[r.ServerName] = server
 | 
			
		||||
 | 
			
		||||
		if r.IsContainer() {
 | 
			
		||||
			results[i].Container.UUID = serverUUID
 | 
			
		||||
			results[i].ServerUUID = server.UUIDs[r.ServerName]
 | 
			
		||||
		} else {
 | 
			
		||||
			results[i].ServerUUID = serverUUID
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for name, server := range c.Conf.Servers {
 | 
			
		||||
		server = cleanForTOMLEncoding(server, c.Conf.Default)
 | 
			
		||||
		c.Conf.Servers[name] = server
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	email := &c.Conf.EMail
 | 
			
		||||
	if email.SMTPAddr == "" {
 | 
			
		||||
		email = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	slack := &c.Conf.Slack
 | 
			
		||||
	if slack.HookURL == "" {
 | 
			
		||||
		slack = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cveDict := &c.Conf.CveDict
 | 
			
		||||
	ovalDict := &c.Conf.OvalDict
 | 
			
		||||
	gost := &c.Conf.Gost
 | 
			
		||||
	exploit := &c.Conf.Exploit
 | 
			
		||||
	http := &c.Conf.HTTP
 | 
			
		||||
	if http.URL == "" {
 | 
			
		||||
		http = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	syslog := &c.Conf.Syslog
 | 
			
		||||
	if syslog.Host == "" {
 | 
			
		||||
		syslog = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	aws := &c.Conf.AWS
 | 
			
		||||
	if aws.S3Bucket == "" {
 | 
			
		||||
		aws = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	azure := &c.Conf.Azure
 | 
			
		||||
	if azure.AccountName == "" {
 | 
			
		||||
		azure = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	stride := &c.Conf.Stride
 | 
			
		||||
	if stride.HookURL == "" {
 | 
			
		||||
		stride = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hipChat := &c.Conf.HipChat
 | 
			
		||||
	if hipChat.AuthToken == "" {
 | 
			
		||||
		hipChat = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	chatWork := &c.Conf.ChatWork
 | 
			
		||||
	if chatWork.APIToken == "" {
 | 
			
		||||
		chatWork = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	saas := &c.Conf.Saas
 | 
			
		||||
	if saas.GroupID == 0 {
 | 
			
		||||
		saas = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c := struct {
 | 
			
		||||
		CveDict  *c.GoCveDictConf `toml:"cveDict"`
 | 
			
		||||
		OvalDict *c.GovalDictConf `toml:"ovalDict"`
 | 
			
		||||
		Gost     *c.GostConf      `toml:"gost"`
 | 
			
		||||
		Exploit  *c.ExploitConf   `toml:"exploit"`
 | 
			
		||||
		Slack    *c.SlackConf     `toml:"slack"`
 | 
			
		||||
		Email    *c.SMTPConf      `toml:"email"`
 | 
			
		||||
		HTTP     *c.HTTPConf      `toml:"http"`
 | 
			
		||||
		Syslog   *c.SyslogConf    `toml:"syslog"`
 | 
			
		||||
		AWS      *c.AWS           `toml:"aws"`
 | 
			
		||||
		Azure    *c.Azure         `toml:"azure"`
 | 
			
		||||
		Stride   *c.StrideConf    `toml:"stride"`
 | 
			
		||||
		HipChat  *c.HipChatConf   `toml:"hipChat"`
 | 
			
		||||
		ChatWork *c.ChatWorkConf  `toml:"chatWork"`
 | 
			
		||||
		Saas     *c.SaasConf      `toml:"saas"`
 | 
			
		||||
 | 
			
		||||
		Default c.ServerInfo            `toml:"default"`
 | 
			
		||||
		Servers map[string]c.ServerInfo `toml:"servers"`
 | 
			
		||||
	}{
 | 
			
		||||
		CveDict:  cveDict,
 | 
			
		||||
		OvalDict: ovalDict,
 | 
			
		||||
		Gost:     gost,
 | 
			
		||||
		Exploit:  exploit,
 | 
			
		||||
		Slack:    slack,
 | 
			
		||||
		Email:    email,
 | 
			
		||||
		HTTP:     http,
 | 
			
		||||
		Syslog:   syslog,
 | 
			
		||||
		AWS:      aws,
 | 
			
		||||
		Azure:    azure,
 | 
			
		||||
		Stride:   stride,
 | 
			
		||||
		HipChat:  hipChat,
 | 
			
		||||
		ChatWork: chatWork,
 | 
			
		||||
		Saas:     saas,
 | 
			
		||||
 | 
			
		||||
		Default: c.Conf.Default,
 | 
			
		||||
		Servers: c.Conf.Servers,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// rename the current config.toml to config.toml.bak
 | 
			
		||||
	info, err := os.Lstat(configPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		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 xerrors.Errorf("Failed to Read link %s: %w", configPath, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if err := os.Rename(realPath, realPath+".bak"); err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to rename %s: %w", configPath, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	if err := toml.NewEncoder(&buf).Encode(c); err != nil {
 | 
			
		||||
		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",
 | 
			
		||||
		"# See REAME for details: https://vuls.io/docs/en/usage-settings.html",
 | 
			
		||||
		str)
 | 
			
		||||
 | 
			
		||||
	return ioutil.WriteFile(realPath, []byte(str), 0600)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func cleanForTOMLEncoding(server c.ServerInfo, def c.ServerInfo) c.ServerInfo {
 | 
			
		||||
	if reflect.DeepEqual(server.Optional, def.Optional) {
 | 
			
		||||
		server.Optional = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if def.User == server.User {
 | 
			
		||||
		server.User = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if def.Host == server.Host {
 | 
			
		||||
		server.Host = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if def.Port == server.Port {
 | 
			
		||||
		server.Port = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if def.KeyPath == server.KeyPath {
 | 
			
		||||
		server.KeyPath = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if reflect.DeepEqual(server.ScanMode, def.ScanMode) {
 | 
			
		||||
		server.ScanMode = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if def.Type == server.Type {
 | 
			
		||||
		server.Type = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if reflect.DeepEqual(server.CpeNames, def.CpeNames) {
 | 
			
		||||
		server.CpeNames = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if def.OwaspDCXMLPath == server.OwaspDCXMLPath {
 | 
			
		||||
		server.OwaspDCXMLPath = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if reflect.DeepEqual(server.IgnoreCves, def.IgnoreCves) {
 | 
			
		||||
		server.IgnoreCves = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if reflect.DeepEqual(server.Enablerepo, def.Enablerepo) {
 | 
			
		||||
		server.Enablerepo = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for k, v := range def.Optional {
 | 
			
		||||
		if vv, ok := server.Optional[k]; ok && v == vv {
 | 
			
		||||
			delete(server.Optional, k)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return server
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -277,9 +277,8 @@ func attachmentText(vinfo models.VulnInfo, osFamily string, cweDict map[string]m
 | 
			
		||||
		} else {
 | 
			
		||||
			if 0 < len(vinfo.DistroAdvisories) {
 | 
			
		||||
				links := []string{}
 | 
			
		||||
				for k, v := range vinfo.VendorLinks(osFamily) {
 | 
			
		||||
					links = append(links, fmt.Sprintf("<%s|%s>",
 | 
			
		||||
						v, k))
 | 
			
		||||
				for _, v := range vinfo.CveContents.PrimarySrcURLs(config.Conf.Lang, osFamily, vinfo.CveID) {
 | 
			
		||||
					links = append(links, fmt.Sprintf("<%s|%s>", v.Value, v.Type))
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				v := fmt.Sprintf("<%s|%s> %s (%s)",
 | 
			
		||||
@@ -303,9 +302,8 @@ func attachmentText(vinfo models.VulnInfo, osFamily string, cweDict map[string]m
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mitigation := ""
 | 
			
		||||
	if vinfo.Mitigations(osFamily)[0].Type != models.Unknown {
 | 
			
		||||
		mitigation = fmt.Sprintf("\nMitigation:\n```%s```\n",
 | 
			
		||||
			vinfo.Mitigations(osFamily)[0].Value)
 | 
			
		||||
	for _, m := range vinfo.Mitigations {
 | 
			
		||||
		mitigation = fmt.Sprintf("\nMitigation:\n<%s|%s>", m.URL, m.CveContentType)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return fmt.Sprintf("*%4.1f (%s)* %s %s\n%s\n```\n%s\n```%s\n%s\n",
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@ type StdoutWriter struct{}
 | 
			
		||||
// WriteScanSummary prints Scan summary at the end of scan
 | 
			
		||||
func (w StdoutWriter) WriteScanSummary(rs ...models.ScanResult) {
 | 
			
		||||
	fmt.Printf("\n\n")
 | 
			
		||||
	fmt.Println("One Line Summary")
 | 
			
		||||
	fmt.Println("Scan Summary")
 | 
			
		||||
	fmt.Println("================")
 | 
			
		||||
	fmt.Printf("%s\n", formatScanSummary(rs...))
 | 
			
		||||
}
 | 
			
		||||
@@ -27,7 +27,7 @@ func (w StdoutWriter) Write(rs ...models.ScanResult) error {
 | 
			
		||||
		fmt.Print("\n")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.FormatList {
 | 
			
		||||
	if c.Conf.FormatList || c.Conf.FormatCsvList {
 | 
			
		||||
		for _, r := range rs {
 | 
			
		||||
			fmt.Println(formatList(r))
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,81 +0,0 @@
 | 
			
		||||
package report
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// StrideWriter send report to Stride
 | 
			
		||||
type StrideWriter struct{}
 | 
			
		||||
type strideSender struct{}
 | 
			
		||||
 | 
			
		||||
func (w StrideWriter) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
	conf := config.Conf.Stride
 | 
			
		||||
 | 
			
		||||
	for _, r := range rs {
 | 
			
		||||
		w := strideSender{}
 | 
			
		||||
 | 
			
		||||
		serverInfo := fmt.Sprintf("%s", r.ServerInfo())
 | 
			
		||||
		message := fmt.Sprintf(`{"body":{"version":1,"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":" %s  "}]}]}}`,
 | 
			
		||||
			serverInfo,
 | 
			
		||||
		)
 | 
			
		||||
		if err = w.sendMessage(conf.HookURL, conf.AuthToken, message); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, vinfo := range r.ScannedCves {
 | 
			
		||||
			maxCvss := vinfo.MaxCvssScore()
 | 
			
		||||
			severity := strings.ToUpper(maxCvss.Value.Severity)
 | 
			
		||||
			if severity == "" {
 | 
			
		||||
				severity = "?"
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			message = fmt.Sprintf(`{"body":{"version":1,"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":" %s ","marks": [ { "type": "link", "attrs": { "href": "https://nvd.nist.gov/vuln/detail/%s", "title": "cve" } } ]}]}]}}`,
 | 
			
		||||
				vinfo.CveID,
 | 
			
		||||
				vinfo.CveID,
 | 
			
		||||
			)
 | 
			
		||||
			if err = w.sendMessage(conf.HookURL, conf.AuthToken, message); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			message = fmt.Sprintf(`{"body":{"version":1,"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":" %s (%s) "}]}]}}`,
 | 
			
		||||
				strconv.FormatFloat(maxCvss.Value.Score, 'f', 1, 64),
 | 
			
		||||
				severity,
 | 
			
		||||
			)
 | 
			
		||||
			if err = w.sendMessage(conf.HookURL, conf.AuthToken, message); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			message = fmt.Sprintf(`{"body":{"version":1,"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":" %s "}]}]}}`,
 | 
			
		||||
				vinfo.Summaries(config.Conf.Lang, r.Family)[0].Value,
 | 
			
		||||
			)
 | 
			
		||||
			if err = w.sendMessage(conf.HookURL, conf.AuthToken, message); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w strideSender) sendMessage(uri, token, jsonStr string) error {
 | 
			
		||||
	reqs, err := http.NewRequest("POST", uri, bytes.NewBuffer([]byte(jsonStr)))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	reqs.Header.Add("Content-Type", "application/json")
 | 
			
		||||
	reqs.Header.Add("Authorization", "Bearer "+token)
 | 
			
		||||
	client := &http.Client{}
 | 
			
		||||
	resp, err := client.Do(reqs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@@ -69,7 +69,7 @@ func (w SyslogWriter) encodeSyslog(result models.ScanResult) (messages []string)
 | 
			
		||||
			kvPairs = append(kvPairs, fmt.Sprintf(`cvss_vector_%s_v3="%s"`, cvss.Type, cvss.Value.Vector))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if content, ok := vinfo.CveContents[models.NvdXML]; ok {
 | 
			
		||||
		if content, ok := vinfo.CveContents[models.Nvd]; ok {
 | 
			
		||||
			cwes := strings.Join(content.CweIDs, ",")
 | 
			
		||||
			kvPairs = append(kvPairs, fmt.Sprintf(`cwe_ids="%s"`, cwes))
 | 
			
		||||
			if config.Conf.Syslog.Verbose {
 | 
			
		||||
 
 | 
			
		||||
@@ -33,11 +33,14 @@ func TestSyslogWriterEncodeSyslog(t *testing.T) {
 | 
			
		||||
							models.PackageFixStatus{Name: "pkg4"},
 | 
			
		||||
						},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							models.NvdXML: models.CveContent{
 | 
			
		||||
							models.Nvd: models.CveContent{
 | 
			
		||||
								Cvss2Score:    5.0,
 | 
			
		||||
								Cvss2Vector:   "AV:L/AC:L/Au:N/C:N/I:N/A:C",
 | 
			
		||||
								Cvss2Severity: "MEDIUM",
 | 
			
		||||
								CweIDs:        []string{"CWE-20"},
 | 
			
		||||
								Cvss3Score:    9.8,
 | 
			
		||||
								Cvss3Vector:   "AV:L/AC:L/Au:N/C:N/I:N/A:C",
 | 
			
		||||
								Cvss3Severity: "HIGH",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
@@ -45,7 +48,7 @@ func TestSyslogWriterEncodeSyslog(t *testing.T) {
 | 
			
		||||
			},
 | 
			
		||||
			expectedMessages: []string{
 | 
			
		||||
				`scanned_at="2018-06-13 16:10:00 +0000 UTC" server_name="teste01" os_family="ubuntu" os_release="16.04" ipv4_addr="192.168.0.1,10.0.2.15" ipv6_addr="" packages="pkg1,pkg2" cve_id="CVE-2017-0001"`,
 | 
			
		||||
				`scanned_at="2018-06-13 16:10:00 +0000 UTC" server_name="teste01" os_family="ubuntu" os_release="16.04" ipv4_addr="192.168.0.1,10.0.2.15" ipv6_addr="" packages="pkg3,pkg4" cve_id="CVE-2017-0002" cvss_score_nvdxml_v2="5.00" cvss_vector_nvdxml_v2="AV:L/AC:L/Au:N/C:N/I:N/A:C" cwe_ids="CWE-20"`,
 | 
			
		||||
				`scanned_at="2018-06-13 16:10:00 +0000 UTC" server_name="teste01" os_family="ubuntu" os_release="16.04" ipv4_addr="192.168.0.1,10.0.2.15" ipv6_addr="" packages="pkg3,pkg4" cve_id="CVE-2017-0002" cvss_score_nvd_v2="5.00" cvss_vector_nvd_v2="AV:L/AC:L/Au:N/C:N/I:N/A:C" cvss_score_nvd_v3="9.80" cvss_vector_nvd_v3="AV:L/AC:L/Au:N/C:N/I:N/A:C" cwe_ids="CWE-20"`,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
 
 | 
			
		||||
@@ -617,8 +617,18 @@ func summaryLines(r models.ScanResult) string {
 | 
			
		||||
		pkgNames = append(pkgNames, vinfo.WpPackageFixStats.Names()...)
 | 
			
		||||
		pkgNames = append(pkgNames, vinfo.LibraryFixedIns.Names()...)
 | 
			
		||||
 | 
			
		||||
		av := vinfo.AttackVector()
 | 
			
		||||
		for _, pname := range vinfo.AffectedPackages.Names() {
 | 
			
		||||
			if r.Packages[pname].HasReachablePort() {
 | 
			
		||||
				av = fmt.Sprintf("%s ◉", av)
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		exploits := ""
 | 
			
		||||
		if 0 < len(vinfo.Exploits) {
 | 
			
		||||
		if 0 < len(vinfo.Metasploits) {
 | 
			
		||||
			exploits = "EXP"
 | 
			
		||||
		} else if 0 < len(vinfo.Exploits) {
 | 
			
		||||
			exploits = "POC"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -627,7 +637,7 @@ func summaryLines(r models.ScanResult) string {
 | 
			
		||||
			fmt.Sprintf(indexFormat, i+1),
 | 
			
		||||
			vinfo.CveID,
 | 
			
		||||
			cvssScore + " |",
 | 
			
		||||
			fmt.Sprintf("%4s |", vinfo.AttackVector()),
 | 
			
		||||
			fmt.Sprintf("%-6s |", av),
 | 
			
		||||
			fmt.Sprintf("%3s |", exploits),
 | 
			
		||||
			fmt.Sprintf("%6s |", vinfo.AlertDict.FormatSource()),
 | 
			
		||||
			fmt.Sprintf("%7s |", vinfo.PatchStatus(r.Packages)),
 | 
			
		||||
@@ -639,6 +649,7 @@ func summaryLines(r models.ScanResult) string {
 | 
			
		||||
		}
 | 
			
		||||
		stable.AddRow(icols...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return fmt.Sprintf("%s", stable)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -710,8 +721,23 @@ func setChangelogLayout(g *gocui.Gui) error {
 | 
			
		||||
 | 
			
		||||
				if len(pack.AffectedProcs) != 0 {
 | 
			
		||||
					for _, p := range pack.AffectedProcs {
 | 
			
		||||
						if len(p.ListenPortStats) == 0 {
 | 
			
		||||
							lines = append(lines, fmt.Sprintf("  * PID: %s %s Port: []",
 | 
			
		||||
								p.PID, p.Name))
 | 
			
		||||
							continue
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						var ports []string
 | 
			
		||||
						for _, pp := range p.ListenPortStats {
 | 
			
		||||
							if len(pp.PortReachableTo) == 0 {
 | 
			
		||||
								ports = append(ports, fmt.Sprintf("%s:%s", pp.BindAddress, pp.Port))
 | 
			
		||||
							} else {
 | 
			
		||||
								ports = append(ports, fmt.Sprintf("%s:%s(◉ Scannable: %s)", pp.BindAddress, pp.Port, pp.PortReachableTo))
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						lines = append(lines, fmt.Sprintf("  * PID: %s %s Port: %s",
 | 
			
		||||
							p.PID, p.Name, p.ListenPorts))
 | 
			
		||||
							p.PID, p.Name, ports))
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
@@ -745,7 +771,7 @@ func setChangelogLayout(g *gocui.Gui) error {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, l := range vinfo.LibraryFixedIns {
 | 
			
		||||
			libs := r.LibraryScanners.Find(l.Name)
 | 
			
		||||
			libs := r.LibraryScanners.Find(l.Path, l.Name)
 | 
			
		||||
			for path, lib := range libs {
 | 
			
		||||
				lines = append(lines, fmt.Sprintf("%s-%s, FixedIn: %s (%s)",
 | 
			
		||||
					lib.Name, lib.Version, l.FixedIn, path))
 | 
			
		||||
@@ -770,6 +796,21 @@ func setChangelogLayout(g *gocui.Gui) error {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(vinfo.Metasploits) != 0 {
 | 
			
		||||
			lines = append(lines, "\n",
 | 
			
		||||
				"Metasploit Modules",
 | 
			
		||||
				"==================",
 | 
			
		||||
			)
 | 
			
		||||
			for _, module := range vinfo.Metasploits {
 | 
			
		||||
				lines = append(lines, fmt.Sprintf("* %s: %s", module.Name, module.Description))
 | 
			
		||||
				if 0 < len(module.URLs) {
 | 
			
		||||
					for _, u := range module.URLs {
 | 
			
		||||
						lines = append(lines, fmt.Sprintf(" - %s", u))
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(vinfo.AlertDict.En) > 0 {
 | 
			
		||||
			lines = append(lines, "\n",
 | 
			
		||||
				"USCERT Alert",
 | 
			
		||||
@@ -822,8 +863,10 @@ type dataForTmpl struct {
 | 
			
		||||
	CveID            string
 | 
			
		||||
	Cvsses           string
 | 
			
		||||
	Exploits         []models.Exploit
 | 
			
		||||
	Metasploits      []models.Metasploit
 | 
			
		||||
	Summary          string
 | 
			
		||||
	Mitigation       string
 | 
			
		||||
	PatchURLs        []string
 | 
			
		||||
	Confidences      models.Confidences
 | 
			
		||||
	Cwes             []models.CweDictEntry
 | 
			
		||||
	Alerts           []models.Alert
 | 
			
		||||
@@ -852,14 +895,8 @@ func detailLines() (string, error) {
 | 
			
		||||
 | 
			
		||||
	vinfo := vinfos[currentVinfo]
 | 
			
		||||
	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)
 | 
			
		||||
	for _, r := range vinfo.CveContents.PrimarySrcURLs(config.Conf.Lang, r.Family, vinfo.CveID) {
 | 
			
		||||
		links = append(links, r.Value)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	refsMap := map[string]models.Reference{}
 | 
			
		||||
@@ -882,7 +919,20 @@ func detailLines() (string, error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	summary := vinfo.Summaries(r.Lang, r.Family)[0]
 | 
			
		||||
	mitigation := vinfo.Mitigations(r.Family)[0]
 | 
			
		||||
 | 
			
		||||
	mitigations := []string{}
 | 
			
		||||
	for _, m := range vinfo.Mitigations {
 | 
			
		||||
		switch m.CveContentType {
 | 
			
		||||
		case models.RedHatAPI, models.Microsoft:
 | 
			
		||||
			mitigations = append(mitigations,
 | 
			
		||||
				fmt.Sprintf("%s (%s)", m.Mitigation, m.CveContentType))
 | 
			
		||||
		case models.Nvd:
 | 
			
		||||
			mitigations = append(mitigations,
 | 
			
		||||
				fmt.Sprintf("* %s (%s)", m.URL, m.CveContentType))
 | 
			
		||||
		default:
 | 
			
		||||
			util.Log.Errorf("Unknown CveContentType: %s", m)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	table := uitable.New()
 | 
			
		||||
	table.MaxColWidth = maxColWidth
 | 
			
		||||
@@ -920,7 +970,8 @@ func detailLines() (string, error) {
 | 
			
		||||
		CveID:       vinfo.CveID,
 | 
			
		||||
		Cvsses:      fmt.Sprintf("%s\n", table),
 | 
			
		||||
		Summary:     fmt.Sprintf("%s (%s)", summary.Value, summary.Type),
 | 
			
		||||
		Mitigation:  fmt.Sprintf("%s (%s)", mitigation.Value, mitigation.Type),
 | 
			
		||||
		Mitigation:  strings.Join(mitigations, "\n"),
 | 
			
		||||
		PatchURLs:   vinfo.CveContents.PatchURLs(),
 | 
			
		||||
		Confidences: vinfo.Confidences,
 | 
			
		||||
		Cwes:        cwes,
 | 
			
		||||
		Links:       util.Distinct(links),
 | 
			
		||||
@@ -949,13 +1000,18 @@ Summary
 | 
			
		||||
 | 
			
		||||
Mitigation
 | 
			
		||||
-----------
 | 
			
		||||
 {{.Mitigation }}
 | 
			
		||||
{{.Mitigation }}
 | 
			
		||||
 | 
			
		||||
Links
 | 
			
		||||
Primary Src
 | 
			
		||||
-----------
 | 
			
		||||
{{range $link := .Links -}}
 | 
			
		||||
* {{$link}}
 | 
			
		||||
{{end}}
 | 
			
		||||
Patch
 | 
			
		||||
-----------
 | 
			
		||||
{{range $url := .PatchURLs -}}
 | 
			
		||||
* {{$url}}
 | 
			
		||||
{{end}}
 | 
			
		||||
CWE
 | 
			
		||||
-----------
 | 
			
		||||
{{range .Cwes -}}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										245
									
								
								report/util.go
									
									
									
									
									
								
							
							
						
						@@ -2,6 +2,7 @@ package report
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/csv"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
@@ -21,7 +22,11 @@ import (
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const maxColWidth = 100
 | 
			
		||||
const (
 | 
			
		||||
	vulsOpenTag  = "<vulsreport>"
 | 
			
		||||
	vulsCloseTag = "</vulsreport>"
 | 
			
		||||
	maxColWidth  = 100
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func formatScanSummary(rs ...models.ScanResult) string {
 | 
			
		||||
	table := uitable.New()
 | 
			
		||||
@@ -71,6 +76,7 @@ func formatOneLineSummary(rs ...models.ScanResult) string {
 | 
			
		||||
				r.ScannedCves.FormatFixedStatus(r.Packages),
 | 
			
		||||
				r.FormatUpdatablePacksSummary(),
 | 
			
		||||
				r.FormatExploitCveSummary(),
 | 
			
		||||
				r.FormatMetasploitCveSummary(),
 | 
			
		||||
				r.FormatAlertSummary(),
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
@@ -96,7 +102,7 @@ func formatOneLineSummary(rs ...models.ScanResult) string {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func formatList(r models.ScanResult) string {
 | 
			
		||||
	header := r.FormatTextReportHeadedr()
 | 
			
		||||
	header := r.FormatTextReportHeader()
 | 
			
		||||
	if len(r.Errors) != 0 {
 | 
			
		||||
		return fmt.Sprintf(
 | 
			
		||||
			"%s\nError: Use configtest subcommand or scan with --debug to view the details\n%s\n\n",
 | 
			
		||||
@@ -126,7 +132,7 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
		// packname += strings.Join(vinfo.CpeURIs, ", ")
 | 
			
		||||
 | 
			
		||||
		exploits := ""
 | 
			
		||||
		if 0 < len(vinfo.Exploits) {
 | 
			
		||||
		if 0 < len(vinfo.Exploits) || 0 < len(vinfo.Metasploits) {
 | 
			
		||||
			exploits = "POC"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -134,7 +140,7 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
		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-"))
 | 
			
		||||
			link = fmt.Sprintf("https://wpscan.com/vulnerabilities/%s", strings.TrimPrefix(vinfo.CveID, "WPVDBID-"))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		data = append(data, []string{
 | 
			
		||||
@@ -170,7 +176,7 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func formatFullPlainText(r models.ScanResult) (lines string) {
 | 
			
		||||
	header := r.FormatTextReportHeadedr()
 | 
			
		||||
	header := r.FormatTextReportHeader()
 | 
			
		||||
	if len(r.Errors) != 0 {
 | 
			
		||||
		return fmt.Sprintf(
 | 
			
		||||
			"%s\nError: Use configtest subcommand or scan with --debug to view the details\n%s\n\n",
 | 
			
		||||
@@ -211,9 +217,91 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
		data = append(data, []string{"Summary", vuln.Summaries(
 | 
			
		||||
			config.Conf.Lang, r.Family)[0].Value})
 | 
			
		||||
 | 
			
		||||
		mitigation := vuln.Mitigations(r.Family)[0]
 | 
			
		||||
		if mitigation.Type != models.Unknown {
 | 
			
		||||
			data = append(data, []string{"Mitigation", mitigation.Value})
 | 
			
		||||
		for _, m := range vuln.Mitigations {
 | 
			
		||||
			data = append(data, []string{"Mitigation", m.URL})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		links := vuln.CveContents.PrimarySrcURLs(
 | 
			
		||||
			config.Conf.Lang, r.Family, vuln.CveID)
 | 
			
		||||
		for _, link := range links {
 | 
			
		||||
			data = append(data, []string{"Primary Src", link.Value})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, url := range vuln.CveContents.PatchURLs() {
 | 
			
		||||
			data = append(data, []string{"Patch", url})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		vuln.AffectedPackages.Sort()
 | 
			
		||||
		for _, affected := range vuln.AffectedPackages {
 | 
			
		||||
			if pack, ok := r.Packages[affected.Name]; ok {
 | 
			
		||||
				var line string
 | 
			
		||||
				if pack.Repository != "" {
 | 
			
		||||
					line = fmt.Sprintf("%s (%s)",
 | 
			
		||||
						pack.FormatVersionFromTo(affected),
 | 
			
		||||
						pack.Repository)
 | 
			
		||||
				} else {
 | 
			
		||||
					line = pack.FormatVersionFromTo(affected)
 | 
			
		||||
				}
 | 
			
		||||
				data = append(data, []string{"Affected Pkg", line})
 | 
			
		||||
 | 
			
		||||
				if len(pack.AffectedProcs) != 0 {
 | 
			
		||||
					for _, p := range pack.AffectedProcs {
 | 
			
		||||
						if len(p.ListenPortStats) == 0 {
 | 
			
		||||
							data = append(data, []string{"",
 | 
			
		||||
								fmt.Sprintf("  - PID: %s %s, Port: []", p.PID, p.Name)})
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						var ports []string
 | 
			
		||||
						for _, pp := range p.ListenPortStats {
 | 
			
		||||
							if len(pp.PortReachableTo) == 0 {
 | 
			
		||||
								ports = append(ports, fmt.Sprintf("%s:%s", pp.BindAddress, pp.Port))
 | 
			
		||||
							} else {
 | 
			
		||||
								ports = append(ports, fmt.Sprintf("%s:%s(◉ Scannable: %s)", pp.BindAddress, pp.Port, pp.PortReachableTo))
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						data = append(data, []string{"",
 | 
			
		||||
							fmt.Sprintf("  - PID: %s %s, Port: %s", p.PID, p.Name, ports)})
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		sort.Strings(vuln.CpeURIs)
 | 
			
		||||
		for _, name := range vuln.CpeURIs {
 | 
			
		||||
			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 _, l := range vuln.LibraryFixedIns {
 | 
			
		||||
			libs := r.LibraryScanners.Find(l.Path, l.Name)
 | 
			
		||||
			for path, lib := range libs {
 | 
			
		||||
				data = append(data, []string{l.Key,
 | 
			
		||||
					fmt.Sprintf("%s-%s, FixedIn: %s (%s)",
 | 
			
		||||
						lib.Name, lib.Version, l.FixedIn, path)})
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, confidence := range vuln.Confidences {
 | 
			
		||||
			data = append(data, []string{"Confidence", confidence.String()})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cweURLs, top10URLs := []string{}, []string{}
 | 
			
		||||
@@ -245,82 +333,6 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
			cweURLs = append(cweURLs, url)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		vuln.AffectedPackages.Sort()
 | 
			
		||||
		for _, affected := range vuln.AffectedPackages {
 | 
			
		||||
			if pack, ok := r.Packages[affected.Name]; ok {
 | 
			
		||||
				var line string
 | 
			
		||||
				if pack.Repository != "" {
 | 
			
		||||
					line = fmt.Sprintf("%s (%s)",
 | 
			
		||||
						pack.FormatVersionFromTo(affected),
 | 
			
		||||
						pack.Repository)
 | 
			
		||||
				} else {
 | 
			
		||||
					line = pack.FormatVersionFromTo(affected)
 | 
			
		||||
				}
 | 
			
		||||
				data = append(data, []string{"Affected Pkg", line})
 | 
			
		||||
 | 
			
		||||
				if len(pack.AffectedProcs) != 0 {
 | 
			
		||||
					for _, p := range pack.AffectedProcs {
 | 
			
		||||
						data = append(data, []string{"",
 | 
			
		||||
							fmt.Sprintf("  - PID: %s %s, Port: %s", p.PID, p.Name, p.ListenPorts)})
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		sort.Strings(vuln.CpeURIs)
 | 
			
		||||
		for _, name := range vuln.CpeURIs {
 | 
			
		||||
			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 _, l := range vuln.LibraryFixedIns {
 | 
			
		||||
			libs := r.LibraryScanners.Find(l.Name)
 | 
			
		||||
			for path, lib := range libs {
 | 
			
		||||
				data = append(data, []string{l.Key,
 | 
			
		||||
					fmt.Sprintf("%s-%s, FixedIn: %s (%s)",
 | 
			
		||||
						lib.Name, lib.Version, l.FixedIn, path)})
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, confidence := range vuln.Confidences {
 | 
			
		||||
			data = append(data, []string{"Confidence", confidence.String()})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		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()})
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		vlinks := vuln.VendorLinks(r.Family)
 | 
			
		||||
		for name, url := range vlinks {
 | 
			
		||||
			data = append(data, []string{name, url})
 | 
			
		||||
		}
 | 
			
		||||
		for _, url := range cweURLs {
 | 
			
		||||
			data = append(data, []string{"CWE", url})
 | 
			
		||||
		}
 | 
			
		||||
@@ -367,6 +379,45 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func formatCsvList(r models.ScanResult, path string) error {
 | 
			
		||||
	data := [][]string{{"CVE-ID", "CVSS", "Attack", "PoC", "CERT", "Fixed", "NVD"}}
 | 
			
		||||
	for _, vinfo := range r.ScannedCves.ToSortedSlice() {
 | 
			
		||||
		max := vinfo.MaxCvssScore().Value.Score
 | 
			
		||||
 | 
			
		||||
		exploits := ""
 | 
			
		||||
		if 0 < len(vinfo.Exploits) || 0 < len(vinfo.Metasploits) {
 | 
			
		||||
			exploits = "POC"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		link := ""
 | 
			
		||||
		if strings.HasPrefix(vinfo.CveID, "CVE-") {
 | 
			
		||||
			link = fmt.Sprintf("https://nvd.nist.gov/vuln/detail/%s", vinfo.CveID)
 | 
			
		||||
		} else if strings.HasPrefix(vinfo.CveID, "WPVDBID-") {
 | 
			
		||||
			link = fmt.Sprintf("https://wpscan.com/vulnerabilities/%s", strings.TrimPrefix(vinfo.CveID, "WPVDBID-"))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		data = append(data, []string{
 | 
			
		||||
			vinfo.CveID,
 | 
			
		||||
			fmt.Sprintf("%4.1f", max),
 | 
			
		||||
			vinfo.AttackVector(),
 | 
			
		||||
			exploits,
 | 
			
		||||
			vinfo.AlertDict.FormatSource(),
 | 
			
		||||
			vinfo.PatchStatus(r.Packages),
 | 
			
		||||
			link,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	file, err := os.Create(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to create a file: %s, err: %w", path, err)
 | 
			
		||||
	}
 | 
			
		||||
	defer file.Close()
 | 
			
		||||
	if err := csv.NewWriter(file).WriteAll(data); err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to write to file: %s, err: %w", path, err)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func cweURL(cweID string) string {
 | 
			
		||||
	return fmt.Sprintf("https://cwe.mitre.org/data/definitions/%s.html",
 | 
			
		||||
		strings.TrimPrefix(cweID, "CWE-"))
 | 
			
		||||
@@ -387,15 +438,23 @@ func formatChangelogs(r models.ScanResult) string {
 | 
			
		||||
	}
 | 
			
		||||
	return strings.Join(buf, "\n")
 | 
			
		||||
}
 | 
			
		||||
func ovalSupported(r *models.ScanResult) bool {
 | 
			
		||||
 | 
			
		||||
func reuseScannedCves(r *models.ScanResult) bool {
 | 
			
		||||
	switch r.Family {
 | 
			
		||||
	case
 | 
			
		||||
		config.Amazon,
 | 
			
		||||
		config.FreeBSD,
 | 
			
		||||
		config.Raspbian:
 | 
			
		||||
		return false
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
	if isTrivyResult(r) {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isTrivyResult(r *models.ScanResult) bool {
 | 
			
		||||
	_, ok := r.Optional["trivy-target"]
 | 
			
		||||
	return ok
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func needToRefreshCve(r models.ScanResult) bool {
 | 
			
		||||
@@ -499,9 +558,9 @@ func getDiffCves(previous, current models.ScanResult) models.VulnInfos {
 | 
			
		||||
				updated[v.CveID] = v
 | 
			
		||||
				util.Log.Debugf("updated: %s", v.CveID)
 | 
			
		||||
 | 
			
		||||
				// TODO commented out beause  a bug of diff logic when multiple oval defs found for a certain CVE-ID and same updated_at
 | 
			
		||||
				// TODO commented out because  a bug of diff logic when multiple oval defs found for a certain CVE-ID and same updated_at
 | 
			
		||||
				// if these OVAL defs have different affected packages, this logic detects as updated.
 | 
			
		||||
				// This logic will be uncommented after integration with ghost https://github.com/knqyf263/gost
 | 
			
		||||
				// This logic will be uncomented after integration with gost https://github.com/knqyf263/gost
 | 
			
		||||
				// } else if isCveFixed(v, previous) {
 | 
			
		||||
				// updated[v.CveID] = v
 | 
			
		||||
				// util.Log.Debugf("fixed: %s", v.CveID)
 | 
			
		||||
@@ -515,6 +574,10 @@ func getDiffCves(previous, current models.ScanResult) models.VulnInfos {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(updated) == 0 {
 | 
			
		||||
		util.Log.Infof("%s: There are %d vulnerabilities, but no difference between current result and previous one.", current.FormatServerName(), len(current.ScannedCves))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for cveID, vuln := range new {
 | 
			
		||||
		updated[cveID] = vuln
 | 
			
		||||
	}
 | 
			
		||||
@@ -538,7 +601,7 @@ func isCveFixed(current models.VulnInfo, previous models.ScanResult) bool {
 | 
			
		||||
 | 
			
		||||
func isCveInfoUpdated(cveID string, previous, current models.ScanResult) bool {
 | 
			
		||||
	cTypes := []models.CveContentType{
 | 
			
		||||
		models.NvdXML,
 | 
			
		||||
		models.Nvd,
 | 
			
		||||
		models.Jvn,
 | 
			
		||||
		models.NewCveContentType(current.Family),
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -42,7 +42,7 @@ func TestIsCveInfoUpdated(t *testing.T) {
 | 
			
		||||
							CveID: "CVE-2017-0001",
 | 
			
		||||
							CveContents: models.NewCveContents(
 | 
			
		||||
								models.CveContent{
 | 
			
		||||
									Type:         models.NvdXML,
 | 
			
		||||
									Type:         models.Nvd,
 | 
			
		||||
									CveID:        "CVE-2017-0001",
 | 
			
		||||
									LastModified: time.Time{},
 | 
			
		||||
								},
 | 
			
		||||
@@ -56,7 +56,7 @@ func TestIsCveInfoUpdated(t *testing.T) {
 | 
			
		||||
							CveID: "CVE-2017-0001",
 | 
			
		||||
							CveContents: models.NewCveContents(
 | 
			
		||||
								models.CveContent{
 | 
			
		||||
									Type:         models.NvdXML,
 | 
			
		||||
									Type:         models.Nvd,
 | 
			
		||||
									CveID:        "CVE-2017-0001",
 | 
			
		||||
									LastModified: time.Time{},
 | 
			
		||||
								},
 | 
			
		||||
@@ -113,7 +113,7 @@ func TestIsCveInfoUpdated(t *testing.T) {
 | 
			
		||||
							CveID: "CVE-2017-0003",
 | 
			
		||||
							CveContents: models.NewCveContents(
 | 
			
		||||
								models.CveContent{
 | 
			
		||||
									Type:         models.NvdXML,
 | 
			
		||||
									Type:         models.Nvd,
 | 
			
		||||
									CveID:        "CVE-2017-0002",
 | 
			
		||||
									LastModified: new,
 | 
			
		||||
								},
 | 
			
		||||
@@ -128,7 +128,7 @@ func TestIsCveInfoUpdated(t *testing.T) {
 | 
			
		||||
							CveID: "CVE-2017-0003",
 | 
			
		||||
							CveContents: models.NewCveContents(
 | 
			
		||||
								models.CveContent{
 | 
			
		||||
									Type:         models.NvdXML,
 | 
			
		||||
									Type:         models.Nvd,
 | 
			
		||||
									CveID:        "CVE-2017-0002",
 | 
			
		||||
									LastModified: old,
 | 
			
		||||
								},
 | 
			
		||||
@@ -150,7 +150,7 @@ func TestIsCveInfoUpdated(t *testing.T) {
 | 
			
		||||
							CveID: "CVE-2017-0004",
 | 
			
		||||
							CveContents: models.NewCveContents(
 | 
			
		||||
								models.CveContent{
 | 
			
		||||
									Type:         models.NvdXML,
 | 
			
		||||
									Type:         models.Nvd,
 | 
			
		||||
									CveID:        "CVE-2017-0002",
 | 
			
		||||
									LastModified: old,
 | 
			
		||||
								},
 | 
			
		||||
@@ -356,7 +356,7 @@ func TestIsCveFixed(t *testing.T) {
 | 
			
		||||
					},
 | 
			
		||||
					CveContents: models.NewCveContents(
 | 
			
		||||
						models.CveContent{
 | 
			
		||||
							Type:         models.NvdXML,
 | 
			
		||||
							Type:         models.Nvd,
 | 
			
		||||
							CveID:        "CVE-2016-6662",
 | 
			
		||||
							LastModified: time.Time{},
 | 
			
		||||
						},
 | 
			
		||||
@@ -374,7 +374,7 @@ func TestIsCveFixed(t *testing.T) {
 | 
			
		||||
							},
 | 
			
		||||
							CveContents: models.NewCveContents(
 | 
			
		||||
								models.CveContent{
 | 
			
		||||
									Type:         models.NvdXML,
 | 
			
		||||
									Type:         models.Nvd,
 | 
			
		||||
									CveID:        "CVE-2016-6662",
 | 
			
		||||
									LastModified: time.Time{},
 | 
			
		||||
								},
 | 
			
		||||
@@ -397,7 +397,7 @@ func TestIsCveFixed(t *testing.T) {
 | 
			
		||||
					},
 | 
			
		||||
					CveContents: models.NewCveContents(
 | 
			
		||||
						models.CveContent{
 | 
			
		||||
							Type:         models.NvdXML,
 | 
			
		||||
							Type:         models.Nvd,
 | 
			
		||||
							CveID:        "CVE-2016-6662",
 | 
			
		||||
							LastModified: time.Time{},
 | 
			
		||||
						},
 | 
			
		||||
@@ -415,7 +415,7 @@ func TestIsCveFixed(t *testing.T) {
 | 
			
		||||
							},
 | 
			
		||||
							CveContents: models.NewCveContents(
 | 
			
		||||
								models.CveContent{
 | 
			
		||||
									Type:         models.NvdXML,
 | 
			
		||||
									Type:         models.Nvd,
 | 
			
		||||
									CveID:        "CVE-2016-6662",
 | 
			
		||||
									LastModified: time.Time{},
 | 
			
		||||
								},
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,14 @@
 | 
			
		||||
package report
 | 
			
		||||
package saas
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws"
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws/credentials"
 | 
			
		||||
@@ -23,8 +21,8 @@ import (
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SaasWriter writes results to SaaS
 | 
			
		||||
type SaasWriter struct{}
 | 
			
		||||
// Writer writes results to SaaS
 | 
			
		||||
type Writer struct{}
 | 
			
		||||
 | 
			
		||||
// TempCredential : TempCredential
 | 
			
		||||
type TempCredential struct {
 | 
			
		||||
@@ -34,7 +32,7 @@ type TempCredential struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type payload struct {
 | 
			
		||||
	GroupID      int    `json:"GroupID"`
 | 
			
		||||
	GroupID      int64  `json:"GroupID"`
 | 
			
		||||
	Token        string `json:"Token"`
 | 
			
		||||
	ScannedBy    string `json:"ScannedBy"`
 | 
			
		||||
	ScannedIPv4s string `json:"ScannedIPv4s"`
 | 
			
		||||
@@ -42,7 +40,7 @@ type payload struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UploadSaas : UploadSaas
 | 
			
		||||
func (w SaasWriter) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
func (w Writer) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
	// dir string, configPath string, config *c.Config
 | 
			
		||||
	if len(rs) == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
@@ -142,11 +140,3 @@ func (w SaasWriter) Write(rs ...models.ScanResult) (err error) {
 | 
			
		||||
	util.Log.Infof("done")
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func renameKeyNameUTC(scannedAt time.Time, uuid string, container models.Container) string {
 | 
			
		||||
	timestr := scannedAt.UTC().Format(time.RFC3339)
 | 
			
		||||
	if len(container.ContainerID) == 0 {
 | 
			
		||||
		return fmt.Sprintf("%s/%s.json", timestr, uuid)
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%s/%s@%s.json", timestr, container.UUID, uuid)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										280
									
								
								saas/uuid.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,280 @@
 | 
			
		||||
package saas
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/BurntSushi/toml"
 | 
			
		||||
	c "github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/hashicorp/go-uuid"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func renameKeyNameUTC(scannedAt time.Time, uuid string, container models.Container) string {
 | 
			
		||||
	timestr := scannedAt.UTC().Format(time.RFC3339)
 | 
			
		||||
	if len(container.ContainerID) == 0 {
 | 
			
		||||
		return fmt.Sprintf("%s/%s.json", timestr, uuid)
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%s/%s@%s.json", timestr, container.UUID, uuid)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const reUUID = "[\\da-f]{8}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{12}"
 | 
			
		||||
 | 
			
		||||
// 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.
 | 
			
		||||
func getOrCreateServerUUID(r models.ScanResult, server c.ServerInfo) (serverUUID string, err error) {
 | 
			
		||||
	if id, ok := server.UUIDs[r.ServerName]; !ok {
 | 
			
		||||
		if serverUUID, err = uuid.GenerateUUID(); err != nil {
 | 
			
		||||
			return "", xerrors.Errorf("Failed to generate UUID: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		matched, err := regexp.MatchString(reUUID, id)
 | 
			
		||||
		if !matched || err != nil {
 | 
			
		||||
			if serverUUID, err = uuid.GenerateUUID(); err != nil {
 | 
			
		||||
				return "", xerrors.Errorf("Failed to generate UUID: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return serverUUID, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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) (err error) {
 | 
			
		||||
	// Sort Host->Container
 | 
			
		||||
	sort.Slice(results, func(i, j int) bool {
 | 
			
		||||
		if results[i].ServerName == results[j].ServerName {
 | 
			
		||||
			return results[i].Container.ContainerID < results[j].Container.ContainerID
 | 
			
		||||
		}
 | 
			
		||||
		return results[i].ServerName < results[j].ServerName
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	re := regexp.MustCompile(reUUID)
 | 
			
		||||
	for i, r := range results {
 | 
			
		||||
		server := c.Conf.Servers[r.ServerName]
 | 
			
		||||
		if server.UUIDs == nil {
 | 
			
		||||
			server.UUIDs = map[string]string{}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		name := ""
 | 
			
		||||
		if r.IsContainer() {
 | 
			
		||||
			name = fmt.Sprintf("%s@%s", r.Container.Name, r.ServerName)
 | 
			
		||||
			serverUUID, err := getOrCreateServerUUID(r, server)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			if serverUUID != "" {
 | 
			
		||||
				server.UUIDs[r.ServerName] = serverUUID
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			name = r.ServerName
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if id, ok := server.UUIDs[name]; ok {
 | 
			
		||||
			ok := re.MatchString(id)
 | 
			
		||||
			if !ok || err != nil {
 | 
			
		||||
				util.Log.Warnf("UUID is invalid. Re-generate UUID %s: %s", id, err)
 | 
			
		||||
			} else {
 | 
			
		||||
				if r.IsContainer() {
 | 
			
		||||
					results[i].Container.UUID = id
 | 
			
		||||
					results[i].ServerUUID = server.UUIDs[r.ServerName]
 | 
			
		||||
				} else {
 | 
			
		||||
					results[i].ServerUUID = id
 | 
			
		||||
				}
 | 
			
		||||
				// continue if the UUID has already assigned and valid
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Generate a new UUID and set to config and scan result
 | 
			
		||||
		serverUUID, err := uuid.GenerateUUID()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		server.UUIDs[name] = serverUUID
 | 
			
		||||
		server = cleanForTOMLEncoding(server, c.Conf.Default)
 | 
			
		||||
		c.Conf.Servers[r.ServerName] = server
 | 
			
		||||
 | 
			
		||||
		if r.IsContainer() {
 | 
			
		||||
			results[i].Container.UUID = serverUUID
 | 
			
		||||
			results[i].ServerUUID = server.UUIDs[r.ServerName]
 | 
			
		||||
		} else {
 | 
			
		||||
			results[i].ServerUUID = serverUUID
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for name, server := range c.Conf.Servers {
 | 
			
		||||
		server = cleanForTOMLEncoding(server, c.Conf.Default)
 | 
			
		||||
		c.Conf.Servers[name] = server
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	email := &c.Conf.EMail
 | 
			
		||||
	if email.SMTPAddr == "" {
 | 
			
		||||
		email = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	slack := &c.Conf.Slack
 | 
			
		||||
	if slack.HookURL == "" {
 | 
			
		||||
		slack = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cveDict := &c.Conf.CveDict
 | 
			
		||||
	ovalDict := &c.Conf.OvalDict
 | 
			
		||||
	gost := &c.Conf.Gost
 | 
			
		||||
	exploit := &c.Conf.Exploit
 | 
			
		||||
	metasploit := &c.Conf.Metasploit
 | 
			
		||||
	http := &c.Conf.HTTP
 | 
			
		||||
	if http.URL == "" {
 | 
			
		||||
		http = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	syslog := &c.Conf.Syslog
 | 
			
		||||
	if syslog.Host == "" {
 | 
			
		||||
		syslog = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	aws := &c.Conf.AWS
 | 
			
		||||
	if aws.S3Bucket == "" {
 | 
			
		||||
		aws = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	azure := &c.Conf.Azure
 | 
			
		||||
	if azure.AccountName == "" {
 | 
			
		||||
		azure = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	chatWork := &c.Conf.ChatWork
 | 
			
		||||
	if chatWork.APIToken == "" {
 | 
			
		||||
		chatWork = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	saas := &c.Conf.Saas
 | 
			
		||||
	if saas.GroupID == 0 {
 | 
			
		||||
		saas = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c := struct {
 | 
			
		||||
		CveDict    *c.GoCveDictConf  `toml:"cveDict"`
 | 
			
		||||
		OvalDict   *c.GovalDictConf  `toml:"ovalDict"`
 | 
			
		||||
		Gost       *c.GostConf       `toml:"gost"`
 | 
			
		||||
		Exploit    *c.ExploitConf    `toml:"exploit"`
 | 
			
		||||
		Metasploit *c.MetasploitConf `toml:"metasploit"`
 | 
			
		||||
		Slack      *c.SlackConf      `toml:"slack"`
 | 
			
		||||
		Email      *c.SMTPConf       `toml:"email"`
 | 
			
		||||
		HTTP       *c.HTTPConf       `toml:"http"`
 | 
			
		||||
		Syslog     *c.SyslogConf     `toml:"syslog"`
 | 
			
		||||
		AWS        *c.AWS            `toml:"aws"`
 | 
			
		||||
		Azure      *c.Azure          `toml:"azure"`
 | 
			
		||||
		ChatWork   *c.ChatWorkConf   `toml:"chatWork"`
 | 
			
		||||
		Saas       *c.SaasConf       `toml:"saas"`
 | 
			
		||||
 | 
			
		||||
		Default c.ServerInfo            `toml:"default"`
 | 
			
		||||
		Servers map[string]c.ServerInfo `toml:"servers"`
 | 
			
		||||
	}{
 | 
			
		||||
		CveDict:    cveDict,
 | 
			
		||||
		OvalDict:   ovalDict,
 | 
			
		||||
		Gost:       gost,
 | 
			
		||||
		Exploit:    exploit,
 | 
			
		||||
		Metasploit: metasploit,
 | 
			
		||||
		Slack:      slack,
 | 
			
		||||
		Email:      email,
 | 
			
		||||
		HTTP:       http,
 | 
			
		||||
		Syslog:     syslog,
 | 
			
		||||
		AWS:        aws,
 | 
			
		||||
		Azure:      azure,
 | 
			
		||||
		ChatWork:   chatWork,
 | 
			
		||||
		Saas:       saas,
 | 
			
		||||
 | 
			
		||||
		Default: c.Conf.Default,
 | 
			
		||||
		Servers: c.Conf.Servers,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// rename the current config.toml to config.toml.bak
 | 
			
		||||
	info, err := os.Lstat(configPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		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 xerrors.Errorf("Failed to Read link %s: %w", configPath, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if err := os.Rename(realPath, realPath+".bak"); err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to rename %s: %w", configPath, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	if err := toml.NewEncoder(&buf).Encode(c); err != nil {
 | 
			
		||||
		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",
 | 
			
		||||
		"# See README for details: https://vuls.io/docs/en/usage-settings.html",
 | 
			
		||||
		str)
 | 
			
		||||
 | 
			
		||||
	return ioutil.WriteFile(realPath, []byte(str), 0600)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func cleanForTOMLEncoding(server c.ServerInfo, def c.ServerInfo) c.ServerInfo {
 | 
			
		||||
	if reflect.DeepEqual(server.Optional, def.Optional) {
 | 
			
		||||
		server.Optional = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if def.User == server.User {
 | 
			
		||||
		server.User = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if def.Host == server.Host {
 | 
			
		||||
		server.Host = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if def.Port == server.Port {
 | 
			
		||||
		server.Port = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if def.KeyPath == server.KeyPath {
 | 
			
		||||
		server.KeyPath = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if reflect.DeepEqual(server.ScanMode, def.ScanMode) {
 | 
			
		||||
		server.ScanMode = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if def.Type == server.Type {
 | 
			
		||||
		server.Type = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if reflect.DeepEqual(server.CpeNames, def.CpeNames) {
 | 
			
		||||
		server.CpeNames = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if def.OwaspDCXMLPath == server.OwaspDCXMLPath {
 | 
			
		||||
		server.OwaspDCXMLPath = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if reflect.DeepEqual(server.IgnoreCves, def.IgnoreCves) {
 | 
			
		||||
		server.IgnoreCves = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if reflect.DeepEqual(server.Enablerepo, def.Enablerepo) {
 | 
			
		||||
		server.Enablerepo = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for k, v := range def.Optional {
 | 
			
		||||
		if vv, ok := server.Optional[k]; ok && v == vv {
 | 
			
		||||
			delete(server.Optional, k)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return server
 | 
			
		||||
}
 | 
			
		||||
@@ -1,10 +1,9 @@
 | 
			
		||||
package report
 | 
			
		||||
package saas
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -147,6 +147,9 @@ func (o *alpine) parseApkInfo(stdout string) (models.Packages, error) {
 | 
			
		||||
		line := scanner.Text()
 | 
			
		||||
		ss := strings.Split(line, "-")
 | 
			
		||||
		if len(ss) < 3 {
 | 
			
		||||
			if strings.Contains(ss[0], "WARNING") {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to parse apk info -v: %s", line)
 | 
			
		||||
		}
 | 
			
		||||
		name := strings.Join(ss[:len(ss)-2], "-")
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										192
									
								
								scan/base.go
									
									
									
									
									
								
							
							
						
						@@ -4,13 +4,14 @@ import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net"
 | 
			
		||||
	"os"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/aquasecurity/fanal/analyzer"
 | 
			
		||||
	"github.com/aquasecurity/fanal/extractor"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
@@ -534,8 +535,7 @@ func (l *base) scanLibraries() (err error) {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	libFilemap := extractor.FileMap{}
 | 
			
		||||
 | 
			
		||||
	libFilemap := map[string][]byte{}
 | 
			
		||||
	detectFiles := l.ServerInfo.Lockfiles
 | 
			
		||||
 | 
			
		||||
	// auto detect lockfile
 | 
			
		||||
@@ -546,8 +546,8 @@ func (l *base) scanLibraries() (err error) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// 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"`)
 | 
			
		||||
		// find / -name "*package-lock.json" -o -name "*yarn.lock" ... 2>&1 | grep -v "find: "
 | 
			
		||||
		cmd := fmt.Sprintf(`find / ` + findopt[:len(findopt)-3] + ` 2>&1 | grep -v "find: "`)
 | 
			
		||||
		r := exec(l.ServerInfo, cmd, noSudo)
 | 
			
		||||
		if r.ExitStatus != 0 && r.ExitStatus != 1 {
 | 
			
		||||
			return xerrors.Errorf("Failed to find lock files")
 | 
			
		||||
@@ -563,25 +563,62 @@ func (l *base) scanLibraries() (err error) {
 | 
			
		||||
		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)
 | 
			
		||||
 | 
			
		||||
		var bytes []byte
 | 
			
		||||
		switch l.Distro.Family {
 | 
			
		||||
		case config.ServerTypePseudo:
 | 
			
		||||
			bytes, err = ioutil.ReadFile(path)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return xerrors.Errorf("Failed to get target file: %s, filepath: %s", err, path)
 | 
			
		||||
			}
 | 
			
		||||
		default:
 | 
			
		||||
			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)
 | 
			
		||||
			}
 | 
			
		||||
			bytes = []byte(r.Stdout)
 | 
			
		||||
		}
 | 
			
		||||
		libFilemap[path] = []byte(r.Stdout)
 | 
			
		||||
		libFilemap[path] = bytes
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
	for path, b := range libFilemap {
 | 
			
		||||
		res, err := analyzer.AnalyzeFile(path, &DummyFileInfo{}, func() ([]byte, error) {
 | 
			
		||||
			return b, nil
 | 
			
		||||
		})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to get libs: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		libscan, err := convertLibWithScanner(res.Applications)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to scan libraries: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		l.LibraryScanners = append(l.LibraryScanners, libscan...)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DummyFileInfo is a dummy struct for libscan
 | 
			
		||||
type DummyFileInfo struct{}
 | 
			
		||||
 | 
			
		||||
// Name is
 | 
			
		||||
func (d *DummyFileInfo) Name() string { return "dummy" }
 | 
			
		||||
 | 
			
		||||
// Size is
 | 
			
		||||
func (d *DummyFileInfo) Size() int64 { return 0 }
 | 
			
		||||
 | 
			
		||||
// Mode is
 | 
			
		||||
func (d *DummyFileInfo) Mode() os.FileMode { return 0 }
 | 
			
		||||
 | 
			
		||||
//ModTime is
 | 
			
		||||
func (d *DummyFileInfo) ModTime() time.Time { return time.Now() }
 | 
			
		||||
 | 
			
		||||
// IsDir is
 | 
			
		||||
func (d *DummyFileInfo) IsDir() bool { return false }
 | 
			
		||||
 | 
			
		||||
//Sys is
 | 
			
		||||
func (d *DummyFileInfo) Sys() interface{} { return nil }
 | 
			
		||||
 | 
			
		||||
func (l *base) scanWordPress() (err error) {
 | 
			
		||||
	wpOpts := []string{l.ServerInfo.WordPress.OSUser,
 | 
			
		||||
		l.ServerInfo.WordPress.DocRoot,
 | 
			
		||||
@@ -704,6 +741,117 @@ func (l *base) detectWpPlugins() ([]models.WpPackage, error) {
 | 
			
		||||
	return plugins, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *base) scanPorts() (err error) {
 | 
			
		||||
	dest := l.detectScanDest()
 | 
			
		||||
	open, err := l.execPortsScan(dest)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	l.updatePortStatus(open)
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *base) detectScanDest() map[string][]string {
 | 
			
		||||
	scanIPPortsMap := map[string][]string{}
 | 
			
		||||
 | 
			
		||||
	for _, p := range l.osPackages.Packages {
 | 
			
		||||
		if p.AffectedProcs == nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		for _, proc := range p.AffectedProcs {
 | 
			
		||||
			if proc.ListenPortStats == nil {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			for _, port := range proc.ListenPortStats {
 | 
			
		||||
				scanIPPortsMap[port.BindAddress] = append(scanIPPortsMap[port.BindAddress], port.Port)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	scanDestIPPorts := map[string][]string{}
 | 
			
		||||
	for addr, ports := range scanIPPortsMap {
 | 
			
		||||
		if addr == "*" {
 | 
			
		||||
			for _, addr := range l.ServerInfo.IPv4Addrs {
 | 
			
		||||
				scanDestIPPorts[addr] = append(scanDestIPPorts[addr], ports...)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			scanDestIPPorts[addr] = append(scanDestIPPorts[addr], ports...)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	uniqScanDestIPPorts := map[string][]string{}
 | 
			
		||||
	for i, scanDest := range scanDestIPPorts {
 | 
			
		||||
		m := map[string]bool{}
 | 
			
		||||
		for _, e := range scanDest {
 | 
			
		||||
			if !m[e] {
 | 
			
		||||
				m[e] = true
 | 
			
		||||
				uniqScanDestIPPorts[i] = append(uniqScanDestIPPorts[i], e)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return uniqScanDestIPPorts
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *base) execPortsScan(scanDestIPPorts map[string][]string) ([]string, error) {
 | 
			
		||||
	listenIPPorts := []string{}
 | 
			
		||||
 | 
			
		||||
	for ip, ports := range scanDestIPPorts {
 | 
			
		||||
		if !isLocalExec(l.ServerInfo.Port, l.ServerInfo.Host) && net.ParseIP(ip).IsLoopback() {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		for _, port := range ports {
 | 
			
		||||
			scanDest := ip + ":" + port
 | 
			
		||||
			conn, err := net.DialTimeout("tcp", scanDest, time.Duration(1)*time.Second)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			conn.Close()
 | 
			
		||||
			listenIPPorts = append(listenIPPorts, scanDest)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return listenIPPorts, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *base) updatePortStatus(listenIPPorts []string) {
 | 
			
		||||
	for name, p := range l.osPackages.Packages {
 | 
			
		||||
		if p.AffectedProcs == nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		for i, proc := range p.AffectedProcs {
 | 
			
		||||
			if proc.ListenPortStats == nil {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			for j, port := range proc.ListenPortStats {
 | 
			
		||||
				l.osPackages.Packages[name].AffectedProcs[i].ListenPortStats[j].PortReachableTo = l.findPortTestSuccessOn(listenIPPorts, port)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *base) findPortTestSuccessOn(listenIPPorts []string, searchListenPort models.PortStat) []string {
 | 
			
		||||
	addrs := []string{}
 | 
			
		||||
 | 
			
		||||
	for _, ipPort := range listenIPPorts {
 | 
			
		||||
		ipPort, err := models.NewPortStat(ipPort)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Warnf("Failed to find: %+v", err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if searchListenPort.BindAddress == "*" {
 | 
			
		||||
			if searchListenPort.Port == ipPort.Port {
 | 
			
		||||
				addrs = append(addrs, ipPort.BindAddress)
 | 
			
		||||
			}
 | 
			
		||||
		} else if searchListenPort.BindAddress == ipPort.BindAddress && searchListenPort.Port == ipPort.Port {
 | 
			
		||||
			addrs = append(addrs, ipPort.BindAddress)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return addrs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *base) ps() (stdout string, err error) {
 | 
			
		||||
	cmd := `LANGUAGE=en_US.UTF-8 ps --no-headers --ppid 2 -p 2 --deselect -o pid,comm`
 | 
			
		||||
	r := l.exec(util.PrependProxyEnv(cmd), noSudo)
 | 
			
		||||
@@ -766,13 +914,13 @@ func (l *base) lsOfListen() (stdout string, err error) {
 | 
			
		||||
	cmd := `lsof -i -P -n | grep LISTEN`
 | 
			
		||||
	r := l.exec(util.PrependProxyEnv(cmd), sudo)
 | 
			
		||||
	if !r.isSuccess(0, 1) {
 | 
			
		||||
		return "", xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
		return "", xerrors.Errorf("Failed to lsof: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	return r.Stdout, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *base) parseLsOf(stdout string) map[string]string {
 | 
			
		||||
	portPid := map[string]string{}
 | 
			
		||||
func (l *base) parseLsOf(stdout string) map[string][]string {
 | 
			
		||||
	portPids := map[string][]string{}
 | 
			
		||||
	scanner := bufio.NewScanner(strings.NewReader(stdout))
 | 
			
		||||
	for scanner.Scan() {
 | 
			
		||||
		ss := strings.Fields(scanner.Text())
 | 
			
		||||
@@ -780,7 +928,7 @@ func (l *base) parseLsOf(stdout string) map[string]string {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		pid, ipPort := ss[1], ss[8]
 | 
			
		||||
		portPid[ipPort] = pid
 | 
			
		||||
		portPids[ipPort] = util.AppendIfMissing(portPids[ipPort], pid)
 | 
			
		||||
	}
 | 
			
		||||
	return portPid
 | 
			
		||||
	return portPids
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ import (
 | 
			
		||||
	_ "github.com/aquasecurity/fanal/analyzer/library/poetry"
 | 
			
		||||
	_ "github.com/aquasecurity/fanal/analyzer/library/yarn"
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestParseDockerPs(t *testing.T) {
 | 
			
		||||
@@ -243,7 +244,7 @@ func Test_base_parseLsOf(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name        string
 | 
			
		||||
		args        args
 | 
			
		||||
		wantPortPid map[string]string
 | 
			
		||||
		wantPortPid map[string][]string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "lsof",
 | 
			
		||||
@@ -256,13 +257,34 @@ node       1498          ubuntu   21u  IPv6  20132      0t0  TCP *:35401 (LISTEN
 | 
			
		||||
node       1498          ubuntu   22u  IPv6  20133      0t0  TCP *:44801 (LISTEN)
 | 
			
		||||
docker-pr  9135            root    4u  IPv6 297133      0t0  TCP *:6379 (LISTEN)`,
 | 
			
		||||
			},
 | 
			
		||||
			wantPortPid: map[string]string{
 | 
			
		||||
				"localhost:53": "474",
 | 
			
		||||
				"*:22":         "644",
 | 
			
		||||
				"*:3128":       "959",
 | 
			
		||||
				"*:35401":      "1498",
 | 
			
		||||
				"*:44801":      "1498",
 | 
			
		||||
				"*:6379":       "9135",
 | 
			
		||||
			wantPortPid: map[string][]string{
 | 
			
		||||
				"localhost:53": {"474"},
 | 
			
		||||
				"*:22":         {"644"},
 | 
			
		||||
				"*:3128":       {"959"},
 | 
			
		||||
				"*:35401":      {"1498"},
 | 
			
		||||
				"*:44801":      {"1498"},
 | 
			
		||||
				"*:6379":       {"9135"},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "lsof-duplicate-port",
 | 
			
		||||
			args: args{
 | 
			
		||||
				stdout: `sshd      832   root    3u  IPv4  15731      0t0  TCP *:22 (LISTEN)
 | 
			
		||||
sshd      832   root    4u  IPv6  15740      0t0  TCP *:22 (LISTEN)
 | 
			
		||||
master   1099   root   13u  IPv4  16657      0t0  TCP 127.0.0.1:25 (LISTEN)
 | 
			
		||||
master   1099   root   14u  IPv6  16658      0t0  TCP [::1]:25 (LISTEN)
 | 
			
		||||
httpd   32250   root    4u  IPv6 334982      0t0  TCP *:80 (LISTEN)
 | 
			
		||||
httpd   32251 apache    4u  IPv6 334982      0t0  TCP *:80 (LISTEN)
 | 
			
		||||
httpd   32252 apache    4u  IPv6 334982      0t0  TCP *:80 (LISTEN)
 | 
			
		||||
httpd   32253 apache    4u  IPv6 334982      0t0  TCP *:80 (LISTEN)
 | 
			
		||||
httpd   32254 apache    4u  IPv6 334982      0t0  TCP *:80 (LISTEN)
 | 
			
		||||
httpd   32255 apache    4u  IPv6 334982      0t0  TCP *:80 (LISTEN)`,
 | 
			
		||||
			},
 | 
			
		||||
			wantPortPid: map[string][]string{
 | 
			
		||||
				"*:22":         {"832"},
 | 
			
		||||
				"127.0.0.1:25": {"1099"},
 | 
			
		||||
				"[::1]:25":     {"1099"},
 | 
			
		||||
				"*:80":         {"32250", "32251", "32252", "32253", "32254", "32255"},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
@@ -275,3 +297,197 @@ docker-pr  9135            root    4u  IPv6 297133      0t0  TCP *:6379 (LISTEN)
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_detectScanDest(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name   string
 | 
			
		||||
		args   base
 | 
			
		||||
		expect map[string][]string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "empty",
 | 
			
		||||
			args: base{osPackages: osPackages{
 | 
			
		||||
				Packages: models.Packages{"curl": models.Package{
 | 
			
		||||
					Name:       "curl",
 | 
			
		||||
					Version:    "7.64.0-4+deb10u1",
 | 
			
		||||
					NewVersion: "7.64.0-4+deb10u1",
 | 
			
		||||
				}},
 | 
			
		||||
			}},
 | 
			
		||||
			expect: map[string][]string{},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "single-addr",
 | 
			
		||||
			args: base{osPackages: osPackages{
 | 
			
		||||
				Packages: models.Packages{"libaudit1": models.Package{
 | 
			
		||||
					Name:       "libaudit1",
 | 
			
		||||
					Version:    "1:2.8.4-3",
 | 
			
		||||
					NewVersion: "1:2.8.4-3",
 | 
			
		||||
					AffectedProcs: []models.AffectedProcess{
 | 
			
		||||
						{PID: "21", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "22"}}}, {PID: "10876", Name: "sshd"}},
 | 
			
		||||
				},
 | 
			
		||||
				}},
 | 
			
		||||
			},
 | 
			
		||||
			expect: map[string][]string{"127.0.0.1": {"22"}},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "dup-addr-port",
 | 
			
		||||
			args: base{osPackages: osPackages{
 | 
			
		||||
				Packages: models.Packages{"libaudit1": models.Package{
 | 
			
		||||
					Name:       "libaudit1",
 | 
			
		||||
					Version:    "1:2.8.4-3",
 | 
			
		||||
					NewVersion: "1:2.8.4-3",
 | 
			
		||||
					AffectedProcs: []models.AffectedProcess{
 | 
			
		||||
						{PID: "21", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "22"}}}, {PID: "21", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "22"}}}},
 | 
			
		||||
				},
 | 
			
		||||
				}},
 | 
			
		||||
			},
 | 
			
		||||
			expect: map[string][]string{"127.0.0.1": {"22"}},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "multi-addr",
 | 
			
		||||
			args: base{osPackages: osPackages{
 | 
			
		||||
				Packages: models.Packages{"libaudit1": models.Package{
 | 
			
		||||
					Name:       "libaudit1",
 | 
			
		||||
					Version:    "1:2.8.4-3",
 | 
			
		||||
					NewVersion: "1:2.8.4-3",
 | 
			
		||||
					AffectedProcs: []models.AffectedProcess{
 | 
			
		||||
						{PID: "21", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "22"}}}, {PID: "21", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "192.168.1.1", Port: "22"}}}, {PID: "6261", Name: "nginx", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "80"}}}},
 | 
			
		||||
				},
 | 
			
		||||
				}},
 | 
			
		||||
			},
 | 
			
		||||
			expect: map[string][]string{"127.0.0.1": {"22", "80"}, "192.168.1.1": {"22"}},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "asterisk",
 | 
			
		||||
			args: base{
 | 
			
		||||
				osPackages: osPackages{
 | 
			
		||||
					Packages: models.Packages{"libaudit1": models.Package{
 | 
			
		||||
						Name:       "libaudit1",
 | 
			
		||||
						Version:    "1:2.8.4-3",
 | 
			
		||||
						NewVersion: "1:2.8.4-3",
 | 
			
		||||
						AffectedProcs: []models.AffectedProcess{
 | 
			
		||||
							{PID: "21", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "*", Port: "22"}}}},
 | 
			
		||||
					},
 | 
			
		||||
					}},
 | 
			
		||||
				ServerInfo: config.ServerInfo{
 | 
			
		||||
					IPv4Addrs: []string{"127.0.0.1", "192.168.1.1"},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expect: map[string][]string{"127.0.0.1": {"22"}, "192.168.1.1": {"22"}},
 | 
			
		||||
		}}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			if dest := tt.args.detectScanDest(); !reflect.DeepEqual(dest, tt.expect) {
 | 
			
		||||
				t.Errorf("base.detectScanDest() = %v, want %v", dest, tt.expect)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_updatePortStatus(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		l             base
 | 
			
		||||
		listenIPPorts []string
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name   string
 | 
			
		||||
		args   args
 | 
			
		||||
		expect models.Packages
 | 
			
		||||
	}{
 | 
			
		||||
		{name: "nil_affected_procs",
 | 
			
		||||
			args: args{
 | 
			
		||||
				l: base{osPackages: osPackages{
 | 
			
		||||
					Packages: models.Packages{"libc-bin": models.Package{Name: "libc-bin"}},
 | 
			
		||||
				}},
 | 
			
		||||
				listenIPPorts: []string{"127.0.0.1:22"}},
 | 
			
		||||
			expect: models.Packages{"libc-bin": models.Package{Name: "libc-bin"}}},
 | 
			
		||||
		{name: "nil_listen_ports",
 | 
			
		||||
			args: args{
 | 
			
		||||
				l: base{osPackages: osPackages{
 | 
			
		||||
					Packages: models.Packages{"bash": models.Package{Name: "bash", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}}}},
 | 
			
		||||
				}},
 | 
			
		||||
				listenIPPorts: []string{"127.0.0.1:22"}},
 | 
			
		||||
			expect: models.Packages{"bash": models.Package{Name: "bash", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}}}}},
 | 
			
		||||
		{name: "update_match_single_address",
 | 
			
		||||
			args: args{
 | 
			
		||||
				l: base{osPackages: osPackages{
 | 
			
		||||
					Packages: models.Packages{"libc6": models.Package{Name: "libc6", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}, {PID: "75", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "22"}}}}}},
 | 
			
		||||
				}},
 | 
			
		||||
				listenIPPorts: []string{"127.0.0.1:22"}},
 | 
			
		||||
			expect: models.Packages{"libc6": models.Package{Name: "libc6", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}, {PID: "75", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "22", PortReachableTo: []string{"127.0.0.1"}}}}}}}},
 | 
			
		||||
		{name: "update_match_multi_address",
 | 
			
		||||
			args: args{
 | 
			
		||||
				l: base{osPackages: osPackages{
 | 
			
		||||
					Packages: models.Packages{"libc6": models.Package{Name: "libc6", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}, {PID: "75", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "22"}, {BindAddress: "192.168.1.1", Port: "22"}}}}}},
 | 
			
		||||
				}},
 | 
			
		||||
				listenIPPorts: []string{"127.0.0.1:22", "192.168.1.1:22"}},
 | 
			
		||||
			expect: models.Packages{"libc6": models.Package{Name: "libc6", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}, {PID: "75", Name: "sshd", ListenPortStats: []models.PortStat{
 | 
			
		||||
				{BindAddress: "127.0.0.1", Port: "22", PortReachableTo: []string{"127.0.0.1"}},
 | 
			
		||||
				{BindAddress: "192.168.1.1", Port: "22", PortReachableTo: []string{"192.168.1.1"}},
 | 
			
		||||
			}}}}}},
 | 
			
		||||
		{name: "update_match_asterisk",
 | 
			
		||||
			args: args{
 | 
			
		||||
				l: base{osPackages: osPackages{
 | 
			
		||||
					Packages: models.Packages{"libc6": models.Package{Name: "libc6", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}, {PID: "75", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "*", Port: "22"}}}}}},
 | 
			
		||||
				}},
 | 
			
		||||
				listenIPPorts: []string{"127.0.0.1:22", "127.0.0.1:80", "192.168.1.1:22"}},
 | 
			
		||||
			expect: models.Packages{"libc6": models.Package{Name: "libc6", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}, {PID: "75", Name: "sshd", ListenPortStats: []models.PortStat{
 | 
			
		||||
				{BindAddress: "*", Port: "22", PortReachableTo: []string{"127.0.0.1", "192.168.1.1"}},
 | 
			
		||||
			}}}}}},
 | 
			
		||||
		{name: "update_multi_packages",
 | 
			
		||||
			args: args{
 | 
			
		||||
				l: base{osPackages: osPackages{
 | 
			
		||||
					Packages: models.Packages{
 | 
			
		||||
						"packa": models.Package{Name: "packa", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "80"}}}}},
 | 
			
		||||
						"packb": models.Package{Name: "packb", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "22"}}}}},
 | 
			
		||||
						"packc": models.Package{Name: "packc", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "22"}, {BindAddress: "192.168.1.1", Port: "22"}}}}},
 | 
			
		||||
						"packd": models.Package{Name: "packd", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "*", Port: "22"}}}}},
 | 
			
		||||
					},
 | 
			
		||||
				}},
 | 
			
		||||
				listenIPPorts: []string{"127.0.0.1:22", "192.168.1.1:22"}},
 | 
			
		||||
			expect: models.Packages{
 | 
			
		||||
				"packa": models.Package{Name: "packa", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "80", PortReachableTo: []string{}}}}}},
 | 
			
		||||
				"packb": models.Package{Name: "packb", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "22", PortReachableTo: []string{"127.0.0.1"}}}}}},
 | 
			
		||||
				"packc": models.Package{Name: "packc", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "22", PortReachableTo: []string{"127.0.0.1"}}, {BindAddress: "192.168.1.1", Port: "22", PortReachableTo: []string{"192.168.1.1"}}}}}},
 | 
			
		||||
				"packd": models.Package{Name: "packd", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "*", Port: "22", PortReachableTo: []string{"127.0.0.1", "192.168.1.1"}}}}}},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			tt.args.l.updatePortStatus(tt.args.listenIPPorts)
 | 
			
		||||
			if !reflect.DeepEqual(tt.args.l.osPackages.Packages, tt.expect) {
 | 
			
		||||
				t.Errorf("l.updatePortStatus() = %v, want %v", tt.args.l.osPackages.Packages, tt.expect)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_matchListenPorts(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		listenIPPorts    []string
 | 
			
		||||
		searchListenPort models.PortStat
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name   string
 | 
			
		||||
		args   args
 | 
			
		||||
		expect []string
 | 
			
		||||
	}{
 | 
			
		||||
		{name: "open_empty", args: args{listenIPPorts: []string{}, searchListenPort: models.PortStat{BindAddress: "127.0.0.1", Port: "22"}}, expect: []string{}},
 | 
			
		||||
		{name: "port_empty", args: args{listenIPPorts: []string{"127.0.0.1:22"}, searchListenPort: models.PortStat{}}, expect: []string{}},
 | 
			
		||||
		{name: "single_match", args: args{listenIPPorts: []string{"127.0.0.1:22"}, searchListenPort: models.PortStat{BindAddress: "127.0.0.1", Port: "22"}}, expect: []string{"127.0.0.1"}},
 | 
			
		||||
		{name: "no_match_address", args: args{listenIPPorts: []string{"127.0.0.1:22"}, searchListenPort: models.PortStat{BindAddress: "192.168.1.1", Port: "22"}}, expect: []string{}},
 | 
			
		||||
		{name: "no_match_port", args: args{listenIPPorts: []string{"127.0.0.1:22"}, searchListenPort: models.PortStat{BindAddress: "127.0.0.1", Port: "80"}}, expect: []string{}},
 | 
			
		||||
		{name: "asterisk_match", args: args{listenIPPorts: []string{"127.0.0.1:22", "127.0.0.1:80", "192.168.1.1:22"}, searchListenPort: models.PortStat{BindAddress: "*", Port: "22"}}, expect: []string{"127.0.0.1", "192.168.1.1"}},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	l := base{}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			if match := l.findPortTestSuccessOn(tt.args.listenIPPorts, tt.args.searchListenPort); !reflect.DeepEqual(match, tt.expect) {
 | 
			
		||||
				t.Errorf("findPortTestSuccessOn() = %v, want %v", match, tt.expect)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										196
									
								
								scan/debian.go
									
									
									
									
									
								
							
							
						
						@@ -2,6 +2,8 @@ package scan
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strconv"
 | 
			
		||||
@@ -305,7 +307,18 @@ func (o *debian) scanPackages() error {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.getServerInfo().Mode.IsDeep() || o.Distro.Family == config.Raspbian {
 | 
			
		||||
	if !o.getServerInfo().Mode.IsDeep() && o.Distro.Family == config.Raspbian {
 | 
			
		||||
		raspbianPacks := o.grepRaspbianPackages(updatable)
 | 
			
		||||
		unsecures, err := o.scanUnsecurePackages(raspbianPacks)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			o.log.Errorf("Failed to scan vulnerable packages: %s", err)
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		o.VulnInfos = unsecures
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.getServerInfo().Mode.IsDeep() {
 | 
			
		||||
		unsecures, err := o.scanUnsecurePackages(updatable)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			o.log.Errorf("Failed to scan vulnerable packages: %s", err)
 | 
			
		||||
@@ -314,6 +327,7 @@ func (o *debian) scanPackages() error {
 | 
			
		||||
		o.VulnInfos = unsecures
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -326,7 +340,7 @@ func (o *debian) rebootRequired() (bool, error) {
 | 
			
		||||
	case 1:
 | 
			
		||||
		return false, nil
 | 
			
		||||
	default:
 | 
			
		||||
		return false, xerrors.Errorf("Failed to check reboot reauired: %s", r)
 | 
			
		||||
		return false, xerrors.Errorf("Failed to check reboot required: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -463,6 +477,17 @@ func (o *debian) aptGetUpdate() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *debian) grepRaspbianPackages(updatables models.Packages) models.Packages {
 | 
			
		||||
	raspbianPacks := models.Packages{}
 | 
			
		||||
 | 
			
		||||
	for _, pack := range updatables {
 | 
			
		||||
		if models.IsRaspbianPackage(pack.Name, pack.Version) {
 | 
			
		||||
			raspbianPacks[pack.Name] = pack
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return raspbianPacks
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *debian) scanUnsecurePackages(updatable models.Packages) (models.VulnInfos, error) {
 | 
			
		||||
	// Setup changelog cache
 | 
			
		||||
	current := cache.Meta{
 | 
			
		||||
@@ -477,12 +502,29 @@ func (o *debian) scanUnsecurePackages(updatable models.Packages) (models.VulnInf
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Make a directory for saving changelog to get changelog in Raspbian
 | 
			
		||||
	tmpClogPath := ""
 | 
			
		||||
	if o.Distro.Family == config.Raspbian {
 | 
			
		||||
		tmpClogPath, err = o.makeTempChangelogDir()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Collect CVE information of upgradable packages
 | 
			
		||||
	vulnInfos, err := o.scanChangelogs(updatable, meta)
 | 
			
		||||
	vulnInfos, err := o.scanChangelogs(updatable, meta, tmpClogPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to scan unsecure packages. err: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Delete a directory for saving changelog to get changelog in Raspbian
 | 
			
		||||
	if o.Distro.Family == config.Raspbian {
 | 
			
		||||
		err := o.deleteTempChangelogDir(tmpClogPath)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to delete directory to save changelog for Raspbian. err: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return vulnInfos, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -505,7 +547,7 @@ func (o *debian) ensureChangelogCache(current cache.Meta) (*cache.Meta, error) {
 | 
			
		||||
 | 
			
		||||
	if current.Distro.Family != cached.Distro.Family ||
 | 
			
		||||
		current.Distro.Release != cached.Distro.Release {
 | 
			
		||||
		o.log.Debugf("Need to refesh meta: %s", current.Name)
 | 
			
		||||
		o.log.Debugf("Need to refresh meta: %s", current.Name)
 | 
			
		||||
		err = cache.DB.EnsureBuckets(current)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to ensure buckets. err: %s", err)
 | 
			
		||||
@@ -601,6 +643,39 @@ func (o *debian) parseAptGetUpgrade(stdout string) (updatableNames []string, err
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *debian) makeTempChangelogDir() (string, error) {
 | 
			
		||||
	suffix, err := generateSuffix()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	path := "/tmp/vuls-" + suffix
 | 
			
		||||
	cmd := fmt.Sprintf(`mkdir -p %s`, path)
 | 
			
		||||
	cmd = util.PrependProxyEnv(cmd)
 | 
			
		||||
	r := o.exec(cmd, noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return "", xerrors.Errorf("Failed to create directory to save changelog for Raspbian. cmd: %s, status: %d, stdout: %s, stderr: %s", cmd, r.ExitStatus, r.Stdout, r.Stderr)
 | 
			
		||||
	}
 | 
			
		||||
	return path, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func generateSuffix() (string, error) {
 | 
			
		||||
	var n uint64
 | 
			
		||||
	if err := binary.Read(rand.Reader, binary.LittleEndian, &n); err != nil {
 | 
			
		||||
		return "", xerrors.Errorf("Failed to generate Suffix. err: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	return strconv.FormatUint(n, 36), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *debian) deleteTempChangelogDir(tmpClogPath string) error {
 | 
			
		||||
	cmd := fmt.Sprintf(`rm -rf %s`, tmpClogPath)
 | 
			
		||||
	cmd = util.PrependProxyEnv(cmd)
 | 
			
		||||
	r := o.exec(cmd, noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return xerrors.Errorf("Failed to delete directory to save changelog for Raspbian. cmd: %s, status: %d, stdout: %s, stderr: %s", cmd, r.ExitStatus, r.Stdout, r.Stderr)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DetectedCveID has CveID, Confidence and DetectionMethod fields
 | 
			
		||||
// LenientMatching will be true if this vulnerability is not detected by accurate version matching.
 | 
			
		||||
// see https://github.com/future-architect/vuls/pull/328
 | 
			
		||||
@@ -609,7 +684,7 @@ type DetectedCveID struct {
 | 
			
		||||
	Confidence models.Confidence
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *debian) scanChangelogs(updatablePacks models.Packages, meta *cache.Meta) (models.VulnInfos, error) {
 | 
			
		||||
func (o *debian) scanChangelogs(updatablePacks models.Packages, meta *cache.Meta, tmpClogPath string) (models.VulnInfos, error) {
 | 
			
		||||
	type response struct {
 | 
			
		||||
		pack           *models.Package
 | 
			
		||||
		DetectedCveIDs []DetectedCveID
 | 
			
		||||
@@ -645,7 +720,7 @@ func (o *debian) scanChangelogs(updatablePacks models.Packages, meta *cache.Meta
 | 
			
		||||
					// if the changelog is not in cache or failed to get from local cache,
 | 
			
		||||
					// get the changelog of the package via internet.
 | 
			
		||||
					// After that, store it in the cache.
 | 
			
		||||
					if cveIDs, pack, err := o.fetchParseChangelog(p); err != nil {
 | 
			
		||||
					if cveIDs, pack, err := o.fetchParseChangelog(p, tmpClogPath); err != nil {
 | 
			
		||||
						errChan <- err
 | 
			
		||||
					} else {
 | 
			
		||||
						resChan <- response{pack, cveIDs}
 | 
			
		||||
@@ -743,13 +818,22 @@ func (o *debian) getChangelogCache(meta *cache.Meta, pack models.Package) string
 | 
			
		||||
	return changelog
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *debian) fetchParseChangelog(pack models.Package) ([]DetectedCveID, *models.Package, error) {
 | 
			
		||||
func (o *debian) fetchParseChangelog(pack models.Package, tmpClogPath string) ([]DetectedCveID, *models.Package, error) {
 | 
			
		||||
	cmd := ""
 | 
			
		||||
 | 
			
		||||
	switch o.Distro.Family {
 | 
			
		||||
	case config.Ubuntu, config.Raspbian:
 | 
			
		||||
	case config.Ubuntu:
 | 
			
		||||
		cmd = fmt.Sprintf(`PAGER=cat apt-get -q=2 changelog %s`, pack.Name)
 | 
			
		||||
	case config.Debian:
 | 
			
		||||
		cmd = fmt.Sprintf(`PAGER=cat aptitude -q=2 changelog %s`, pack.Name)
 | 
			
		||||
	case config.Raspbian:
 | 
			
		||||
		changelogPath, err := o.getChangelogPath(pack.Name, tmpClogPath)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			// Ignore this Error.
 | 
			
		||||
			o.log.Warnf("Failed to get Path to Changelog for Package: %s, err: %s", pack.Name, err)
 | 
			
		||||
			return nil, nil, nil
 | 
			
		||||
		}
 | 
			
		||||
		cmd = fmt.Sprintf(`gzip -cd %s | cat`, changelogPath)
 | 
			
		||||
	}
 | 
			
		||||
	cmd = util.PrependProxyEnv(cmd)
 | 
			
		||||
 | 
			
		||||
@@ -765,7 +849,7 @@ func (o *debian) fetchParseChangelog(pack models.Package) ([]DetectedCveID, *mod
 | 
			
		||||
 | 
			
		||||
	if clogFilledPack.Changelog.Method != models.FailedToGetChangelog {
 | 
			
		||||
		err := cache.DB.PutChangelog(
 | 
			
		||||
			o.getServerInfo().GetServerName(), pack.Name, pack.Changelog.Contents)
 | 
			
		||||
			o.getServerInfo().GetServerName(), pack.Name, stdout)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, nil, xerrors.New("Failed to put changelog into cache")
 | 
			
		||||
		}
 | 
			
		||||
@@ -775,6 +859,64 @@ func (o *debian) fetchParseChangelog(pack models.Package) ([]DetectedCveID, *mod
 | 
			
		||||
	return cveIDs, clogFilledPack, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *debian) getChangelogPath(packName, tmpClogPath string) (string, error) {
 | 
			
		||||
	// `apt download` downloads deb package to current directory
 | 
			
		||||
	cmd := fmt.Sprintf(`cd %s && apt download %s`, tmpClogPath, packName)
 | 
			
		||||
	cmd = util.PrependProxyEnv(cmd)
 | 
			
		||||
	r := o.exec(cmd, noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return "", xerrors.Errorf("Failed to Fetch deb package. cmd: %s, status: %d, stdout: %s, stderr: %s", cmd, r.ExitStatus, r.Stdout, r.Stderr)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd = fmt.Sprintf(`find %s -name "%s_*.deb"`, tmpClogPath, packName)
 | 
			
		||||
	cmd = util.PrependProxyEnv(cmd)
 | 
			
		||||
	r = o.exec(cmd, noSudo)
 | 
			
		||||
	if !r.isSuccess() || r.Stdout == "" {
 | 
			
		||||
		return "", xerrors.Errorf("Failed to find deb package. cmd: %s, status: %d, stdout: %s, stderr: %s", cmd, r.ExitStatus, r.Stdout, r.Stderr)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// e.g. <tmpPath>/ffmpeg_7%3a4.1.6-1~deb10u1+rpt1_armhf.deb\n => <tmpPath>/ffmpeg_7%3a4.1.6-1~deb10u1+rpt1_armhf
 | 
			
		||||
	packChangelogDir := strings.Split(r.Stdout, ".deb")[0]
 | 
			
		||||
	cmd = fmt.Sprintf(`dpkg-deb -x %s.deb %s`, packChangelogDir, packChangelogDir)
 | 
			
		||||
	cmd = util.PrependProxyEnv(cmd)
 | 
			
		||||
	r = o.exec(cmd, noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return "", xerrors.Errorf("Failed to dpkg-deb. cmd: %s, status: %d, stdout: %s, stderr: %s", cmd, r.ExitStatus, r.Stdout, r.Stderr)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// recurse if doc/packName is symbolic link
 | 
			
		||||
	changelogDocDir := fmt.Sprintf("%s/usr/share/doc/%s", packChangelogDir, packName)
 | 
			
		||||
	cmd = fmt.Sprintf(`test -L %s && readlink --no-newline %s`, changelogDocDir, changelogDocDir)
 | 
			
		||||
	cmd = util.PrependProxyEnv(cmd)
 | 
			
		||||
	r = o.exec(cmd, noSudo)
 | 
			
		||||
	if r.isSuccess() {
 | 
			
		||||
		return o.getChangelogPath(r.Stdout, tmpClogPath)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var results = make(map[string]execResult, 2)
 | 
			
		||||
	packChangelogPath := fmt.Sprintf("%s/changelog.Debian.gz", changelogDocDir)
 | 
			
		||||
	cmd = fmt.Sprintf(`test -e %s`, packChangelogPath)
 | 
			
		||||
	cmd = util.PrependProxyEnv(cmd)
 | 
			
		||||
	r = o.exec(cmd, noSudo)
 | 
			
		||||
	if r.isSuccess() {
 | 
			
		||||
		return packChangelogPath, nil
 | 
			
		||||
	}
 | 
			
		||||
	results["changelog.Debian.gz"] = r
 | 
			
		||||
 | 
			
		||||
	packChangelogPath = fmt.Sprintf("%s/changelog.gz", changelogDocDir)
 | 
			
		||||
	cmd = fmt.Sprintf(`test -e %s`, packChangelogPath)
 | 
			
		||||
	cmd = util.PrependProxyEnv(cmd)
 | 
			
		||||
	r = o.exec(cmd, noSudo)
 | 
			
		||||
	if r.isSuccess() {
 | 
			
		||||
		return packChangelogPath, nil
 | 
			
		||||
	}
 | 
			
		||||
	results["changelog.gz"] = r
 | 
			
		||||
 | 
			
		||||
	return "", xerrors.Errorf(
 | 
			
		||||
		"Failed to get changelog.\nresult(changelog.Debian.gz):%v\nresult(changelog.Debian.gz):%v",
 | 
			
		||||
		results["changelog.Debian.gz"], results["changelog.gz"])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *debian) getCveIDsFromChangelog(
 | 
			
		||||
	changelog, name, ver string) ([]DetectedCveID, *models.Package) {
 | 
			
		||||
 | 
			
		||||
@@ -874,6 +1016,21 @@ func (o *debian) parseChangelog(changelog, name, ver string, confidence models.C
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !found {
 | 
			
		||||
		if o.Distro.Family == config.Raspbian {
 | 
			
		||||
			pack := o.Packages[name]
 | 
			
		||||
			pack.Changelog = models.Changelog{
 | 
			
		||||
				Contents: strings.Join(buf, "\n"),
 | 
			
		||||
				Method:   models.ChangelogLenientMatchStr,
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			cves := []DetectedCveID{}
 | 
			
		||||
			for _, id := range cveIDs {
 | 
			
		||||
				cves = append(cves, DetectedCveID{id, confidence})
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return cves, &pack, nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pack := o.Packages[name]
 | 
			
		||||
		pack.Changelog = models.Changelog{
 | 
			
		||||
			Contents: "",
 | 
			
		||||
@@ -1137,14 +1294,21 @@ func (o *debian) dpkgPs() error {
 | 
			
		||||
		pidLoadedFiles[pid] = append(pidLoadedFiles[pid], ss...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pidListenPorts := map[string][]string{}
 | 
			
		||||
	pidListenPorts := map[string][]models.PortStat{}
 | 
			
		||||
	stdout, err = o.lsOfListen()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to ls of: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	portPid := o.parseLsOf(stdout)
 | 
			
		||||
	for port, pid := range portPid {
 | 
			
		||||
		pidListenPorts[pid] = append(pidListenPorts[pid], port)
 | 
			
		||||
	portPids := o.parseLsOf(stdout)
 | 
			
		||||
	for ipPort, pids := range portPids {
 | 
			
		||||
		for _, pid := range pids {
 | 
			
		||||
			portStat, err := models.NewPortStat(ipPort)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				o.log.Warnf("Failed to parse ip:port: %s, err: %+v", ipPort, err)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			pidListenPorts[pid] = append(pidListenPorts[pid], *portStat)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for pid, loadedFiles := range pidLoadedFiles {
 | 
			
		||||
@@ -1160,9 +1324,9 @@ func (o *debian) dpkgPs() error {
 | 
			
		||||
			procName = pidNames[pid]
 | 
			
		||||
		}
 | 
			
		||||
		proc := models.AffectedProcess{
 | 
			
		||||
			PID:         pid,
 | 
			
		||||
			Name:        procName,
 | 
			
		||||
			ListenPorts: pidListenPorts[pid],
 | 
			
		||||
			PID:             pid,
 | 
			
		||||
			Name:            procName,
 | 
			
		||||
			ListenPortStats: pidListenPorts[pid],
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, n := range pkgNames {
 | 
			
		||||
 
 | 
			
		||||
@@ -225,7 +225,7 @@ systemd (228-5) unstable; urgency=medium`,
 | 
			
		||||
	for i, tt := range tests {
 | 
			
		||||
		aCveIDs, aPack := d.getCveIDsFromChangelog(tt.in[2], tt.in[0], tt.in[1])
 | 
			
		||||
		if len(aCveIDs) != len(tt.cveIDs) {
 | 
			
		||||
			t.Errorf("[%d] Len of return array are'nt same. expected %#v, actual %#v", i, tt.cveIDs, aCveIDs)
 | 
			
		||||
			t.Errorf("[%d] Len of return array aren't same. expected %#v, actual %#v", i, tt.cveIDs, aCveIDs)
 | 
			
		||||
			t.Errorf(pp.Sprintf("%s", tt.in))
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
@@ -433,7 +433,7 @@ func TestGetChangelogCache(t *testing.T) {
 | 
			
		||||
	d := newDebian(config.ServerInfo{})
 | 
			
		||||
	actual := d.getChangelogCache(&meta, pack)
 | 
			
		||||
	if actual != "" {
 | 
			
		||||
		t.Errorf("Failed to get empty stirng from cache:")
 | 
			
		||||
		t.Errorf("Failed to get empty string from cache:")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	clog := "changelog-text"
 | 
			
		||||
@@ -746,3 +746,121 @@ libuuid1:amd64: /lib/x86_64-linux-gnu/libuuid.so.1.3.0`,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParseChangelog(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		changelog string
 | 
			
		||||
		name      string
 | 
			
		||||
		ver       string
 | 
			
		||||
	}
 | 
			
		||||
	type expect struct {
 | 
			
		||||
		cveIDs []DetectedCveID
 | 
			
		||||
		pack   models.Package
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		packName string
 | 
			
		||||
		args     args
 | 
			
		||||
		expect   expect
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			packName: "vlc",
 | 
			
		||||
			args: args{
 | 
			
		||||
				changelog: `vlc (3.0.11-0+deb10u1+rpt2) buster; urgency=medium
 | 
			
		||||
 | 
			
		||||
  * Add MMAL patch 19
 | 
			
		||||
 | 
			
		||||
 -- Serge Schneider <serge@raspberrypi.com>  Wed, 29 Jul 2020 14:28:28 +0100
 | 
			
		||||
 | 
			
		||||
vlc (3.0.11-0+deb10u1+rpt1) buster; urgency=high
 | 
			
		||||
 | 
			
		||||
  * Add MMAL patch 18
 | 
			
		||||
  * Add libxrandr-dev dependency
 | 
			
		||||
  * Add libdrm-dev dependency
 | 
			
		||||
  * Disable vdpau, libva, aom
 | 
			
		||||
  * Enable dav1d
 | 
			
		||||
 | 
			
		||||
 -- Serge Schneider <serge@raspberrypi.com>  Wed, 17 Jun 2020 10:30:58 +0100
 | 
			
		||||
 | 
			
		||||
vlc (3.0.11-0+deb10u1) buster-security; urgency=high
 | 
			
		||||
 | 
			
		||||
  * New upstream release
 | 
			
		||||
    - Fix heap-based buffer overflow in hxxx_nall (CVE-2020-13428)
 | 
			
		||||
 | 
			
		||||
 -- Sebastian Ramacher <sramacher@debian.org>  Mon, 15 Jun 2020 23:08:37 +0200
 | 
			
		||||
 | 
			
		||||
vlc (3.0.10-0+deb10u1) buster-security; urgency=medium`,
 | 
			
		||||
				name: "vlc",
 | 
			
		||||
				ver:  "3.0.10-0+deb10u1+rpt2",
 | 
			
		||||
			},
 | 
			
		||||
			expect: expect{
 | 
			
		||||
				cveIDs: []DetectedCveID{{"CVE-2020-13428", models.ChangelogExactMatch}},
 | 
			
		||||
				pack: models.Package{Changelog: models.Changelog{
 | 
			
		||||
					Contents: `vlc (3.0.11-0+deb10u1+rpt2) buster; urgency=medium
 | 
			
		||||
 | 
			
		||||
  * Add MMAL patch 19
 | 
			
		||||
 | 
			
		||||
 -- Serge Schneider <serge@raspberrypi.com>  Wed, 29 Jul 2020 14:28:28 +0100
 | 
			
		||||
 | 
			
		||||
vlc (3.0.11-0+deb10u1+rpt1) buster; urgency=high
 | 
			
		||||
 | 
			
		||||
  * Add MMAL patch 18
 | 
			
		||||
  * Add libxrandr-dev dependency
 | 
			
		||||
  * Add libdrm-dev dependency
 | 
			
		||||
  * Disable vdpau, libva, aom
 | 
			
		||||
  * Enable dav1d
 | 
			
		||||
 | 
			
		||||
 -- Serge Schneider <serge@raspberrypi.com>  Wed, 17 Jun 2020 10:30:58 +0100
 | 
			
		||||
 | 
			
		||||
vlc (3.0.11-0+deb10u1) buster-security; urgency=high
 | 
			
		||||
 | 
			
		||||
  * New upstream release
 | 
			
		||||
    - Fix heap-based buffer overflow in hxxx_nall (CVE-2020-13428)
 | 
			
		||||
 | 
			
		||||
 -- Sebastian Ramacher <sramacher@debian.org>  Mon, 15 Jun 2020 23:08:37 +0200
 | 
			
		||||
`,
 | 
			
		||||
					Method: models.ChangelogExactMatchStr,
 | 
			
		||||
				}},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			packName: "realvnc-vnc-server",
 | 
			
		||||
			args: args{
 | 
			
		||||
				changelog: `realvnc-vnc (6.7.2.42622) stable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Debian package for VNC Server
 | 
			
		||||
 | 
			
		||||
 -- RealVNC <noreply@realvnc.com>  Wed, 13 May 2020 19:51:40 +0100
 | 
			
		||||
 | 
			
		||||
`,
 | 
			
		||||
				name: "realvnc-vnc-server",
 | 
			
		||||
				ver:  "6.7.1.42348",
 | 
			
		||||
			},
 | 
			
		||||
			expect: expect{
 | 
			
		||||
				cveIDs: []DetectedCveID{},
 | 
			
		||||
				pack: models.Package{Changelog: models.Changelog{
 | 
			
		||||
					Contents: `realvnc-vnc (6.7.2.42622) stable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Debian package for VNC Server
 | 
			
		||||
 | 
			
		||||
 -- RealVNC <noreply@realvnc.com>  Wed, 13 May 2020 19:51:40 +0100
 | 
			
		||||
`,
 | 
			
		||||
					Method: models.ChangelogLenientMatchStr,
 | 
			
		||||
				}},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	o := newDebian(config.ServerInfo{})
 | 
			
		||||
	o.Distro = config.Distro{Family: config.Raspbian}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.packName, func(t *testing.T) {
 | 
			
		||||
			cveIDs, pack, _ := o.parseChangelog(tt.args.changelog, tt.args.name, tt.args.ver, models.ChangelogExactMatch)
 | 
			
		||||
			if !reflect.DeepEqual(cveIDs, tt.expect.cveIDs) {
 | 
			
		||||
				t.Errorf("[%s]->cveIDs: expected: %s, actual: %s", tt.packName, tt.expect.cveIDs, cveIDs)
 | 
			
		||||
			}
 | 
			
		||||
			if !reflect.DeepEqual(pack.Changelog.Contents, tt.expect.pack.Changelog.Contents) {
 | 
			
		||||
				t.Errorf("[%s]->changelog.Contents: expected: %s, actual: %s", tt.packName, tt.expect.pack.Changelog.Contents, pack.Changelog.Contents)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -260,7 +260,9 @@ func sshExecExternal(c conf.ServerInfo, cmd string, sudo bool) (result execResul
 | 
			
		||||
 | 
			
		||||
	defaultSSHArgs := []string{"-tt"}
 | 
			
		||||
 | 
			
		||||
	if !conf.Conf.SSHConfig {
 | 
			
		||||
	if 0 < len(c.SSHConfigPath) {
 | 
			
		||||
		defaultSSHArgs = append(defaultSSHArgs, "-F", c.SSHConfigPath)
 | 
			
		||||
	} else {
 | 
			
		||||
		home, err := homedir.Dir()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			msg := fmt.Sprintf("Failed to get HOME directory: %s", err)
 | 
			
		||||
@@ -285,6 +287,10 @@ func sshExecExternal(c conf.ServerInfo, cmd string, sudo bool) (result execResul
 | 
			
		||||
		defaultSSHArgs = append(defaultSSHArgs, "-vvv")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(c.JumpServer) != 0 {
 | 
			
		||||
		defaultSSHArgs = append(defaultSSHArgs, "-J", strings.Join(c.JumpServer, ","))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	args := append(defaultSSHArgs, fmt.Sprintf("%s@%s", c.User, c.Host))
 | 
			
		||||
	args = append(args, "-p", c.Port)
 | 
			
		||||
	if 0 < len(c.KeyPath) {
 | 
			
		||||
@@ -327,7 +333,7 @@ func sshExecExternal(c conf.ServerInfo, cmd string, sudo bool) (result execResul
 | 
			
		||||
 | 
			
		||||
func getSSHLogger(log ...*logrus.Entry) *logrus.Entry {
 | 
			
		||||
	if len(log) == 0 {
 | 
			
		||||
		return util.NewCustomLogger(conf.ServerInfo{})
 | 
			
		||||
		return util.Log
 | 
			
		||||
	}
 | 
			
		||||
	return log[0]
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -163,12 +163,24 @@ func (o *bsd) rebootRequired() (bool, error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *bsd) scanInstalledPackages() (models.Packages, error) {
 | 
			
		||||
	cmd := util.PrependProxyEnv("pkg version -v")
 | 
			
		||||
	// https://github.com/future-architect/vuls/issues/1042
 | 
			
		||||
	cmd := util.PrependProxyEnv("pkg info")
 | 
			
		||||
	r := o.exec(cmd, noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	return o.parsePkgVersion(r.Stdout), nil
 | 
			
		||||
	pkgs := o.parsePkgInfo(r.Stdout)
 | 
			
		||||
 | 
			
		||||
	cmd = util.PrependProxyEnv("pkg version -v")
 | 
			
		||||
	r = o.exec(cmd, noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
	// `pkg-audit` has a new version, overwrite it.
 | 
			
		||||
	for name, p := range o.parsePkgVersion(r.Stdout) {
 | 
			
		||||
		pkgs[name] = p
 | 
			
		||||
	}
 | 
			
		||||
	return pkgs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *bsd) scanUnsecurePackages() (models.VulnInfos, error) {
 | 
			
		||||
@@ -247,6 +259,27 @@ func (o *bsd) scanUnsecurePackages() (models.VulnInfos, error) {
 | 
			
		||||
	return vinfos, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *bsd) parsePkgInfo(stdout string) models.Packages {
 | 
			
		||||
	packs := models.Packages{}
 | 
			
		||||
	lines := strings.Split(stdout, "\n")
 | 
			
		||||
	for _, l := range lines {
 | 
			
		||||
		fields := strings.Fields(l)
 | 
			
		||||
		if len(fields) < 2 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		packVer := fields[0]
 | 
			
		||||
		splitted := strings.Split(packVer, "-")
 | 
			
		||||
		ver := splitted[len(splitted)-1]
 | 
			
		||||
		name := strings.Join(splitted[:len(splitted)-1], "-")
 | 
			
		||||
		packs[name] = models.Package{
 | 
			
		||||
			Name:    name,
 | 
			
		||||
			Version: ver,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return packs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *bsd) parsePkgVersion(stdout string) models.Packages {
 | 
			
		||||
	packs := models.Packages{}
 | 
			
		||||
	lines := strings.Split(stdout, "\n")
 | 
			
		||||
 
 | 
			
		||||
@@ -197,3 +197,50 @@ WWW: https://vuxml.FreeBSD.org/freebsd/ab3e98d9-8175-11e4-907d-d050992ecde8.html
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParsePkgInfo(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in       string
 | 
			
		||||
		expected models.Packages
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			`bash-4.2.45                        Universal Command Line Interface for Amazon Web Services
 | 
			
		||||
gettext-0.18.3.1                   Startup scripts for FreeBSD/EC2 environment
 | 
			
		||||
tcl84-8.4.20_2,1                   Update the system using freebsd-update when it first boots
 | 
			
		||||
ntp-4.2.8p8_1                      GNU gettext runtime libraries and programs
 | 
			
		||||
teTeX-base-3.0_25                  Foreign Function Interface`,
 | 
			
		||||
			models.Packages{
 | 
			
		||||
				"bash": {
 | 
			
		||||
					Name:    "bash",
 | 
			
		||||
					Version: "4.2.45",
 | 
			
		||||
				},
 | 
			
		||||
				"gettext": {
 | 
			
		||||
					Name:    "gettext",
 | 
			
		||||
					Version: "0.18.3.1",
 | 
			
		||||
				},
 | 
			
		||||
				"tcl84": {
 | 
			
		||||
					Name:    "tcl84",
 | 
			
		||||
					Version: "8.4.20_2,1",
 | 
			
		||||
				},
 | 
			
		||||
				"teTeX-base": {
 | 
			
		||||
					Name:    "teTeX-base",
 | 
			
		||||
					Version: "3.0_25",
 | 
			
		||||
				},
 | 
			
		||||
				"ntp": {
 | 
			
		||||
					Name:    "ntp",
 | 
			
		||||
					Version: "4.2.8p8_1",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	d := newBsd(config.ServerInfo{})
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		actual := d.parsePkgInfo(tt.in)
 | 
			
		||||
		if !reflect.DeepEqual(tt.expected, actual) {
 | 
			
		||||
			e := pp.Sprintf("%v", tt.expected)
 | 
			
		||||
			a := pp.Sprintf("%v", actual)
 | 
			
		||||
			t.Errorf("expected %s, actual %s", e, a)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -279,13 +279,13 @@ func (o *redhatBase) parseInstalledPackages(stdout string) (models.Packages, mod
 | 
			
		||||
	// openssl 0 1.0.1e	30.el6.11 x86_64
 | 
			
		||||
	lines := strings.Split(stdout, "\n")
 | 
			
		||||
	for _, line := range lines {
 | 
			
		||||
		if trimed := strings.TrimSpace(line); len(trimed) != 0 {
 | 
			
		||||
		if trimmed := strings.TrimSpace(line); len(trimmed) != 0 {
 | 
			
		||||
			pack, err := o.parseInstalledPackagesLine(line)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, nil, err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Kernel package may be isntalled multiple versions.
 | 
			
		||||
			// `Kernel` and `kernel-devel` package may be installed multiple versions.
 | 
			
		||||
			// From the viewpoint of vulnerability detection,
 | 
			
		||||
			// pay attention only to the running kernel
 | 
			
		||||
			isKernel, running := isRunningKernel(pack, o.Distro.Family, o.Kernel)
 | 
			
		||||
@@ -361,7 +361,7 @@ func (o *redhatBase) scanUpdatablePackages() (models.Packages, error) {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Collect Updateble packages, installed, candidate version and repository.
 | 
			
		||||
	// Collect Updatable packages, installed, candidate version and repository.
 | 
			
		||||
	return o.parseUpdatablePacksLines(r.Stdout)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -491,14 +491,21 @@ func (o *redhatBase) yumPs() error {
 | 
			
		||||
		pidLoadedFiles[pid] = append(pidLoadedFiles[pid], ss...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pidListenPorts := map[string][]string{}
 | 
			
		||||
	pidListenPorts := map[string][]models.PortStat{}
 | 
			
		||||
	stdout, err = o.lsOfListen()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to ls of: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	portPid := o.parseLsOf(stdout)
 | 
			
		||||
	for port, pid := range portPid {
 | 
			
		||||
		pidListenPorts[pid] = append(pidListenPorts[pid], port)
 | 
			
		||||
	portPids := o.parseLsOf(stdout)
 | 
			
		||||
	for ipPort, pids := range portPids {
 | 
			
		||||
		for _, pid := range pids {
 | 
			
		||||
			portStat, err := models.NewPortStat(ipPort)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				o.log.Warnf("Failed to parse ip:port: %s, err: %+v", ipPort, err)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			pidListenPorts[pid] = append(pidListenPorts[pid], *portStat)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for pid, loadedFiles := range pidLoadedFiles {
 | 
			
		||||
@@ -519,9 +526,9 @@ func (o *redhatBase) yumPs() error {
 | 
			
		||||
			procName = pidNames[pid]
 | 
			
		||||
		}
 | 
			
		||||
		proc := models.AffectedProcess{
 | 
			
		||||
			PID:         pid,
 | 
			
		||||
			Name:        procName,
 | 
			
		||||
			ListenPorts: pidListenPorts[pid],
 | 
			
		||||
			PID:             pid,
 | 
			
		||||
			Name:            procName,
 | 
			
		||||
			ListenPortStats: pidListenPorts[pid],
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for fqpn := range uniq {
 | 
			
		||||
@@ -630,8 +637,8 @@ func (o *redhatBase) procPathToFQPN(execCommand string) (string, error) {
 | 
			
		||||
func (o *redhatBase) getPkgName(paths []string) (pkgNames []string, err error) {
 | 
			
		||||
	cmd := o.rpmQf(o.Distro) + strings.Join(paths, " ")
 | 
			
		||||
	r := o.exec(util.PrependProxyEnv(cmd), noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to SSH: %s", r)
 | 
			
		||||
	if !r.isSuccess(0, 2, 4, 8) {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to rpm -qf: %s, cmd: %s", r, cmd)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	scanner := bufio.NewScanner(strings.NewReader(r.Stdout))
 | 
			
		||||
 
 | 
			
		||||