Compare commits

...

43 Commits

Author SHA1 Message Date
kota kanbe
f82e5a281d Update CHANGELOG 2016-05-25 08:30:24 +09:00
kota kanbe
904e6241e4 Bump up version 2016-05-25 08:24:25 +09:00
Kota Kanbe
ce39a3daf9 Update README.md 2016-05-24 20:38:50 +09:00
Kota Kanbe
f2c7f74beb Merge pull request #69 from future-architect/enable_to_show_history
Enable to show previous scan result
2016-05-24 20:35:43 +09:00
kota kanbe
20db997fc2 Enable to show previous scan result 2016-05-24 20:19:56 +09:00
Kota Kanbe
7188e97444 Merge pull request #68 from future-architect/ignore-unscored-cves
Add ignore-unscored-cves option
2016-05-24 09:17:12 +09:00
kota kanbe
6d528e741d Add ignore-unscored-cves option 2016-05-24 08:35:36 +09:00
Kota Kanbe
d356e8370d Merge pull request #67 from future-architect/docker_container
Add docker container support
2016-05-24 08:05:01 +09:00
kota kanbe
5e336b5928 Add docker container support 2016-05-23 09:38:52 +09:00
Kota Kanbe
787ad0629b Merge pull request #57 from theonlydoo/patch-2
Update Dockerfile
2016-05-20 13:45:29 +09:00
Kota Kanbe
53e4adf24e Merge pull request #56 from theonlydoo/patch-1
Update run.sh
2016-05-20 13:45:04 +09:00
Kota Kanbe
6af811d63e Merge pull request #66 from future-architect/pointless_sudo_debian
Fix pointless sudo in debian.go #29
2016-05-19 17:21:41 +09:00
kota kanbe
359dab3380 fix pointless sudo in debian.go #29 2016-05-19 17:20:09 +09:00
Kota Kanbe
97a8e6e965 Merge pull request #65 from future-architect/version
Add version flag
2016-05-19 15:22:04 +09:00
kota kanbe
8ea699aa08 Add version flag 2016-05-19 15:02:17 +09:00
Kota Kanbe
7d924d2b0c Merge pull request #64 from future-architect/fix_nilpointer_when_error_in_cve_client
Fix error handling of httpGet in cve-client #58
2016-05-16 20:06:05 +09:00
kota kanbe
3c85613ada Fix error handling of httpGet in cve-client #58 2016-05-16 20:00:22 +09:00
Kota Kanbe
c536d26db3 Merge pull request #63 from future-architect/fix_nilpointer_when_error_in_cve_client
Fix nil pointer at error handling of cve_client #58
2016-05-16 19:26:53 +09:00
kota kanbe
4350ff2692 Fix nil pointer at error handling of cve_client #58 2016-05-16 19:23:43 +09:00
Kota Kanbe
0b9a1e7bb4 Merge pull request #55 from pabroff/modify
Fix scan on Japanese environment.
2016-05-16 14:48:27 +09:00
Kota Kanbe
714ad18fa0 Merge pull request #54 from jody-frankowski/fix_deprecated_typo
Fix a typo: replace Depricated by Deprecated.
2016-05-16 14:41:16 +09:00
pabroff
f81f785813 Fix again on Japanese environment. 2016-05-16 14:20:57 +09:00
Kota Kanbe
76c32af46f Merge pull request #60 from future-architect/fix_cvss_over
Fix -cvss-over flag #59
2016-05-16 12:31:07 +09:00
kota kanbe
cd108263e1 Fix -cvss-over flag #59 2016-05-16 12:13:21 +09:00
theonlydoo
093c47b59c Update Dockerfile
to comply with https://github.com/future-architect/vuls/compare/master...theonlydoo:patch-1
2016-05-11 17:37:29 +02:00
theonlydoo
56a40ec51a Update run.sh
Update to provide some more reliability on server boot time
2016-05-11 17:36:55 +02:00
pabroff
1337be2b84 Fix scan on Japanese environment. 2016-05-11 20:31:44 +09:00
Jody Frankowski
eecd2c60f5 Fix a typo: replace Depricated by Deprecated. 2016-05-11 11:18:32 +02:00
Kota Kanbe
da071cb120 Merge pull request #50 from pabroff/modify
Fix yes no infinite loop while doing yum update --changelog on root@CentOS #47
2016-05-10 13:58:28 +09:00
Dog of Pavlov
012cfa3cbe Fix #47
Default set  "Is this ok [y/N]:" is N.
2016-05-10 12:22:51 +09:00
Kota Kanbe
21180847dc Update README.fr.md 2016-04-30 11:07:36 +09:00
Kota Kanbe
9e9e538846 Merge pull request #45 from future-architect/fix_prefix_of_servername_slack
Fix $servername in output of discover command
2016-04-30 11:06:26 +09:00
kota kanbe
66025b1ae2 Fix $servername in output of discover command 2016-04-30 11:01:27 +09:00
Kota Kanbe
5999361358 Update README.md 2016-04-30 10:57:16 +09:00
Kota Kanbe
e8699d1cb7 Update README.md 2016-04-25 15:38:56 +09:00
Kota Kanbe
9292448e73 Merge pull request #33 from mattn/windows
Support Windows
2016-04-22 18:37:09 +09:00
kota kanbe
d7e156613d Merge branch 'master' of https://github.com/future-architect/vuls
* 'master' of https://github.com/future-architect/vuls:
  Fix yum to yum --color=never #36
  Update README
2016-04-21 23:24:22 +09:00
kota kanbe
c3604aa66d Update CHANGELOG 2016-04-21 23:23:51 +09:00
Kota Kanbe
5e037b1743 Merge pull request #41 from theonlydoo/patch-1
Update README
2016-04-21 19:17:27 +09:00
Kota Kanbe
ebc79805ed Merge pull request #42 from future-architect/yum_color_never
Fix yum to yum --color=never #36
2016-04-21 19:13:20 +09:00
kota kanbe
c37e56e51d Fix yum to yum --color=never #36 2016-04-21 19:10:36 +09:00
theonlydoo
28a93c02e6 Update README
not so sparse documentation
2016-04-21 11:46:24 +02:00
Yasuhiro Matsumoto
da16f9673e Support Windows 2016-04-15 19:25:41 +09:00
33 changed files with 1389 additions and 424 deletions

View File

@@ -1,5 +1,66 @@
# Change Log
## [v0.1.4](https://github.com/future-architect/vuls/tree/v0.1.4) (2016-05-24)
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.1.3...v0.1.4)
**Implemented enhancements:**
- Initial fetch from NVD is too heavy \(2.3 GB of memory consumed\) [\#27](https://github.com/future-architect/vuls/issues/27)
- Enable to show previous scan result [\#69](https://github.com/future-architect/vuls/pull/69) ([kotakanbe](https://github.com/kotakanbe))
- Add ignore-unscored-cves option [\#68](https://github.com/future-architect/vuls/pull/68) ([kotakanbe](https://github.com/kotakanbe))
- Support dynamic scanning docker container [\#67](https://github.com/future-architect/vuls/pull/67) ([kotakanbe](https://github.com/kotakanbe))
- Add version flag [\#65](https://github.com/future-architect/vuls/pull/65) ([kotakanbe](https://github.com/kotakanbe))
- Update Dockerfile [\#57](https://github.com/future-architect/vuls/pull/57) ([theonlydoo](https://github.com/theonlydoo))
- Update run.sh [\#56](https://github.com/future-architect/vuls/pull/56) ([theonlydoo](https://github.com/theonlydoo))
- Support Windows [\#33](https://github.com/future-architect/vuls/pull/33) ([mattn](https://github.com/mattn))
**Fixed bugs:**
- vuls scan -cvss-over does not work. [\#59](https://github.com/future-architect/vuls/issues/59)
- `panic: runtime error: invalid memory address or nil pointer dereference` when scan CentOS5.5 [\#58](https://github.com/future-architect/vuls/issues/58)
- It rans out of memory. [\#47](https://github.com/future-architect/vuls/issues/47)
- BUG: vuls scan on CentOS with Japanese environment. [\#43](https://github.com/future-architect/vuls/issues/43)
- yum --color=never [\#36](https://github.com/future-architect/vuls/issues/36)
- Failed to parse yum check-update [\#32](https://github.com/future-architect/vuls/issues/32)
- Pointless sudo [\#29](https://github.com/future-architect/vuls/issues/29)
- Can't init database in a path having blanks [\#26](https://github.com/future-architect/vuls/issues/26)
- Fix pointless sudo in debian.go \#29 [\#66](https://github.com/future-architect/vuls/pull/66) ([kotakanbe](https://github.com/kotakanbe))
- Fix error handling of httpGet in cve-client \#58 [\#64](https://github.com/future-architect/vuls/pull/64) ([kotakanbe](https://github.com/kotakanbe))
- Fix nil pointer at error handling of cve\_client \#58 [\#63](https://github.com/future-architect/vuls/pull/63) ([kotakanbe](https://github.com/kotakanbe))
- Set language en\_US. [\#61](https://github.com/future-architect/vuls/pull/61) ([pabroff](https://github.com/pabroff))
- Fix -cvss-over flag \#59 [\#60](https://github.com/future-architect/vuls/pull/60) ([kotakanbe](https://github.com/kotakanbe))
- Fix scan on Japanese environment. [\#55](https://github.com/future-architect/vuls/pull/55) ([pabroff](https://github.com/pabroff))
- Fix a typo: replace Depricated by Deprecated. [\#54](https://github.com/future-architect/vuls/pull/54) ([jody-frankowski](https://github.com/jody-frankowski))
- Fix yes no infinite loop while doing yum update --changelog on root@CentOS \#47 [\#50](https://github.com/future-architect/vuls/pull/50) ([pabroff](https://github.com/pabroff))
- Fix $servername in output of discover command [\#45](https://github.com/future-architect/vuls/pull/45) ([kotakanbe](https://github.com/kotakanbe))
## [v0.1.3](https://github.com/future-architect/vuls/tree/v0.1.3) (2016-04-21)
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.1.2...v0.1.3)
**Implemented enhancements:**
- Add sudo support for prepare [\#11](https://github.com/future-architect/vuls/issues/11)
- Dockerfile? [\#10](https://github.com/future-architect/vuls/issues/10)
- Update README [\#41](https://github.com/future-architect/vuls/pull/41) ([theonlydoo](https://github.com/theonlydoo))
- Sparse dockerization [\#38](https://github.com/future-architect/vuls/pull/38) ([theonlydoo](https://github.com/theonlydoo))
- No password in config [\#35](https://github.com/future-architect/vuls/pull/35) ([kotakanbe](https://github.com/kotakanbe))
- Fr readme translation [\#23](https://github.com/future-architect/vuls/pull/23) ([novakin](https://github.com/novakin))
**Fixed bugs:**
- Issues updating CVE database behind https proxy [\#39](https://github.com/future-architect/vuls/issues/39)
- Vuls failed to parse yum check-update [\#24](https://github.com/future-architect/vuls/issues/24)
- Fix yum to yum --color=never \#36 [\#42](https://github.com/future-architect/vuls/pull/42) ([kotakanbe](https://github.com/kotakanbe))
- Fix parse yum check update [\#40](https://github.com/future-architect/vuls/pull/40) ([kotakanbe](https://github.com/kotakanbe))
- fix typo [\#31](https://github.com/future-architect/vuls/pull/31) ([blue119](https://github.com/blue119))
- Fix error while parsing yum check-update \#24 [\#30](https://github.com/future-architect/vuls/pull/30) ([kotakanbe](https://github.com/kotakanbe))
**Closed issues:**
- Unable to scan on ubuntu because changelog.ubuntu.com is down... [\#21](https://github.com/future-architect/vuls/issues/21)
- err: Not initialize\(d\) yet.. [\#16](https://github.com/future-architect/vuls/issues/16)
- Errors when using fish shell [\#8](https://github.com/future-architect/vuls/issues/8)
## [v0.1.2](https://github.com/future-architect/vuls/tree/v0.1.2) (2016-04-12)
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.1.1...v0.1.2)

View File

@@ -7,7 +7,6 @@
fmtcheck \
pretest \
test \
integration \
cov \
clean
@@ -16,16 +15,16 @@ PKGS = ./. ./db ./config ./models ./report ./cveapi ./scan ./util ./commands
all: test
vendor:
@ go get -v github.com/mjibson/party
party -d external -c -u
# vendor:
# @ go get -v github.com/mjibson/party
# party -d external -c -u
lint:
@ go get -v github.com/golang/lint/golint
$(foreach file,$(SRCS),golint $(file) || exit;)
vet:
@-go get -v golang.org/x/tools/cmd/vet
# @-go get -v golang.org/x/tools/cmd/vet
$(foreach pkg,$(PKGS),go vet $(pkg);)
fmt:

View File

@@ -297,7 +297,7 @@ $ vuls discover 172.31.4.0/24
[slack]
hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
channel = "#channel-name"
#channel = "#{servername}"
#channel = "${servername}"
iconEmoji = ":ghost:"
authUser = "username"
notifyUsers = ["@username"]
@@ -344,7 +344,7 @@ Vous pouvez customiser votre configuration en utilisant ce modèle.
[slack]
hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
channel = "#channel-name"
#channel = "#{servername}"
#channel = "${servername}"
iconEmoji = ":ghost:"
authUser = "username"
notifyUsers = ["@username"]
@@ -352,12 +352,12 @@ Vous pouvez customiser votre configuration en utilisant ce modèle.
- hookURL : Incomming webhook's URL
- channel : channel name.
If you set #{servername} to channel, the report will be sent to #servername channel.
If you set ${servername} to channel, the report will be sent to each channel.
In the following example, the report will be sent to the #server1 and #server2.
Be sure to create these channels before scanning.
```
[slack]
channel = "#{servername}"
channel = "${servername}"
...snip...
[servers]

124
README.md
View File

@@ -2,6 +2,8 @@
# Vuls: VULnerability Scanner
[![Slack](https://img.shields.io/badge/slack-join-blue.svg)](http://goo.gl/forms/xm5KFo35tu)
[![License](https://img.shields.io/github/license/future-architect/vuls.svg?style=flat-square)](https://github.com/future-architect/vuls/blob/master/LICENSE.txt)
Vulnerability scanner for Linux, agentless, written in golang.
@@ -251,7 +253,9 @@ see https://github.com/future-architect/vuls/tree/master/docker
## Vuls
- Scan vulnerabilities on the servers and create a list of the CVE ID
- For more detailed information of the detected CVE, send HTTP request to go-cve-dictinary
- To scan Docker containers, Vuls connect via ssh to the Docker host and then `docker exec` to the containers. So, no need to run sshd daemon on the containers.
- Fetch more detailed information of the detected CVE from go-cve-dictionary
- Insert scan result into SQLite3
- Send a report by Slack, Email
- System operator can view the latest report by terminal
@@ -303,7 +307,7 @@ $ vuls discover 172.31.4.0/24
[slack]
hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
channel = "#channel-name"
#channel = "#{servername}"
#channel = "${servername}"
iconEmoji = ":ghost:"
authUser = "username"
notifyUsers = ["@username"]
@@ -333,6 +337,7 @@ host = "172.31.4.82"
#cpeNames = [
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]
#containers = ["${running}"]
```
You can customize your configuration using this template.
@@ -346,7 +351,7 @@ You can customize your configuration using this template.
[slack]
hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
channel = "#channel-name"
#channel = "#{servername}"
#channel = "${servername}"
iconEmoji = ":ghost:"
authUser = "username"
notifyUsers = ["@username"]
@@ -354,12 +359,12 @@ You can customize your configuration using this template.
- hookURL : Incomming webhook's URL
- channel : channel name.
If you set #{servername} to channel, the report will be sent to #servername channel.
If you set ${servername} to channel, the report will be sent to each channel.
In the following example, the report will be sent to the #server1 and #server2.
Be sure to create these channels before scanning.
```
[slack]
channel = "#{servername}"
channel = "${servername}"
...snip...
[servers]
@@ -398,6 +403,10 @@ You can customize your configuration using this template.
#port = "22"
#user = "username"
#keyPath = "/home/username/.ssh/id_rsa"
#cpeNames = [
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]
#containers = ["${running}"]
```
Items of the default section will be used if not specified.
@@ -413,6 +422,7 @@ You can customize your configuration using this template.
#cpeNames = [
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]
#containers = ["${running}"]
```
You can overwrite the default value specified in default section.
Vuls supports multiple SSH authentication methods.
@@ -469,6 +479,7 @@ scan:
[-dbpath=/path/to/vuls.sqlite3]
[-cve-dictionary-url=http://127.0.0.1:1323]
[-cvss-over=7]
[-ignore-unscored-cves]
[-report-slack]
[-report-mail]
[-http-proxy=http://192.168.0.1:8080]
@@ -494,6 +505,8 @@ scan:
SQL debug mode
-http-proxy string
http://proxy-url:port (default: empty)
-ignore-unscored-cves
Don't report the unscored CVEs
-lang string
[en|ja] (default "en")
-report-mail
@@ -576,6 +589,96 @@ To detect the vulnerbility of Ruby on Rails v4.2.1, cpeNames needs to be set in
"cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
]
```
# Usage: Scan Docker containers
It is common that keep Docker containers runnning without SSHd daemon.
see [Docker Blog:Why you don't need to run SSHd in your Docker containers](https://blog.docker.com/2014/06/why-you-dont-need-to-run-sshd-in-docker/)
Vuls scans Docker containers via `docker exec` instead of SSH.
For more details, see [Architecture section](https://github.com/future-architect/vuls#architecture)
- To scan all of running containers
"${running}" needs to be set in the containers item.
```
[servers]
[servers.172-31-4-82]
host = "172.31.4.82"
user = "ec2-user"
keyPath = "/home/username/.ssh/id_rsa"
containers = ["${running}"]
```
- To scan specific containers
The container ID or container name needs to be set in the containers item.
In the following example, only "container_name_a" and "4aa37a8b63b9" will be scanned.
Be sure to check these containers are running state before scanning.
If specified containers are exited, vuls gives up scanning with printing error message.
```
[servers]
[servers.172-31-4-82]
host = "172.31.4.82"
user = "ec2-user"
keyPath = "/home/username/.ssh/id_rsa"
containers = ["container_name_a", "4aa37a8b63b9"]
```
# Usage: TUI
## Display the latest scan results
```
$ vuls tui -h
tui:
tui [-dbpath=/path/to/vuls.sqlite3]
-dbpath string
/path/to/sqlite3 (default "$PWD/vuls.sqlite3")
-debug-sql
debug SQL
```
Key binding is bellow.
| key | |
|:-----------------|:-------|:------|
| TAB | move cursor among the panes |
| Arrow up/down | move cursor to up/down |
| Ctrl+j, Ctrl+k | move cursor to up/donw |
| Ctrl+u, Ctrl+d | page up/donw |
For details, see https://github.com/future-architect/vuls/blob/master/report/tui.go
## Display the previous scan results
- Display the list of scan results.
```
$ ./vuls history
2 2016-05-24 19:49 scanned 1 servers: amazon2
1 2016-05-24 19:48 scanned 2 servers: amazon1, romantic_goldberg
```
- Display the result of scanID 1
```
$ ./vuls tui 1
```
- Display the result of scanID 2
```
$ ./vuls tui 2
```
# Display the previous scan results using peco
```
$ ./vuls history | peco | ./vuls tui
```
[![asciicast](https://asciinema.org/a/emi7y7docxr60bq080z10t7v8.png)](https://asciinema.org/a/emi7y7docxr60bq080z10t7v8)
# Usage: Update NVD Data.
@@ -614,6 +717,10 @@ $ go-cve-dictionary fetchnvd -last2y
# Misc
- Unable to go get vuls
Update git to the latest version. Old version of git can't get some repositories.
see https://groups.google.com/forum/#!topic/mgo-users/rO1-gUDFo_g
- HTTP Proxy Support
If your system is behind HTTP proxy, you have to specify --http-proxy option.
@@ -651,6 +758,13 @@ Use Microsoft Baseline Security Analyzer. [MBSA](https://technet.microsoft.com/e
----
# Related Projects
- [k1LoW/ssh_config_to_vuls_config](https://github.com/k1LoW/ssh_config_to_vuls_config)
ssh_config to vuls config TOML format
----
# Data Source
- [NVD](https://nvd.nist.gov/)

View File

@@ -93,7 +93,7 @@ func printConfigToml(ips []string) (err error) {
[slack]
hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
channel = "#channel-name"
#channel = "#{servername}"
#channel = "${servername}"
iconEmoji = ":ghost:"
authUser = "username"
notifyUsers = ["@username"]
@@ -112,6 +112,10 @@ subjectPrefix = "[vuls]"
#port = "22"
#user = "username"
#keyPath = "/home/username/.ssh/id_rsa"
#cpeNames = [
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]
#containers = ["${running}"]
[servers]
{{- $names:= .Names}}
@@ -124,6 +128,7 @@ host = "{{$ip}}"
#cpeNames = [
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]
#containers = ["${running}"]
{{end}}
`

108
commands/history.go Normal file
View File

@@ -0,0 +1,108 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package commands
import (
"flag"
"fmt"
"os"
"path/filepath"
"strings"
"golang.org/x/net/context"
"github.com/Sirupsen/logrus"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/db"
"github.com/future-architect/vuls/models"
"github.com/google/subcommands"
)
// HistoryCmd is Subcommand of list scanned results
type HistoryCmd struct {
debug bool
debugSQL bool
dbpath string
}
// Name return subcommand name
func (*HistoryCmd) Name() string { return "history" }
// Synopsis return synopsis
func (*HistoryCmd) Synopsis() string {
return `List history of scanning.`
}
// Usage return usage
func (*HistoryCmd) Usage() string {
return `history:
history
[-dbpath=/path/to/vuls.sqlite3]
`
}
// SetFlags set flag
func (p *HistoryCmd) SetFlags(f *flag.FlagSet) {
f.BoolVar(&p.debugSQL, "debug-sql", false, "SQL debug mode")
wd, _ := os.Getwd()
defaultDBPath := filepath.Join(wd, "vuls.sqlite3")
f.StringVar(&p.dbpath, "dbpath", defaultDBPath, "/path/to/sqlite3")
}
// Execute execute
func (p *HistoryCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
c.Conf.DebugSQL = p.debugSQL
c.Conf.DBPath = p.dbpath
// _, err := scanHistories()
histories, err := scanHistories()
if err != nil {
logrus.Error("Failed to select scan histories: ", err)
return subcommands.ExitFailure
}
const timeLayout = "2006-01-02 15:04"
for _, history := range histories {
names := []string{}
for _, result := range history.ScanResults {
if 0 < len(result.Container.ContainerID) {
names = append(names, result.Container.Name)
} else {
names = append(names, result.ServerName)
}
}
fmt.Printf("%-3d %s scanned %d servers: %s\n",
history.ID,
history.ScannedAt.Format(timeLayout),
len(history.ScanResults),
strings.Join(names, ", "),
)
}
return subcommands.ExitSuccess
}
func scanHistories() (histories []models.ScanHistory, err error) {
if err := db.OpenDB(); err != nil {
return histories, fmt.Errorf(
"Failed to open DB. datafile: %s, err: %s", c.Conf.DBPath, err)
}
histories, err = db.SelectScanHistories()
return
}

View File

@@ -20,6 +20,7 @@ package commands
import (
"flag"
"os"
"path/filepath"
"github.com/Sirupsen/logrus"
c "github.com/future-architect/vuls/config"
@@ -72,7 +73,9 @@ func (p *PrepareCmd) SetFlags(f *flag.FlagSet) {
f.BoolVar(&p.debug, "debug", false, "debug mode")
defaultConfPath := os.Getenv("PWD") + "/config.toml"
wd, _ := os.Getwd()
defaultConfPath := filepath.Join(wd, "config.toml")
f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
f.BoolVar(
@@ -93,7 +96,7 @@ func (p *PrepareCmd) SetFlags(f *flag.FlagSet) {
&p.useUnattendedUpgrades,
"use-unattended-upgrades",
false,
"[Depricated] For Ubuntu, install unattended-upgrades",
"[Deprecated] For Ubuntu, install unattended-upgrades",
)
}

View File

@@ -20,6 +20,7 @@ package commands
import (
"flag"
"os"
"path/filepath"
"github.com/Sirupsen/logrus"
c "github.com/future-architect/vuls/config"
@@ -42,8 +43,11 @@ type ScanCmd struct {
dbpath string
cveDictionaryURL string
cvssScoreOver float64
httpProxy string
cvssScoreOver float64
ignoreUnscoredCves bool
httpProxy string
// reporting
reportSlack bool
@@ -71,6 +75,7 @@ func (*ScanCmd) Usage() string {
[-dbpath=/path/to/vuls.sqlite3]
[-cve-dictionary-url=http://127.0.0.1:1323]
[-cvss-over=7]
[-ignore-unscored-cves]
[-report-slack]
[-report-mail]
[-http-proxy=http://192.168.0.1:8080]
@@ -87,10 +92,12 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
f.BoolVar(&p.debug, "debug", false, "debug mode")
f.BoolVar(&p.debugSQL, "debug-sql", false, "SQL debug mode")
defaultConfPath := os.Getenv("PWD") + "/config.toml"
wd, _ := os.Getwd()
defaultConfPath := filepath.Join(wd, "config.toml")
f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
defaultDBPath := os.Getenv("PWD") + "/vuls.sqlite3"
defaultDBPath := filepath.Join(wd, "vuls.sqlite3")
f.StringVar(&p.dbpath, "dbpath", defaultDBPath, "/path/to/sqlite3")
defaultURL := "http://127.0.0.1:1323"
@@ -106,6 +113,12 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
0,
"-cvss-over=6.5 means reporting CVSS Score 6.5 and over (default: 0 (means report all))")
f.BoolVar(
&p.ignoreUnscoredCves,
"ignore-unscored-cves",
false,
"Don't report the unscored CVEs")
f.StringVar(
&p.httpProxy,
"http-proxy",
@@ -134,14 +147,14 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
&p.useYumPluginSecurity,
"use-yum-plugin-security",
false,
"[Depricated] For CentOS 5. Scan by yum-plugin-security or not (use yum check-update by default)",
"[Deprecated] For CentOS 5. Scan by yum-plugin-security or not (use yum check-update by default)",
)
f.BoolVar(
&p.useUnattendedUpgrades,
"use-unattended-upgrades",
false,
"[Depricated] For Ubuntu. Scan by unattended-upgrades or not (use apt-get upgrade --dry-run by default)",
"[Deprecated] For Ubuntu. Scan by unattended-upgrades or not (use apt-get upgrade --dry-run by default)",
)
}
@@ -212,6 +225,8 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
c.Conf.DBPath = p.dbpath
c.Conf.CveDictionaryURL = p.cveDictionaryURL
c.Conf.CvssScoreOver = p.cvssScoreOver
c.Conf.IgnoreUnscoredCves = p.ignoreUnscoredCves
c.Conf.HTTPProxy = p.httpProxy
c.Conf.UseYumPluginSecurity = p.useYumPluginSecurity
c.Conf.UseUnattendedUpgrades = p.useUnattendedUpgrades
@@ -227,7 +242,7 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
return subcommands.ExitFailure
}
Log.Info("Detecting the type of OS... ")
Log.Info("Detecting Server OS... ")
err = scan.InitServers(Log)
if err != nil {
Log.Errorf("Failed to init servers. Check the configuration. err: %s", err)

View File

@@ -20,11 +20,16 @@ package commands
import (
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/report"
"github.com/google/subcommands"
"github.com/labstack/gommon/log"
"golang.org/x/net/context"
)
@@ -54,7 +59,9 @@ func (p *TuiCmd) SetFlags(f *flag.FlagSet) {
// f.StringVar(&p.lang, "lang", "en", "[en|ja]")
f.BoolVar(&p.debugSQL, "debug-sql", false, "debug SQL")
defaultDBPath := os.Getenv("PWD") + "/vuls.sqlite3"
wd, _ := os.Getwd()
defaultDBPath := filepath.Join(wd, "vuls.sqlite3")
f.StringVar(&p.dbpath, "dbpath", defaultDBPath,
fmt.Sprintf("/path/to/sqlite3 (default: %s)", defaultDBPath))
}
@@ -64,5 +71,24 @@ func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) s
c.Conf.Lang = "en"
c.Conf.DebugSQL = p.debugSQL
c.Conf.DBPath = p.dbpath
return report.RunTui()
historyID := ""
if 0 < len(f.Args()) {
if _, err := strconv.Atoi(f.Args()[0]); err != nil {
log.Errorf("First Argument have to be scan_histores record ID: %s", err)
return subcommands.ExitFailure
}
historyID = f.Args()[0]
} else {
bytes, err := ioutil.ReadAll(os.Stdin)
if err != nil {
log.Errorf("Failed to read stdin: %s", err)
return subcommands.ExitFailure
}
fields := strings.Fields(string(bytes))
if 0 < len(fields) {
historyID = fields[0]
}
}
return report.RunTui(historyID)
}

View File

@@ -41,9 +41,11 @@ type Config struct {
CveDictionaryURL string `valid:"url"`
CvssScoreOver float64
HTTPProxy string `valid:"url"`
DBPath string
CvssScoreOver float64
IgnoreUnscoredCves bool
HTTPProxy string `valid:"url"`
DBPath string
// CpeNames []string
// SummaryMode bool
UseYumPluginSecurity bool
@@ -188,8 +190,6 @@ func (c *SlackConf) Validate() (errs []error) {
errs = append(errs, err)
}
// TODO check if slack configration is valid
return
}
@@ -202,12 +202,33 @@ type ServerInfo struct {
Port string
KeyPath string
KeyPassword string
SudoOpt SudoOption
CpeNames []string
// DebugLog Color
LogMsgAnsiColor string
// Container Names or IDs
Containers []string
// userd internal
LogMsgAnsiColor string // DebugLog Color
SudoOpt SudoOption
Container Container
}
// IsContainer returns whether this ServerInfo is about container
func (s ServerInfo) IsContainer() bool {
return 0 < len(s.Container.ContainerID)
}
// SetContainer set container
func (s *ServerInfo) SetContainer(d Container) {
s.Container = d
}
// Container has Container information.
type Container struct {
ContainerID string
Name string
Type string
}
// SudoOption is flag of sudo option.

View File

@@ -57,7 +57,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass, sudoPass string) (err error) {
for name, v := range conf.Servers {
if 0 < len(v.KeyPassword) || 0 < len(v.Password) {
log.Warn("[Depricated] password and keypassword in config file are unsecure. Remove them immediately for a security reason. They will be removed in a future release.")
log.Warn("[Deprecated] password and keypassword in config file are unsecure. Remove them immediately for a security reason. They will be removed in a future release.")
}
s := ServerInfo{ServerName: name}
@@ -101,6 +101,11 @@ func (c TOMLLoader) Load(pathToToml, keyPass, sudoPass string) (err error) {
s.CpeNames = d.CpeNames
}
s.Containers = v.Containers
if len(s.Containers) == 0 {
s.Containers = d.Containers
}
s.LogMsgAnsiColor = Colors[i%len(Colors)]
i++

View File

@@ -133,8 +133,8 @@ func (api cvedictClient) httpGet(key, url string, resChan chan<- response, errCh
f := func() (err error) {
// resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
resp, body, errs = gorequest.New().Get(url).End()
if len(errs) > 0 || resp.StatusCode != 200 {
return fmt.Errorf("HTTP GET error: %v, code: %d, url: %s", errs, resp.StatusCode, url)
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return fmt.Errorf("HTTP GET error: %v, url: %s, resp: %v", errs, url, resp)
}
return nil
}

View File

@@ -20,6 +20,7 @@ package db
import (
"fmt"
"sort"
"strconv"
"time"
"github.com/future-architect/vuls/config"
@@ -49,6 +50,7 @@ func MigrateDB() error {
&m.ScanHistory{},
&m.ScanResult{},
// &m.NWLink{},
&m.Container{},
&m.CveInfo{},
&m.CpeName{},
&m.PackageInfo{},
@@ -67,6 +69,10 @@ func MigrateDB() error {
// AddIndex("idx_n_w_links_scan_result_id", "scan_result_id").Error; err != nil {
// return fmt.Errorf(errMsg, err)
// }
if err := db.Model(&m.Container{}).
AddIndex("idx_containers_scan_result_id", "scan_result_id").Error; err != nil {
return fmt.Errorf(errMsg, err)
}
if err := db.Model(&m.CveInfo{}).
AddIndex("idx_cve_infos_scan_result_id", "scan_result_id").Error; err != nil {
return fmt.Errorf(errMsg, err)
@@ -85,11 +91,11 @@ func MigrateDB() error {
return fmt.Errorf(errMsg, err)
}
if err := db.Model(&cve.CveDetail{}).
AddIndex("idx_cve_detail_cve_info_id", "cve_info_id").Error; err != nil {
AddIndex("idx_cve_details_cve_info_id", "cve_info_id").Error; err != nil {
return fmt.Errorf(errMsg, err)
}
if err := db.Model(&cve.CveDetail{}).
AddIndex("idx_cve_detail_cveid", "cve_id").Error; err != nil {
AddIndex("idx_cve_details_cveid", "cve_id").Error; err != nil {
return fmt.Errorf(errMsg, err)
}
if err := db.Model(&cve.Nvd{}).
@@ -141,6 +147,10 @@ func Insert(results []m.ScanResult) error {
if err := db.Create(&scanResult).Error; err != nil {
return err
}
scanResult.Container.ScanResultID = scanResult.ID
if err := db.Create(&scanResult.Container).Error; err != nil {
return err
}
if err := insertCveInfos(scanResult.ID, scanResult.KnownCves); err != nil {
return err
}
@@ -220,16 +230,29 @@ func resetGormIDs(infos []m.CveInfo) []m.CveInfo {
return infos
}
// SelectLatestScanHistory select latest scan history from DB
func SelectLatestScanHistory() (m.ScanHistory, error) {
// SelectScanHistory select scan history from DB
func SelectScanHistory(historyID string) (m.ScanHistory, error) {
var err error
scanHistory := m.ScanHistory{}
db.Order("scanned_at desc").First(&scanHistory)
if historyID == "" {
// select latest
db.Order("scanned_at desc").First(&scanHistory)
} else {
var id int
if id, err = strconv.Atoi(historyID); err != nil {
return m.ScanHistory{},
fmt.Errorf("historyID have to be numeric number: %s", err)
}
db.First(&scanHistory, id)
}
if scanHistory.ID == 0 {
return m.ScanHistory{}, fmt.Errorf("No scanHistory records")
}
results := []m.ScanResult{}
// results := []m.ScanResult{}
results := m.ScanResults{}
db.Model(&scanHistory).Related(&results, "ScanResults")
scanHistory.ScanResults = results
@@ -238,10 +261,16 @@ func SelectLatestScanHistory() (m.ScanHistory, error) {
// db.Model(&r).Related(&nw, "NWLinks")
// scanHistory.ScanResults[i].NWLinks = nw
di := m.Container{}
db.Model(&r).Related(&di, "Container")
scanHistory.ScanResults[i].Container = di
knownCves := selectCveInfos(&r, "KnownCves")
sort.Sort(m.CveInfos(knownCves))
scanHistory.ScanResults[i].KnownCves = knownCves
}
sort.Sort(scanHistory.ScanResults)
return scanHistory, nil
}
@@ -270,3 +299,26 @@ func selectCveInfos(result *m.ScanResult, fieldName string) []m.CveInfo {
}
return cveInfos
}
// SelectScanHistories select latest scan history from DB
func SelectScanHistories() ([]m.ScanHistory, error) {
scanHistories := []m.ScanHistory{}
db.Order("scanned_at desc").Find(&scanHistories)
if len(scanHistories) == 0 {
return []m.ScanHistory{}, fmt.Errorf("No scanHistory records")
}
for i, history := range scanHistories {
results := m.ScanResults{}
db.Model(&history).Related(&results, "ScanResults")
scanHistories[i].ScanResults = results
for j, r := range results {
di := m.Container{}
db.Model(&r).Related(&di, "Container")
scanHistories[i].ScanResults[j].Container = di
}
}
return scanHistories, nil
}

View File

@@ -1,7 +1,7 @@
FROM golang:1.6
RUN apt-get update \
&& apt-get upgrade -y \
&& apt-get install -y git openssh-client gcc
&& apt-get install -y git openssh-client gcc nmap
WORKDIR /app
RUN go get github.com/kotakanbe/go-cve-dictionary
RUN go get github.com/future-architect/vuls

View File

@@ -1,2 +1,7 @@
# Must do
* Edit your config.toml to match your infrastructure
# Before building the docker
Since it's not on docker hub because blablabla, you have to :
* Edit your [config.toml](https://github.com/future-architect/vuls#step6-config) to match your infrastructure
* generate a keypair dedicated to this docker : ```ssh-keygen -t rsa -b 4096 -C "your_email@example.com"```
* it's **highly** recommanded to use a restrained `authorized_keys` files with this key to be sure that it will be only usable from a single IP (after all it's a root executed software) : ```from="1.2.3.4,1.2.3.5" ssh-rsa [...] your_email@example.com```
* Deploy your ssh key on the targetted machines

View File

@@ -1,4 +1,28 @@
#!/bin/bash
go-cve-dictionary server &
sleep 2
vuls scan -config /app/config.toml -report-slack
tries=0
function isopen {
tries=$1
nmap -Pn -T4 -p 1323 127.0.0.1|grep -iq open
if [ $? -ne 0 ]; then
if [ $tries -lt 5 ]; then
let tries++
startserver $tries
else
return 1
fi
else
return 0
fi
}
function startserver {
tries=$1
go-cve-dictionary server &
sleep 2
isopen $tries
}
startserver $tries
if [ $? -ne 1 ]; then
vuls scan -config /app/config.toml -report-slack
fi

View File

@@ -17,7 +17,7 @@
<node id="n0">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.cloud">
<y:Geometry height="50.0" width="80.0" x="269.4041252136233" y="446.4841308593749"/>
<y:Geometry height="50.0" width="80.0" x="1046.0141170024865" y="70.63541666666657"/>
<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" modelName="custom" textColor="#000000" visible="true" width="4.0" x="38.0" y="23.0">
@@ -37,14 +37,14 @@
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="285.54366048177087" width="173.0" x="66.40412521362327" y="347.9090576171874"/>
<y:Geometry height="289.5891316731771" width="137.0" x="1274.5282340049735" y="-47.136413574218864"/>
<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="173.0" x="0.0" y="0.0">Vulnerbility Database</y:NodeLabel>
<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="159.0390625" x="-11.01953125" y="0.0">Vulnerbility 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="54" bottomF="53.63557942708337" left="30" leftF="29.90412521362373" right="28" rightF="28.09587478637627" top="27" topF="27.242065429687557"/>
<y:BorderInsets bottom="29" bottomF="28.96858723958337" left="9" leftF="9.35411262512207" right="13" rightF="12.64588737487793" top="27" topF="27.242065429687557"/>
</y:GroupNode>
<y:GroupNode>
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
@@ -63,7 +63,7 @@
<node id="n1::n0">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="70.0" width="85.0" x="111.308250427247" y="494.8171386718749"/>
<y:Geometry height="70.0" width="85.0" x="1298.8823466300955" y="128.4841308593749"/>
<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="32.265625" modelName="custom" textColor="#000000" visible="true" width="63.279296875" x="10.8603515625" y="18.8671875">JVN
@@ -81,7 +81,7 @@
<node id="n1::n1">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="70.0" width="85.0" x="111.308250427247" y="411.81713867187494"/>
<y:Geometry height="70.0" width="85.0" x="1298.8823466300955" y="16.771667480468693"/>
<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="29.69921875" x="27.650390625" y="25.93359375">NVD<y:LabelModel>
@@ -103,14 +103,14 @@
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="285.54366048177087" width="137.0" x="1209.345874786376" y="347.9090576171874"/>
<y:Geometry height="357.5982869466146" width="137.0" x="1274.5282340049735" y="285.984130859375"/>
<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="137.0" x="0.0" y="0.0">Linux Support</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="4" bottomF="4.059529622395871" left="5" leftF="4.85411262512207" right="8" rightF="8.14588737487793" top="0" topF="0.0"/>
<y:BorderInsets bottom="15" bottomF="14.801595052083258" left="5" leftF="4.85411262512207" right="8" rightF="8.14588737487793" top="11" topF="10.747945149739394"/>
</y:GroupNode>
<y:GroupNode>
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
@@ -129,7 +129,7 @@
<node id="n2::n0">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="71.484130859375" width="94.0" x="1229.1999874114981" y="463.7420654296874"/>
<y:Geometry height="71.484130859375" width="94.0" x="1294.3823466300955" y="443.21842447916663"/>
<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="32.265625" modelName="custom" textColor="#000000" visible="true" width="64.158203125" x="14.9208984375" y="19.6092529296875">apptitude
@@ -147,7 +147,7 @@ changelog<y:LabelModel>
<node id="n2::n1">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="71.484130859375" width="94.0" x="1229.1999874114981" y="384.5750732421874"/>
<y:Geometry height="71.484130859375" width="94.0" x="1294.3823466300955" y="333.3980916341144"/>
<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="32.265625" modelName="custom" textColor="#000000" visible="true" width="64.158203125" x="14.9208984375" y="19.6092529296875">yum
@@ -165,7 +165,7 @@ changelog<y:LabelModel>
<node id="n2::n2">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="71.484130859375" width="94.0" x="1229.1999874114981" y="542.9090576171874"/>
<y:Geometry height="71.484130859375" width="94.0" x="1294.3823466300955" y="542.2966918945314"/>
<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="32.265625" modelName="custom" textColor="#000000" visible="true" width="92.828125" x="0.5859375" y="19.6092529296875">RHSA (RedHat)
@@ -185,10 +185,10 @@ ALAS (Amazon)<y:LabelModel>
<node id="n3">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.cloud">
<y:Geometry height="50.0" width="80.0" x="1109.272931098937" y="399.1136678059895"/>
<y:Geometry height="50.0" width="56.554100036621094" x="1180.4405970573423" y="439.7832743326823"/>
<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" modelName="custom" textColor="#000000" visible="true" width="4.0" x="38.0" y="23.0">
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="26.277050018310547" y="23.0">
<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
@@ -199,48 +199,20 @@ ALAS (Amazon)<y:LabelModel>
</y:GenericNode>
</data>
</node>
<node id="n4">
<data key="d6">
<y:SVGNode>
<y:Geometry height="121.666015625" width="137.0" x="942.2729310989371" y="363.2806599934895"/>
<y:Fill color="#CCCCFF" 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="45.63671875" x="45.681640625" y="-30.475463867187386">servers<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.5" labelRatioY="0.5" nodeRatioX="0.16655736770072993" nodeRatioY="-0.5" offsetX="0.0" offsetY="-12.342651367187386" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="66.5" y="58.8330078125">
<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:SVGNodeProperties usingVisualBounds="false"/>
<y:SVGModel svgBoundsPolicy="0">
<y:SVGContent refid="1"/>
</y:SVGModel>
</y:SVGNode>
</data>
</node>
<node id="n5" yfiles.foldertype="group">
<node id="n4" yfiles.foldertype="group">
<data key="d4"/>
<data key="d6">
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="285.54366048177087" width="220.24999999999977" x="662.2499999999998" y="347.9090576171874"/>
<y:Geometry height="365.54366048177087" width="251.74999999999977" x="641.4999999999995" y="285.984130859375"/>
<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="220.24999999999977" x="0.0" y="0.0">Vuls</y:NodeLabel>
<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="251.74999999999977" x="0.0" y="0.0">Vuls</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="12" bottomF="11.710652669270871" left="10" leftF="9.999999999999773" right="0" rightF="0.0" top="31" topF="30.57621256510413"/>
<y:BorderInsets bottom="14" bottomF="13.710652669270871" left="25" leftF="25.499999999999773" right="0" rightF="0.0" top="31" topF="30.57621256510413"/>
</y:GroupNode>
<y:GroupNode>
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
@@ -255,11 +227,11 @@ ALAS (Amazon)<y:LabelModel>
</y:Realizers>
</y:ProxyAutoBoundsNode>
</data>
<graph edgedefault="directed" id="n5:">
<node id="n5::n0">
<graph edgedefault="directed" id="n4:">
<node id="n4::n0">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="80.0" x="787.4999999999995" y="495.1512858072915"/>
<y:Geometry height="50.0" width="80.0" x="798.2499999999993" y="566.4311319986981"/>
<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.595703125" x="18.7021484375" y="15.93359375">Report<y:LabelModel>
@@ -273,10 +245,10 @@ ALAS (Amazon)<y:LabelModel>
</y:ShapeNode>
</data>
</node>
<node id="n5::n1">
<node id="n4::n1">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="80.0" x="787.4999999999995" y="556.7420654296874"/>
<y:Geometry height="50.0" width="80.0" x="690.2499999999993" y="572.817138671875"/>
<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="54.40234375" x="12.798828125" y="15.93359375">TUI View<y:LabelModel>
@@ -290,11 +262,11 @@ ALAS (Amazon)<y:LabelModel>
</y:ShapeNode>
</data>
</node>
<node id="n5::n2">
<node id="n4::n2">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="180.25" x="687.2499999999995" y="415.1512858072915"/>
<y:Fill color="#C0C0C0" transparent="false"/>
<y:Geometry height="50.0" width="180.25" x="681.9999999999993" y="353.22635904947913"/>
<y:Fill color="#FFCC00" 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="30.68359375" x="74.783203125" y="15.93359375">Scan<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
@@ -309,10 +281,10 @@ ALAS (Amazon)<y:LabelModel>
</node>
</graph>
</node>
<node id="n6">
<node id="n5">
<data key="d6">
<y:SVGNode>
<y:Geometry height="64.96826171875" width="56.554100036621094" x="1083.5729436874383" y="568.4844563802083"/>
<y:Geometry height="64.96826171875" width="56.554100036621094" x="743.8479499816888" y="734.5277913411459"/>
<y:Fill color="#CCCCFF" 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="100.890625" x="-22.168262481689453" y="68.96826171875">System Operator<y:LabelModel>
@@ -324,15 +296,15 @@ ALAS (Amazon)<y:LabelModel>
</y:NodeLabel>
<y:SVGNodeProperties usingVisualBounds="true"/>
<y:SVGModel svgBoundsPolicy="0">
<y:SVGContent refid="2"/>
<y:SVGContent refid="1"/>
</y:SVGModel>
</y:SVGNode>
</data>
</node>
<node id="n7">
<node id="n6">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
<y:Geometry height="70.0" width="60.5" x="696.9999999999995" y="546.7420654296874"/>
<y:Geometry height="70.0" width="60.5" x="699.9999999999993" y="453.02174886067706"/>
<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" modelName="custom" textColor="#000000" visible="true" width="4.0" x="28.25" y="33.0">
@@ -353,24 +325,24 @@ ALAS (Amazon)<y:LabelModel>
</y:GenericNode>
</data>
</node>
<node id="n8">
<node id="n7">
<data key="d6">
<y:SVGNode>
<y:Geometry height="37.0" width="109.57881927490234" x="991.1335277557366" y="532.4466756184895"/>
<y:Geometry height="37.0" width="109.57881927490234" x="889.737640380859" y="748.5119222005209"/>
<y:Fill color="#CCCCFF" 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" modelName="sandwich" modelPosition="s" textColor="#000000" visible="true" width="4.0" x="52.78940963745117" y="41.0"/>
<y:SVGNodeProperties usingVisualBounds="true"/>
<y:SVGModel svgBoundsPolicy="0">
<y:SVGContent refid="3"/>
<y:SVGContent refid="2"/>
</y:SVGModel>
</y:SVGNode>
</data>
</node>
<node id="n9">
<node id="n8">
<data key="d6">
<y:GenericNode configuration="com.yworks.bpmn.Artifact.withShadow">
<y:Geometry height="24.0" width="35.0" x="943.5205974578851" y="538.9466756184895"/>
<y:Geometry height="24.0" width="35.0" x="811.8264548778523" y="681.5277913411459"/>
<y:Fill color="#FFFFFFE6" 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" modelName="custom" textColor="#000000" visible="true" width="4.0" x="15.5" y="28.0">
@@ -390,20 +362,20 @@ ALAS (Amazon)<y:LabelModel>
</y:GenericNode>
</data>
</node>
<node id="n10" yfiles.foldertype="group">
<node id="n9" yfiles.foldertype="group">
<data key="d4"/>
<data key="d6">
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="285.54366048177087" width="233.0" x="379.4041252136233" y="347.9090576171874"/>
<y:Geometry height="293.63460286458337" width="251.74999999999977" x="645.7499999999998" y="-51.181884765625114"/>
<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="233.0" x="0.0" y="0.0">go-cve-dictionary</y:NodeLabel>
<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="251.74999999999977" x="0.0" y="0.0">go-cve-dictionary</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="2" leftF="1.5" right="5" rightF="5.0" top="62" topF="61.9090576171875"/>
<y:BorderInsets bottom="29" bottomF="28.96858723958337" left="12" leftF="12.249999999999773" right="0" rightF="0.0" top="49" topF="49.0"/>
</y:GroupNode>
<y:GroupNode>
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
@@ -418,11 +390,11 @@ ALAS (Amazon)<y:LabelModel>
</y:Realizers>
</y:ProxyAutoBoundsNode>
</data>
<graph edgedefault="directed" id="n10:">
<node id="n10::n0">
<graph edgedefault="directed" id="n9:">
<node id="n9::n0">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
<y:Geometry height="70.0" width="60.5" x="447.15412521362305" y="548.4527180989583"/>
<y:Geometry height="70.0" width="60.5" x="747.1249999999995" y="34.484130859374886"/>
<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" modelName="custom" textColor="#000000" visible="true" width="4.0" x="28.25" y="33.0">
@@ -443,10 +415,10 @@ ALAS (Amazon)<y:LabelModel>
</y:GenericNode>
</data>
</node>
<node id="n10::n1">
<node id="n9::n1">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="101.0" x="491.4041252136233" y="446.4841308593749"/>
<y:Geometry height="50.0" width="101.0" x="672.9999999999995" y="148.4841308593749"/>
<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="73.943359375" x="13.5283203125" y="15.93359375">HTTP server<y:LabelModel>
@@ -460,10 +432,10 @@ ALAS (Amazon)<y:LabelModel>
</y:ShapeNode>
</data>
</node>
<node id="n10::n2">
<node id="n9::n2">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="80.0" x="395.9041252136233" y="446.4841308593749"/>
<y:Geometry height="50.0" width="80.0" x="802.4999999999995" y="148.4841308593749"/>
<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="46.796875" x="16.6015625" y="15.93359375">Fetcher<y:LabelModel>
@@ -479,28 +451,151 @@ ALAS (Amazon)<y:LabelModel>
</node>
</graph>
</node>
<edge id="n10::e0" source="n10::n1" target="n10::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="line" width="1.0"/>
<y:Arrows source="none" target="none"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
<node id="n10" yfiles.foldertype="group">
<data key="d4"/>
<data key="d5"/>
<data key="d6">
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="201.49428304036473" width="192.109991788863" x="964.6223485469814" y="296.7320760091144"/>
<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="192.109991788863" x="0.0" y="0.0">Docker Containers</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="32" leftF="32.0" right="45" rightF="45.109991788863" 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>
</edge>
<edge id="e0" source="n10::n2" target="n0">
<graph edgedefault="directed" id="n10:">
<node id="n10::n0">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="85.0" x="1011.6223485469814" y="433.22635904947913"/>
<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="71.91015625" x="6.544921875" y="15.93359375">DockerHost<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="n10::n1">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="85.0" x="1011.6223485469814" y="333.3980916341144"/>
<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="32.265625" modelName="custom" textColor="#000000" visible="true" width="60.748046875" x="12.1259765625" y="8.8671875">Docker
Container<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="n11" yfiles.foldertype="group">
<data key="d4"/>
<data key="d5"/>
<data key="d6">
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="131.666015625" width="192.109991788863" x="964.6223485469814" y="516.3727416992189"/>
<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="192.109991788863" x="0.0" y="0.0">Linux Servers</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="17" bottomF="16.607625325520758" left="32" leftF="32.0" right="24" rightF="24.109991788863" 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 6</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="n11:">
<node id="n11::n0">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="85.0" x="1011.6223485469814" y="553.0387573242189"/>
<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="39.865234375" x="22.5673828125" y="15.93359375">Server<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="n11::n1">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="85.0" x="1032.6223485469814" y="566.4311319986981"/>
<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="39.865234375" x="22.5673828125" y="15.93359375">Server<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="n9::n2" target="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="line" width="1.0"/>
<y:Arrows source="none" target="none"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="106.240234375" x="-141.12006313536395" y="46.068162980709076">Fetch
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="106.240234375" x="98.64874426818972" y="-10.207796256635163">Fetch
Vulnerability data<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="62.2009754807092" distanceToCenter="true" position="left" ratio="41.499957391955974" segment="-1"/>
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="51.8582779043468" distanceToCenter="true" position="right" ratio="0.7727738017619232" 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>
@@ -514,17 +609,7 @@ Vulnerability data<y:LabelModel>
<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="e2" source="n5::n2" target="n10::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="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="34.626953125" x="-64.74898719787609" y="-33.01884181664411">HTTP<y:LabelModel>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="34.626953125" x="8.29332590103104" y="20.93360392252606">HTTP<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -536,67 +621,17 @@ Vulnerability data<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e3" source="n5::n1" 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="none"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e4" source="n0" target="n1">
<edge id="e2" source="n4::n2" target="n9::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="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e5" source="n5::n0" target="n6">
<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="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="n5::e0" source="n5::n2" target="n5::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="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e7" source="n5::n2" target="n4">
<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:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="25.744140625" x="44.03690814971833" y="17.093924778052497">SSH<y:LabelModel>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="34.626953125" x="-70.78547462732604" y="-104.38485166353485">HTTP<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="1.0" segment="-1"/>
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="left" ratio="0.6418953379495804" 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>
@@ -604,7 +639,137 @@ Vulnerability data<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e8" source="n6" target="n5::n1">
<edge id="e3" source="n0" 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="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="34.626953125" x="65.78936398029236" y="21.58855026779011">HTTP<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.5" 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="e4" source="n4::n0" 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:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="31.802734375" x="-23.436084747315135" y="96.06095129235564">send<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="1.0" 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="n4::e0" source="n4::n2" target="n4::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="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" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="56.201171875" x="39.557346848955035" y="107.80743708610783">Generate<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.7612118931433132" 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="e5" source="n5" target="n4::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="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="109.7265625" x="-125.3179906251471" y="-50.63345557215132">Detail Information<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="58.40488430810431" distanceToCenter="true" position="left" ratio="0.3174763616620919" 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="e6" source="n4::n2" target="n11::n0">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="87.9098606499872" sy="24.54915453361525" tx="0.0" ty="0.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="bold" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#0000FF" visible="true" width="27.07421875" x="63.50980239529861" y="100.92919596059016">SSH<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.6332970715271986" 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="n4::n2" target="n10::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:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#0000FF" visible="true" width="27.07421875" x="47.789244589009286" y="-10.21369683159321">SSH<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="17.840986205821807" distanceToCenter="true" position="left" ratio="0.3567784326754698" 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="n10::e0" source="n10::n0" target="n10::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="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#0000FF" visible="true" width="77.576171875" x="-68.78805184364364" y="-33.974731445312614">docker exec<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.5" 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="e8" source="n10::n1" target="n3">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -614,22 +779,120 @@ Vulnerability data<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n10::e1" source="n10::n2" target="n10::n0">
<edge id="e9" source="n11::n0" target="n3">
<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:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e9" source="n5::n2" target="n7">
<edge id="n9::e0" source="n9::n2" target="n9::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="none"/>
<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" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="37.10546875" x="-0.014769665799690301" y="-35.98834449405365">Insert<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.5" 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="n9::e1" source="n9::n0" target="n9::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="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="37.9375" x="-65.17704859198193" y="14.912883039360338">Select<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.5" 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="n4::n2" target="n6">
<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:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="70.275390625" x="-75.4169220517914" y="5.2924810128525905">Insert
Scan Result<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.5" 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="n6" target="n4::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="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="37.9375" x="-48.96875000000057" y="19.334788004557254">Select<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.5" 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="e12" source="n4::n0" 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: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="n7" target="n5">
<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:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="38.875" x="-43.904876708984716" y="20.93361409505212">Notify<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.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>
@@ -638,162 +901,6 @@ Vulnerability data<y:LabelModel>
<data key="d7">
<y:Resources>
<y:Resource id="1">&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;svg version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px" y="0px" width="68px" height="60px" viewBox="-0.435 -0.869 68 60" enable-background="new -0.435 -0.869 68 60"
xml:space="preserve"&gt;
&lt;defs&gt;
&lt;/defs&gt;
&lt;path fill="#666666" d="M52.462,30.881c-0.021,0-0.037,0.01-0.059,0.012c-0.021-0.002-0.037-0.012-0.059-0.012h-18.5v-7.555
c0-0.414-0.335-0.75-0.75-0.75c-0.414,0-0.75,0.336-0.75,0.75v7.555h-18.5c-0.02,0-0.037,0.01-0.057,0.012
c-0.02-0.002-0.037-0.012-0.057-0.012c-0.414,0-0.75,0.336-0.75,0.75v3.834c0,0.414,0.336,0.75,0.75,0.75s0.75-0.336,0.75-0.75
v-3.084H51.71v3.084c0,0.414,0.336,0.75,0.75,0.75s0.75-0.336,0.75-0.75v-3.834C53.212,31.217,52.876,30.881,52.462,30.881z"/&gt;
&lt;linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="130.7236" y1="-184.1631" x2="130.7236" y2="-191.9565" gradientTransform="matrix(1 0 0 -1 -97.6001 -158.6377)"&gt;
&lt;stop offset="0" style="stop-color:#9CD7FF"/&gt;
&lt;stop offset="1" style="stop-color:#3C89C9"/&gt;
&lt;/linearGradient&gt;
&lt;path fill="url(#SVGID_1_)" d="M36.296,29.976c-0.832,0-1.513-0.681-1.513-1.513v-1.424c0-0.832-0.681-1.513-1.513-1.513h-0.214
c-0.832,0-1.513,0.681-1.513,1.513v1.424c0,0.832-0.681,1.513-1.513,1.513h-2.499c-0.832,0-1.513,0.681-1.513,1.513v0.317
c0,0.832,0.681,1.513,1.513,1.513h11.187c0.832,0,1.513-0.681,1.513-1.513v-0.317c0-0.833-0.681-1.513-1.513-1.513H36.296z"/&gt;
&lt;linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="605.8877" y1="2040.6665" x2="593.1709" y2="2040.6665" gradientTransform="matrix(1 0 0 1 -585.5996 -1982.4023)"&gt;
&lt;stop offset="0" style="stop-color:#4D4D4D"/&gt;
&lt;stop offset="1" style="stop-color:#999999"/&gt;
&lt;/linearGradient&gt;
&lt;path fill="url(#SVGID_2_)" d="M20.205,57.452c0,0.519-3.619,0.752-6.627,0.752c-2.083,0-5.846-0.186-6.089-0.678
c0,0.238,0,0.806,0,0.89c0,0.389,2.573,0.661,6.084,0.661c3.511,0,6.632-0.344,6.632-0.729C20.205,58.264,20.205,57.7,20.205,57.452
z"/&gt;
&lt;path fill="#808080" d="M13.846,56.806c3.512,0,6.358,0.313,6.358,0.699s-2.846,0.763-6.358,0.763c-3.59,0-6.358-0.375-6.358-0.763
S10.335,56.806,13.846,56.806z"/&gt;
&lt;linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="600.833" y1="2037.4702" x2="598.1563" y2="2037.4702" gradientTransform="matrix(1 0 0 1 -585.5996 -1982.4023)"&gt;
&lt;stop offset="0" style="stop-color:#999999"/&gt;
&lt;stop offset="0.0417" style="stop-color:#8D8D8D"/&gt;
&lt;stop offset="0.1617" style="stop-color:#717171"/&gt;
&lt;stop offset="0.2821" style="stop-color:#5D5D5D"/&gt;
&lt;stop offset="0.4021" style="stop-color:#515151"/&gt;
&lt;stop offset="0.5212" style="stop-color:#4D4D4D"/&gt;
&lt;stop offset="0.6202" style="stop-color:#565656"/&gt;
&lt;stop offset="0.7817" style="stop-color:#6E6E6E"/&gt;
&lt;stop offset="0.9844" style="stop-color:#969696"/&gt;
&lt;stop offset="1" style="stop-color:#999999"/&gt;
&lt;/linearGradient&gt;
&lt;path fill="url(#SVGID_3_)" d="M15.215,57.657c0,0-0.792,0.053-1.339,0.053s-1.338-0.053-1.338-0.053v-5.231h2.677V57.657z"/&gt;
&lt;radialGradient id="SVGID_4_" cx="465.1113" cy="2023.4497" r="12.8975" gradientTransform="matrix(1.15 0 0 1 -526.6041 -1982.4023)" gradientUnits="userSpaceOnUse"&gt;
&lt;stop offset="0" style="stop-color:#F2F2F2"/&gt;
&lt;stop offset="1" style="stop-color:#666666"/&gt;
&lt;/radialGradient&gt;
&lt;path fill="url(#SVGID_4_)" d="M0.065,36.888c0-0.59,0.482-1.071,1.072-1.071H26.98c0.589,0,1.071,0.481,1.071,1.071v16.108
c0,0.589-0.482,1.07-1.071,1.07H1.137c-0.59,0.002-1.072-0.481-1.072-1.07V36.888z"/&gt;
&lt;path fill="none" stroke="#666666" stroke-width="0.1305" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="
M0.065,36.888c0-0.59,0.482-1.071,1.072-1.071H26.98c0.589,0,1.071,0.481,1.071,1.071v16.108c0,0.589-0.482,1.07-1.071,1.07H1.137
c-0.59,0.002-1.072-0.481-1.072-1.07V36.888z"/&gt;
&lt;radialGradient id="SVGID_5_" cx="439.1309" cy="2019.0845" r="28.5715" fx="461.6079" fy="2015.234" gradientTransform="matrix(1.1935 0 0 1 -509.6013 -1982.4023)" gradientUnits="userSpaceOnUse"&gt;
&lt;stop offset="0" style="stop-color:#4D4D4D"/&gt;
&lt;stop offset="1" style="stop-color:#999999"/&gt;
&lt;/radialGradient&gt;
&lt;path fill="url(#SVGID_5_)" d="M0.613,37.436c0-0.591,0.482-1.072,1.071-1.072h24.871c0.589,0,1.071,0.481,1.071,1.072v14.893
c0,0.59-0.482,1.072-1.071,1.072H1.685c-0.589,0-1.071-0.482-1.071-1.072V37.436z"/&gt;
&lt;radialGradient id="SVGID_6_" cx="440.0439" cy="2019.1304" r="18.3134" gradientTransform="matrix(1.1923 0 0 1 -510.0601 -1982.4023)" gradientUnits="userSpaceOnUse"&gt;
&lt;stop offset="0" style="stop-color:#9CD7FF"/&gt;
&lt;stop offset="1" style="stop-color:#3C89C9"/&gt;
&lt;/radialGradient&gt;
&lt;path fill="url(#SVGID_6_)" d="M0.917,37.679c0-0.59,0.482-1.071,1.072-1.071h24.262c0.589,0,1.071,0.481,1.071,1.071v14.406
c0,0.588-0.482,1.069-1.071,1.069H1.989c-0.59,0-1.072-0.481-1.072-1.069V37.679z"/&gt;
&lt;path opacity="0.24" fill="#F2F2F2" d="M0.917,49.11V37.679c0-0.59,0.482-1.071,1.072-1.071h24.262c0.589,0,1.071,0.481,1.071,1.071
v7.252l-12.407,2.646c-0.57,0.146-1.52,0.293-2.107,0.326L0.917,49.11z"/&gt;
&lt;linearGradient id="SVGID_7_" gradientUnits="userSpaceOnUse" x1="644.3887" y1="2040.6665" x2="631.6719" y2="2040.6665" gradientTransform="matrix(1 0 0 1 -585.5996 -1982.4023)"&gt;
&lt;stop offset="0" style="stop-color:#4D4D4D"/&gt;
&lt;stop offset="1" style="stop-color:#999999"/&gt;
&lt;/linearGradient&gt;
&lt;path fill="url(#SVGID_7_)" d="M58.706,57.452c0,0.518-3.621,0.752-6.627,0.752c-2.084,0-5.848-0.186-6.09-0.678
c0,0.237,0,0.805,0,0.889c0,0.389,2.572,0.662,6.084,0.662s6.633-0.344,6.633-0.729C58.706,58.263,58.706,57.7,58.706,57.452z"/&gt;
&lt;path fill="#808080" d="M52.347,56.805c3.512,0,6.357,0.313,6.357,0.699s-2.847,0.762-6.357,0.762c-3.59,0-6.357-0.373-6.357-0.762
C45.989,57.118,48.837,56.805,52.347,56.805z"/&gt;
&lt;linearGradient id="SVGID_8_" gradientUnits="userSpaceOnUse" x1="639.333" y1="2037.4683" x2="636.6553" y2="2037.4683" gradientTransform="matrix(1 0 0 1 -585.5996 -1982.4023)"&gt;
&lt;stop offset="0" style="stop-color:#999999"/&gt;
&lt;stop offset="0.0417" style="stop-color:#8D8D8D"/&gt;
&lt;stop offset="0.1617" style="stop-color:#717171"/&gt;
&lt;stop offset="0.2821" style="stop-color:#5D5D5D"/&gt;
&lt;stop offset="0.4021" style="stop-color:#515151"/&gt;
&lt;stop offset="0.5212" style="stop-color:#4D4D4D"/&gt;
&lt;stop offset="0.6202" style="stop-color:#565656"/&gt;
&lt;stop offset="0.7817" style="stop-color:#6E6E6E"/&gt;
&lt;stop offset="0.9844" style="stop-color:#969696"/&gt;
&lt;stop offset="1" style="stop-color:#999999"/&gt;
&lt;/linearGradient&gt;
&lt;path fill="url(#SVGID_8_)" d="M53.716,57.657c0,0-0.791,0.052-1.34,0.052c-0.547,0-1.338-0.052-1.338-0.052v-5.232h2.678V57.657z"
/&gt;
&lt;radialGradient id="SVGID_9_" cx="498.5898" cy="2023.4487" r="12.8975" gradientTransform="matrix(1.15 0 0 1 -526.6041 -1982.4023)" gradientUnits="userSpaceOnUse"&gt;
&lt;stop offset="0" style="stop-color:#F2F2F2"/&gt;
&lt;stop offset="1" style="stop-color:#666666"/&gt;
&lt;/radialGradient&gt;
&lt;path fill="url(#SVGID_9_)" d="M38.566,36.887c0-0.59,0.481-1.072,1.071-1.072h25.844c0.589,0,1.07,0.482,1.07,1.072v16.107
c0,0.59-0.481,1.072-1.07,1.072H39.638c-0.59,0-1.071-0.482-1.071-1.072V36.887z"/&gt;
&lt;path fill="none" stroke="#666666" stroke-width="0.1305" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="
M38.566,36.887c0-0.59,0.481-1.072,1.071-1.072h25.844c0.589,0,1.07,0.482,1.07,1.072v16.107c0,0.59-0.481,1.072-1.07,1.072H39.638
c-0.59,0-1.071-0.482-1.071-1.072V36.887z"/&gt;
&lt;radialGradient id="SVGID_10_" cx="471.3896" cy="2019.0845" r="28.5697" fx="493.8652" fy="2015.2343" gradientTransform="matrix(1.1935 0 0 1 -509.6013 -1982.4023)" gradientUnits="userSpaceOnUse"&gt;
&lt;stop offset="0" style="stop-color:#4D4D4D"/&gt;
&lt;stop offset="1" style="stop-color:#999999"/&gt;
&lt;/radialGradient&gt;
&lt;path fill="url(#SVGID_10_)" d="M39.114,37.434c0-0.59,0.482-1.072,1.071-1.072h24.87c0.589,0,1.07,0.482,1.07,1.072v14.895
c0,0.589-0.481,1.07-1.07,1.07h-24.87c-0.589,0-1.071-0.481-1.071-1.07V37.434z"/&gt;
&lt;radialGradient id="SVGID_11_" cx="472.334" cy="2019.1294" r="18.3139" gradientTransform="matrix(1.1923 0 0 1 -510.0601 -1982.4023)" gradientUnits="userSpaceOnUse"&gt;
&lt;stop offset="0" style="stop-color:#9CD7FF"/&gt;
&lt;stop offset="1" style="stop-color:#3C89C9"/&gt;
&lt;/radialGradient&gt;
&lt;path fill="url(#SVGID_11_)" d="M39.419,37.678c0-0.59,0.481-1.072,1.07-1.072h24.264c0.588,0,1.07,0.482,1.07,1.072v14.406
c0,0.588-0.482,1.07-1.07,1.07H40.489c-0.589,0-1.07-0.482-1.07-1.07V37.678z"/&gt;
&lt;path opacity="0.24" fill="#F2F2F2" d="M39.419,49.108v-11.43c0-0.59,0.481-1.072,1.07-1.072h24.264c0.588,0,1.07,0.482,1.07,1.072
v7.252l-12.408,2.645c-0.57,0.146-1.52,0.295-2.106,0.326L39.419,49.108z"/&gt;
&lt;linearGradient id="SVGID_12_" gradientUnits="userSpaceOnUse" x1="624.8936" y1="2004.9155" x2="612.1787" y2="2004.9155" gradientTransform="matrix(1 0 0 1 -585.5996 -1982.4023)"&gt;
&lt;stop offset="0" style="stop-color:#4D4D4D"/&gt;
&lt;stop offset="1" style="stop-color:#999999"/&gt;
&lt;/linearGradient&gt;
&lt;path fill="url(#SVGID_12_)" d="M39.212,21.701c0,0.518-3.621,0.752-6.626,0.752c-2.083,0-5.847-0.186-6.089-0.678
c0,0.238,0,0.805,0,0.889c0,0.389,2.573,0.662,6.084,0.662c3.51,0,6.631-0.344,6.631-0.729
C39.212,22.513,39.212,21.949,39.212,21.701z"/&gt;
&lt;path fill="#808080" d="M32.854,21.055c3.511,0,6.358,0.313,6.358,0.699c0,0.386-2.848,0.762-6.358,0.762
c-3.589,0-6.358-0.374-6.358-0.762C26.496,21.367,29.342,21.055,32.854,21.055z"/&gt;
&lt;linearGradient id="SVGID_13_" gradientUnits="userSpaceOnUse" x1="619.8379" y1="2001.7183" x2="617.1611" y2="2001.7183" gradientTransform="matrix(1 0 0 1 -585.5996 -1982.4023)"&gt;
&lt;stop offset="0" style="stop-color:#999999"/&gt;
&lt;stop offset="0.0417" style="stop-color:#8D8D8D"/&gt;
&lt;stop offset="0.1617" style="stop-color:#717171"/&gt;
&lt;stop offset="0.2821" style="stop-color:#5D5D5D"/&gt;
&lt;stop offset="0.4021" style="stop-color:#515151"/&gt;
&lt;stop offset="0.5212" style="stop-color:#4D4D4D"/&gt;
&lt;stop offset="0.6202" style="stop-color:#565656"/&gt;
&lt;stop offset="0.7817" style="stop-color:#6E6E6E"/&gt;
&lt;stop offset="0.9844" style="stop-color:#969696"/&gt;
&lt;stop offset="1" style="stop-color:#999999"/&gt;
&lt;/linearGradient&gt;
&lt;path fill="url(#SVGID_13_)" d="M34.222,21.906c0,0-0.791,0.052-1.338,0.052c-0.547,0-1.338-0.052-1.338-0.052v-5.232h2.677
L34.222,21.906L34.222,21.906z"/&gt;
&lt;radialGradient id="SVGID_14_" cx="481.6387" cy="1987.6978" r="12.8975" gradientTransform="matrix(1.15 0 0 1 -526.6041 -1982.4023)" gradientUnits="userSpaceOnUse"&gt;
&lt;stop offset="0" style="stop-color:#F2F2F2"/&gt;
&lt;stop offset="1" style="stop-color:#666666"/&gt;
&lt;/radialGradient&gt;
&lt;path fill="url(#SVGID_14_)" d="M19.072,1.137c0-0.59,0.482-1.072,1.071-1.072h25.843c0.589,0,1.071,0.482,1.071,1.072v16.108
c0,0.589-0.482,1.071-1.071,1.071H20.145c-0.589,0-1.071-0.482-1.071-1.071L19.072,1.137L19.072,1.137z"/&gt;
&lt;path fill="none" stroke="#666666" stroke-width="0.1305" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="
M19.072,1.137c0-0.59,0.482-1.072,1.071-1.072h25.843c0.589,0,1.071,0.482,1.071,1.072v16.108c0,0.589-0.482,1.071-1.071,1.071
H20.145c-0.589,0-1.071-0.482-1.071-1.071L19.072,1.137L19.072,1.137z"/&gt;
&lt;radialGradient id="SVGID_15_" cx="455.0566" cy="1983.3345" r="28.5689" fx="477.5316" fy="1979.4844" gradientTransform="matrix(1.1935 0 0 1 -509.6013 -1982.4023)" gradientUnits="userSpaceOnUse"&gt;
&lt;stop offset="0" style="stop-color:#4D4D4D"/&gt;
&lt;stop offset="1" style="stop-color:#999999"/&gt;
&lt;/radialGradient&gt;
&lt;path fill="url(#SVGID_15_)" d="M19.621,1.685c0-0.59,0.482-1.072,1.072-1.072h24.87c0.589,0,1.071,0.482,1.071,1.072v14.894
c0,0.589-0.482,1.071-1.071,1.071h-24.87c-0.589,0-1.072-0.482-1.072-1.071V1.685z"/&gt;
&lt;radialGradient id="SVGID_16_" cx="455.9854" cy="1983.3784" r="18.3134" gradientTransform="matrix(1.1923 0 0 1 -510.0601 -1982.4023)" gradientUnits="userSpaceOnUse"&gt;
&lt;stop offset="0" style="stop-color:#9CD7FF"/&gt;
&lt;stop offset="1" style="stop-color:#3C89C9"/&gt;
&lt;/radialGradient&gt;
&lt;path fill="url(#SVGID_16_)" d="M19.924,1.928c0-0.59,0.482-1.072,1.072-1.072h24.262c0.589,0,1.07,0.482,1.07,1.072v14.406
c0,0.588-0.481,1.07-1.07,1.07H20.997c-0.589,0-1.072-0.482-1.072-1.07V1.928z"/&gt;
&lt;path opacity="0.24" fill="#F2F2F2" d="M19.924,13.358V1.928c0-0.59,0.482-1.072,1.072-1.072h24.262c0.589,0,1.07,0.482,1.07,1.072
V9.18l-12.408,2.646c-0.569,0.146-1.519,0.294-2.106,0.326L19.924,13.358z"/&gt;
&lt;/svg&gt;
</y:Resource>
<y:Resource id="2">&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="57px" height="65px" viewBox="0 0 57 65" enable-background="new 0 0 57 65" xml:space="preserve"&gt;
&lt;g&gt;
@@ -871,7 +978,7 @@ Vulnerability data<y:LabelModel>
&lt;/g&gt;
&lt;/svg&gt;
</y:Resource>
<y:Resource id="3">&lt;?xml version="1.0" encoding="UTF-8" standalone="no"?&gt;
<y:Resource id="2">&lt;?xml version="1.0" encoding="UTF-8" standalone="no"?&gt;
&lt;svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 73 KiB

10
main.go
View File

@@ -19,6 +19,7 @@ package main
import (
"flag"
"fmt"
"os"
"golang.org/x/net/context"
@@ -37,8 +38,17 @@ func main() {
subcommands.Register(&commands.TuiCmd{}, "tui")
subcommands.Register(&commands.ScanCmd{}, "scan")
subcommands.Register(&commands.PrepareCmd{}, "prepare")
subcommands.Register(&commands.HistoryCmd{}, "history")
var version = flag.Bool("v", false, "Show version")
flag.Parse()
if *version {
fmt.Printf("%s %s\n", Name, Version)
os.Exit(int(subcommands.ExitSuccess))
}
ctx := context.Background()
os.Exit(int(subcommands.Execute(ctx)))
}

View File

@@ -30,16 +30,34 @@ import (
// ScanHistory is the history of Scanning.
type ScanHistory struct {
gorm.Model
ScanResults []ScanResult
ScanResults ScanResults
ScannedAt time.Time
}
// ScanResults is slice of ScanResult.
type ScanResults []ScanResult
// Len implement Sort Interface
func (s ScanResults) Len() int {
return len(s)
}
// Swap implement Sort Interface
func (s ScanResults) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
// Less implement Sort Interface
func (s ScanResults) Less(i, j int) bool {
if s[i].ServerName == s[j].ServerName {
return s[i].Container.ContainerID < s[i].Container.ContainerID
}
return s[i].ServerName < s[j].ServerName
}
// FilterByCvssOver is filter function.
func (results ScanResults) FilterByCvssOver() (filtered ScanResults) {
for _, result := range results {
func (s ScanResults) FilterByCvssOver() (filtered ScanResults) {
for _, result := range s {
cveInfos := []CveInfo{}
for _, cveInfo := range result.KnownCves {
if config.Conf.CvssScoreOver < cveInfo.CveDetail.CvssScore(config.Conf.Lang) {
@@ -61,12 +79,60 @@ type ScanResult struct {
// Hostname string
Family string
Release string
Container Container
// Fqdn string
// NWLinks []NWLink
KnownCves []CveInfo
UnknownCves []CveInfo
}
// ServerInfo returns server name one line
func (r ScanResult) ServerInfo() string {
hostinfo := ""
if r.Container.ContainerID == "" {
hostinfo = fmt.Sprintf(
"%s (%s%s)",
r.ServerName,
r.Family,
r.Release,
)
} else {
hostinfo = fmt.Sprintf(
"%s / %s (%s%s) on %s",
r.Container.Name,
r.Container.ContainerID,
r.Family,
r.Release,
r.ServerName,
)
}
return hostinfo
}
// ServerInfoTui returns server infromation for TUI sidebar
func (r ScanResult) ServerInfoTui() string {
hostinfo := ""
if r.Container.ContainerID == "" {
hostinfo = fmt.Sprintf(
"%s (%s%s)",
r.ServerName,
r.Family,
r.Release,
)
} else {
hostinfo = fmt.Sprintf(
"|-- %s (%s%s)",
r.Container.Name,
r.Family,
r.Release,
// r.Container.ContainerID,
)
}
return hostinfo
}
// CveSummary summarize the number of CVEs group by CVSSv2 Severity
func (r ScanResult) CveSummary() string {
var high, middle, low, unknown int
@@ -84,10 +150,13 @@ func (r ScanResult) CveSummary() string {
unknown++
}
}
if config.Conf.IgnoreUnscoredCves {
return fmt.Sprintf("Total: %d (High:%d Middle:%d Low:%d)",
high+middle+low, high, middle, low)
}
return fmt.Sprintf("Total: %d (High:%d Middle:%d Low:%d ?:%d)",
high+middle+low+unknown,
high, middle, low, unknown,
)
high+middle+low+unknown, high, middle, low, unknown)
}
// NWLink has network link information.
@@ -232,7 +301,6 @@ func (p PackageInfo) ToStringNewVersion() string {
}
// DistroAdvisory has Amazon Linux AMI Security Advisory information.
//TODO Rename to DistroAdvisory
type DistroAdvisory struct {
gorm.Model
CveInfoID uint
@@ -242,3 +310,12 @@ type DistroAdvisory struct {
Issued time.Time
Updated time.Time
}
// Container has Container information
type Container struct {
gorm.Model
ScanResultID uint
ContainerID string
Name string
}

View File

@@ -19,6 +19,8 @@ package report
import (
"os"
"path/filepath"
"runtime"
"github.com/Sirupsen/logrus"
"github.com/future-architect/vuls/models"
@@ -31,6 +33,9 @@ type LogrusWriter struct {
func (w LogrusWriter) Write(scanResults []models.ScanResult) error {
path := "/var/log/vuls/report.log"
if runtime.GOOS == "windows" {
path = filepath.Join(os.Getenv("APPDATA"), "vuls", "report.log")
}
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
return err

View File

@@ -40,7 +40,7 @@ func (w MailWriter) Write(scanResults []models.ScanResult) (err error) {
subject := fmt.Sprintf("%s%s %s",
conf.Mail.SubjectPrefix,
s.ServerName,
s.ServerInfo(),
s.CveSummary(),
)
m.SetHeader("Subject", subject)

View File

@@ -103,18 +103,18 @@ func msgText(r models.ScanResult) string {
notifyUsers = getNotifyUsers(config.Conf.Slack.NotifyUsers)
}
hostinfo := fmt.Sprintf(
"*%s* (%s %s)",
r.ServerName,
r.Family,
r.Release,
)
return fmt.Sprintf("%s\n%s\n>%s", notifyUsers, hostinfo, r.CveSummary())
serverInfo := fmt.Sprintf("*%s*", r.ServerInfo())
return fmt.Sprintf("%s\n%s\n>%s", notifyUsers, serverInfo, r.CveSummary())
}
func toSlackAttachments(scanResult models.ScanResult) (attaches []*attachment) {
scanResult.KnownCves = append(scanResult.KnownCves, scanResult.UnknownCves...)
cves := scanResult.KnownCves
if !config.Conf.IgnoreUnscoredCves {
cves = append(cves, scanResult.UnknownCves...)
}
scanResult.KnownCves = cves
for _, cveInfo := range scanResult.KnownCves {
cveID := cveInfo.CveDetail.CveID

View File

@@ -40,9 +40,9 @@ var currentCveInfo int
var currentDetailLimitY int
// RunTui execute main logic
func RunTui() subcommands.ExitStatus {
func RunTui(historyID string) subcommands.ExitStatus {
var err error
scanHistory, err = latestScanHistory()
scanHistory, err = selectScanHistory(historyID)
if err != nil {
log.Fatal(err)
return subcommands.ExitFailure
@@ -70,12 +70,12 @@ func RunTui() subcommands.ExitStatus {
return subcommands.ExitSuccess
}
func latestScanHistory() (latest models.ScanHistory, err error) {
func selectScanHistory(historyID string) (latest models.ScanHistory, err error) {
if err := db.OpenDB(); err != nil {
return latest, fmt.Errorf(
"Failed to open DB. datafile: %s, err: %s", config.Conf.DBPath, err)
}
latest, err = db.SelectLatestScanHistory()
latest, err = db.SelectScanHistory(historyID)
return
}
@@ -410,7 +410,7 @@ func changeHost(g *gocui.Gui, v *gocui.View) error {
serverName := strings.TrimSpace(l)
for _, r := range scanHistory.ScanResults {
if serverName == r.ServerName {
if serverName == strings.TrimSpace(r.ServerInfoTui()) {
currentScanResult = r
break
}
@@ -509,14 +509,14 @@ func layout(g *gocui.Gui) error {
func setSideLayout(g *gocui.Gui) error {
_, maxY := g.Size()
if v, err := g.SetView("side", -1, -1, 30, maxY); err != nil {
if v, err := g.SetView("side", -1, -1, 40, maxY); err != nil {
if err != gocui.ErrUnknownView {
return err
}
v.Highlight = true
for _, result := range scanHistory.ScanResults {
fmt.Fprintln(v, result.ServerName)
fmt.Fprintln(v, result.ServerInfoTui())
}
currentScanResult = scanHistory.ScanResults[0]
if err := g.SetCurrentView("side"); err != nil {
@@ -528,7 +528,7 @@ func setSideLayout(g *gocui.Gui) error {
func setSummaryLayout(g *gocui.Gui) error {
maxX, maxY := g.Size()
if v, err := g.SetView("summary", 30, -1, maxX, int(float64(maxY)*0.2)); err != nil {
if v, err := g.SetView("summary", 40, -1, maxX, int(float64(maxY)*0.2)); err != nil {
if err != gocui.ErrUnknownView {
return err
}
@@ -616,7 +616,7 @@ func setDetailLayout(g *gocui.Gui) error {
_, oy := summaryView.Origin()
currentCveInfo = cy + oy
if v, err := g.SetView("detail", 30, int(float64(maxY)*0.2), maxX, maxY); err != nil {
if v, err := g.SetView("detail", 40, int(float64(maxY)*0.2), maxX, maxY); err != nil {
if err != gocui.ErrUnknownView {
return err
}

View File

@@ -28,18 +28,13 @@ import (
)
func toPlainText(scanResult models.ScanResult) (string, error) {
hostinfo := fmt.Sprintf(
"%s (%s %s)",
scanResult.ServerName,
scanResult.Family,
scanResult.Release,
)
serverInfo := scanResult.ServerInfo()
var buffer bytes.Buffer
for i := 0; i < len(hostinfo); i++ {
for i := 0; i < len(serverInfo); i++ {
buffer.WriteString("=")
}
header := fmt.Sprintf("%s\n%s", hostinfo, buffer.String())
header := fmt.Sprintf("%s\n%s", serverInfo, buffer.String())
if len(scanResult.KnownCves) == 0 && len(scanResult.UnknownCves) == 0 {
return fmt.Sprintf(`
@@ -53,7 +48,12 @@ No unsecure packages.
scoredReport, unscoredReport = toPlainTextDetails(scanResult, scanResult.Family)
scored := strings.Join(scoredReport, "\n\n")
unscored := strings.Join(unscoredReport, "\n\n")
unscored := ""
if !config.Conf.IgnoreUnscoredCves {
unscored = strings.Join(unscoredReport, "\n\n")
}
detail := fmt.Sprintf(`
%s
@@ -72,7 +72,12 @@ func ToPlainTextSummary(r models.ScanResult) string {
stable := uitable.New()
stable.MaxColWidth = 84
stable.Wrap = true
cves := append(r.KnownCves, r.UnknownCves...)
cves := r.KnownCves
if !config.Conf.IgnoreUnscoredCves {
cves = append(cves, r.UnknownCves...)
}
for _, d := range cves {
var scols []string

View File

@@ -226,7 +226,7 @@ func (o *debian) parseScanedPackagesLine(line string) (name, version string, err
func (o *debian) checkRequiredPackagesInstalled() error {
if o.Family == "debian" {
if r := o.ssh("test -f /usr/bin/aptitude", sudo); !r.isSuccess() {
if r := o.ssh("test -f /usr/bin/aptitude", noSudo); !r.isSuccess() {
msg := "aptitude is not installed"
o.log.Errorf(msg)
return fmt.Errorf(msg)

View File

@@ -20,6 +20,7 @@ package scan
import (
"fmt"
"sort"
"strings"
"github.com/Sirupsen/logrus"
"github.com/future-architect/vuls/config"
@@ -34,6 +35,8 @@ type linux struct {
Release string
osPackages
log *logrus.Entry
errs []error
}
func (l *linux) ssh(cmd string, sudo bool) sshResult {
@@ -44,7 +47,7 @@ func (l *linux) setServerInfo(c config.ServerInfo) {
l.ServerInfo = c
}
func (l *linux) getServerInfo() config.ServerInfo {
func (l linux) getServerInfo() config.ServerInfo {
return l.ServerInfo
}
@@ -57,6 +60,77 @@ func (l *linux) getDistributionInfo() string {
return fmt.Sprintf("%s %s", l.Family, l.Release)
}
func (l linux) allContainers() (containers []config.Container, err error) {
switch l.ServerInfo.Container.Type {
case "", "docker":
stdout, err := l.dockerPs("-a --format '{{.ID}} {{.Names}}'")
if err != nil {
return containers, err
}
return l.parseDockerPs(stdout)
default:
return containers, fmt.Errorf(
"Not supported yet: %s", l.ServerInfo.Container.Type)
}
}
func (l *linux) runningContainers() (containers []config.Container, err error) {
switch l.ServerInfo.Container.Type {
case "", "docker":
stdout, err := l.dockerPs("--format '{{.ID}} {{.Names}}'")
if err != nil {
return containers, err
}
return l.parseDockerPs(stdout)
default:
return containers, fmt.Errorf(
"Not supported yet: %s", l.ServerInfo.Container.Type)
}
}
func (l *linux) exitedContainers() (containers []config.Container, err error) {
switch l.ServerInfo.Container.Type {
case "", "docker":
stdout, err := l.dockerPs("--filter 'status=exited' --format '{{.ID}} {{.Names}}'")
if err != nil {
return containers, err
}
return l.parseDockerPs(stdout)
default:
return containers, fmt.Errorf(
"Not supported yet: %s", l.ServerInfo.Container.Type)
}
}
func (l *linux) dockerPs(option string) (string, error) {
cmd := fmt.Sprintf("docker ps %s", option)
r := l.ssh(cmd, noSudo)
if !r.isSuccess() {
return "", fmt.Errorf(
"Failed to %s. status: %d, stdout: %s, stderr: %s",
cmd, r.ExitStatus, r.Stdout, r.Stderr)
}
return r.Stdout, nil
}
func (l *linux) parseDockerPs(stdout string) (containers []config.Container, err error) {
lines := strings.Split(stdout, "\n")
for _, line := range lines {
fields := strings.Fields(line)
if len(fields) == 0 {
break
}
if len(fields) != 2 {
return containers, fmt.Errorf("Unknown format: %s", line)
}
containers = append(containers, config.Container{
ContainerID: fields[0],
Name: fields[1],
})
}
return
}
func (l *linux) convertToModel() (models.ScanResult, error) {
var cves, unknownScoreCves []models.CveInfo
for _, p := range l.UnsecurePackages {
@@ -84,10 +158,16 @@ func (l *linux) convertToModel() (models.ScanResult, error) {
cves = append(cves, cve)
}
container := models.Container{
ContainerID: l.ServerInfo.Container.ContainerID,
Name: l.ServerInfo.Container.Name,
}
return models.ScanResult{
ServerName: l.ServerInfo.ServerName,
Family: l.Family,
Release: l.Release,
Container: container,
KnownCves: cves,
UnknownCves: unknownScoreCves,
}, nil
@@ -132,3 +212,11 @@ func (l *linux) scanVulnByCpeName() error {
l.setUnsecurePackages(unsecurePacks)
return nil
}
func (l *linux) setErrs(errs []error) {
l.errs = errs
}
func (l linux) getErrs() []error {
return l.errs
}

View File

@@ -16,3 +16,43 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package scan
import (
"reflect"
"testing"
"github.com/future-architect/vuls/config"
)
func TestParseDockerPs(t *testing.T) {
var test = struct {
in string
expected []config.Container
}{
`c7ca0992415a romantic_goldberg
f570ae647edc agitated_lovelace`,
[]config.Container{
{
ContainerID: "c7ca0992415a",
Name: "romantic_goldberg",
},
{
ContainerID: "f570ae647edc",
Name: "agitated_lovelace",
},
},
}
r := newRedhat(config.ServerInfo{})
actual, err := r.parseDockerPs(test.in)
if err != nil {
t.Errorf("Error occurred. in: %s, err: %s", test.in, err)
return
}
for i, e := range test.expected {
if !reflect.DeepEqual(e, actual[i]) {
t.Errorf("expected %v, actual %v", e, actual[i])
}
}
}

View File

@@ -287,7 +287,7 @@ func (o *redhat) scanUnsecurePackages() ([]CvePacksInfo, error) {
//TODO return whether already expired.
func (o *redhat) scanUnsecurePackagesUsingYumCheckUpdate() (CvePacksList, error) {
cmd := "yum check-update"
cmd := "LANG=en_US.UTF-8 yum --color=never check-update"
r := o.ssh(util.PrependProxyEnv(cmd), sudo)
if !r.isSuccess(0, 100) {
//returns an exit code of 100 if there are available updates.
@@ -462,11 +462,13 @@ func (o *redhat) parseYumCheckUpdateLine(line string) (models.PackageInfo, error
func (o *redhat) getChangelog(packageNames string) (stdout string, err error) {
command := ""
if o.ServerInfo.User == "root" {
command = "yes no | "
command = "echo N | "
}
if 0 < len(config.Conf.HTTPProxy) {
command += util.ProxyEnv()
}
// yum update --changelog doesn't have --color option.
command += fmt.Sprintf(" yum update --changelog %s | grep CVE", packageNames)
r := o.ssh(command, sudo)
@@ -493,7 +495,7 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (CvePacksList, err
"yum updateinfo is not suppported on CentOS")
}
cmd := "yum repolist"
cmd := "yum --color=never repolist"
r := o.ssh(util.PrependProxyEnv(cmd), sudo)
if !r.isSuccess() {
return nil, fmt.Errorf(
@@ -502,7 +504,7 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (CvePacksList, err
}
// get advisoryID(RHSA, ALAS) - package name,version
cmd = "yum updateinfo list available --security"
cmd = "yum --color=never updateinfo list available --security"
r = o.ssh(util.PrependProxyEnv(cmd), sudo)
if !r.isSuccess() {
return nil, fmt.Errorf(
@@ -513,7 +515,7 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (CvePacksList, err
// get package name, version, rel to be upgrade.
// cmd = "yum check-update --security"
cmd = "yum check-update"
cmd = "LANG=en_US.UTF-8 yum --color=never check-update"
r = o.ssh(util.PrependProxyEnv(cmd), sudo)
if !r.isSuccess(0, 100) {
//returns an exit code of 100 if there are available updates.
@@ -543,7 +545,7 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (CvePacksList, err
}
// get advisoryID(RHSA, ALAS) - CVE IDs
cmd = "yum updateinfo --security update"
cmd = "yum --color=never updateinfo --security update"
r = o.ssh(util.PrependProxyEnv(cmd), sudo)
if !r.isSuccess() {
return nil, fmt.Errorf(
@@ -859,3 +861,7 @@ func (o *redhat) parseYumUpdateinfoListAvailable(stdout string) (advisoryIDPacks
}
return result, nil
}
func (o *redhat) clone() osTypeInterface {
return o
}

View File

@@ -26,6 +26,13 @@ type osTypeInterface interface {
scanVulnByCpeName() error
install() error
convertToModel() (models.ScanResult, error)
runningContainers() ([]config.Container, error)
exitedContainers() ([]config.Container, error)
allContainers() ([]config.Container, error)
getErrs() []error
setErrs([]error)
}
// osPackages included by linux struct
@@ -94,49 +101,68 @@ func (s CvePacksList) Less(i, j int) bool {
return s[i].CveDetail.CvssScore("en") > s[j].CveDetail.CvssScore("en")
}
func detectOs(c config.ServerInfo) (osType osTypeInterface) {
func detectOS(c config.ServerInfo) (osType osTypeInterface) {
var itsMe bool
itsMe, osType = detectDebian(c)
if itsMe {
return
}
itsMe, osType = detectRedhat(c)
if itsMe {
return
}
osType.setErrs([]error{fmt.Errorf("Unknown OS Type")})
return
}
// InitServers detect the kind of OS distribution of target servers
func InitServers(localLogger *logrus.Entry) (err error) {
func InitServers(localLogger *logrus.Entry) error {
Log = localLogger
if servers, err = detectServersOS(); err != nil {
err = fmt.Errorf("Failed to detect the type of OS. err: %s", err)
hosts, err := detectServerOSes()
if err != nil {
return fmt.Errorf("Failed to detect server OSes. err: %s", err)
}
return
servers = hosts
Log.Info("Detecting Container OS...")
containers, err := detectContainerOSes()
if err != nil {
return fmt.Errorf("Failed to detect Container OSes. err: %s", err)
}
servers = append(servers, containers...)
return nil
}
func detectServersOS() (osi []osTypeInterface, err error) {
func detectServerOSes() (oses []osTypeInterface, err error) {
osTypeChan := make(chan osTypeInterface, len(config.Conf.Servers))
defer close(osTypeChan)
for _, s := range config.Conf.Servers {
go func(s config.ServerInfo) {
osTypeChan <- detectOs(s)
//TODO handling Unknown OS
osTypeChan <- detectOS(s)
}(s)
}
timeout := time.After(60 * time.Second)
timeout := time.After(300 * time.Second)
for i := 0; i < len(config.Conf.Servers); i++ {
select {
case res := <-osTypeChan:
Log.Infof("(%d/%d) Successfully detected. %s: %s",
if 0 < len(res.getErrs()) {
continue
}
Log.Infof("(%d/%d) Detected %s: %s",
i+1, len(config.Conf.Servers),
res.getServerInfo().ServerName,
res.getDistributionInfo())
osi = append(osi, res)
oses = append(oses, res)
case <-timeout:
Log.Error("Timeout occured while detecting")
err = fmt.Errorf("Timeout")
msg := "Timeout occurred while detecting"
Log.Error(msg)
for servername := range config.Conf.Servers {
found := false
for _, o := range osi {
for _, o := range oses {
if servername == o.getServerInfo().ServerName {
found = true
break
@@ -146,12 +172,158 @@ func detectServersOS() (osi []osTypeInterface, err error) {
Log.Errorf("Failed to detect. servername: %s", servername)
}
}
return
return oses, fmt.Errorf(msg)
}
}
errorOccurred := false
for _, osi := range oses {
if errs := osi.getErrs(); 0 < len(errs) {
errorOccurred = true
Log.Errorf("Some errors occurred on %s",
osi.getServerInfo().ServerName)
for _, err := range errs {
Log.Error(err)
}
}
}
if errorOccurred {
return oses, fmt.Errorf("Some errors occurred")
}
return
}
func detectContainerOSes() (oses []osTypeInterface, err error) {
osTypesChan := make(chan []osTypeInterface, len(servers))
defer close(osTypesChan)
for _, s := range servers {
go func(s osTypeInterface) {
osTypesChan <- detectContainerOSesOnServer(s)
}(s)
}
timeout := time.After(300 * time.Second)
for i := 0; i < len(config.Conf.Servers); i++ {
select {
case res := <-osTypesChan:
for _, osi := range res {
if 0 < len(osi.getErrs()) {
continue
}
sinfo := osi.getServerInfo()
Log.Infof("Detected %s/%s on %s: %s",
sinfo.Container.ContainerID, sinfo.Container.Name,
sinfo.ServerName, osi.getDistributionInfo())
}
oses = append(oses, res...)
case <-timeout:
msg := "Timeout occurred while detecting"
Log.Error(msg)
for servername := range config.Conf.Servers {
found := false
for _, o := range oses {
if servername == o.getServerInfo().ServerName {
found = true
break
}
}
if !found {
Log.Errorf("Failed to detect. servername: %s", servername)
}
}
return oses, fmt.Errorf(msg)
}
}
errorOccurred := false
for _, osi := range oses {
if errs := osi.getErrs(); 0 < len(errs) {
errorOccurred = true
Log.Errorf("Some errors occurred on %s",
osi.getServerInfo().ServerName)
for _, err := range errs {
Log.Error(err)
}
}
}
if errorOccurred {
return oses, fmt.Errorf("Some errors occurred")
}
return
}
func detectContainerOSesOnServer(containerHost osTypeInterface) (oses []osTypeInterface) {
containerHostInfo := containerHost.getServerInfo()
if len(containerHostInfo.Containers) == 0 {
return
}
running, err := containerHost.runningContainers()
if err != nil {
containerHost.setErrs([]error{fmt.Errorf(
"Failed to get running containers on %s. err: %s",
containerHost.getServerInfo().ServerName, err)})
return append(oses, containerHost)
}
if containerHostInfo.Containers[0] == "${running}" {
for _, containerInfo := range running {
copied := containerHostInfo
copied.SetContainer(config.Container{
ContainerID: containerInfo.ContainerID,
Name: containerInfo.Name,
})
os := detectOS(copied)
oses = append(oses, os)
}
return oses
}
exitedContainers, err := containerHost.exitedContainers()
if err != nil {
containerHost.setErrs([]error{fmt.Errorf(
"Failed to get exited containers on %s. err: %s",
containerHost.getServerInfo().ServerName, err)})
return append(oses, containerHost)
}
var exited, unknown []string
for _, container := range containerHostInfo.Containers {
found := false
for _, c := range running {
if c.ContainerID == container || c.Name == container {
copied := containerHostInfo
copied.SetContainer(c)
os := detectOS(copied)
oses = append(oses, os)
found = true
break
}
}
if !found {
foundInExitedContainers := false
for _, c := range exitedContainers {
if c.ContainerID == container || c.Name == container {
exited = append(exited, container)
foundInExitedContainers = true
break
}
}
if !foundInExitedContainers {
unknown = append(unknown, container)
}
}
}
if 0 < len(exited) || 0 < len(unknown) {
containerHost.setErrs([]error{fmt.Errorf(
"Some containers on %s are exited or unknown. exited: %s, unknown: %s",
containerHost.getServerInfo().ServerName, exited, unknown)})
return append(oses, containerHost)
}
return oses
}
// Prepare installs requred packages to scan vulnerabilities.
func Prepare() []error {
return parallelSSHExec(func(o osTypeInterface) error {

View File

@@ -122,9 +122,9 @@ func sshExec(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (re
} else {
logger = log[0]
}
c.SudoOpt.ExecBySudo = true
var err error
if sudo && c.User != "root" {
if sudo && c.User != "root" && !c.IsContainer() {
switch {
case c.SudoOpt.ExecBySudo:
cmd = fmt.Sprintf("echo %s | sudo -S %s", c.Password, cmd)
@@ -140,6 +140,13 @@ func sshExec(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (re
logger.Debugf("Command: %s",
strings.Replace(maskPassword(cmd, c.Password), "\n", "", -1))
if c.IsContainer() {
switch c.Container.Type {
case "", "docker":
cmd = fmt.Sprintf(`docker exec %s /bin/bash -c "%s"`, c.Container.ContainerID, cmd)
}
}
var client *ssh.Client
client, err = sshConnect(c)
defer client.Close()

View File

@@ -20,6 +20,8 @@ package util
import (
"fmt"
"os"
"path/filepath"
"runtime"
"github.com/Sirupsen/logrus"
"github.com/rifflock/lfshook"
@@ -40,6 +42,9 @@ func NewCustomLogger(c config.ServerInfo) *logrus.Entry {
// File output
logDir := "/var/log/vuls"
if runtime.GOOS == "windows" {
logDir = filepath.Join(os.Getenv("APPDATA"), "vuls")
}
if _, err := os.Stat(logDir); os.IsNotExist(err) {
if err := os.Mkdir(logDir, 0666); err != nil {
logrus.Errorf("Failed to create log directory: %s", err)
@@ -48,11 +53,16 @@ func NewCustomLogger(c config.ServerInfo) *logrus.Entry {
whereami := "localhost"
if 0 < len(c.ServerName) {
whereami = fmt.Sprintf("%s:%s", c.ServerName, c.Port)
if 0 < len(c.Container.ContainerID) {
whereami = fmt.Sprintf(
"%s_%s", c.ServerName, c.Container.Name)
} else {
whereami = fmt.Sprintf("%s", c.ServerName)
}
}
if _, err := os.Stat(logDir); err == nil {
path := fmt.Sprintf("%s/%s.log", logDir, whereami)
path := filepath.Join(logDir, whereami)
log.Hooks.Add(lfshook.NewHook(lfshook.PathMap{
logrus.DebugLevel: path,
logrus.InfoLevel: path,

View File

@@ -21,4 +21,4 @@ package main
const Name string = "vuls"
// Version of Vuls
const Version string = "0.1.3"
const Version string = "0.1.4"