Compare commits
	
		
			37 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					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 | 
							
								
								
									
										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
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,8 @@ release:
 | 
			
		||||
    owner: future-architect
 | 
			
		||||
    name: vuls
 | 
			
		||||
builds:
 | 
			
		||||
- goos:
 | 
			
		||||
- id: vuls
 | 
			
		||||
  goos:
 | 
			
		||||
  - linux
 | 
			
		||||
  goarch:
 | 
			
		||||
  - amd64
 | 
			
		||||
@@ -15,8 +16,50 @@ builds:
 | 
			
		||||
      - -a
 | 
			
		||||
  ldflags: -s -w -X main.version={{.Version}} -X main.revision={{.Commit}} 
 | 
			
		||||
  binary: vuls
 | 
			
		||||
 | 
			
		||||
- id: trivy-to-vuls
 | 
			
		||||
  goos:
 | 
			
		||||
  - linux
 | 
			
		||||
  goarch:
 | 
			
		||||
  - amd64
 | 
			
		||||
  main: ./contrib/trivy/cmd/main.go
 | 
			
		||||
  binary: trivy-to-vuls
 | 
			
		||||
 | 
			
		||||
- id: future-vuls
 | 
			
		||||
  goos:
 | 
			
		||||
  - linux
 | 
			
		||||
  goarch:
 | 
			
		||||
  - amd64
 | 
			
		||||
  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: 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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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,7 +36,7 @@ func (*ConfigtestCmd) Usage() string {
 | 
			
		||||
			[-log-dir=/path/to/log]
 | 
			
		||||
			[-ask-key-password]
 | 
			
		||||
			[-timeout=300]
 | 
			
		||||
			[-ssh-external]
 | 
			
		||||
			[-ssh-config]
 | 
			
		||||
			[-containers-only]
 | 
			
		||||
			[-http-proxy=http://192.168.0.1:8080]
 | 
			
		||||
			[-debug]
 | 
			
		||||
@@ -69,7 +69,7 @@ func (p *ConfigtestCmd) SetFlags(f *flag.FlagSet) {
 | 
			
		||||
		"Use Native Go implementation of SSH. Default: Use the external command")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.SSHConfig, "ssh-config", false,
 | 
			
		||||
		"Use SSH options specified in ssh_config preferentially")
 | 
			
		||||
		"[Deprecated] Use SSH options specified in ssh_config preferentially")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.ContainersOnly, "containers-only", false,
 | 
			
		||||
		"Test containers only. Default: Test both of hosts and containers")
 | 
			
		||||
@@ -79,7 +79,6 @@ func (p *ConfigtestCmd) SetFlags(f *flag.FlagSet) {
 | 
			
		||||
 | 
			
		||||
// Execute execute
 | 
			
		||||
func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
 | 
			
		||||
	// Setup Logger
 | 
			
		||||
	util.Log = util.NewCustomLogger(c.ServerInfo{})
 | 
			
		||||
 | 
			
		||||
	if err := mkdirDotVuls(); err != nil {
 | 
			
		||||
@@ -108,6 +107,16 @@ func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfa
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.SSHConfig {
 | 
			
		||||
		msg := []string{
 | 
			
		||||
			"-ssh-config is deprecated",
 | 
			
		||||
			"If you update Vuls and get this error, there may be incompatible changes in config.toml",
 | 
			
		||||
			"Please check config.toml template : https://vuls.io/docs/en/usage-settings.html",
 | 
			
		||||
		}
 | 
			
		||||
		util.Log.Errorf("%s", strings.Join(msg, "\n"))
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var servernames []string
 | 
			
		||||
	if 0 < len(f.Args()) {
 | 
			
		||||
		servernames = f.Args()
 | 
			
		||||
 
 | 
			
		||||
@@ -95,6 +95,11 @@ type        = "sqlite3"
 | 
			
		||||
sqlite3Path = "/path/to/go-exploitdb.sqlite3"
 | 
			
		||||
#url        = ""
 | 
			
		||||
 | 
			
		||||
[metasploit]
 | 
			
		||||
type        = "sqlite3"
 | 
			
		||||
sqlite3Path = "/path/to/go-msfdb.sqlite3"
 | 
			
		||||
#url        = ""
 | 
			
		||||
 | 
			
		||||
# https://vuls.io/docs/en/usage-settings.html#slack-section
 | 
			
		||||
#[slack]
 | 
			
		||||
#hookURL      = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
 | 
			
		||||
@@ -187,6 +192,7 @@ sqlite3Path = "/path/to/go-exploitdb.sqlite3"
 | 
			
		||||
host                = "{{$ip}}"
 | 
			
		||||
#port               = "22"
 | 
			
		||||
#user               = "root"
 | 
			
		||||
#sshConfigPath		= "/home/username/.ssh/config"
 | 
			
		||||
#keyPath            = "/home/username/.ssh/id_rsa"
 | 
			
		||||
#scanMode           = ["fast", "fast-root", "deep", "offline"]
 | 
			
		||||
#type               = "pseudo"
 | 
			
		||||
 
 | 
			
		||||
@@ -11,22 +11,23 @@ import (
 | 
			
		||||
	"github.com/future-architect/vuls/exploit"
 | 
			
		||||
	"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/report"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/google/subcommands"
 | 
			
		||||
	"github.com/k0kubun/pp"
 | 
			
		||||
	cvelog "github.com/kotakanbe/go-cve-dictionary/log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ReportCmd is subcommand for reporting
 | 
			
		||||
type ReportCmd struct {
 | 
			
		||||
	configPath  string
 | 
			
		||||
	cveDict     c.GoCveDictConf
 | 
			
		||||
	ovalDict    c.GovalDictConf
 | 
			
		||||
	gostConf    c.GostConf
 | 
			
		||||
	exploitConf c.ExploitConf
 | 
			
		||||
	httpConf    c.HTTPConf
 | 
			
		||||
	configPath     string
 | 
			
		||||
	cveDict        c.GoCveDictConf
 | 
			
		||||
	ovalDict       c.GovalDictConf
 | 
			
		||||
	gostConf       c.GostConf
 | 
			
		||||
	exploitConf    c.ExploitConf
 | 
			
		||||
	metasploitConf c.MetasploitConf
 | 
			
		||||
	httpConf       c.HTTPConf
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Name return subcommand name
 | 
			
		||||
@@ -87,6 +88,9 @@ func (*ReportCmd) Usage() string {
 | 
			
		||||
		[-exploitdb-type=sqlite3|mysql|redis|http]
 | 
			
		||||
		[-exploitdb-sqlite3-path=/path/to/exploitdb.sqlite3]
 | 
			
		||||
		[-exploitdb-url=http://127.0.0.1:1326 or DB connection string]
 | 
			
		||||
		[-msfdb-type=sqlite3|mysql|redis|http]
 | 
			
		||||
		[-msfdb-sqlite3-path=/path/to/msfdb.sqlite3]
 | 
			
		||||
		[-msfdb-url=http://127.0.0.1:1327 or DB connection string]
 | 
			
		||||
		[-http="http://vuls-report-server"]
 | 
			
		||||
		[-trivy-cachedb-dir=/path/to/dir]
 | 
			
		||||
 | 
			
		||||
@@ -139,6 +143,7 @@ func (p *ReportCmd) SetFlags(f *flag.FlagSet) {
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.FormatJSON, "format-json", false, "JSON format")
 | 
			
		||||
	f.BoolVar(&c.Conf.FormatXML, "format-xml", false, "XML format")
 | 
			
		||||
	f.BoolVar(&c.Conf.FormatCsvList, "format-csv", false, "CSV format")
 | 
			
		||||
	f.BoolVar(&c.Conf.FormatOneEMail, "format-one-email", false,
 | 
			
		||||
		"Send all the host report via only one EMail (Specify with -to-email)")
 | 
			
		||||
	f.BoolVar(&c.Conf.FormatOneLineText, "format-one-line-text", false,
 | 
			
		||||
@@ -192,6 +197,12 @@ func (p *ReportCmd) SetFlags(f *flag.FlagSet) {
 | 
			
		||||
	f.StringVar(&p.exploitConf.URL, "exploitdb-url", "",
 | 
			
		||||
		"http://exploit.com:1326 or DB connection string")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(&p.metasploitConf.Type, "msfdb-type", "",
 | 
			
		||||
		"DB type of msf (sqlite3, mysql, postgres, redis or http)")
 | 
			
		||||
	f.StringVar(&p.metasploitConf.SQLite3Path, "msfdb-sqlite3-path", "", "/path/to/sqlite3")
 | 
			
		||||
	f.StringVar(&p.metasploitConf.URL, "msfdb-url", "",
 | 
			
		||||
		"http://metasploit.com:1327 or DB connection string")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(&p.httpConf.URL, "http", "", "-to-http http://vuls-report")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(&c.Conf.TrivyCacheDBDir, "trivy-cachedb-dir",
 | 
			
		||||
@@ -201,8 +212,6 @@ func (p *ReportCmd) SetFlags(f *flag.FlagSet) {
 | 
			
		||||
// Execute execute
 | 
			
		||||
func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
 | 
			
		||||
	util.Log = util.NewCustomLogger(c.ServerInfo{})
 | 
			
		||||
	cvelog.SetLogger(c.Conf.LogDir, false, c.Conf.Debug, false)
 | 
			
		||||
 | 
			
		||||
	if err := c.Load(p.configPath, ""); err != nil {
 | 
			
		||||
		util.Log.Errorf("Error loading %s, %+v", p.configPath, err)
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
@@ -212,6 +221,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
 | 
			
		||||
	c.Conf.OvalDict.Overwrite(p.ovalDict)
 | 
			
		||||
	c.Conf.Gost.Overwrite(p.gostConf)
 | 
			
		||||
	c.Conf.Exploit.Overwrite(p.exploitConf)
 | 
			
		||||
	c.Conf.Metasploit.Overwrite(p.metasploitConf)
 | 
			
		||||
	c.Conf.HTTP.Overwrite(p.httpConf)
 | 
			
		||||
 | 
			
		||||
	var dir string
 | 
			
		||||
@@ -226,6 +236,127 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	util.Log.Info("Validating config...")
 | 
			
		||||
	if !c.Conf.ValidateOnReport() {
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !(c.Conf.FormatJSON || c.Conf.FormatOneLineText ||
 | 
			
		||||
		c.Conf.FormatList || c.Conf.FormatFullText || c.Conf.FormatXML || c.Conf.FormatCsvList) {
 | 
			
		||||
		c.Conf.FormatList = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var loaded models.ScanResults
 | 
			
		||||
	if loaded, err = report.LoadScanResults(dir); err != nil {
 | 
			
		||||
		util.Log.Error(err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
	util.Log.Infof("Loaded: %s", dir)
 | 
			
		||||
 | 
			
		||||
	var res models.ScanResults
 | 
			
		||||
	hasError := false
 | 
			
		||||
	for _, r := range loaded {
 | 
			
		||||
		if len(r.Errors) == 0 {
 | 
			
		||||
			res = append(res, r)
 | 
			
		||||
		} else {
 | 
			
		||||
			util.Log.Errorf("Ignored since errors occurred during scanning: %s, err: %v",
 | 
			
		||||
				r.ServerName, r.Errors)
 | 
			
		||||
			hasError = true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(res) == 0 {
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, r := range res {
 | 
			
		||||
		util.Log.Debugf("%s: %s",
 | 
			
		||||
			r.ServerInfo(),
 | 
			
		||||
			pp.Sprintf("%s", c.Conf.Servers[r.ServerName]))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.UUID {
 | 
			
		||||
		// Ensure UUIDs of scan target servers in config.toml
 | 
			
		||||
		if err := report.EnsureUUIDs(p.configPath, res); err != nil {
 | 
			
		||||
			util.Log.Errorf("Failed to ensure UUIDs. err: %+v", err)
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !c.Conf.ToSaas {
 | 
			
		||||
		util.Log.Info("Validating db config...")
 | 
			
		||||
		if !c.Conf.ValidateOnReportDB() {
 | 
			
		||||
			return subcommands.ExitUsageError
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if c.Conf.CveDict.URL != "" {
 | 
			
		||||
			if err := report.CveClient.CheckHealth(); err != nil {
 | 
			
		||||
				util.Log.Errorf("CVE HTTP server is not running. err: %+v", err)
 | 
			
		||||
				util.Log.Errorf("Run go-cve-dictionary as server mode before reporting or run with `-cvedb-type=sqlite3 -cvedb-sqlite3-path` option instead of -cvedb-url")
 | 
			
		||||
				return subcommands.ExitFailure
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if c.Conf.OvalDict.URL != "" {
 | 
			
		||||
			err := oval.Base{}.CheckHTTPHealth()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				util.Log.Errorf("OVAL HTTP server is not running. err: %+v", err)
 | 
			
		||||
				util.Log.Errorf("Run goval-dictionary as server mode before reporting or run with `-ovaldb-type=sqlite3 -ovaldb-sqlite3-path` option instead of -ovaldb-url")
 | 
			
		||||
				return subcommands.ExitFailure
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if c.Conf.Gost.URL != "" {
 | 
			
		||||
			util.Log.Infof("gost: %s", c.Conf.Gost.URL)
 | 
			
		||||
			err := gost.Base{}.CheckHTTPHealth()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				util.Log.Errorf("gost HTTP server is not running. err: %+v", err)
 | 
			
		||||
				util.Log.Errorf("Run gost as server mode before reporting or run with `-gostdb-type=sqlite3 -gostdb-sqlite3-path` option instead of -gostdb-url")
 | 
			
		||||
				return subcommands.ExitFailure
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if c.Conf.Exploit.URL != "" {
 | 
			
		||||
			err := exploit.CheckHTTPHealth()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				util.Log.Errorf("exploit HTTP server is not running. err: %+v", err)
 | 
			
		||||
				util.Log.Errorf("Run go-exploitdb as server mode before reporting")
 | 
			
		||||
				return subcommands.ExitFailure
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if c.Conf.Metasploit.URL != "" {
 | 
			
		||||
			err := msf.CheckHTTPHealth()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				util.Log.Errorf("metasploit HTTP server is not running. err: %+v", err)
 | 
			
		||||
				util.Log.Errorf("Run go-msfdb as server mode before reporting")
 | 
			
		||||
				return subcommands.ExitFailure
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		dbclient, locked, err := report.NewDBClient(report.DBClientConf{
 | 
			
		||||
			CveDictCnf:    c.Conf.CveDict,
 | 
			
		||||
			OvalDictCnf:   c.Conf.OvalDict,
 | 
			
		||||
			GostCnf:       c.Conf.Gost,
 | 
			
		||||
			ExploitCnf:    c.Conf.Exploit,
 | 
			
		||||
			MetasploitCnf: c.Conf.Metasploit,
 | 
			
		||||
			DebugSQL:      c.Conf.DebugSQL,
 | 
			
		||||
		})
 | 
			
		||||
		if locked {
 | 
			
		||||
			util.Log.Errorf("SQLite3 is locked. Close other DB connections and try again. err: %+v", err)
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Errorf("Failed to init DB Clients. err: %+v", err)
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
		defer dbclient.CloseDB()
 | 
			
		||||
 | 
			
		||||
		if res, err = report.FillCveInfos(*dbclient, res, dir); err != nil {
 | 
			
		||||
			util.Log.Errorf("%+v", err)
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// report
 | 
			
		||||
	reports := []report.ResultWriter{
 | 
			
		||||
		report.StdoutWriter{},
 | 
			
		||||
@@ -307,117 +438,6 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
 | 
			
		||||
		reports = append(reports, report.SaasWriter{})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !(c.Conf.FormatJSON || c.Conf.FormatOneLineText ||
 | 
			
		||||
		c.Conf.FormatList || c.Conf.FormatFullText || c.Conf.FormatXML) {
 | 
			
		||||
		c.Conf.FormatList = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	util.Log.Info("Validating config...")
 | 
			
		||||
	if !c.Conf.ValidateOnReport() {
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var loaded models.ScanResults
 | 
			
		||||
	if loaded, err = report.LoadScanResults(dir); err != nil {
 | 
			
		||||
		util.Log.Error(err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
	util.Log.Infof("Loaded: %s", dir)
 | 
			
		||||
 | 
			
		||||
	var res models.ScanResults
 | 
			
		||||
	hasError := false
 | 
			
		||||
	for _, r := range loaded {
 | 
			
		||||
		if len(r.Errors) == 0 {
 | 
			
		||||
			res = append(res, r)
 | 
			
		||||
		} else {
 | 
			
		||||
			util.Log.Errorf("Ignored since errors occurred during scanning: %s, err: %v",
 | 
			
		||||
				r.ServerName, r.Errors)
 | 
			
		||||
			hasError = true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(res) == 0 {
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, r := range res {
 | 
			
		||||
		util.Log.Debugf("%s: %s",
 | 
			
		||||
			r.ServerInfo(),
 | 
			
		||||
			pp.Sprintf("%s", c.Conf.Servers[r.ServerName]))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.UUID {
 | 
			
		||||
		// Ensure UUIDs of scan target servers in config.toml
 | 
			
		||||
		if err := report.EnsureUUIDs(p.configPath, res); err != nil {
 | 
			
		||||
			util.Log.Errorf("Failed to ensure UUIDs. err: %+v", err)
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !c.Conf.ToSaas {
 | 
			
		||||
		util.Log.Info("Validating db config...")
 | 
			
		||||
		if !c.Conf.ValidateOnReportDB() {
 | 
			
		||||
			return subcommands.ExitUsageError
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if c.Conf.CveDict.URL != "" {
 | 
			
		||||
			if err := report.CveClient.CheckHealth(); err != nil {
 | 
			
		||||
				util.Log.Errorf("CVE HTTP server is not running. err: %+v", err)
 | 
			
		||||
				util.Log.Errorf("Run go-cve-dictionary as server mode before reporting or run with `-cvedb-type=sqlite3 -cvedb-sqlite3-path` option instead of -cvedb-url")
 | 
			
		||||
				return subcommands.ExitFailure
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if c.Conf.OvalDict.URL != "" {
 | 
			
		||||
			err := oval.Base{}.CheckHTTPHealth()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				util.Log.Errorf("OVAL HTTP server is not running. err: %+v", err)
 | 
			
		||||
				util.Log.Errorf("Run goval-dictionary as server mode before reporting or run with `-ovaldb-type=sqlite3 -ovaldb-sqlite3-path` option instead of -ovaldb-url")
 | 
			
		||||
				return subcommands.ExitFailure
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if c.Conf.Gost.URL != "" {
 | 
			
		||||
			util.Log.Infof("gost: %s", c.Conf.Gost.URL)
 | 
			
		||||
			err := gost.Base{}.CheckHTTPHealth()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				util.Log.Errorf("gost HTTP server is not running. err: %+v", err)
 | 
			
		||||
				util.Log.Errorf("Run gost as server mode before reporting or run with `-gostdb-type=sqlite3 -gostdb-sqlite3-path` option instead of -gostdb-url")
 | 
			
		||||
				return subcommands.ExitFailure
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if c.Conf.Exploit.URL != "" {
 | 
			
		||||
			err := exploit.CheckHTTPHealth()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				util.Log.Errorf("exploit HTTP server is not running. err: %+v", err)
 | 
			
		||||
				util.Log.Errorf("Run go-exploitdb as server mode before reporting")
 | 
			
		||||
				return subcommands.ExitFailure
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		dbclient, locked, err := report.NewDBClient(report.DBClientConf{
 | 
			
		||||
			CveDictCnf:  c.Conf.CveDict,
 | 
			
		||||
			OvalDictCnf: c.Conf.OvalDict,
 | 
			
		||||
			GostCnf:     c.Conf.Gost,
 | 
			
		||||
			ExploitCnf:  c.Conf.Exploit,
 | 
			
		||||
			DebugSQL:    c.Conf.DebugSQL,
 | 
			
		||||
		})
 | 
			
		||||
		if locked {
 | 
			
		||||
			util.Log.Errorf("SQLite3 is locked. Close other DB connections and try again. err: %+v", err)
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Errorf("Failed to init DB Clients. err: %+v", err)
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
		defer dbclient.CloseDB()
 | 
			
		||||
 | 
			
		||||
		if res, err = report.FillCveInfos(*dbclient, res, dir); err != nil {
 | 
			
		||||
			util.Log.Errorf("%+v", err)
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, w := range reports {
 | 
			
		||||
		if err := w.Write(res...); err != nil {
 | 
			
		||||
			util.Log.Errorf("Failed to report. err: %+v", err)
 | 
			
		||||
 
 | 
			
		||||
@@ -49,6 +49,7 @@ func (*ScanCmd) Usage() string {
 | 
			
		||||
		[-timeout=300]
 | 
			
		||||
		[-timeout-scan=7200]
 | 
			
		||||
		[-debug]
 | 
			
		||||
		[-quiet]
 | 
			
		||||
		[-pipe]
 | 
			
		||||
		[-vvv]
 | 
			
		||||
		[-ips]
 | 
			
		||||
@@ -61,6 +62,7 @@ func (*ScanCmd) Usage() string {
 | 
			
		||||
// SetFlags set flag
 | 
			
		||||
func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
 | 
			
		||||
	f.BoolVar(&c.Conf.Debug, "debug", false, "debug mode")
 | 
			
		||||
	f.BoolVar(&c.Conf.Quiet, "quiet", false, "Quiet mode. No output on stdout")
 | 
			
		||||
 | 
			
		||||
	wd, _ := os.Getwd()
 | 
			
		||||
	defaultConfPath := filepath.Join(wd, "config.toml")
 | 
			
		||||
@@ -80,7 +82,7 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
 | 
			
		||||
		"Use Native Go implementation of SSH. Default: Use the external command")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.SSHConfig, "ssh-config", false,
 | 
			
		||||
		"Use SSH options specified in ssh_config preferentially")
 | 
			
		||||
		"[Deprecated] Use SSH options specified in ssh_config preferentially")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&c.Conf.ContainersOnly, "containers-only", false,
 | 
			
		||||
		"Scan running containers only. Default: Scan both of hosts and running containers")
 | 
			
		||||
@@ -146,6 +148,16 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.SSHConfig {
 | 
			
		||||
		msg := []string{
 | 
			
		||||
			"-ssh-config is deprecated",
 | 
			
		||||
			"If you update Vuls and get this error, there may be incompatible changes in config.toml",
 | 
			
		||||
			"Please check config.toml template : https://vuls.io/docs/en/usage-settings.html",
 | 
			
		||||
		}
 | 
			
		||||
		util.Log.Errorf("%s", strings.Join(msg, "\n"))
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	util.Log.Info("Start scanning")
 | 
			
		||||
	util.Log.Infof("config: %s", p.configPath)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -13,22 +13,23 @@ import (
 | 
			
		||||
	c "github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/exploit"
 | 
			
		||||
	"github.com/future-architect/vuls/gost"
 | 
			
		||||
	"github.com/future-architect/vuls/msf"
 | 
			
		||||
	"github.com/future-architect/vuls/oval"
 | 
			
		||||
	"github.com/future-architect/vuls/report"
 | 
			
		||||
	"github.com/future-architect/vuls/server"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/google/subcommands"
 | 
			
		||||
	cvelog "github.com/kotakanbe/go-cve-dictionary/log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ServerCmd is subcommand for server
 | 
			
		||||
type ServerCmd struct {
 | 
			
		||||
	configPath  string
 | 
			
		||||
	listen      string
 | 
			
		||||
	cveDict     c.GoCveDictConf
 | 
			
		||||
	ovalDict    c.GovalDictConf
 | 
			
		||||
	gostConf    c.GostConf
 | 
			
		||||
	exploitConf c.ExploitConf
 | 
			
		||||
	configPath     string
 | 
			
		||||
	listen         string
 | 
			
		||||
	cveDict        c.GoCveDictConf
 | 
			
		||||
	ovalDict       c.GovalDictConf
 | 
			
		||||
	gostConf       c.GostConf
 | 
			
		||||
	exploitConf    c.ExploitConf
 | 
			
		||||
	metasploitConf c.MetasploitConf
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Name return subcommand name
 | 
			
		||||
@@ -65,6 +66,9 @@ func (*ServerCmd) Usage() string {
 | 
			
		||||
		[-exploitdb-type=sqlite3|mysql|redis|http]
 | 
			
		||||
		[-exploitdb-sqlite3-path=/path/to/exploitdb.sqlite3]
 | 
			
		||||
		[-exploitdb-url=http://127.0.0.1:1326 or DB connection string]
 | 
			
		||||
		[-msfdb-type=sqlite3|mysql|redis|http]
 | 
			
		||||
		[-msfdb-sqlite3-path=/path/to/msfdb.sqlite3]
 | 
			
		||||
		[-msfdb-url=http://127.0.0.1:1327 or DB connection string]
 | 
			
		||||
 | 
			
		||||
		[RFC3339 datetime format under results dir]
 | 
			
		||||
`
 | 
			
		||||
@@ -126,13 +130,17 @@ func (p *ServerCmd) SetFlags(f *flag.FlagSet) {
 | 
			
		||||
	f.StringVar(&p.exploitConf.SQLite3Path, "exploitdb-sqlite3-path", "", "/path/to/sqlite3")
 | 
			
		||||
	f.StringVar(&p.exploitConf.URL, "exploitdb-url", "",
 | 
			
		||||
		"http://exploit.com:1326 or DB connection string")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(&p.metasploitConf.Type, "msfdb-type", "",
 | 
			
		||||
		"DB type of msf (sqlite3, mysql, postgres, redis or http)")
 | 
			
		||||
	f.StringVar(&p.metasploitConf.SQLite3Path, "msfdb-sqlite3-path", "", "/path/to/sqlite3")
 | 
			
		||||
	f.StringVar(&p.metasploitConf.URL, "msfdb-url", "",
 | 
			
		||||
		"http://metasploit.com:1327 or DB connection string")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Execute execute
 | 
			
		||||
func (p *ServerCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
 | 
			
		||||
	util.Log = util.NewCustomLogger(c.ServerInfo{})
 | 
			
		||||
	cvelog.SetLogger(c.Conf.LogDir, false, c.Conf.Debug, false)
 | 
			
		||||
 | 
			
		||||
	if p.configPath != "" {
 | 
			
		||||
		if err := c.Load(p.configPath, ""); err != nil {
 | 
			
		||||
			util.Log.Errorf("Error loading %s. err: %+v", p.configPath, err)
 | 
			
		||||
@@ -144,6 +152,7 @@ func (p *ServerCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
 | 
			
		||||
	c.Conf.OvalDict.Overwrite(p.ovalDict)
 | 
			
		||||
	c.Conf.Gost.Overwrite(p.gostConf)
 | 
			
		||||
	c.Conf.Exploit.Overwrite(p.exploitConf)
 | 
			
		||||
	c.Conf.Metasploit.Overwrite(p.metasploitConf)
 | 
			
		||||
 | 
			
		||||
	util.Log.Info("Validating config...")
 | 
			
		||||
	if !c.Conf.ValidateOnReport() {
 | 
			
		||||
@@ -191,12 +200,21 @@ func (p *ServerCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.Metasploit.URL != "" {
 | 
			
		||||
		err := msf.CheckHTTPHealth()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Errorf("metasploit HTTP server is not running. err: %+v", err)
 | 
			
		||||
			util.Log.Errorf("Run go-msfdb as server mode before reporting")
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	dbclient, locked, err := report.NewDBClient(report.DBClientConf{
 | 
			
		||||
		CveDictCnf:  c.Conf.CveDict,
 | 
			
		||||
		OvalDictCnf: c.Conf.OvalDict,
 | 
			
		||||
		GostCnf:     c.Conf.Gost,
 | 
			
		||||
		ExploitCnf:  c.Conf.Exploit,
 | 
			
		||||
		DebugSQL:    c.Conf.DebugSQL,
 | 
			
		||||
		CveDictCnf:    c.Conf.CveDict,
 | 
			
		||||
		OvalDictCnf:   c.Conf.OvalDict,
 | 
			
		||||
		GostCnf:       c.Conf.Gost,
 | 
			
		||||
		ExploitCnf:    c.Conf.Exploit,
 | 
			
		||||
		MetasploitCnf: c.Conf.Metasploit,
 | 
			
		||||
		DebugSQL:      c.Conf.DebugSQL,
 | 
			
		||||
	})
 | 
			
		||||
	if locked {
 | 
			
		||||
		util.Log.Errorf("SQLite3 is locked. Close other DB connections and try again: %+v", err)
 | 
			
		||||
 
 | 
			
		||||
@@ -11,20 +11,21 @@ import (
 | 
			
		||||
	"github.com/future-architect/vuls/exploit"
 | 
			
		||||
	"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/report"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/google/subcommands"
 | 
			
		||||
	cvelog "github.com/kotakanbe/go-cve-dictionary/log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// TuiCmd is Subcommand of host discovery mode
 | 
			
		||||
type TuiCmd struct {
 | 
			
		||||
	configPath  string
 | 
			
		||||
	cveDict     c.GoCveDictConf
 | 
			
		||||
	ovalDict    c.GovalDictConf
 | 
			
		||||
	gostConf    c.GostConf
 | 
			
		||||
	exploitConf c.ExploitConf
 | 
			
		||||
	configPath     string
 | 
			
		||||
	cveDict        c.GoCveDictConf
 | 
			
		||||
	ovalDict       c.GovalDictConf
 | 
			
		||||
	gostConf       c.GostConf
 | 
			
		||||
	exploitConf    c.ExploitConf
 | 
			
		||||
	metasploitConf c.MetasploitConf
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Name return subcommand name
 | 
			
		||||
@@ -62,6 +63,9 @@ func (*TuiCmd) Usage() string {
 | 
			
		||||
		[-exploitdb-type=sqlite3|mysql|redis|http]
 | 
			
		||||
		[-exploitdb-sqlite3-path=/path/to/exploitdb.sqlite3]
 | 
			
		||||
		[-exploitdb-url=http://127.0.0.1:1326 or DB connection string]
 | 
			
		||||
		[-msfdb-type=sqlite3|mysql|redis|http]
 | 
			
		||||
		[-msfdb-sqlite3-path=/path/to/msfdb.sqlite3]
 | 
			
		||||
		[-msfdb-url=http://127.0.0.1:1327 or DB connection string]
 | 
			
		||||
		[-trivy-cachedb-dir=/path/to/dir]
 | 
			
		||||
 | 
			
		||||
`
 | 
			
		||||
@@ -127,27 +131,30 @@ func (p *TuiCmd) SetFlags(f *flag.FlagSet) {
 | 
			
		||||
	f.StringVar(&p.exploitConf.URL, "exploitdb-url", "",
 | 
			
		||||
		"http://exploit.com:1326 or DB connection string")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(&p.metasploitConf.Type, "msfdb-type", "",
 | 
			
		||||
		"DB type of msf (sqlite3, mysql, postgres, redis or http)")
 | 
			
		||||
	f.StringVar(&p.metasploitConf.SQLite3Path, "msfdb-sqlite3-path", "", "/path/to/sqlite3")
 | 
			
		||||
	f.StringVar(&p.metasploitConf.URL, "msfdb-url", "",
 | 
			
		||||
		"http://metasploit.com:1327 or DB connection string")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(&c.Conf.TrivyCacheDBDir, "trivy-cachedb-dir",
 | 
			
		||||
		utils.DefaultCacheDir(), "/path/to/dir")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Execute execute
 | 
			
		||||
func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
 | 
			
		||||
	c.Conf.Lang = "en"
 | 
			
		||||
 | 
			
		||||
	// Setup Logger
 | 
			
		||||
	util.Log = util.NewCustomLogger(c.ServerInfo{})
 | 
			
		||||
	cvelog.SetLogger(c.Conf.LogDir, false, c.Conf.Debug, false)
 | 
			
		||||
 | 
			
		||||
	if err := c.Load(p.configPath, ""); err != nil {
 | 
			
		||||
		util.Log.Errorf("Error loading %s, err: %+v", p.configPath, err)
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.Conf.Lang = "en"
 | 
			
		||||
	c.Conf.CveDict.Overwrite(p.cveDict)
 | 
			
		||||
	c.Conf.OvalDict.Overwrite(p.ovalDict)
 | 
			
		||||
	c.Conf.Gost.Overwrite(p.gostConf)
 | 
			
		||||
	c.Conf.Exploit.Overwrite(p.exploitConf)
 | 
			
		||||
	c.Conf.Metasploit.Overwrite(p.metasploitConf)
 | 
			
		||||
 | 
			
		||||
	var dir string
 | 
			
		||||
	var err error
 | 
			
		||||
@@ -213,12 +220,22 @@ func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) s
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.Metasploit.URL != "" {
 | 
			
		||||
		err := msf.CheckHTTPHealth()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Errorf("metasploit HTTP server is not running. err: %+v", err)
 | 
			
		||||
			util.Log.Errorf("Run go-msfdb as server mode before reporting")
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	dbclient, locked, err := report.NewDBClient(report.DBClientConf{
 | 
			
		||||
		CveDictCnf:  c.Conf.CveDict,
 | 
			
		||||
		OvalDictCnf: c.Conf.OvalDict,
 | 
			
		||||
		GostCnf:     c.Conf.Gost,
 | 
			
		||||
		ExploitCnf:  c.Conf.Exploit,
 | 
			
		||||
		DebugSQL:    c.Conf.DebugSQL,
 | 
			
		||||
		CveDictCnf:    c.Conf.CveDict,
 | 
			
		||||
		OvalDictCnf:   c.Conf.OvalDict,
 | 
			
		||||
		GostCnf:       c.Conf.Gost,
 | 
			
		||||
		ExploitCnf:    c.Conf.Exploit,
 | 
			
		||||
		MetasploitCnf: c.Conf.Metasploit,
 | 
			
		||||
		DebugSQL:      c.Conf.DebugSQL,
 | 
			
		||||
	})
 | 
			
		||||
	if locked {
 | 
			
		||||
		util.Log.Errorf("SQLite3 is locked. Close other DB connections and try again: %+v", err)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										118
									
								
								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
 | 
			
		||||
@@ -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:"-"`
 | 
			
		||||
@@ -150,6 +151,7 @@ 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"`
 | 
			
		||||
@@ -245,6 +247,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)
 | 
			
		||||
	}
 | 
			
		||||
@@ -517,11 +523,11 @@ func (c *HipChatConf) Validate() (errs []error) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if len(c.Room) == 0 {
 | 
			
		||||
		errs = append(errs, xerrors.New("hipcaht.room must not be empty"))
 | 
			
		||||
		errs = append(errs, xerrors.New("hipchat.room must not be empty"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(c.AuthToken) == 0 {
 | 
			
		||||
		errs = append(errs, xerrors.New("hipcaht.AuthToken must not be empty"))
 | 
			
		||||
		errs = append(errs, xerrors.New("hipchat.AuthToken must not be empty"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := valid.ValidateStruct(c)
 | 
			
		||||
@@ -543,11 +549,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)
 | 
			
		||||
@@ -1000,6 +1006,64 @@ 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"
 | 
			
		||||
 | 
			
		||||
// Overwrite set options with the following priority.
 | 
			
		||||
// 1. Command line option
 | 
			
		||||
// 2. Environment variable
 | 
			
		||||
// 3. config.toml
 | 
			
		||||
func (cnf *MetasploitConf) Overwrite(cmdOpt MetasploitConf) {
 | 
			
		||||
	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)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if cmdOpt.Type != "" {
 | 
			
		||||
		cnf.Type = cmdOpt.Type
 | 
			
		||||
	}
 | 
			
		||||
	if cmdOpt.URL != "" {
 | 
			
		||||
		cnf.URL = cmdOpt.URL
 | 
			
		||||
	}
 | 
			
		||||
	if cmdOpt.SQLite3Path != "" {
 | 
			
		||||
		cnf.SQLite3Path = cmdOpt.SQLite3Path
 | 
			
		||||
	}
 | 
			
		||||
	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
 | 
			
		||||
@@ -1035,7 +1099,9 @@ 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"`
 | 
			
		||||
	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"`
 | 
			
		||||
@@ -1056,18 +1122,17 @@ type ServerInfo struct {
 | 
			
		||||
	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
 | 
			
		||||
@@ -1166,7 +1231,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
 | 
			
		||||
@@ -1185,21 +1250,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
 | 
			
		||||
 
 | 
			
		||||
@@ -35,6 +35,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 +45,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 +58,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 +83,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 +116,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 {
 | 
			
		||||
@@ -197,14 +208,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,8 +268,13 @@ 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
 | 
			
		||||
	}
 | 
			
		||||
@@ -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)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -58,12 +58,14 @@ func main() {
 | 
			
		||||
				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
 | 
			
		||||
@@ -72,7 +74,8 @@ func main() {
 | 
			
		||||
			config.Conf.Saas.Token = token
 | 
			
		||||
			config.Conf.Saas.URL = url
 | 
			
		||||
			if err = (report.SaasWriter{}).Write(scanResult); err != nil {
 | 
			
		||||
				fmt.Println("Failed to create json", err)
 | 
			
		||||
				fmt.Println(err)
 | 
			
		||||
				os.Exit(1)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			return
 | 
			
		||||
 
 | 
			
		||||
@@ -34,12 +34,14 @@ func main() {
 | 
			
		||||
				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
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
@@ -50,11 +52,13 @@ func main() {
 | 
			
		||||
			}
 | 
			
		||||
			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))
 | 
			
		||||
@@ -69,5 +73,6 @@ func main() {
 | 
			
		||||
	rootCmd.AddCommand(cmdTrivyToVuls)
 | 
			
		||||
	if err = rootCmd.Execute(); err != nil {
 | 
			
		||||
		fmt.Println("Failed to execute command", err)
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -45,13 +45,6 @@ func Parse(vulnJSON []byte, scanResult *models.ScanResult) (result *models.ScanR
 | 
			
		||||
				notFixedYet = true
 | 
			
		||||
				fixState = "Affected"
 | 
			
		||||
			}
 | 
			
		||||
			vulnInfo.AffectedPackages = append(vulnInfo.AffectedPackages, models.PackageFixStatus{
 | 
			
		||||
				Name:        vuln.PkgName,
 | 
			
		||||
				NotFixedYet: notFixedYet,
 | 
			
		||||
				FixState:    fixState,
 | 
			
		||||
				FixedIn:     vuln.FixedVersion,
 | 
			
		||||
			})
 | 
			
		||||
 | 
			
		||||
			var references models.References
 | 
			
		||||
			for _, reference := range vuln.References {
 | 
			
		||||
				references = append(references, models.Reference{
 | 
			
		||||
@@ -78,6 +71,13 @@ func Parse(vulnJSON []byte, scanResult *models.ScanResult) (result *models.ScanR
 | 
			
		||||
					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
 | 
			
		||||
@@ -92,6 +92,7 @@ func Parse(vulnJSON []byte, scanResult *models.ScanResult) (result *models.ScanR
 | 
			
		||||
				vulnInfo.LibraryFixedIns = append(vulnInfo.LibraryFixedIns, models.LibraryFixedIn{
 | 
			
		||||
					Key:     trivyResult.Type,
 | 
			
		||||
					Name:    vuln.PkgName,
 | 
			
		||||
					Path:    trivyResult.Target,
 | 
			
		||||
					FixedIn: vuln.FixedVersion,
 | 
			
		||||
				})
 | 
			
		||||
				libScanner := uniqueLibraryScannerPaths[trivyResult.Target]
 | 
			
		||||
 
 | 
			
		||||
@@ -154,14 +154,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
								DetectionMethod: "TrivyMatch",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							models.PackageFixStatus{
 | 
			
		||||
								Name:        "guzzlehttp/guzzle",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
								FixState:    "",
 | 
			
		||||
								FixedIn:     "4.2.4, 5.3.1, 6.2.1",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": {
 | 
			
		||||
								Title:         "PHP: sets environmental variable based on user supplied Proxy request header",
 | 
			
		||||
@@ -202,6 +195,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							{
 | 
			
		||||
								Path:    "php-app/composer.lock",
 | 
			
		||||
								Key:     "composer",
 | 
			
		||||
								Name:    "guzzlehttp/guzzle",
 | 
			
		||||
								FixedIn: "4.2.4, 5.3.1, 6.2.1",
 | 
			
		||||
@@ -217,14 +211,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
								DetectionMethod: "TrivyMatch",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							models.PackageFixStatus{
 | 
			
		||||
								Name:        "lodash",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
								FixState:    "",
 | 
			
		||||
								FixedIn:     ">=4.17.5",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": {
 | 
			
		||||
								Cvss3Severity: "LOW",
 | 
			
		||||
@@ -240,6 +227,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							{
 | 
			
		||||
								Path:    "node-app/package-lock.json",
 | 
			
		||||
								Key:     "npm",
 | 
			
		||||
								Name:    "lodash",
 | 
			
		||||
								FixedIn: ">=4.17.5",
 | 
			
		||||
@@ -255,14 +243,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
								DetectionMethod: "TrivyMatch",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							models.PackageFixStatus{
 | 
			
		||||
								Name:        "rails-html-sanitizer",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
								FixState:    "",
 | 
			
		||||
								FixedIn:     ">= 1.0.4",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": {
 | 
			
		||||
								Cvss3Severity: "MEDIUM",
 | 
			
		||||
@@ -277,6 +258,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							{
 | 
			
		||||
								Path:    "ruby-app/Gemfile.lock",
 | 
			
		||||
								Key:     "bundler",
 | 
			
		||||
								Name:    "rails-html-sanitizer",
 | 
			
		||||
								FixedIn: ">= 1.0.4",
 | 
			
		||||
@@ -497,14 +479,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
								DetectionMethod: "TrivyMatch",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							models.PackageFixStatus{
 | 
			
		||||
								Name:        "lodash",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
								FixState:    "",
 | 
			
		||||
								FixedIn:     ">=4.17.11",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": {
 | 
			
		||||
								Cvss3Severity: "HIGH",
 | 
			
		||||
@@ -520,6 +495,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							{
 | 
			
		||||
								Path:    "node-app/package-lock.json",
 | 
			
		||||
								Key:     "npm",
 | 
			
		||||
								Name:    "lodash",
 | 
			
		||||
								FixedIn: ">=4.17.11",
 | 
			
		||||
@@ -1837,14 +1813,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
								DetectionMethod: "TrivyMatch",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							models.PackageFixStatus{
 | 
			
		||||
								Name:        "jquery",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
								FixState:    "",
 | 
			
		||||
								FixedIn:     ">=3.4.0",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": {
 | 
			
		||||
								Cvss3Severity: "MEDIUM",
 | 
			
		||||
@@ -1857,6 +1826,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							{
 | 
			
		||||
								Path:    "node-app/package-lock.json",
 | 
			
		||||
								Key:     "npm",
 | 
			
		||||
								Name:    "jquery",
 | 
			
		||||
								FixedIn: ">=3.4.0",
 | 
			
		||||
@@ -1872,14 +1842,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
								DetectionMethod: "TrivyMatch",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							models.PackageFixStatus{
 | 
			
		||||
								Name:        "nokogiri",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
								FixState:    "",
 | 
			
		||||
								FixedIn:     ">= 1.10.4",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": {
 | 
			
		||||
								Cvss3Severity: "HIGH",
 | 
			
		||||
@@ -1900,6 +1863,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							{
 | 
			
		||||
								Path:    "ruby-app/Gemfile.lock",
 | 
			
		||||
								Key:     "bundler",
 | 
			
		||||
								Name:    "nokogiri",
 | 
			
		||||
								FixedIn: ">= 1.10.4",
 | 
			
		||||
@@ -2127,14 +2091,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
								DetectionMethod: "TrivyMatch",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							models.PackageFixStatus{
 | 
			
		||||
								Name:        "urllib3",
 | 
			
		||||
								NotFixedYet: true,
 | 
			
		||||
								FixState:    "Affected",
 | 
			
		||||
								FixedIn:     "",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": {
 | 
			
		||||
								Cvss3Severity: "MEDIUM",
 | 
			
		||||
@@ -2160,6 +2117,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							{
 | 
			
		||||
								Path:    "python-app/Pipfile.lock",
 | 
			
		||||
								Key:     "pipenv",
 | 
			
		||||
								Name:    "urllib3",
 | 
			
		||||
								FixedIn: "",
 | 
			
		||||
@@ -2175,14 +2133,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
								DetectionMethod: "TrivyMatch",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							models.PackageFixStatus{
 | 
			
		||||
								Name:        "urllib3",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
								FixState:    "",
 | 
			
		||||
								FixedIn:     "1.24.2",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": {
 | 
			
		||||
								Cvss3Severity: "MEDIUM",
 | 
			
		||||
@@ -2206,6 +2157,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							{
 | 
			
		||||
								Path:    "python-app/Pipfile.lock",
 | 
			
		||||
								Key:     "pipenv",
 | 
			
		||||
								Name:    "urllib3",
 | 
			
		||||
								FixedIn: "1.24.2",
 | 
			
		||||
@@ -2221,14 +2173,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
								DetectionMethod: "TrivyMatch",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							models.PackageFixStatus{
 | 
			
		||||
								Name:        "jquery",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
								FixState:    "",
 | 
			
		||||
								FixedIn:     ">=3.4.0",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": {
 | 
			
		||||
								Cvss3Severity: "MEDIUM",
 | 
			
		||||
@@ -2305,6 +2250,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							{
 | 
			
		||||
								Path:    "node-app/package-lock.json",
 | 
			
		||||
								Key:     "npm",
 | 
			
		||||
								Name:    "jquery",
 | 
			
		||||
								FixedIn: ">=3.4.0",
 | 
			
		||||
@@ -2367,14 +2313,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
								DetectionMethod: "TrivyMatch",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							models.PackageFixStatus{
 | 
			
		||||
								Name:        "nokogiri",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
								FixState:    "",
 | 
			
		||||
								FixedIn:     ">= 1.10.5",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": {
 | 
			
		||||
								Cvss3Severity: "MEDIUM",
 | 
			
		||||
@@ -2399,6 +2338,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							{
 | 
			
		||||
								Path:    "ruby-app/Gemfile.lock",
 | 
			
		||||
								Key:     "bundler",
 | 
			
		||||
								Name:    "nokogiri",
 | 
			
		||||
								FixedIn: ">= 1.10.5",
 | 
			
		||||
@@ -2526,14 +2466,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
								DetectionMethod: "TrivyMatch",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							models.PackageFixStatus{
 | 
			
		||||
								Name:        "loofah",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
								FixState:    "",
 | 
			
		||||
								FixedIn:     ">= 2.3.1",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": {
 | 
			
		||||
								Cvss3Severity: "MEDIUM",
 | 
			
		||||
@@ -2552,6 +2485,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							{
 | 
			
		||||
								Path:    "ruby-app/Gemfile.lock",
 | 
			
		||||
								Key:     "bundler",
 | 
			
		||||
								Name:    "loofah",
 | 
			
		||||
								FixedIn: ">= 2.3.1",
 | 
			
		||||
@@ -2653,14 +2587,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
								DetectionMethod: "TrivyMatch",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							{
 | 
			
		||||
								Name:        "rack",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
								FixState:    "",
 | 
			
		||||
								FixedIn:     "~> 1.6.12, >= 2.0.8",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": {
 | 
			
		||||
								Cvss3Severity: "MEDIUM",
 | 
			
		||||
@@ -2681,6 +2608,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							{
 | 
			
		||||
								Path:    "ruby-app/Gemfile.lock",
 | 
			
		||||
								Key:     "bundler",
 | 
			
		||||
								Name:    "rack",
 | 
			
		||||
								FixedIn: "~> 1.6.12, >= 2.0.8",
 | 
			
		||||
@@ -2695,14 +2623,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
								DetectionMethod: "TrivyMatch",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							{
 | 
			
		||||
								Name:        "pyyaml",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
								FixState:    "",
 | 
			
		||||
								FixedIn:     "5.3.1",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": {
 | 
			
		||||
								Cvss3Severity: "HIGH",
 | 
			
		||||
@@ -2719,6 +2640,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							{
 | 
			
		||||
								Path:    "python-app/Pipfile.lock",
 | 
			
		||||
								Key:     "pipenv",
 | 
			
		||||
								Name:    "pyyaml",
 | 
			
		||||
								FixedIn: "5.3.1",
 | 
			
		||||
@@ -2733,14 +2655,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
								DetectionMethod: "TrivyMatch",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							models.PackageFixStatus{
 | 
			
		||||
								Name:        "actionview",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
								FixState:    "",
 | 
			
		||||
								FixedIn:     "~> 5.2.4, >= 5.2.4.2, >= 6.0.2.2",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": {
 | 
			
		||||
								Cvss3Severity: "LOW",
 | 
			
		||||
@@ -2760,6 +2675,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							{
 | 
			
		||||
								Path:    "ruby-app/Gemfile.lock",
 | 
			
		||||
								Key:     "bundler",
 | 
			
		||||
								Name:    "actionview",
 | 
			
		||||
								FixedIn: "~> 5.2.4, >= 5.2.4.2, >= 6.0.2.2",
 | 
			
		||||
@@ -2774,14 +2690,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
								DetectionMethod: "TrivyMatch",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							models.PackageFixStatus{
 | 
			
		||||
								Name:        "nokogiri",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
								FixState:    "",
 | 
			
		||||
								FixedIn:     ">= 1.10.8",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": models.CveContent{
 | 
			
		||||
								Cvss3Severity: "MEDIUM",
 | 
			
		||||
@@ -2799,6 +2708,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							models.LibraryFixedIn{
 | 
			
		||||
								Path:    "ruby-app/Gemfile.lock",
 | 
			
		||||
								Key:     "bundler",
 | 
			
		||||
								Name:    "nokogiri",
 | 
			
		||||
								FixedIn: ">= 1.10.8",
 | 
			
		||||
@@ -2813,14 +2723,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
								DetectionMethod: "TrivyMatch",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							models.PackageFixStatus{
 | 
			
		||||
								Name:        "rake",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
								FixState:    "",
 | 
			
		||||
								FixedIn:     ">= 12.3.3",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": models.CveContent{
 | 
			
		||||
								Cvss3Severity: "CRITICAL",
 | 
			
		||||
@@ -2839,6 +2742,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							models.LibraryFixedIn{
 | 
			
		||||
								Path:    "ruby-app/Gemfile.lock",
 | 
			
		||||
								Key:     "bundler",
 | 
			
		||||
								Name:    "rake",
 | 
			
		||||
								FixedIn: ">= 12.3.3",
 | 
			
		||||
@@ -2853,14 +2757,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
								DetectionMethod: "TrivyMatch",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							models.PackageFixStatus{
 | 
			
		||||
								Name:        "rack",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
								FixState:    "",
 | 
			
		||||
								FixedIn:     "~> 2.1.3, >= 2.2.0",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": models.CveContent{
 | 
			
		||||
								Cvss3Severity: "MEDIUM",
 | 
			
		||||
@@ -2874,6 +2771,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							models.LibraryFixedIn{
 | 
			
		||||
								Path:    "ruby-app/Gemfile.lock",
 | 
			
		||||
								Key:     "bundler",
 | 
			
		||||
								Name:    "rack",
 | 
			
		||||
								FixedIn: "~> 2.1.3, >= 2.2.0",
 | 
			
		||||
@@ -2888,14 +2786,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
								DetectionMethod: "TrivyMatch",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							models.PackageFixStatus{
 | 
			
		||||
								Name:        "activestorage",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
								FixState:    "",
 | 
			
		||||
								FixedIn:     "~> 5.2.4.3, >= 6.0.3.1",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": models.CveContent{
 | 
			
		||||
								Cvss3Severity: "UNKNOWN",
 | 
			
		||||
@@ -2909,6 +2800,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							models.LibraryFixedIn{
 | 
			
		||||
								Path:    "ruby-app/Gemfile.lock",
 | 
			
		||||
								Key:     "bundler",
 | 
			
		||||
								Name:    "activestorage",
 | 
			
		||||
								FixedIn: "~> 5.2.4.3, >= 6.0.3.1",
 | 
			
		||||
@@ -2923,14 +2815,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
								DetectionMethod: "TrivyMatch",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							models.PackageFixStatus{
 | 
			
		||||
								Name:        "json",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
								FixState:    "",
 | 
			
		||||
								FixedIn:     ">= 2.3.0",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": models.CveContent{
 | 
			
		||||
								Cvss3Severity: "MEDIUM",
 | 
			
		||||
@@ -2947,6 +2832,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							models.LibraryFixedIn{
 | 
			
		||||
								Path:    "ruby-app/Gemfile.lock",
 | 
			
		||||
								Key:     "bundler",
 | 
			
		||||
								Name:    "json",
 | 
			
		||||
								FixedIn: ">= 2.3.0",
 | 
			
		||||
@@ -2961,14 +2847,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
								DetectionMethod: "TrivyMatch",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							models.PackageFixStatus{
 | 
			
		||||
								Name:        "actionpack",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
								FixState:    "",
 | 
			
		||||
								FixedIn:     "~> 5.2.4.3, >= 6.0.3.1",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": models.CveContent{
 | 
			
		||||
								Cvss3Severity: "UNKNOWN",
 | 
			
		||||
@@ -2979,6 +2858,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							{
 | 
			
		||||
								Path:    "ruby-app/Gemfile.lock",
 | 
			
		||||
								Key:     "bundler",
 | 
			
		||||
								Name:    "actionpack",
 | 
			
		||||
								FixedIn: "~> 5.2.4.3, >= 6.0.3.1",
 | 
			
		||||
@@ -2993,14 +2873,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
								DetectionMethod: "TrivyMatch",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							models.PackageFixStatus{
 | 
			
		||||
								Name:        "activesupport",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
								FixState:    "",
 | 
			
		||||
								FixedIn:     "~> 5.2.4.3, >= 6.0.3.1",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": models.CveContent{
 | 
			
		||||
								Cvss3Severity: "UNKNOWN",
 | 
			
		||||
@@ -3011,6 +2884,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							{
 | 
			
		||||
								Path:    "ruby-app/Gemfile.lock",
 | 
			
		||||
								Key:     "bundler",
 | 
			
		||||
								Name:    "activesupport",
 | 
			
		||||
								FixedIn: "~> 5.2.4.3, >= 6.0.3.1",
 | 
			
		||||
@@ -3025,14 +2899,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
								DetectionMethod: "TrivyMatch",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							models.PackageFixStatus{
 | 
			
		||||
								Name:        "actionpack",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
								FixState:    "",
 | 
			
		||||
								FixedIn:     "~> 5.2.4.3, >= 6.0.3.1",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": models.CveContent{
 | 
			
		||||
								Cvss3Severity: "UNKNOWN",
 | 
			
		||||
@@ -3043,6 +2910,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							{
 | 
			
		||||
								Path:    "ruby-app/Gemfile.lock",
 | 
			
		||||
								Key:     "bundler",
 | 
			
		||||
								Name:    "actionpack",
 | 
			
		||||
								FixedIn: "~> 5.2.4.3, >= 6.0.3.1",
 | 
			
		||||
@@ -3057,14 +2925,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
								DetectionMethod: "TrivyMatch",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							models.PackageFixStatus{
 | 
			
		||||
								Name:        "actionview",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
								FixState:    "",
 | 
			
		||||
								FixedIn:     "~> 5.2.4.3, >= 6.0.3.1",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": models.CveContent{
 | 
			
		||||
								Cvss3Severity: "UNKNOWN",
 | 
			
		||||
@@ -3075,6 +2936,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							{
 | 
			
		||||
								Path:    "ruby-app/Gemfile.lock",
 | 
			
		||||
								Key:     "bundler",
 | 
			
		||||
								Name:    "actionview",
 | 
			
		||||
								FixedIn: "~> 5.2.4.3, >= 6.0.3.1",
 | 
			
		||||
@@ -3089,14 +2951,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
								DetectionMethod: "TrivyMatch",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							models.PackageFixStatus{
 | 
			
		||||
								Name:        "lodash",
 | 
			
		||||
								NotFixedYet: true,
 | 
			
		||||
								FixState:    "Affected",
 | 
			
		||||
								FixedIn:     "",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": models.CveContent{
 | 
			
		||||
								Cvss3Severity: "HIGH",
 | 
			
		||||
@@ -3107,6 +2962,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							{
 | 
			
		||||
								Path:    "node-app/package-lock.json",
 | 
			
		||||
								Key:     "npm",
 | 
			
		||||
								Name:    "lodash",
 | 
			
		||||
								FixedIn: "",
 | 
			
		||||
@@ -3121,14 +2977,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
								DetectionMethod: "TrivyMatch",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							models.PackageFixStatus{
 | 
			
		||||
								Name:        "django-cors-headers",
 | 
			
		||||
								NotFixedYet: false,
 | 
			
		||||
								FixState:    "",
 | 
			
		||||
								FixedIn:     "3.0.0",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": models.CveContent{
 | 
			
		||||
								Cvss3Severity: "UNKNOWN",
 | 
			
		||||
@@ -3137,6 +2986,7 @@ func TestParse(t *testing.T) {
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							{
 | 
			
		||||
								Path:    "python-app/Pipfile.lock",
 | 
			
		||||
								Key:     "pipenv",
 | 
			
		||||
								Name:    "django-cors-headers",
 | 
			
		||||
								FixedIn: "3.0.0",
 | 
			
		||||
@@ -3144,16 +2994,9 @@ func TestParse(t *testing.T) {
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"RUSTSEC-2016-0001": {
 | 
			
		||||
						CveID:       "RUSTSEC-2016-0001",
 | 
			
		||||
						Confidences: models.Confidences{{Score: 100, DetectionMethod: "TrivyMatch"}},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							{
 | 
			
		||||
								Name:        "openssl",
 | 
			
		||||
								NotFixedYet: true,
 | 
			
		||||
								FixState:    "Affected",
 | 
			
		||||
								FixedIn:     "",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						CveID:            "RUSTSEC-2016-0001",
 | 
			
		||||
						Confidences:      models.Confidences{{Score: 100, DetectionMethod: "TrivyMatch"}},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": {
 | 
			
		||||
								Cvss3Severity: "UNKNOWN",
 | 
			
		||||
@@ -3163,20 +3006,18 @@ func TestParse(t *testing.T) {
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							{Key: "cargo", Name: "openssl", FixedIn: ""},
 | 
			
		||||
							{
 | 
			
		||||
								Path:    "rust-app/Cargo.lock",
 | 
			
		||||
								Key:     "cargo",
 | 
			
		||||
								Name:    "openssl",
 | 
			
		||||
								FixedIn: "",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"RUSTSEC-2018-0003": {
 | 
			
		||||
						CveID:       "RUSTSEC-2018-0003",
 | 
			
		||||
						Confidences: models.Confidences{{Score: 100, DetectionMethod: "TrivyMatch"}},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							{
 | 
			
		||||
								Name:        "smallvec",
 | 
			
		||||
								NotFixedYet: true,
 | 
			
		||||
								FixState:    "Affected",
 | 
			
		||||
								FixedIn:     "",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						CveID:            "RUSTSEC-2018-0003",
 | 
			
		||||
						Confidences:      models.Confidences{{Score: 100, DetectionMethod: "TrivyMatch"}},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": {
 | 
			
		||||
								Cvss3Severity: "UNKNOWN",
 | 
			
		||||
@@ -3186,20 +3027,18 @@ func TestParse(t *testing.T) {
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							{Key: "cargo", Name: "smallvec", FixedIn: ""},
 | 
			
		||||
							{
 | 
			
		||||
								Path:    "rust-app/Cargo.lock",
 | 
			
		||||
								Key:     "cargo",
 | 
			
		||||
								Name:    "smallvec",
 | 
			
		||||
								FixedIn: "",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"RUSTSEC-2018-0010": {
 | 
			
		||||
						CveID:       "RUSTSEC-2018-0010",
 | 
			
		||||
						Confidences: models.Confidences{{Score: 100, DetectionMethod: "TrivyMatch"}},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							models.PackageFixStatus{
 | 
			
		||||
								Name:        "openssl",
 | 
			
		||||
								NotFixedYet: true,
 | 
			
		||||
								FixState:    "Affected",
 | 
			
		||||
								FixedIn:     "",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						CveID:            "RUSTSEC-2018-0010",
 | 
			
		||||
						Confidences:      models.Confidences{{Score: 100, DetectionMethod: "TrivyMatch"}},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": {
 | 
			
		||||
								Cvss3Severity: "UNKNOWN",
 | 
			
		||||
@@ -3209,19 +3048,18 @@ func TestParse(t *testing.T) {
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							{Key: "cargo", Name: "openssl", FixedIn: ""},
 | 
			
		||||
							{
 | 
			
		||||
								Path:    "rust-app/Cargo.lock",
 | 
			
		||||
								Key:     "cargo",
 | 
			
		||||
								Name:    "openssl",
 | 
			
		||||
								FixedIn: "",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"RUSTSEC-2018-0017": {
 | 
			
		||||
						CveID:       "RUSTSEC-2018-0017",
 | 
			
		||||
						Confidences: models.Confidences{{Score: 100, DetectionMethod: "TrivyMatch"}},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							{
 | 
			
		||||
								Name:        "tempdir",
 | 
			
		||||
								NotFixedYet: true,
 | 
			
		||||
								FixState:    "Affected",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						CveID:            "RUSTSEC-2018-0017",
 | 
			
		||||
						Confidences:      models.Confidences{{Score: 100, DetectionMethod: "TrivyMatch"}},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": {
 | 
			
		||||
								Cvss3Severity: "UNKNOWN",
 | 
			
		||||
@@ -3231,20 +3069,18 @@ func TestParse(t *testing.T) {
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							{Key: "cargo", Name: "tempdir", FixedIn: ""},
 | 
			
		||||
							{
 | 
			
		||||
								Path:    "rust-app/Cargo.lock",
 | 
			
		||||
								Key:     "cargo",
 | 
			
		||||
								Name:    "tempdir",
 | 
			
		||||
								FixedIn: "",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"RUSTSEC-2019-0001": {
 | 
			
		||||
						CveID:       "RUSTSEC-2019-0001",
 | 
			
		||||
						Confidences: models.Confidences{{Score: 100, DetectionMethod: "TrivyMatch"}},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							{
 | 
			
		||||
								Name:        "ammonia",
 | 
			
		||||
								NotFixedYet: true,
 | 
			
		||||
								FixState:    "Affected",
 | 
			
		||||
								FixedIn:     "",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						CveID:            "RUSTSEC-2019-0001",
 | 
			
		||||
						Confidences:      models.Confidences{{Score: 100, DetectionMethod: "TrivyMatch"}},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": {
 | 
			
		||||
								Cvss3Severity: "UNKNOWN",
 | 
			
		||||
@@ -3254,18 +3090,17 @@ func TestParse(t *testing.T) {
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							{Key: "cargo", Name: "ammonia", FixedIn: ""},
 | 
			
		||||
							{
 | 
			
		||||
								Path:    "rust-app/Cargo.lock",
 | 
			
		||||
								Key:     "cargo",
 | 
			
		||||
								Name:    "ammonia",
 | 
			
		||||
								FixedIn: "",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"RUSTSEC-2019-0009": {CveID: "RUSTSEC-2019-0009",
 | 
			
		||||
						Confidences: models.Confidences{{Score: 100, DetectionMethod: "TrivyMatch"}},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							{
 | 
			
		||||
								Name:        "smallvec",
 | 
			
		||||
								NotFixedYet: true,
 | 
			
		||||
								FixState:    "Affected",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						Confidences:      models.Confidences{{Score: 100, DetectionMethod: "TrivyMatch"}},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": {
 | 
			
		||||
								Cvss3Severity: "UNKNOWN",
 | 
			
		||||
@@ -3275,20 +3110,18 @@ func TestParse(t *testing.T) {
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							{Key: "cargo", Name: "smallvec", FixedIn: ""},
 | 
			
		||||
							{
 | 
			
		||||
								Path:    "rust-app/Cargo.lock",
 | 
			
		||||
								Key:     "cargo",
 | 
			
		||||
								Name:    "smallvec",
 | 
			
		||||
								FixedIn: "",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"RUSTSEC-2019-0012": {
 | 
			
		||||
						CveID:       "RUSTSEC-2019-0012",
 | 
			
		||||
						Confidences: models.Confidences{{Score: 100, DetectionMethod: "TrivyMatch"}},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{
 | 
			
		||||
							{
 | 
			
		||||
								Name:        "smallvec",
 | 
			
		||||
								NotFixedYet: true,
 | 
			
		||||
								FixState:    "Affected",
 | 
			
		||||
								FixedIn:     "",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						CveID:            "RUSTSEC-2019-0012",
 | 
			
		||||
						Confidences:      models.Confidences{{Score: 100, DetectionMethod: "TrivyMatch"}},
 | 
			
		||||
						AffectedPackages: models.PackageFixStatuses{},
 | 
			
		||||
						CveContents: models.CveContents{
 | 
			
		||||
							"trivy": {
 | 
			
		||||
								Cvss3Severity: "UNKNOWN",
 | 
			
		||||
@@ -3298,7 +3131,12 @@ func TestParse(t *testing.T) {
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						LibraryFixedIns: models.LibraryFixedIns{
 | 
			
		||||
							{Key: "cargo", Name: "smallvec", FixedIn: ""},
 | 
			
		||||
							{
 | 
			
		||||
								Path:    "rust-app/Cargo.lock",
 | 
			
		||||
								Key:     "cargo",
 | 
			
		||||
								Name:    "smallvec",
 | 
			
		||||
								FixedIn: "",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
 
 | 
			
		||||
@@ -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(
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										54
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						@@ -1,6 +1,6 @@
 | 
			
		||||
module github.com/future-architect/vuls
 | 
			
		||||
 | 
			
		||||
go 1.13
 | 
			
		||||
go 1.14
 | 
			
		||||
 | 
			
		||||
replace (
 | 
			
		||||
	gopkg.in/mattn/go-colorable.v0 => github.com/mattn/go-colorable v0.1.0
 | 
			
		||||
@@ -8,50 +8,60 @@ replace (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/Azure/azure-sdk-for-go v42.0.0+incompatible
 | 
			
		||||
	github.com/Azure/azure-sdk-for-go v43.3.0+incompatible
 | 
			
		||||
	github.com/BurntSushi/toml v0.3.1
 | 
			
		||||
	github.com/Masterminds/semver/v3 v3.1.0
 | 
			
		||||
	github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91
 | 
			
		||||
	github.com/aquasecurity/fanal v0.0.0-20200505074551-9239a362deca
 | 
			
		||||
	github.com/aquasecurity/go-dep-parser v0.0.0-20200123140603-4dc0125084da // indirect
 | 
			
		||||
	github.com/aquasecurity/trivy v0.8.0
 | 
			
		||||
	github.com/aquasecurity/trivy-db v0.0.0-20200514134639-7e57e3e02470
 | 
			
		||||
	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-20200820074632-6de62ef86882
 | 
			
		||||
	github.com/aquasecurity/trivy v0.12.0
 | 
			
		||||
	github.com/aquasecurity/trivy-db v0.0.0-20200826140828-6da6467703aa
 | 
			
		||||
	github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef
 | 
			
		||||
	github.com/aws/aws-sdk-go v1.33.21
 | 
			
		||||
	github.com/boltdb/bolt v1.3.1
 | 
			
		||||
	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.13.0
 | 
			
		||||
	github.com/go-redis/redis v6.15.9+incompatible // indirect
 | 
			
		||||
	github.com/google/subcommands v1.2.0
 | 
			
		||||
	github.com/gosuri/uitable v0.0.4
 | 
			
		||||
	github.com/hashicorp/go-uuid v1.0.2
 | 
			
		||||
	github.com/hashicorp/go-version v1.2.0
 | 
			
		||||
	github.com/hashicorp/go-version v1.2.1
 | 
			
		||||
	github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c
 | 
			
		||||
	github.com/inconshreveable/log15 v0.0.0-20201112154412-8562bdadbbac // indirect
 | 
			
		||||
	github.com/jesseduffield/gocui v0.3.0
 | 
			
		||||
	github.com/jinzhu/gorm v1.9.16 // indirect
 | 
			
		||||
	github.com/k0kubun/pp v3.0.1+incompatible
 | 
			
		||||
	github.com/knqyf263/go-apk-version v0.0.0-20200507080916-9f84b1e3c54c
 | 
			
		||||
	github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f
 | 
			
		||||
	github.com/knqyf263/go-cpe v0.0.0-20180327054844-659663f6eca2
 | 
			
		||||
	github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d
 | 
			
		||||
	github.com/knqyf263/go-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.4
 | 
			
		||||
	github.com/kotakanbe/go-cve-dictionary v0.5.0
 | 
			
		||||
	github.com/kotakanbe/go-pingscanner v0.1.0
 | 
			
		||||
	github.com/kotakanbe/goval-dictionary v0.2.5
 | 
			
		||||
	github.com/kotakanbe/goval-dictionary v0.2.14
 | 
			
		||||
	github.com/kotakanbe/logrus-prefixed-formatter v0.0.0-20180123152602-928f7356cb96
 | 
			
		||||
	github.com/lib/pq v1.8.0 // indirect
 | 
			
		||||
	github.com/mattn/go-colorable v0.1.8 // indirect
 | 
			
		||||
	github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
 | 
			
		||||
	github.com/mitchellh/go-homedir v1.1.0
 | 
			
		||||
	github.com/mozqnet/go-exploitdb v0.0.0-20190911093644-f647f17ea8ca
 | 
			
		||||
	github.com/mozqnet/go-exploitdb v0.1.0
 | 
			
		||||
	github.com/nlopes/slack v0.6.0
 | 
			
		||||
	github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1 // indirect
 | 
			
		||||
	github.com/olekukonko/tablewriter v0.0.4
 | 
			
		||||
	github.com/parnurzeal/gorequest v0.2.16
 | 
			
		||||
	github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
 | 
			
		||||
	github.com/sirupsen/logrus v1.5.0
 | 
			
		||||
	github.com/spf13/afero v1.2.2
 | 
			
		||||
	github.com/spf13/cobra v0.0.5
 | 
			
		||||
	golang.org/x/crypto v0.0.0-20200414173820-0848c9571904
 | 
			
		||||
	github.com/sirupsen/logrus v1.6.0
 | 
			
		||||
	github.com/spf13/afero v1.3.0
 | 
			
		||||
	github.com/spf13/cobra v1.0.0
 | 
			
		||||
	github.com/takuzoo3868/go-msfdb v0.1.1
 | 
			
		||||
	github.com/valyala/fasttemplate v1.2.1 // indirect
 | 
			
		||||
	golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9
 | 
			
		||||
	golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect
 | 
			
		||||
	golang.org/x/net v0.0.0-20201110031124-69a78807bb2b // indirect
 | 
			
		||||
	golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
 | 
			
		||||
	golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d // indirect
 | 
			
		||||
	golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
 | 
			
		||||
	k8s.io/utils v0.0.0-20191114184206-e782cd3c129f
 | 
			
		||||
	golang.org/x/sys v0.0.0-20201117222635-ba5294a509c7 // indirect
 | 
			
		||||
	golang.org/x/text v0.3.4 // indirect
 | 
			
		||||
	golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
 | 
			
		||||
	k8s.io/utils v0.0.0-20200619165400-6e3d28b6ed19
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -21,8 +21,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 +52,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 +87,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 +101,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)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -23,7 +23,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{}
 | 
			
		||||
 
 | 
			
		||||
| 
		 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,8 @@ 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) (totalCnt int, err error) {
 | 
			
		||||
	if len(r.LibraryScanners) == 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
@@ -46,7 +46,12 @@ func FillLibrary(r *models.ScanResult) (totalCnt int, err error) {
 | 
			
		||||
		}
 | 
			
		||||
		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)
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -256,7 +256,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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@ package models
 | 
			
		||||
import (
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	"github.com/Masterminds/semver/v3"
 | 
			
		||||
	"github.com/aquasecurity/trivy-db/pkg/db"
 | 
			
		||||
	trivyDBTypes "github.com/aquasecurity/trivy-db/pkg/types"
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/detector/library"
 | 
			
		||||
@@ -10,20 +11,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,14 +39,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)
 | 
			
		||||
		v, err := semver.StrictNewVersion(pkg.Version)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Debugf("new version cant detected %s@%s", pkg.Name, pkg.Version)
 | 
			
		||||
			continue
 | 
			
		||||
@@ -72,7 +70,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 +92,7 @@ func (s LibraryScanner) getVulnDetail(tvuln types.DetectedVulnerability) (vinfo
 | 
			
		||||
				Key:     s.GetLibraryKey(),
 | 
			
		||||
				Name:    tvuln.PkgName,
 | 
			
		||||
				FixedIn: tvuln.FixedVersion,
 | 
			
		||||
				Path:    s.Path,
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -141,4 +140,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,29 @@ 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 []ListenPort `json:"listenPorts,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListenPort has the result of parsing the port information to the address and port.
 | 
			
		||||
type ListenPort struct {
 | 
			
		||||
	Address           string   `json:"address"`
 | 
			
		||||
	Port              string   `json:"port"`
 | 
			
		||||
	PortScanSuccessOn []string `json:"portScanSuccessOn"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HasPortScanSuccessOn checks if Package.AffectedProcs has PortScanSuccessOn
 | 
			
		||||
func (p Package) HasPortScanSuccessOn() bool {
 | 
			
		||||
	for _, ap := range p.AffectedProcs {
 | 
			
		||||
		for _, lp := range ap.ListenPorts {
 | 
			
		||||
			if len(lp.PortScanSuccessOn) > 0 {
 | 
			
		||||
				return true
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NeedRestartProcess keep a processes information affected by software update
 | 
			
		||||
@@ -227,3 +248,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,87 @@ 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])
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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,50 @@ 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -688,7 +688,7 @@ func TestIsDisplayUpdatableNum(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			mode:     []byte{config.Fast},
 | 
			
		||||
			family:   config.FreeBSD,
 | 
			
		||||
			expected: true,
 | 
			
		||||
			expected: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			mode:     []byte{config.Fast},
 | 
			
		||||
 
 | 
			
		||||
@@ -150,6 +150,7 @@ type VulnInfo struct {
 | 
			
		||||
	DistroAdvisories     DistroAdvisories     `json:"distroAdvisories,omitempty"` // for Aamazon, RHEL, FreeBSD
 | 
			
		||||
	CveContents          CveContents          `json:"cveContents,omitempty"`
 | 
			
		||||
	Exploits             []Exploit            `json:"exploits,omitempty"`
 | 
			
		||||
	Metasploits          []Metasploit         `json:"metasploits,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"`
 | 
			
		||||
@@ -791,6 +792,14 @@ type Exploit struct {
 | 
			
		||||
	BinaryURL    *string                   `json:"binaryURL,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Metasploit :
 | 
			
		||||
type Metasploit struct {
 | 
			
		||||
	Name        string   `json:"name"`
 | 
			
		||||
	Title       string   `json:"title"`
 | 
			
		||||
	Description string   `json:"description,omitempty"`
 | 
			
		||||
	URLs        []string `json:",omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AlertDict has target cve's JPCERT and USCERT alert data
 | 
			
		||||
type AlertDict struct {
 | 
			
		||||
	Ja []Alert `json:"ja"`
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										73
									
								
								msf/msf.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,73 @@
 | 
			
		||||
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
 | 
			
		||||
}
 | 
			
		||||
@@ -38,7 +38,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
 | 
			
		||||
	}
 | 
			
		||||
@@ -132,12 +138,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
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -100,7 +100,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)
 | 
			
		||||
 
 | 
			
		||||
@@ -327,7 +327,8 @@ 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
 | 
			
		||||
			}
 | 
			
		||||
@@ -362,7 +363,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
 | 
			
		||||
 
 | 
			
		||||
@@ -1089,7 +1089,7 @@ func TestIsOvalDefAffected(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestMajor(t *testing.T) {
 | 
			
		||||
func Test_major(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in       string
 | 
			
		||||
		expected string
 | 
			
		||||
 
 | 
			
		||||
@@ -9,24 +9,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
 | 
			
		||||
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 +69,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 +190,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
 | 
			
		||||
}
 | 
			
		||||
@@ -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
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										292
									
								
								report/report.go
									
									
									
									
									
								
							
							
						
						@@ -22,6 +22,7 @@ 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"
 | 
			
		||||
@@ -31,6 +32,7 @@ import (
 | 
			
		||||
	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"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -41,98 +43,112 @@ const (
 | 
			
		||||
 | 
			
		||||
// 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 !useScannedCves(&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...)
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		nCVEs, err := libmanager.DetectLibsCves(&r)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to fill with Library dependency: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		util.Log.Infof("%s: %d CVEs are detected with Library",
 | 
			
		||||
			r.FormatServerName(), nCVEs)
 | 
			
		||||
 | 
			
		||||
		// Integrations
 | 
			
		||||
		githubInts := GithubSecurityAlerts(c.Conf.Servers[r.ServerName].GitHubRepos)
 | 
			
		||||
 | 
			
		||||
		wpVulnCaches := map[string]string{}
 | 
			
		||||
		wpOpt := WordPressOption{c.Conf.Servers[r.ServerName].WordPress.WPVulnDBToken, &wpVulnCaches}
 | 
			
		||||
 | 
			
		||||
		if err := FillCveInfo(dbclient,
 | 
			
		||||
			&r,
 | 
			
		||||
			cpeURIs,
 | 
			
		||||
			true,
 | 
			
		||||
			githubInts,
 | 
			
		||||
			wpOpt); 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,23 +157,15 @@ 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")
 | 
			
		||||
 | 
			
		||||
	nCVEs, err := libmanager.FillLibrary(r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to fill with Library dependency: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	util.Log.Infof("%s: %d CVEs are detected with Library",
 | 
			
		||||
		r.FormatServerName(), nCVEs)
 | 
			
		||||
 | 
			
		||||
	nCVEs, err = FillWithOval(dbclient.OvalDB, r)
 | 
			
		||||
	nCVEs, err := DetectPkgsCvesWithOval(dbclient.OvalDB, r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to fill with OVAL: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
@@ -173,7 +181,7 @@ func FillCveInfo(dbclient DBClient, r *models.ScanResult, cpeURIs []string, igno
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	nCVEs, err = fillVulnByCpeURIs(dbclient.CveDB, r, cpeURIs)
 | 
			
		||||
	nCVEs, err = DetectCpeURIsCves(dbclient.CveDB, r, cpeURIs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to detect vulns of `%s`: %w", cpeURIs, err)
 | 
			
		||||
	}
 | 
			
		||||
@@ -187,7 +195,7 @@ func FillCveInfo(dbclient DBClient, r *models.ScanResult, cpeURIs []string, igno
 | 
			
		||||
	}
 | 
			
		||||
	util.Log.Infof("%s: %d CVEs are detected with GitHub Security Alerts", r.FormatServerName(), ints.GithubAlertsCveCounts)
 | 
			
		||||
 | 
			
		||||
	nCVEs, err = FillWithGost(dbclient.GostDB, r, ignoreWillNotFix)
 | 
			
		||||
	nCVEs, err = DetectPkgsCvesWithGost(dbclient.GostDB, r, ignoreWillNotFix)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to fill with gost: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
@@ -195,24 +203,32 @@ func FillCveInfo(dbclient DBClient, r *models.ScanResult, cpeURIs []string, igno
 | 
			
		||||
		r.FormatServerName(), nCVEs)
 | 
			
		||||
 | 
			
		||||
	util.Log.Infof("Fill CVE detailed information with CVE-DB")
 | 
			
		||||
	if err := fillCveDetail(dbclient.CveDB, r); err != nil {
 | 
			
		||||
	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)
 | 
			
		||||
	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 information 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)
 | 
			
		||||
 | 
			
		||||
	fillCweDict(r)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// fillCveDetail fetches NVD, JVN from CVE Database
 | 
			
		||||
func fillCveDetail(driver cvedb.DB, r *models.ScanResult) error {
 | 
			
		||||
// fillCvesWithNvdJvn fetches NVD, JVN from CVE Database
 | 
			
		||||
func fillCvesWithNvdJvn(driver cvedb.DB, r *models.ScanResult) error {
 | 
			
		||||
	cveIDs := []string{}
 | 
			
		||||
	for _, v := range r.ScannedCves {
 | 
			
		||||
		cveIDs = append(cveIDs, v.CveID)
 | 
			
		||||
@@ -268,13 +284,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) (nCVEs int, err 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,7 +316,7 @@ 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:
 | 
			
		||||
	case c.FreeBSD, c.Windows:
 | 
			
		||||
		return 0, nil
 | 
			
		||||
	case c.ServerTypePseudo:
 | 
			
		||||
		return 0, nil
 | 
			
		||||
@@ -337,29 +353,35 @@ func FillWithOval(driver ovaldb.DB, r *models.ScanResult) (nCVEs int, err error)
 | 
			
		||||
	return ovalClient.FillWithOval(driver, r)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillWithGost fills CVEs with gost dataabase
 | 
			
		||||
// DetectPkgsCvesWithGost fills CVEs with gost dataabase
 | 
			
		||||
// https://github.com/knqyf263/gost
 | 
			
		||||
func FillWithGost(driver gostdb.DB, r *models.ScanResult, ignoreWillNotFix bool) (nCVEs int, err error) {
 | 
			
		||||
func DetectPkgsCvesWithGost(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
 | 
			
		||||
	// TODO check if fetched
 | 
			
		||||
	// TODO check if fresh enough
 | 
			
		||||
	if nCVEs, err = gostClient.DetectUnfixed(driver, r, ignoreWillNotFix); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	return nCVEs, gostClient.FillCVEsWithRedHat(driver, r)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillWithExploit fills Exploits with exploit dataabase
 | 
			
		||||
// 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) {
 | 
			
		||||
	// TODO check if fetched
 | 
			
		||||
	// TODO check if fresh enough
 | 
			
		||||
	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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func DetectCpeURIsCves(driver cvedb.DB, r *models.ScanResult, cpeURIs []string) (nCVEs int, err error) {
 | 
			
		||||
	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 0, 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)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -429,14 +451,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)
 | 
			
		||||
	}
 | 
			
		||||
@@ -603,6 +626,7 @@ func EnsureUUIDs(configPath string, results models.ScanResults) (err error) {
 | 
			
		||||
	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
 | 
			
		||||
@@ -644,38 +668,40 @@ func EnsureUUIDs(configPath string, results models.ScanResults) (err error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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"`
 | 
			
		||||
		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"`
 | 
			
		||||
		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,
 | 
			
		||||
		CveDict:    cveDict,
 | 
			
		||||
		OvalDict:   ovalDict,
 | 
			
		||||
		Gost:       gost,
 | 
			
		||||
		Exploit:    exploit,
 | 
			
		||||
		Metasploit: metasploit,
 | 
			
		||||
		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,
 | 
			
		||||
@@ -702,7 +728,7 @@ func EnsureUUIDs(configPath string, results models.ScanResults) (err error) {
 | 
			
		||||
	}
 | 
			
		||||
	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",
 | 
			
		||||
		"# See README for details: https://vuls.io/docs/en/usage-settings.html",
 | 
			
		||||
		str)
 | 
			
		||||
 | 
			
		||||
	return ioutil.WriteFile(realPath, []byte(str), 0600)
 | 
			
		||||
 
 | 
			
		||||
@@ -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))
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -617,8 +617,16 @@ 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].HasPortScanSuccessOn() {
 | 
			
		||||
				av = fmt.Sprintf("%s ◉", av)
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		exploits := ""
 | 
			
		||||
		if 0 < len(vinfo.Exploits) {
 | 
			
		||||
		if 0 < len(vinfo.Exploits) || 0 < len(vinfo.Metasploits) {
 | 
			
		||||
			exploits = "POC"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -627,7 +635,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 +647,7 @@ func summaryLines(r models.ScanResult) string {
 | 
			
		||||
		}
 | 
			
		||||
		stable.AddRow(icols...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return fmt.Sprintf("%s", stable)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -710,8 +719,23 @@ func setChangelogLayout(g *gocui.Gui) error {
 | 
			
		||||
 | 
			
		||||
				if len(pack.AffectedProcs) != 0 {
 | 
			
		||||
					for _, p := range pack.AffectedProcs {
 | 
			
		||||
						if len(p.ListenPorts) == 0 {
 | 
			
		||||
							lines = append(lines, fmt.Sprintf("  * PID: %s %s Port: []",
 | 
			
		||||
								p.PID, p.Name))
 | 
			
		||||
							continue
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						var ports []string
 | 
			
		||||
						for _, pp := range p.ListenPorts {
 | 
			
		||||
							if len(pp.PortScanSuccessOn) == 0 {
 | 
			
		||||
								ports = append(ports, fmt.Sprintf("%s:%s", pp.Address, pp.Port))
 | 
			
		||||
							} else {
 | 
			
		||||
								ports = append(ports, fmt.Sprintf("%s:%s(◉ Scannable: %s)", pp.Address, pp.Port, pp.PortScanSuccessOn))
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						lines = append(lines, fmt.Sprintf("  * PID: %s %s Port: %s",
 | 
			
		||||
							p.PID, p.Name, p.ListenPorts))
 | 
			
		||||
							p.PID, p.Name, ports))
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
@@ -745,7 +769,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 +794,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,6 +861,7 @@ type dataForTmpl struct {
 | 
			
		||||
	CveID            string
 | 
			
		||||
	Cvsses           string
 | 
			
		||||
	Exploits         []models.Exploit
 | 
			
		||||
	Metasploits      []models.Metasploit
 | 
			
		||||
	Summary          string
 | 
			
		||||
	Mitigation       string
 | 
			
		||||
	Confidences      models.Confidences
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ package report
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/csv"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
@@ -71,6 +72,7 @@ func formatOneLineSummary(rs ...models.ScanResult) string {
 | 
			
		||||
				r.ScannedCves.FormatFixedStatus(r.Packages),
 | 
			
		||||
				r.FormatUpdatablePacksSummary(),
 | 
			
		||||
				r.FormatExploitCveSummary(),
 | 
			
		||||
				r.FormatMetasploitCveSummary(),
 | 
			
		||||
				r.FormatAlertSummary(),
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
@@ -96,7 +98,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 +128,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"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -170,7 +172,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",
 | 
			
		||||
@@ -260,8 +262,22 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
 | 
			
		||||
				if len(pack.AffectedProcs) != 0 {
 | 
			
		||||
					for _, p := range pack.AffectedProcs {
 | 
			
		||||
						if len(p.ListenPorts) == 0 {
 | 
			
		||||
							data = append(data, []string{"",
 | 
			
		||||
								fmt.Sprintf("  - PID: %s %s, Port: []", p.PID, p.Name)})
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						var ports []string
 | 
			
		||||
						for _, pp := range p.ListenPorts {
 | 
			
		||||
							if len(pp.PortScanSuccessOn) == 0 {
 | 
			
		||||
								ports = append(ports, fmt.Sprintf("%s:%s", pp.Address, pp.Port))
 | 
			
		||||
							} else {
 | 
			
		||||
								ports = append(ports, fmt.Sprintf("%s:%s(◉ Scannable: %s)", pp.Address, pp.Port, pp.PortScanSuccessOn))
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						data = append(data, []string{"",
 | 
			
		||||
							fmt.Sprintf("  - PID: %s %s, Port: %s", p.PID, p.Name, p.ListenPorts)})
 | 
			
		||||
							fmt.Sprintf("  - PID: %s %s, Port: %s", p.PID, p.Name, ports)})
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
@@ -292,7 +308,7 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, l := range vuln.LibraryFixedIns {
 | 
			
		||||
			libs := r.LibraryScanners.Find(l.Name)
 | 
			
		||||
			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)",
 | 
			
		||||
@@ -367,6 +383,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://wpvulndb.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 +442,14 @@ func formatChangelogs(r models.ScanResult) string {
 | 
			
		||||
	}
 | 
			
		||||
	return strings.Join(buf, "\n")
 | 
			
		||||
}
 | 
			
		||||
func ovalSupported(r *models.ScanResult) bool {
 | 
			
		||||
func useScannedCves(r *models.ScanResult) bool {
 | 
			
		||||
	switch r.Family {
 | 
			
		||||
	case
 | 
			
		||||
		config.Amazon,
 | 
			
		||||
		config.FreeBSD,
 | 
			
		||||
		config.Raspbian:
 | 
			
		||||
		return false
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func needToRefreshCve(r models.ScanResult) bool {
 | 
			
		||||
@@ -499,9 +553,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 ghost https://github.com/knqyf263/gost
 | 
			
		||||
				// } else if isCveFixed(v, previous) {
 | 
			
		||||
				// updated[v.CveID] = v
 | 
			
		||||
				// util.Log.Debugf("fixed: %s", v.CveID)
 | 
			
		||||
@@ -515,6 +569,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
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -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], "-")
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										184
									
								
								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,50 @@ 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type DummyFileInfo struct{}
 | 
			
		||||
 | 
			
		||||
func (d *DummyFileInfo) Name() string       { return "dummy" }
 | 
			
		||||
func (d *DummyFileInfo) Size() int64        { return 0 }
 | 
			
		||||
func (d *DummyFileInfo) Mode() os.FileMode  { return 0 }
 | 
			
		||||
func (d *DummyFileInfo) ModTime() time.Time { return time.Now() }
 | 
			
		||||
func (d *DummyFileInfo) IsDir() bool        { return false }
 | 
			
		||||
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 +729,113 @@ 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.ListenPorts == nil {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			for _, port := range proc.ListenPorts {
 | 
			
		||||
				scanIPPortsMap[port.Address] = append(scanIPPortsMap[port.Address], 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.ListenPorts == nil {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			for j, port := range proc.ListenPorts {
 | 
			
		||||
				l.osPackages.Packages[name].AffectedProcs[i].ListenPorts[j].PortScanSuccessOn = l.findPortScanSuccessOn(listenIPPorts, port)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *base) findPortScanSuccessOn(listenIPPorts []string, searchListenPort models.ListenPort) []string {
 | 
			
		||||
	addrs := []string{}
 | 
			
		||||
 | 
			
		||||
	for _, ipPort := range listenIPPorts {
 | 
			
		||||
		ipPort := l.parseListenPorts(ipPort)
 | 
			
		||||
		if searchListenPort.Address == "*" {
 | 
			
		||||
			if searchListenPort.Port == ipPort.Port {
 | 
			
		||||
				addrs = append(addrs, ipPort.Address)
 | 
			
		||||
			}
 | 
			
		||||
		} else if searchListenPort.Address == ipPort.Address && searchListenPort.Port == ipPort.Port {
 | 
			
		||||
			addrs = append(addrs, ipPort.Address)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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 +898,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 +912,15 @@ 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *base) parseListenPorts(port string) models.ListenPort {
 | 
			
		||||
	sep := strings.LastIndex(port, ":")
 | 
			
		||||
	if sep == -1 {
 | 
			
		||||
		return models.ListenPort{}
 | 
			
		||||
	}
 | 
			
		||||
	return models.ListenPort{Address: port[:sep], Port: port[sep+1:]}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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,242 @@ 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", ListenPorts: []models.ListenPort{{Address: "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", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22"}}}, {PID: "21", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "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", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22"}}}, {PID: "21", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "192.168.1.1", Port: "22"}}}, {PID: "6261", Name: "nginx", ListenPorts: []models.ListenPort{{Address: "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", ListenPorts: []models.ListenPort{{Address: "*", 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", ListenPorts: []models.ListenPort{{Address: "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", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22", PortScanSuccessOn: []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", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22"}, {Address: "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", ListenPorts: []models.ListenPort{
 | 
			
		||||
				{Address: "127.0.0.1", Port: "22", PortScanSuccessOn: []string{"127.0.0.1"}},
 | 
			
		||||
				{Address: "192.168.1.1", Port: "22", PortScanSuccessOn: []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", ListenPorts: []models.ListenPort{{Address: "*", 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", ListenPorts: []models.ListenPort{
 | 
			
		||||
				{Address: "*", Port: "22", PortScanSuccessOn: []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", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "80"}}}}},
 | 
			
		||||
						"packb": models.Package{Name: "packb", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22"}}}}},
 | 
			
		||||
						"packc": models.Package{Name: "packc", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22"}, {Address: "192.168.1.1", Port: "22"}}}}},
 | 
			
		||||
						"packd": models.Package{Name: "packd", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "*", 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", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "80", PortScanSuccessOn: []string{}}}}}},
 | 
			
		||||
				"packb": models.Package{Name: "packb", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22", PortScanSuccessOn: []string{"127.0.0.1"}}}}}},
 | 
			
		||||
				"packc": models.Package{Name: "packc", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22", PortScanSuccessOn: []string{"127.0.0.1"}}, {Address: "192.168.1.1", Port: "22", PortScanSuccessOn: []string{"192.168.1.1"}}}}}},
 | 
			
		||||
				"packd": models.Package{Name: "packd", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "*", Port: "22", PortScanSuccessOn: []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.ListenPort
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name   string
 | 
			
		||||
		args   args
 | 
			
		||||
		expect []string
 | 
			
		||||
	}{
 | 
			
		||||
		{name: "open_empty", args: args{listenIPPorts: []string{}, searchListenPort: models.ListenPort{Address: "127.0.0.1", Port: "22"}}, expect: []string{}},
 | 
			
		||||
		{name: "port_empty", args: args{listenIPPorts: []string{"127.0.0.1:22"}, searchListenPort: models.ListenPort{}}, expect: []string{}},
 | 
			
		||||
		{name: "single_match", args: args{listenIPPorts: []string{"127.0.0.1:22"}, searchListenPort: models.ListenPort{Address: "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.ListenPort{Address: "192.168.1.1", Port: "22"}}, expect: []string{}},
 | 
			
		||||
		{name: "no_match_port", args: args{listenIPPorts: []string{"127.0.0.1:22"}, searchListenPort: models.ListenPort{Address: "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.ListenPort{Address: "*", 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.findPortScanSuccessOn(tt.args.listenIPPorts, tt.args.searchListenPort); !reflect.DeepEqual(match, tt.expect) {
 | 
			
		||||
				t.Errorf("findPortScanSuccessOn() = %v, want %v", match, tt.expect)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_base_parseListenPorts(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name   string
 | 
			
		||||
		args   string
 | 
			
		||||
		expect models.ListenPort
 | 
			
		||||
	}{{
 | 
			
		||||
		name: "empty",
 | 
			
		||||
		args: "",
 | 
			
		||||
		expect: models.ListenPort{
 | 
			
		||||
			Address: "",
 | 
			
		||||
			Port:    "",
 | 
			
		||||
		},
 | 
			
		||||
	}, {
 | 
			
		||||
		name: "normal",
 | 
			
		||||
		args: "127.0.0.1:22",
 | 
			
		||||
		expect: models.ListenPort{
 | 
			
		||||
			Address: "127.0.0.1",
 | 
			
		||||
			Port:    "22",
 | 
			
		||||
		},
 | 
			
		||||
	}, {
 | 
			
		||||
		name: "asterisk",
 | 
			
		||||
		args: "*:22",
 | 
			
		||||
		expect: models.ListenPort{
 | 
			
		||||
			Address: "*",
 | 
			
		||||
			Port:    "22",
 | 
			
		||||
		},
 | 
			
		||||
	}, {
 | 
			
		||||
		name: "ipv6_loopback",
 | 
			
		||||
		args: "[::1]:22",
 | 
			
		||||
		expect: models.ListenPort{
 | 
			
		||||
			Address: "[::1]",
 | 
			
		||||
			Port:    "22",
 | 
			
		||||
		},
 | 
			
		||||
	}}
 | 
			
		||||
 | 
			
		||||
	l := base{}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			if listenPort := l.parseListenPorts(tt.args); !reflect.DeepEqual(listenPort, tt.expect) {
 | 
			
		||||
				t.Errorf("base.parseListenPorts() = %v, want %v", listenPort, tt.expect)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										185
									
								
								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,16 @@ func (o *debian) dpkgPs() error {
 | 
			
		||||
		pidLoadedFiles[pid] = append(pidLoadedFiles[pid], ss...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pidListenPorts := map[string][]string{}
 | 
			
		||||
	pidListenPorts := map[string][]models.ListenPort{}
 | 
			
		||||
	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 port, pids := range portPids {
 | 
			
		||||
		for _, pid := range pids {
 | 
			
		||||
			pidListenPorts[pid] = append(pidListenPorts[pid], o.parseListenPorts(port))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for pid, loadedFiles := range pidLoadedFiles {
 | 
			
		||||
 
 | 
			
		||||
@@ -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,16 @@ func (o *redhatBase) yumPs() error {
 | 
			
		||||
		pidLoadedFiles[pid] = append(pidLoadedFiles[pid], ss...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pidListenPorts := map[string][]string{}
 | 
			
		||||
	pidListenPorts := map[string][]models.ListenPort{}
 | 
			
		||||
	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 port, pids := range portPids {
 | 
			
		||||
		for _, pid := range pids {
 | 
			
		||||
			pidListenPorts[pid] = append(pidListenPorts[pid], o.parseListenPorts(port))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for pid, loadedFiles := range pidLoadedFiles {
 | 
			
		||||
@@ -630,8 +632,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))
 | 
			
		||||
 
 | 
			
		||||
@@ -25,10 +25,10 @@ func TestParseInstalledPackagesLinesRedhat(t *testing.T) {
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			in: `openssl	0	1.0.1e	30.el6.11 x86_64
 | 
			
		||||
                 Percona-Server-shared-56	1	5.6.19	rel67.0.el6 x84_64
 | 
			
		||||
                 kernel 0 2.6.32 696.20.1.el6 x86_64
 | 
			
		||||
                 kernel 0 2.6.32 696.20.3.el6 x86_64
 | 
			
		||||
				 kernel 0 2.6.32 695.20.3.el6 x86_64`,
 | 
			
		||||
Percona-Server-shared-56	1	5.6.19	rel67.0.el6 x84_64
 | 
			
		||||
kernel 0 2.6.32 696.20.1.el6 x86_64
 | 
			
		||||
kernel 0 2.6.32 696.20.3.el6 x86_64
 | 
			
		||||
kernel 0 2.6.32 695.20.3.el6 x86_64`,
 | 
			
		||||
			kernel: models.Kernel{},
 | 
			
		||||
			packages: models.Packages{
 | 
			
		||||
				"openssl": models.Package{
 | 
			
		||||
@@ -50,10 +50,46 @@ func TestParseInstalledPackagesLinesRedhat(t *testing.T) {
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: `openssl	0	1.0.1e	30.el6.11 x86_64
 | 
			
		||||
                 Percona-Server-shared-56	1	5.6.19	rel67.0.el6 x84_64
 | 
			
		||||
                 kernel 0 2.6.32 696.20.1.el6 x86_64
 | 
			
		||||
                 kernel 0 2.6.32 696.20.3.el6 x86_64
 | 
			
		||||
				 kernel 0 2.6.32 695.20.3.el6 x86_64`,
 | 
			
		||||
Percona-Server-shared-56	1	5.6.19	rel67.0.el6 x84_64
 | 
			
		||||
kernel 0 2.6.32 696.20.1.el6 x86_64
 | 
			
		||||
kernel 0 2.6.32 696.20.3.el6 x86_64
 | 
			
		||||
kernel 0 2.6.32 695.20.3.el6 x86_64
 | 
			
		||||
kernel-devel 0 2.6.32 696.20.1.el6 x86_64
 | 
			
		||||
kernel-devel 0 2.6.32 696.20.3.el6 x86_64
 | 
			
		||||
kernel-devel 0 2.6.32 695.20.3.el6 x86_64`,
 | 
			
		||||
			kernel: models.Kernel{Release: "2.6.32-696.20.3.el6.x86_64"},
 | 
			
		||||
			packages: models.Packages{
 | 
			
		||||
				"openssl": models.Package{
 | 
			
		||||
					Name:    "openssl",
 | 
			
		||||
					Version: "1.0.1e",
 | 
			
		||||
					Release: "30.el6.11",
 | 
			
		||||
				},
 | 
			
		||||
				"Percona-Server-shared-56": models.Package{
 | 
			
		||||
					Name:    "Percona-Server-shared-56",
 | 
			
		||||
					Version: "1:5.6.19",
 | 
			
		||||
					Release: "rel67.0.el6",
 | 
			
		||||
				},
 | 
			
		||||
				"kernel": models.Package{
 | 
			
		||||
					Name:    "kernel",
 | 
			
		||||
					Version: "2.6.32",
 | 
			
		||||
					Release: "696.20.3.el6",
 | 
			
		||||
				},
 | 
			
		||||
				"kernel-devel": models.Package{
 | 
			
		||||
					Name:    "kernel-devel",
 | 
			
		||||
					Version: "2.6.32",
 | 
			
		||||
					Release: "696.20.3.el6",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: `openssl	0	1.0.1e	30.el6.11 x86_64
 | 
			
		||||
Percona-Server-shared-56	1	5.6.19	rel67.0.el6 x84_64
 | 
			
		||||
kernel 0 2.6.32 696.20.1.el6 x86_64
 | 
			
		||||
kernel 0 2.6.32 696.20.3.el6 x86_64
 | 
			
		||||
kernel 0 2.6.32 695.20.3.el6 x86_64
 | 
			
		||||
kernel-devel 0 2.6.32 696.20.1.el6 x86_64
 | 
			
		||||
kernel-devel 0 2.6.32 696.20.3.el6 x86_64
 | 
			
		||||
kernel-devel 0 2.6.32 695.20.3.el6 x86_64`,
 | 
			
		||||
			kernel: models.Kernel{Release: "2.6.32-695.20.3.el6.x86_64"},
 | 
			
		||||
			packages: models.Packages{
 | 
			
		||||
				"openssl": models.Package{
 | 
			
		||||
@@ -71,6 +107,11 @@ func TestParseInstalledPackagesLinesRedhat(t *testing.T) {
 | 
			
		||||
					Version: "2.6.32",
 | 
			
		||||
					Release: "695.20.3.el6",
 | 
			
		||||
				},
 | 
			
		||||
				"kernel-devel": models.Package{
 | 
			
		||||
					Name:    "kernel-devel",
 | 
			
		||||
					Version: "2.6.32",
 | 
			
		||||
					Release: "695.20.3.el6",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -48,6 +48,7 @@ type osTypeInterface interface {
 | 
			
		||||
	postScan() error
 | 
			
		||||
	scanWordPress() error
 | 
			
		||||
	scanLibraries() error
 | 
			
		||||
	scanPorts() error
 | 
			
		||||
	scanPackages() error
 | 
			
		||||
	convertToModel() models.ScanResult
 | 
			
		||||
 | 
			
		||||
@@ -599,7 +600,7 @@ func setupChangelogCache() error {
 | 
			
		||||
			needToSetupCache = true
 | 
			
		||||
			break
 | 
			
		||||
		case config.Ubuntu, config.Debian:
 | 
			
		||||
			//TODO changelopg cache for RedHat, Oracle, Amazon, CentOS is not implemented yet.
 | 
			
		||||
			//TODO changelog cache for RedHat, Oracle, Amazon, CentOS is not implemented yet.
 | 
			
		||||
			if s.getServerInfo().Mode.IsDeep() {
 | 
			
		||||
				needToSetupCache = true
 | 
			
		||||
			}
 | 
			
		||||
@@ -634,6 +635,9 @@ func GetScanResults(scannedAt time.Time, timeoutSec int) (results models.ScanRes
 | 
			
		||||
		if err = o.scanLibraries(); err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to scan Library: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		if err = o.scanPorts(); err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to scan Ports: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}, timeoutSec)
 | 
			
		||||
 | 
			
		||||
@@ -642,6 +646,7 @@ func GetScanResults(scannedAt time.Time, timeoutSec int) (results models.ScanRes
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		util.Log.Errorf("Failed to fetch scannedIPs. err: %+v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, s := range append(servers, errServers...) {
 | 
			
		||||
		r := s.convertToModel()
 | 
			
		||||
		r.ScannedAt = scannedAt
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,8 @@ func isRunningKernel(pack models.Package, family string, kernel models.Kernel) (
 | 
			
		||||
		return false, false
 | 
			
		||||
 | 
			
		||||
	case config.RedHat, config.Oracle, config.CentOS, config.Amazon:
 | 
			
		||||
		if pack.Name == "kernel" {
 | 
			
		||||
		switch pack.Name {
 | 
			
		||||
		case "kernel", "kernel-devel":
 | 
			
		||||
			ver := fmt.Sprintf("%s-%s.%s", pack.Version, pack.Release, pack.Arch)
 | 
			
		||||
			return true, kernel.Release == ver
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	c "github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/libmanager"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/report"
 | 
			
		||||
	"github.com/future-architect/vuls/scan"
 | 
			
		||||
@@ -56,12 +57,26 @@ func (h VulsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	nCVEs, err := libmanager.DetectLibsCves(&result)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		util.Log.Error("Failed to fill with Library dependency: %w", err)
 | 
			
		||||
		http.Error(w, err.Error(), http.StatusServiceUnavailable)
 | 
			
		||||
	}
 | 
			
		||||
	util.Log.Infof("%s: %d CVEs are detected with Library",
 | 
			
		||||
		result.FormatServerName(), nCVEs)
 | 
			
		||||
 | 
			
		||||
	if err := report.FillCveInfo(h.DBclient, &result, []string{}, true); err != nil {
 | 
			
		||||
		util.Log.Error(err)
 | 
			
		||||
		http.Error(w, err.Error(), http.StatusServiceUnavailable)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// set ReportedAt to current time when it's set to the epoch, ensures that ReportedAt will be set
 | 
			
		||||
	// properly for scans sent to vuls when running in server mode
 | 
			
		||||
	if result.ReportedAt.IsZero() {
 | 
			
		||||
		result.ReportedAt = time.Now()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// report
 | 
			
		||||
	reports := []report.ResultWriter{
 | 
			
		||||
		report.HTTPResponseWriter{Writer: w},
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ import (
 | 
			
		||||
	formatter "github.com/kotakanbe/logrus-prefixed-formatter"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Log for localhsot
 | 
			
		||||
// Log for localhost
 | 
			
		||||
var Log *logrus.Entry
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
@@ -34,6 +34,10 @@ func NewCustomLogger(c config.ServerInfo) *logrus.Entry {
 | 
			
		||||
		log.Level = logrus.DebugLevel
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if flag.Lookup("test.v") != nil {
 | 
			
		||||
		return logrus.NewEntry(log)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// File output
 | 
			
		||||
	logDir := GetDefaultLogDir()
 | 
			
		||||
	if 0 < len(config.Conf.LogDir) {
 | 
			
		||||
@@ -52,6 +56,7 @@ func NewCustomLogger(c config.ServerInfo) *logrus.Entry {
 | 
			
		||||
		if file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644); err == nil {
 | 
			
		||||
			log.Out = file
 | 
			
		||||
		} else {
 | 
			
		||||
			log.Out = os.Stderr
 | 
			
		||||
			log.Errorf("Failed to create log file. path: %s, err: %s", logFile, err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
@@ -65,14 +70,18 @@ func NewCustomLogger(c config.ServerInfo) *logrus.Entry {
 | 
			
		||||
 | 
			
		||||
	if _, err := os.Stat(logDir); err == nil {
 | 
			
		||||
		path := filepath.Join(logDir, fmt.Sprintf("%s.log", whereami))
 | 
			
		||||
		log.Hooks.Add(lfshook.NewHook(lfshook.PathMap{
 | 
			
		||||
			logrus.DebugLevel: path,
 | 
			
		||||
			logrus.InfoLevel:  path,
 | 
			
		||||
			logrus.WarnLevel:  path,
 | 
			
		||||
			logrus.ErrorLevel: path,
 | 
			
		||||
			logrus.FatalLevel: path,
 | 
			
		||||
			logrus.PanicLevel: path,
 | 
			
		||||
		}, nil))
 | 
			
		||||
		if _, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644); err == nil {
 | 
			
		||||
			log.Hooks.Add(lfshook.NewHook(lfshook.PathMap{
 | 
			
		||||
				logrus.DebugLevel: path,
 | 
			
		||||
				logrus.InfoLevel:  path,
 | 
			
		||||
				logrus.WarnLevel:  path,
 | 
			
		||||
				logrus.ErrorLevel: path,
 | 
			
		||||
				logrus.FatalLevel: path,
 | 
			
		||||
				logrus.PanicLevel: path,
 | 
			
		||||
			}, nil))
 | 
			
		||||
		} else {
 | 
			
		||||
			log.Errorf("Failed to create log file. path: %s, err: %s", path, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fields := logrus.Fields{"prefix": whereami}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								util/util.go
									
									
									
									
									
								
							
							
						
						@@ -42,11 +42,11 @@ func AppendIfMissing(slice []string, s string) []string {
 | 
			
		||||
// URLPathJoin make URL
 | 
			
		||||
func URLPathJoin(baseURL string, paths ...string) (string, error) {
 | 
			
		||||
	baseURL = strings.TrimSuffix(baseURL, "/")
 | 
			
		||||
	trimedPaths := []string{}
 | 
			
		||||
	trimmedPaths := []string{}
 | 
			
		||||
	for _, path := range paths {
 | 
			
		||||
		trimed := strings.Trim(path, " /")
 | 
			
		||||
		if len(trimed) != 0 {
 | 
			
		||||
			trimedPaths = append(trimedPaths, trimed)
 | 
			
		||||
		trimmed := strings.Trim(path, " /")
 | 
			
		||||
		if len(trimmed) != 0 {
 | 
			
		||||
			trimmedPaths = append(trimmedPaths, trimmed)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	var url *url.URL
 | 
			
		||||
@@ -54,7 +54,7 @@ func URLPathJoin(baseURL string, paths ...string) (string, error) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	url.Path += "/" + strings.Join(trimedPaths, "/")
 | 
			
		||||
	url.Path += "/" + strings.Join(trimmedPaths, "/")
 | 
			
		||||
	return url.String(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -48,20 +48,28 @@ type References struct {
 | 
			
		||||
 | 
			
		||||
// FillWordPress access to wpvulndb and fetch scurity alerts and then set to the given ScanResult.
 | 
			
		||||
// https://wpvulndb.com/
 | 
			
		||||
func FillWordPress(r *models.ScanResult, token string) (int, error) {
 | 
			
		||||
func FillWordPress(r *models.ScanResult, token string, wpVulnCaches *map[string]string) (int, error) {
 | 
			
		||||
	// Core
 | 
			
		||||
	ver := strings.Replace(r.WordPressPackages.CoreVersion(), ".", "", -1)
 | 
			
		||||
	if ver == "" {
 | 
			
		||||
		return 0, xerrors.New("Failed to get WordPress core version")
 | 
			
		||||
	}
 | 
			
		||||
	url := fmt.Sprintf("https://wpvulndb.com/api/v3/wordpresses/%s", ver)
 | 
			
		||||
	body, err := httpRequest(url, token)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	if body == "" {
 | 
			
		||||
		util.Log.Warnf("A result of REST access is empty: %s", url)
 | 
			
		||||
 | 
			
		||||
	body, ok := searchCache(ver, wpVulnCaches)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		url := fmt.Sprintf("https://wpvulndb.com/api/v3/wordpresses/%s", ver)
 | 
			
		||||
		var err error
 | 
			
		||||
		body, err = httpRequest(url, token)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		if body == "" {
 | 
			
		||||
			util.Log.Warnf("A result of REST access is empty: %s", url)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		(*wpVulnCaches)[ver] = body
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	wpVinfos, err := convertToVinfos(models.WPCore, body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
@@ -77,11 +85,17 @@ func FillWordPress(r *models.ScanResult, token string) (int, error) {
 | 
			
		||||
 | 
			
		||||
	// Themes
 | 
			
		||||
	for _, p := range themes {
 | 
			
		||||
		url := fmt.Sprintf("https://wpvulndb.com/api/v3/themes/%s", p.Name)
 | 
			
		||||
		body, err := httpRequest(url, token)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		body, ok := searchCache(p.Name, wpVulnCaches)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			url := fmt.Sprintf("https://wpvulndb.com/api/v3/themes/%s", p.Name)
 | 
			
		||||
			var err error
 | 
			
		||||
			body, err = httpRequest(url, token)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
			}
 | 
			
		||||
			(*wpVulnCaches)[p.Name] = body
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if body == "" {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
@@ -113,11 +127,17 @@ func FillWordPress(r *models.ScanResult, token string) (int, error) {
 | 
			
		||||
 | 
			
		||||
	// Plugins
 | 
			
		||||
	for _, p := range plugins {
 | 
			
		||||
		url := fmt.Sprintf("https://wpvulndb.com/api/v3/plugins/%s", p.Name)
 | 
			
		||||
		body, err := httpRequest(url, token)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		body, ok := searchCache(p.Name, wpVulnCaches)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			url := fmt.Sprintf("https://wpvulndb.com/api/v3/plugins/%s", p.Name)
 | 
			
		||||
			var err error
 | 
			
		||||
			body, err = httpRequest(url, token)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
			}
 | 
			
		||||
			(*wpVulnCaches)[p.Name] = body
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if body == "" {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
@@ -277,3 +297,11 @@ func removeInactives(pkgs models.WordPressPackages) (removed models.WordPressPac
 | 
			
		||||
	}
 | 
			
		||||
	return removed
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func searchCache(name string, wpVulnCaches *map[string]string) (string, bool) {
 | 
			
		||||
	value, ok := (*wpVulnCaches)[name]
 | 
			
		||||
	if ok {
 | 
			
		||||
		return value, true
 | 
			
		||||
	}
 | 
			
		||||
	return "", false
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -79,3 +79,52 @@ func TestRemoveInactive(t *testing.T) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestSearchCache(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		name        string
 | 
			
		||||
		wpVulnCache map[string]string
 | 
			
		||||
		value       string
 | 
			
		||||
		ok          bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "akismet",
 | 
			
		||||
			wpVulnCache: map[string]string{
 | 
			
		||||
				"akismet": "body",
 | 
			
		||||
			},
 | 
			
		||||
			value: "body",
 | 
			
		||||
			ok:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "akismet",
 | 
			
		||||
			wpVulnCache: map[string]string{
 | 
			
		||||
				"BackWPup": "body",
 | 
			
		||||
				"akismet":  "body",
 | 
			
		||||
			},
 | 
			
		||||
			value: "body",
 | 
			
		||||
			ok:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "akismet",
 | 
			
		||||
			wpVulnCache: map[string]string{
 | 
			
		||||
				"BackWPup": "body",
 | 
			
		||||
			},
 | 
			
		||||
			value: "",
 | 
			
		||||
			ok:    false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:        "akismet",
 | 
			
		||||
			wpVulnCache: nil,
 | 
			
		||||
			value:       "",
 | 
			
		||||
			ok:          false,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, tt := range tests {
 | 
			
		||||
		value, ok := searchCache(tt.name, &tt.wpVulnCache)
 | 
			
		||||
		if value != tt.value || ok != tt.ok {
 | 
			
		||||
			t.Errorf("[%d] searchCache error ", i)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||